This commit is contained in:
2026-10-30 19:36:39 +07:00
parent b4a996e44c
commit 1711ce0e9d
22 changed files with 300 additions and 485 deletions
+4 -8
View File
@@ -2,12 +2,12 @@
use gpui::prelude::FluentBuilder as _;
use gpui::{
AnyView, App, BoxShadow, ClickEvent, ElementId, InteractiveElement as _, IntoElement,
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
Styled as _, Window, px, relative,
AnyView, App, ClickEvent, ElementId, InteractiveElement as _, IntoElement, ParentElement as _,
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Window, relative,
};
use gpui_native_icons::{IconRole, icon};
use gpui_native_theme::{ActiveTheme as _, Size};
use gpui_native_utils::focus_ring;
use std::rc::Rc;
use crate::common::{Sizable, h_flex};
@@ -193,11 +193,7 @@ impl RenderOnce for Button {
.tab_stop(true)
.hover(move |style| style.bg(hover))
.active(move |style| style.bg(pressed))
.focus_visible(|style| {
style.shadow(vec![
BoxShadow::new(px(0.), px(0.), colors.ring).spread_radius(px(3.)),
])
})
.focus_visible(|style| style.shadow(focus_ring(colors.ring)))
.when_some(on_click, |button, on_click| {
button.on_click(move |event, window, cx| on_click(event, window, cx))
})
+4 -10
View File
@@ -2,12 +2,12 @@
use gpui::prelude::FluentBuilder as _;
use gpui::{
App, BoxShadow, ElementId, InteractiveElement as _, IntoElement, ParentElement as _,
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Toggled, Window,
px,
App, ElementId, InteractiveElement as _, IntoElement, ParentElement as _, RenderOnce, Role,
SharedString, StatefulInteractiveElement as _, Styled as _, Toggled, Window,
};
use gpui_native_icons::{IconRole, icon};
use gpui_native_theme::{ActiveTheme as _, Size};
use gpui_native_utils::focus_ring;
use crate::common::{Sizable, h_flex};
@@ -112,13 +112,7 @@ impl RenderOnce for Checkbox {
.cursor_pointer()
.focusable()
.tab_stop(true)
.focus_visible(|style| {
// Paint-only keyboard ring; the width joins the native
// metrics table when that lands.
style.shadow(vec![
BoxShadow::new(px(0.), px(0.), colors.ring).spread_radius(px(3.)),
])
})
.focus_visible(|style| style.shadow(focus_ring(colors.ring)))
.when_some(on_change, |checkbox, on_change| {
checkbox.on_click(move |_, window, cx| on_change(next, window, cx))
})
+1 -12
View File
@@ -1,18 +1,7 @@
//! Shared element constructors and small style helpers.
use gpui::{Div, Styled as _, div};
pub use gpui_native_theme::Size;
/// A row whose children center on the cross axis.
pub fn h_flex() -> Div {
div().flex().flex_row().items_center()
}
/// A column whose children stretch across the cross axis.
pub fn v_flex() -> Div {
div().flex().flex_col()
}
pub use gpui_native_utils::{h_flex, v_flex};
/// A component that can be told which size to render at.
pub trait Sizable: Sized {
+4 -7
View File
@@ -157,16 +157,15 @@ impl RenderOnce for Dialog {
let alert = self.alert;
// Keyboard focus rides the panel across frames.
let focus_handle = window.use_keyed_state("dialog-focus", cx, |_, cx| cx.focus_handle());
let focus = gpui_native_utils::focus_handle(&self.id, window, cx);
let content = self.content.map(|build| build(window, cx));
let actions = self.actions.map(|build| build(window, cx));
let panel_bounds: BoundsCell = Rc::new(RefCell::new(None));
let panel = v_flex()
.id("dialog-panel")
.track_focus(&focus_handle.read(cx).clone())
.track_focus(&focus)
.role(role)
.aria_label(label)
.min_w_64()
@@ -214,11 +213,9 @@ impl RenderOnce for Dialog {
move |bounds, _window, _cx| *cell.borrow_mut() = Some(bounds)
},
{
let focus_handle = focus_handle.clone();
move |_, _, window, cx| {
let handle = focus_handle.read(cx).clone();
if !handle.contains_focused(window, cx) {
window.focus(&handle, cx);
if !focus.contains_focused(window, cx) {
window.focus(&focus, cx);
}
}
},
+4 -8
View File
@@ -1,10 +1,10 @@
use gpui::prelude::FluentBuilder as _;
use gpui::{
App, BoxShadow, ClickEvent, ElementId, InteractiveElement as _, IntoElement,
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
Styled as _, Window, px,
App, ClickEvent, ElementId, InteractiveElement as _, IntoElement, ParentElement as _,
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Window,
};
use gpui_native_theme::{ActiveTheme as _, Size};
use gpui_native_utils::focus_ring;
use crate::common::{Sizable, h_flex};
@@ -113,11 +113,7 @@ impl RenderOnce for Link {
.focusable()
.tab_stop(true)
.hover(move |style| style.text_color(ramps.primary.hover))
.focus_visible(|style| {
style.shadow(vec![
BoxShadow::new(px(0.), px(0.), colors.ring).spread_radius(px(3.)),
])
})
.focus_visible(|style| style.shadow(focus_ring(colors.ring)))
.on_click(move |event, window, cx| match on_click.as_ref() {
Some(on_click) => on_click(event, window, cx),
None => {
+4 -5
View File
@@ -305,7 +305,7 @@ impl RenderOnce for Menu {
};
// Keyboard focus rides the panel across frames.
let focus_handle = window.use_keyed_state("menu-focus", cx, |_, cx| cx.focus_handle());
let focus = gpui_native_utils::focus_handle(&self.id, window, cx);
let scroll = window.use_keyed_state("menu-scroll", cx, |_, _| ScrollHandle::new());
let selected = if *was_open.read(cx) {
@@ -334,7 +334,7 @@ impl RenderOnce for Menu {
let panel = v_flex()
.id("menu-panel")
.track_focus(&focus_handle.read(cx).clone())
.track_focus(&focus)
.role(Role::Menu)
.aria_label(label)
.mt_1()
@@ -371,10 +371,9 @@ impl RenderOnce for Menu {
},
{
let on_change = on_change.clone();
let focus = focus_handle;
move |_, _, window, cx| {
if !focus.read(cx).contains_focused(window, cx) {
window.focus(&focus.read(cx).clone(), cx);
if !focus.contains_focused(window, cx) {
window.focus(&focus, cx);
}
window.on_mouse_event({
+5 -9
View File
@@ -161,15 +161,12 @@ impl RenderOnce for Popover {
if self.open {
let on_change = self.on_change;
let label = fallback_label.clone();
let focus_handle =
window.use_keyed_state("popover-focus", cx, |_, cx| cx.focus_handle());
let focus = gpui_native_utils::focus_handle(&self.id, window, cx);
let content = self.content.map(|content| content(window, cx));
let panel = v_flex()
.id("popover-panel")
.track_focus(&focus_handle.read(cx).clone())
.track_focus(&focus)
.role(Role::Dialog)
.aria_label(label)
.mt_1()
@@ -201,12 +198,11 @@ impl RenderOnce for Popover {
},
{
let on_change = on_change.clone();
let focus = focus_handle;
let focus = focus;
move |_, _, window, cx| {
if !focus.read(cx).contains_focused(window, cx) {
window.focus(&focus.read(cx).clone(), cx);
if !focus.contains_focused(window, cx) {
window.focus(&focus, cx);
}
window.on_mouse_event({
let on_change = on_change.clone();
move |event: &MouseDownEvent, phase, window, cx| {
+4 -9
View File
@@ -1,10 +1,10 @@
use gpui::prelude::FluentBuilder as _;
use gpui::{
App, BoxShadow, ElementId, InteractiveElement as _, IntoElement, ParentElement as _,
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Toggled, Window,
div, px,
App, ElementId, InteractiveElement as _, IntoElement, ParentElement as _, RenderOnce, Role,
SharedString, StatefulInteractiveElement as _, Styled as _, Toggled, Window, div,
};
use gpui_native_theme::{ActiveTheme as _, Size};
use gpui_native_utils::focus_ring;
use crate::common::{Sizable, h_flex, v_flex};
@@ -178,12 +178,7 @@ where
row.cursor_pointer()
.focusable()
.tab_stop(true)
.focus_visible(|style| {
style.shadow(vec![
BoxShadow::new(px(0.), px(0.), colors.ring)
.spread_radius(px(3.)),
])
})
.focus_visible(|style| style.shadow(focus_ring(colors.ring)))
.when_some(row_on_change, |row, on_change| {
row.on_click(move |_, window, cx| {
on_change(value.clone(), window, cx)
+6 -13
View File
@@ -3,12 +3,12 @@ use std::rc::Rc;
use gpui::prelude::FluentBuilder as _;
use gpui::{
App, Bounds, BoxShadow, DispatchPhase, ElementId, InteractiveElement as _, IntoElement,
KeyDownEvent, MouseButton, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels,
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas,
div, px, relative,
App, Bounds, DispatchPhase, ElementId, InteractiveElement as _, IntoElement, KeyDownEvent,
MouseButton, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, RenderOnce, Role,
SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas, div, relative,
};
use gpui_native_theme::{ActiveTheme as _, Size};
use gpui_native_utils::focus_ring;
use crate::common::{Sizable, h_flex};
@@ -138,15 +138,12 @@ impl RenderOnce for Slider {
// The track bounds, recorded by the overlay canvas each frame,
// turn pointer positions into values inside the listeners.
let bounds_cell = Rc::new(Cell::new(None::<Bounds<Pixels>>));
// Dragging crosses frames, so it lives in keyed element state,
// a per-render flag would reset when the owner's update repaints.
let dragging = window.use_keyed_state("slider-dragging", cx, |_, _| false);
// A held handle gives the arrow-key listener something to check focus against,
// and the click-to-focus behavior a target.
let focus = window.use_keyed_state("slider-focus", cx, |_, cx| cx.focus_handle());
let focus_handle = focus.read(cx).clone();
let focus_handle = gpui_native_utils::focus_handle(&self.id, window, cx);
let set_value: ValueSetter = {
let bounds_cell = bounds_cell.clone();
@@ -188,11 +185,7 @@ impl RenderOnce for Slider {
slider
.track_focus(&focus_handle)
.tab_stop(true)
.focus_visible(|style| {
style.shadow(vec![
BoxShadow::new(px(0.), px(0.), colors.ring).spread_radius(px(3.)),
])
})
.focus_visible(|style| style.shadow(focus_ring(colors.ring)))
.on_mouse_down(MouseButton::Left, move |event, window, cx| {
down_dragging.update(cx, |dragging, _| *dragging = true);
down_set_value(event.position.x, window, cx);
+5 -8
View File
@@ -2,11 +2,12 @@
use gpui::prelude::FluentBuilder as _;
use gpui::{
AnimationExt as _, App, BoxShadow, ElementId, InteractiveElement as _, IntoElement,
ParentElement as _, RenderOnce, Role, SharedString, SpringAnimation, SpringConfig,
StatefulInteractiveElement as _, Styled as _, Toggled, Window, div, px,
AnimationExt as _, App, ElementId, InteractiveElement as _, IntoElement, ParentElement as _,
RenderOnce, Role, SharedString, SpringAnimation, SpringConfig, StatefulInteractiveElement as _,
Styled as _, Toggled, Window, div, px,
};
use gpui_native_theme::{ActiveTheme as _, Size};
use gpui_native_utils::focus_ring;
use crate::common::{Sizable, h_flex};
@@ -122,11 +123,7 @@ impl RenderOnce for Switch {
.cursor_pointer()
.focusable()
.tab_stop(true)
.focus_visible(|style| {
style.shadow(vec![
BoxShadow::new(px(0.), px(0.), colors.ring).spread_radius(px(3.)),
])
})
.focus_visible(|style| style.shadow(focus_ring(colors.ring)))
.when_some(on_change, |switch, on_change| {
switch.on_click(move |_, window, cx| on_change(next, window, cx))
})
+4 -8
View File
@@ -1,10 +1,10 @@
use gpui::prelude::FluentBuilder as _;
use gpui::{
App, BoxShadow, ElementId, InteractiveElement as _, IntoElement, ParentElement as _,
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Toggled, Window,
px,
App, ElementId, InteractiveElement as _, IntoElement, ParentElement as _, RenderOnce, Role,
SharedString, StatefulInteractiveElement as _, Styled as _, Toggled, Window,
};
use gpui_native_theme::{ActiveTheme as _, Size};
use gpui_native_utils::focus_ring;
use crate::common::{Sizable, h_flex};
@@ -123,11 +123,7 @@ impl RenderOnce for Toggle {
.cursor_pointer()
.focusable()
.tab_stop(true)
.focus_visible(|style| {
style.shadow(vec![
BoxShadow::new(px(0.), px(0.), colors.ring).spread_radius(px(3.)),
])
})
.focus_visible(|style| style.shadow(focus_ring(colors.ring)))
.when_some(on_change, |toggle, on_change| {
toggle.on_click(move |_, window, cx| on_change(next, window, cx))
})