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

> Discover how the OpenLogi Actions Ring overlay renders eight-slot layouts precisely around your cursor using polar coordinates. Learn the math behind the placement.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-08-26

---

**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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/ring.rs)). This function converts the cursor's screen coordinates into eight peripheral points arranged in a perfect circle:

```rust
// 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:

```rust
// 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:

```rust
// 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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/overlay.rs), the `OverlayWindow::handle_event` method listens for `Event::CursorMoved(p)` updates:

```rust
// 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`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-ui/src/ring.rs), while window management and event handling live in [`openlogi-overlay/src/overlay.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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.