Color Ring

Edit a color channel on a full circular track.

Try it

Drag around the hue ring, or use arrow keys, to update the preview and committed hue. This preview runs the exact code sample below.

Open example separately ↗

Basic use

Hue ring with preview and commit
use gpui::{Context, Entity, Render, Window, div, prelude::*, px};
use xgpui_luma::controls::slider::{SliderControl, SliderEventExt};
use xgpui_luma_color::color_ring::ColorRingBuilder;
use xgpui_luma_color::style::Size;

struct Example {
    control: Entity<SliderControl>,
    preview: f32,
    committed: f32,
}
impl Example {
    fn new(cx: &mut Context<Self>) -> Self {
        let control = ColorRingBuilder::hue("hue-ring", 210.0, 0.8, 0.5)
            .size(Size::Medium).allow_inner_target(true)
            .keyboard_step_degrees(1.0)
            .spawn(cx)
            .on_value_changed(cx, |owner: &mut Self, _, value, cx| {
                owner.preview = value;
                cx.notify();
            })
            .on_release(cx, |owner, _, value, cx| {
                owner.committed = value;
                cx.notify();
            });
        Self { control, preview: 210.0, committed: 210.0 }
    }
}
impl Render for Example {
    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
        div().w(px(240.0)).child(self.control.clone())
    }
}

Channels and appearance

hue(id, hue, saturation, lightness) builds a hue ring. saturation(id, saturation, hue, hsv_value) and lightness(id, lightness, hue, saturation) build the other channel tracks; their scalar values use 0..1.

Use size(Size), ring_thickness, thumb_size, rotation_degrees, and inner/outer border options. allow_inner_target(true) also accepts interaction inside the ring; leave it false when another control occupies the center. Raster rendering is the default; hue_with_renderer and the equivalent channel constructors accept ColorRingRenderer.

Events

EventPayloadCallback
Changethumb_id: ThumbId
value: f32
on_value_changed
Update the live preview.
Releasethumb_id: ThumbId
value: f32
on_release
Commit the interaction value.
DragStartthumb_id: ThumbIdon_drag_start
Begin a pointer gesture.
DragEndthumb_id: ThumbId
value: f32
on_drag_end
Finish a pointer gesture.
ThumbAddedthumb_id: ThumbId
value: f32
on_thumb_added
Track an added gradient stop.
ThumbRemovedthumb_id: ThumbIdon_thumb_removed
Track a removed stop.
ThumbSelectedthumb_id: ThumbIdon_thumb_selected
Update the selected thumb.
FocusChangedfocused: boolon_focus_changed
React to keyboard focus.
HoverChangedhovered: boolon_hover_changed
React to pointer hover.
EnabledChangedenabled: boolon_enabled_changed
React to enablement changes.

These are SDK SliderEvent callbacks, available through SliderEventExt. The builders return Entity<SliderControl>. Each callback receives the owner, the listed payload values, and the owner context. Register callbacks after spawn; use on_event or cx.subscribe for the full event stream. See Slider events.

Keyboard and synchronization

Arrow keys move by 1 degree around the track, or 10 with Shift. keyboard_step_degrees changes the angular increment; step separately controls scalar snapping. Navigation wraps at the angular seam and preserves the current side of mirrored saturation/lightness tracks.

Retain domain_renderer() and track_context() before spawning. Update dependent color delegates and refresh thumb previews with update_ring_delegate / refresh_color_ring, or use the corresponding ColorCompositionSync helper. Numeric value changes alone do not update the base colors of the track.

Source

Implementation · Color controls