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_2aligns slot 0 directly above the cursor at the 12 o'clock position. - DPI awareness: The
ScaleFactorfrom GPUI multiplies the base radius before trigonometric conversion, ensuring consistent physical sizing across displays. - Live tracking: Cursor movements captured by the
openlogi-injectcrate trigger position recalculation and redraw via theOverlayWindowevent loop. - Modular architecture: Positioning logic resides in
openlogi-ui/src/ring.rs, while window management and event handling live inopenlogi-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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →