From ac4a0f2add42b56e29533abe2e5e086792495097 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Sun, 1 Nov 2026 10:33:51 +0700 Subject: [PATCH] add StyledExt::refine_style to utils --- crates/core/src/avatar.rs | 22 +++++++++------------- crates/core/src/badge.rs | 15 ++++++--------- crates/core/src/common.rs | 2 +- crates/core/src/scroll_area.rs | 12 +++++++----- crates/utils/src/lib.rs | 14 ++++++++++++-- docs/PLAN.md | 2 +- 6 files changed, 36 insertions(+), 31 deletions(-) diff --git a/crates/core/src/avatar.rs b/crates/core/src/avatar.rs index 6d02801..b706044 100644 --- a/crates/core/src/avatar.rs +++ b/crates/core/src/avatar.rs @@ -2,13 +2,12 @@ use std::fmt::Display; use gpui::prelude::FluentBuilder as _; use gpui::{ - App, ElementId, InteractiveElement as _, IntoElement, ParentElement as _, Refineable as _, - RenderOnce, Role, SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled as _, - Window, div, + App, ElementId, InteractiveElement as _, IntoElement, ParentElement as _, RenderOnce, Role, + SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled as _, Window, div, }; use gpui_native_theme::{ActiveTheme as _, Oklch, Size, ThemeVariant}; -use crate::common::{Sizable, h_flex}; +use crate::common::{Sizable, StyledExt, h_flex}; /// Number of rows and columns in the pixel grid. const GRID_SIZE: usize = 8; @@ -85,7 +84,7 @@ impl RenderOnce for Avatar { let radius = theme.radius.control; let side = theme.sizes.resolve(self.size).avatar_size; - let mut base = h_flex() + let base = h_flex() .id(ElementId::from(self.id.clone())) .flex_none() .justify_center() @@ -97,10 +96,8 @@ impl RenderOnce for Avatar { .when_some( self.name.clone().or(Some(self.id.clone())), |avatar, label| avatar.role(Role::Image).aria_label(label), - ); - - // Apply custom styles - base.style().refine(&self.style); + ) + .refine_style(&self.style); match self.picture { Some(url) => base.child( @@ -174,7 +171,7 @@ impl RenderOnce for PixelAvatar { } } - let mut grid = div() + div() .grid() .grid_cols(GRID_SIZE as u16) .grid_rows(GRID_SIZE as u16) @@ -183,9 +180,8 @@ impl RenderOnce for PixelAvatar { .rounded(radius) .overflow_hidden() .bg(gpui::Hsla { a: 0.16, ..main }) - .children(cells); - grid.style().refine(&self.style); - grid + .children(cells) + .refine_style(&self.style) } } diff --git a/crates/core/src/badge.rs b/crates/core/src/badge.rs index 3edda3f..7cc04fc 100644 --- a/crates/core/src/badge.rs +++ b/crates/core/src/badge.rs @@ -1,10 +1,10 @@ use gpui::{ - App, Hsla, IntoElement, ParentElement as _, Refineable as _, RenderOnce, SharedString, - StyleRefinement, Styled as _, Window, + App, Hsla, IntoElement, ParentElement as _, RenderOnce, SharedString, StyleRefinement, + Styled as _, Window, }; use gpui_native_theme::{ActiveTheme as _, Size, Theme}; -use crate::common::{Sizable, h_flex}; +use crate::common::{Sizable, StyledExt, h_flex}; /// How loud a badge is. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] @@ -88,7 +88,7 @@ impl RenderOnce for Badge { // Counts read smaller than control labels, like every native badge. let label_size = metrics.text_size * 0.8; - let mut pill = h_flex() + h_flex() .flex_none() .justify_center() .rounded(theme.radius.full) @@ -98,10 +98,7 @@ impl RenderOnce for Badge { .line_height(label_size * 1.25) .px(metrics.padding_x * 0.5) .py_0p5() - .child(self.label); - - pill.style().refine(&self.style); - - pill + .child(self.label) + .refine_style(&self.style) } } diff --git a/crates/core/src/common.rs b/crates/core/src/common.rs index f5fcb54..53a9d00 100644 --- a/crates/core/src/common.rs +++ b/crates/core/src/common.rs @@ -1,7 +1,7 @@ //! Shared element constructors and small style helpers. pub use gpui_native_theme::Size; -pub use gpui_native_utils::{h_flex, v_flex}; +pub use gpui_native_utils::{StyledExt, h_flex, v_flex}; /// A handler told to clean up after a dismissed surface. pub type DismissHandler = std::rc::Rc; diff --git a/crates/core/src/scroll_area.rs b/crates/core/src/scroll_area.rs index 2d227bd..f50b733 100644 --- a/crates/core/src/scroll_area.rs +++ b/crates/core/src/scroll_area.rs @@ -1,11 +1,13 @@ use gpui::prelude::FluentBuilder as _; use gpui::{ App, Bounds, DispatchPhase, ElementId, InteractiveElement as _, IntoElement, MouseDownEvent, - MouseMoveEvent, ParentElement, Pixels, Refineable as _, RenderOnce, ScrollHandle, SharedString, + MouseMoveEvent, ParentElement, Pixels, RenderOnce, ScrollHandle, SharedString, StatefulInteractiveElement as _, Styled, Window, canvas, div, fill, point, }; use gpui_native_theme::ActiveTheme as _; +use crate::common::StyledExt; + /// Which directions the content scrolls in. #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum ScrollAxes { @@ -116,13 +118,13 @@ impl RenderOnce for ScrollArea { .on_scroll(|_, _, cx| { // Keep wheel events from scrolling an outer scroll area too. cx.stop_propagation(); - }); + }) + .refine_style(&self.style); - pane.style().refine(&self.style); + // Extend with children pane.extend(self.children); - let mut wrapper = div().relative(); - wrapper.style().refine(&self.style); + let mut wrapper = div().relative().refine_style(&self.style); wrapper.extend(std::iter::once(pane.into_any_element())); if let Some(handle) = self.handle { diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 114c2d5..7297b2b 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,7 +1,7 @@ use gpui::prelude::FluentBuilder as _; use gpui::{ - App, BoxShadow, Div, ElementId, FocusHandle, Hsla, IntoElement, StatefulInteractiveElement, - Styled, Window, div, px, + App, BoxShadow, Div, ElementId, FocusHandle, Hsla, IntoElement, Refineable as _, + StatefulInteractiveElement, StyleRefinement, Styled, Window, div, px, }; use gpui_native_theme::ActiveTheme as _; @@ -15,6 +15,16 @@ pub fn v_flex() -> Div { div().flex().flex_col() } +/// Extensions over [`Styled`], shared by the components. +pub trait StyledExt: Styled + Sized { + fn refine_style(mut self, style: &StyleRefinement) -> Self { + self.style().refine(style); + self + } +} + +impl StyledExt for E {} + /// Returns a focus handle for the given element ID. pub fn focus_handle(id: &ElementId, window: &mut Window, cx: &mut App) -> FocusHandle { window diff --git a/docs/PLAN.md b/docs/PLAN.md index 1c6a22e..20ffd35 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -353,7 +353,7 @@ Dropped from rev 3: D2, D5, D9 (port-era decisions); the review gate returns in 4. ~~Phase D input~~ ✅ — single-line Input (unit 10) + the §6.2 completion pass (modifiers, undo/redo, read-only, actions + key context, caret blink, paint polish). NumberInput stays deferred; multiline returns as §6.3 M7. 5. ~~Phase E overlays~~ ✅ — Popover (unit 11), ScrollArea (unit 11.5), Menu (unit 12), Tooltip + Dialog/AlertDialog + Toast (unit 11+ row; Sheet dropped). Phase gate held: escape/dismiss semantics and focus behavior per overlay. 6. ~~Unit 14 — motion, per component (§6 row 14, D21)~~ ✅ Landed in five passes (2026-10-30): (a) theme `reduce_motion` → `cx.set_reduce_motion` in `reapply`; (b) Switch thumb `with_spring`; (c) Toast 8 px slide-in; (d) Dialog scrim enters with the panel; (e) Progress 30 fps cap. Exits and hover washes stay deferred (§6 deferred list). -7. ~~Phase F, data first~~ ✅ data complete (2026-10-30) — `crates/data` opened with List, then Tree, Tabs, Sidebar (the dead `sheet` feature dropped from core along the way; Table dropped from the roadmap). Also extracted `crates/utils` (2026-10-30): `h_flex`/`v_flex` (core's `common` re-exports them), `focus_handle` (the namespaced keyed handle, 8 call sites), `focus_ring` (7 sites), `adjacent_selectable` + `nav_key` (4 sites), and the selectable-row paint helpers `selectable_label_color`/`paint_selectable_row` (3 sites, reading `&Theme` directly — no intermediate color struct) — `core` and `data` depend on it; the facade doesn't re-export it. **Linux theme sources landed (2026-10-30)**: Omarchy (colors.toml parse + state-dir detection + inotify on `current`), KDE (kdeglobals `[Colors:*]` + AccentColor + `~/.config` inotify), GNOME/generic (portal `ReadAll` + `SettingChanged` via zbus 5 blocking on its own thread, gsettings fallback with the libadwaita accent table) — `SourceKind::Omarchy/Kde/Gnome`, detection cascade Omarchy → KDE → portal → fallback, all parsers fixture-tested on every OS and the Linux build compile-checked via `cargo check --target x86_64-unknown-linux-gnu`. **Windows source + icon resolvers landed (2026-10-30)**: `SourceKind::Windows` (WinRT `UISettings` + `Personalize` fallback + `ColorValuesChanged`/`HighContrastChanged`, §5.3), the Segoe Fluent codepoint renderer, and the freedesktop theme walker (§4.4); the WinRT surface was verified against the real `windows` 0.62 bindings via a standalone cross-check (gpui's own manifest build needs llvm-rc from macOS). Remaining Phase F: **side-by-side gallery screenshots per platform** — Omarchy/KDE/GNOME need a Linux run, Windows needs its first hardware build. NumberInput stays deferred until needed; multiline is §6.3 M7. +7. ~~Phase F, data first~~ ✅ data complete (2026-10-30) — `crates/data` opened with List, then Tree, Tabs, Sidebar (the dead `sheet` feature dropped from core along the way; Table dropped from the roadmap). Also extracted `crates/utils` (2026-10-30): `h_flex`/`v_flex` (core's `common` re-exports them), `focus_handle` (the namespaced keyed handle, 8 call sites), `focus_ring` (7 sites), `adjacent_selectable` + `nav_key` (4 sites), and the selectable-row paint helpers `selectable_label_color`/`paint_selectable_row` (3 sites, reading `&Theme` directly — no intermediate color struct), plus the `StyledExt` trait (`refine_style` — adapted from gpui-kit `base`, D15 — replacing the per-component `style().refine` pattern; 5 sites) — `core` and `data` depend on it; the facade doesn't re-export it. **Linux theme sources landed (2026-10-30)**: Omarchy (colors.toml parse + state-dir detection + inotify on `current`), KDE (kdeglobals `[Colors:*]` + AccentColor + `~/.config` inotify), GNOME/generic (portal `ReadAll` + `SettingChanged` via zbus 5 blocking on its own thread, gsettings fallback with the libadwaita accent table) — `SourceKind::Omarchy/Kde/Gnome`, detection cascade Omarchy → KDE → portal → fallback, all parsers fixture-tested on every OS and the Linux build compile-checked via `cargo check --target x86_64-unknown-linux-gnu`. **Windows source + icon resolvers landed (2026-10-30)**: `SourceKind::Windows` (WinRT `UISettings` + `Personalize` fallback + `ColorValuesChanged`/`HighContrastChanged`, §5.3), the Segoe Fluent codepoint renderer, and the freedesktop theme walker (§4.4); the WinRT surface was verified against the real `windows` 0.62 bindings via a standalone cross-check (gpui's own manifest build needs llvm-rc from macOS). Remaining Phase F: **side-by-side gallery screenshots per platform** — Omarchy/KDE/GNOME need a Linux run, Windows needs its first hardware build. NumberInput stays deferred until needed; multiline is §6.3 M7. 8. Then — Phase G is the migration additions (§6.3): Badge, Select, Segmented control, Context menu, Empty state, password input, multiline Textarea, Avatar image first — each unblocks a concrete signed screen. Pickers (Calendar/DatePicker, TimeField, ColorPicker) stay decided-at-unit-time. `crates/editor` starts when the port reaches file viewing (M15). 9. Then — Phase H, the signed migration itself (§7): theme swap first (native-theme → `gpui-native-theme`), then views one at a time, keeping the gpui-component build side by side until parity.