How the OpenLogi Actions Ring Overlay Renders Eight-Slot Layouts Relative to the Cursor Position

The OpenLogi Actions Ring overlay calculates eight equally-spaced slot positions using polar coordinate trigonometry relative to the cursor, rendering each slot at a fixed 80-pixel radius with angle offsets starting from -π/2 to place the first slot directly above the pointer.

The Actions Ring is the radial command palette that appears when invoking the overlay (default shortcut ⌥ ⇧ ⌘ F on macOS). According to the AprilNEA/OpenLogi source code, this eight-slot layout is computed in real-time by the openlogi-ui crate, positioning configurable actions in a circular pattern around the current mouse coordinates using the GPUI framework's rendering pipeline.

Architecture of the Actions Ring Overlay

Entry Point and Window Creation

The overlay lifecycle begins in crates/openlogi-overlay/src/main.rs, where the binary initializes the GPUI application and spawns a single OverlayWindow. The run function in this file serves as the application entry point, establishing the rendering context before instantiating the ring widget.

The Ring Widget Structure

The radial layout logic resides in crates/openlogi-ui/src/ring.rs. This file defines the Ring struct, which maintains a Vec<ActionSlot> representing the eight action containers and stores the current cursor_position: PointPx. When the overlay activates, the widget receives the cursor coordinates captured via the openlogi-inject crate's inject::cursor_position() function, establishing the origin point for all subsequent geometric calculations.

Positioning the Eight-Slot Layout Relative to the Cursor

Polar Coordinate Mathematics

The core positioning algorithm lives in the slot_center method (lines 80-95 of ring.rs). This function converts the cursor's screen coordinates into eight peripheral points arranged in a perfect circle:

// crates/openlogi-ui/src/ring.rs
const SLOT_COUNT: usize = 8;
const RADIUS_PX: f32 = 80.0;

fn slot_center(&self, i: usize) -> PointPx {
    let angle = (i as f32) * (std::f32::consts::TAU / SLOT_COUNT as f32) 
                - std::f32::consts::FRAC_PI_2;
    PointPx::new(
        self.cursor_position.x + RADIUS_PX * angle.cos(),
        self.cursor_position.y + RADIUS_PX * angle.sin(),
    )
}

The calculation divides the full circle (TAU, approximately 2π) by eight to create 45-degree spacing between slots. The subtraction of FRAC_PI_2 (π/2) rotates the starting angle counter-clockwise by 90 degrees, ensuring slot 0 appears directly above the cursor at the 12 o'clock position rather than the 3 o'clock position. The cos and sin operations convert these polar coordinates into X and Y screen offsets relative to the cursor's current location.

High-DPI Scale Factor Adjustment

Before performing trigonometric calculations, the Ring::new constructor (lines 58-63) adjusts the base radius for the monitor's pixel density:

// crates/openlogi-ui/src/ring.rs
let radius = base_radius * scale_factor.to_f32();

This multiplication ensures the 80-pixel radius represents the same physical distance on Hi-DPI Retina displays as on standard monitors, maintaining consistent interaction targets across different screen resolutions.

Rendering the Visual Ring

Canvas Drawing Operations

The Ring::paint method (lines 110-124) iterates through the slots and renders each using GPUI's Canvas API. For every slot index, it calculates the center point, inflates a rectangle around that point, and draws a rounded background with the action's icon:

// crates/openlogi-ui/src/ring.rs
for (i, slot) in self.slots.iter().enumerate() {
    let centre = self.slot_center(i);
    let rect = Rect::from_point(centre).inflate(SLOT_SIZE, SLOT_SIZE);
    canvas.draw_rounded_rect(rect, RADIUS, slot.background_color);
    canvas.draw_image(slot.icon, rect.center());
}

The icons themselves are loaded from the openlogi-assets crate, which manages the application's resource catalog.

Real-Time Cursor Tracking

Handling Movement Events

The overlay dynamically follows the cursor while visible. In crates/openlogi-overlay/src/overlay.rs, the OverlayWindow::handle_event method listens for Event::CursorMoved(p) updates:

// crates/openlogi-overlay/src/overlay.rs
match event {
    Event::CursorMoved(p) => {
        ui_state.update_cursor(p);
        ui_state.request_redraw();
    }
    // ...
}

When the cursor moves, this handler updates the cursor_position field in the UI state and triggers a redraw request. The slot_center method recalculates all eight positions relative to the new coordinates, causing the ring to float smoothly with the pointer until dismissed.

Summary

  • Radial geometry: Eight slots are positioned using polar coordinates with a constant 80-pixel radius, dividing the circle into 45-degree segments using TAU / 8.
  • Angular offset: Subtracting FRAC_PI_2 aligns slot 0 directly above the cursor at the 12 o'clock position.
  • DPI awareness: The ScaleFactor from GPUI multiplies the base radius before trigonometric conversion, ensuring consistent physical sizing across displays.
  • Live tracking: Cursor movements captured by the openlogi-inject crate trigger position recalculation and redraw via the OverlayWindow event loop.
  • Modular architecture: Positioning logic resides in openlogi-ui/src/ring.rs, while window management and event handling live in openlogi-overlay/src/overlay.rs.

Frequently Asked Questions

How does the OpenLogi Actions Ring determine the angular distance between slots?

The ring divides the full circular rotation (TAU, approximately 6.283 radians) by the SLOT_COUNT constant (8), yielding 45-degree arcs between adjacent slots. The slot_center method multiplies this base angle by the slot index to determine each position's offset from the vertical starting point.

Why does the first slot appear above the cursor rather than to the right?

The angle calculation subtracts FRAC_PI_2 (π/2 radians or 90 degrees) from the raw angular position. This rotation shifts the starting angle from the positive X-axis (3 o'clock) to the negative Y-axis (12 o'clock), placing slot 0 directly above the cursor as users typically expect from radial menus.

How does the overlay maintain consistent sizing across Retina and standard displays?

Before calculating slot positions, the Ring::new constructor multiplies the base 80-pixel radius by the monitor's ScaleFactor obtained from GPUI. This scaling occurs prior to the cosine and sine operations, ensuring the ring renders at the same physical size regardless of pixel density.

Can developers customize the number of slots or the ring's radius?

Currently, SLOT_COUNT and RADIUS_PX are defined as compile-time constants in crates/openlogi-ui/src/ring.rs. Modifying these values requires recompiling the openlogi-ui crate, as the eight-slot layout and 80-pixel radius are baked into the positioning logic and UI asset arrangements.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →