layout: make strips the titlebar with window controls
This commit is contained in:
+2
-2
@@ -290,7 +290,7 @@ Units, each through the review gate:
|
||||
| L3 | layout | Dock ✅ Landed (2026-11-02) | The center pane tree, controlled: `DockState` holds the `Pane { tabs, active }` / `Split { axis, ratio, a, b }` tree plus a registry of `PanelDescriptor`s (`id` + title + optional icon + a view builder called once per open; the built view is retained, so panel state survives tab switches). `open` / `activate` / `close` / `close_others` / `close_pane` / `split` / `move` are plain state ops keyed by panel id — opening an open panel re-activates it, one instance per descriptor for now, and `active_panel()` reports what the active pane shows. Any op emptying a pane collapses it: the pane detaches and single-child splits are replaced by the surviving subtree, so only the root can sit empty. Split seams are `ResizeHandle`s (arrow keys move them for free); the ratio lives in the state and the seam turns pixel deltas into fractions against a per-split span that a canvas captures into dock-namespaced keyed state each frame — double-click resets to 0.5, both sides clamp at 10% of the split. Panes render a `strip_height` header row composing the data crate's `Tabs` (revised in L4 — see below), `TabList`/`Tab`/`TabPanel` roles, and a core `ContextMenu` on the header — Close / Close Others / Close Pane; the active tab's view fills the pane. Feature `dock` pulls core `button` + `context_menu` and re-uses `resize_handle`; 7 headless tests in `crates/layout/tests/dock.rs` (open/activate, tab clicks, neighbor-on-close, split + collapse, seam drag/reset/clamp with a 1 px flex-rounding tolerance, merge-by-move, close-others/close-pane + reopen); the gallery's shell center hosts a dock with demo panels and open/split buttons |
|
||||
| L4 | layout | Dock drag-to-split ✅ Landed (2026-11-02) | Dragging a tab uses gpui's own drag system: the dock attaches `on_drag` through data `Tabs`' `Tab::wrap` seam with a `PanelDrag { panel }` payload (ghost = a small bordered title card). While `App::has_active_drag()`, each pane's content mounts five absolute drop zones — four edge bands that call the new `DockState::split_panel_at(panel, anchor, direction)` (a split beside the *target* pane, not the dragged tab's own; the empty source still collapses) and a center zone that calls `move_panel`. Zones paint a `selection` wash via `drag_over::<PanelDrag>` while hovered and are mounted only during a drag, so content clicks are never blocked; gpui cancels the drag on a mouse-up over nothing, which unmounts them. Dropped tabs land active in their new pane. Same pass: pane content renders through `AnyView::cached` (`StyleRefinement` absolute + size full, the gpui-kit pattern) so an un-notified panel never re-renders on dock frames; pane headers moved from hand-rolled tabs to data `Tabs` (`Pill` variant — renamed from `Card` and reshaped to signed's exact tab: leading element via `Tab::lead`, wide title-to-controls gap, `Tab::busy` loading slot standing in with a quiet ring until the Spinner control lands, a ghost core `Button` close control (the first data → core dependency edge, feature-gated: `tabs` pulls `core/button`), and the pill radius is the button radius, not `full`; the pill bar is chrome-less because the dock's tab strip doubles as the L4.5 titlebar), which now ships the two designs (default stays `Underline`); tab strips carry `{tabs-id}-tab-{label}` / `{tabs-id}-close-{label}` debug selectors for headless tests. 10 headless tests in `crates/layout/tests/dock.rs` — the new ones cover a real simulated drag (mouse down → threshold move → drag starts → drop on a right edge band → split) and `split_panel_at`'s target-side placement; the rest carried over with the new tab selectors |
|
||||
| L2.5 | layout | window frame ✅ Landed (2026-11-01) | `window_frame::frame(child, window, cx)` — the client-side window frame for windows the OS does not decorate: under `Decorations::Server` (macOS, Windows, KDE, X11) it passes the child through untouched; under `Decorations::Client` (Wayland/GNOME) it insets the content, draws the hairline border and `Radius::overlay` corners (tiled sides stay square and lose their band), casts the dialog elevation's shadow dimmed to 0.7× when the window is inactive, and turns a 4 px band inside each live edge into resize hits (`start_window_resize`, edge cursors; a maximized window counts as flush everywhere). Requires a transparent window background, and must be the window's root — the bands hit-test against `Window::window_bounds`. **The `Shell` applies it by default**, so the module ships ungated — no separate feature. Adapted from bezel's `window::frame` + gpui-kit's `WindowBorder` (D15/D20), simplified to one inset band and theme-owned tokens |
|
||||
| L4.5 | layout | Titlebar + window controls | The strips across the shell's top become the window's titlebar: they drag the window (`WindowControlArea::Drag` on the strip) and host the controls where the platform puts them. A `WindowControls` element — minimize, maximize/restore, close, capability-gated by `Window::window_controls()` — renders per platform: **macOS** draws nothing where the system traffic lights sit (they are system-drawn; `TitlebarOptions.appears_transparent` + `traffic_light_position` is app window config), and the shell reserves the pad in the left sidebar's header leading edge — or across the center's strip when the left sidebar is hidden; **Windows** draws native-glyph buttons (`ChromeMinimize`/`ChromeMaximize`/`ChromeClose`, Segoe E921/E922/E8BB verified against the MDL2 table) marked `WindowControlArea::Min/Max/Close` so Windows handles the clicks and snap layouts natively, maximize flipping to Restore via `is_maximized`; **Linux** draws the same ops as click handlers (`minimize_window` / `zoom_window` / `remove_window`) in the platform's own dress — Adwaita's small gray circle wrappers around symbolic glyphs, resolved through the freedesktop cascade with the bundled fallback (`Close` already leads with `window-close-symbolic`; new `WindowMinimize`/`WindowMaximize` roles land in the icons crate). Placement follows the shell's shape: Windows/Linux put the controls at the trailing end of the right-hand strip — the right sidebar's strip when visible, else the center region's titlebar strip, which is now the dock's `Pill` tab bar (the pill strip ships chrome-less for exactly this — in signed the tab bar doubles as the titlebar, 44 px, with 34 px-wide controls and a danger hover on close, which the pill tab already matches apart from the metrics). Reference: signed's `crates/dock/src/window_controls.rs` |
|
||||
| L4.5 | layout | Titlebar + window controls ✅ Landed (2026-11-02) | The strips are the window's titlebar: every strip (shell sidebars, dock pill bar) marks itself `WindowControlArea::Drag` and double-click-maximizes (`titlebar_double_click` on macOS, `zoom_window` on Linux; Windows does it natively through the caption hit test). New `window_controls` module (feature `window_controls`, pulled by `shell` and `dock`): a `WindowControls` element rendered at the trailing end of the right-hand strip — the right sidebar's strip when visible, else the dock pill bar via `Dock::window_controls(bool)`. **Windows**: caption buttons (36 px wide, Segoe codepoints through the icons crate — new `WindowMinimize`/`WindowMaximize`/`WindowRestore` roles, E921/E922/E923 verified against zed's own table) marked `WindowControlArea::Min/Max/Close` so the OS owns the clicks and snap layouts; maximize flips to restore via `is_maximized`; capability-gated by `Window::window_controls()` + `is_minimizable`/`is_resizable`; close hovers the platform red (a platform convention const, like the Segoe codepoints). **Linux**: Adwaita circular buttons (the platform dress, hand-rolled like zed's) under `Decorations::Client` only, clicking `minimize_window`/`zoom_window`/`remove_window`. **macOS** draws nothing — system traffic lights; the shell reserves `TRAFFIC_LIGHT_PAD` (78 px, Tahoe SDK) in the left sidebar strip's leading edge, and `Dock::traffic_light_pad(bool)` pads the pill bar when no left sidebar sits over the lights. The gpui dispatch fact that makes this safe: Windows forwards `WM_NCLBUTTONDOWN` into gpui's normal event stream, so buttons inside a Drag-marked strip still get their clicks, and only unconsumed presses fall through to native caption behavior. Adapted from zed's `platform_title_bar` (D15/D20); the gallery wires both dock flags from `LayoutState`. 2 platform-gated headless tests (macOS pad shifts the pill bar's tab by the pad width; controls render only under the flag on their platforms); CI feature loop gained `window_controls` |
|
||||
| L5 | layout | Sidebar auto-open | Panels declare `needs_right_sidebar` on their descriptor; activating such a panel opens the right sidebar; closing it leaves visibility to the user. The gallery gains a workspace-style shell page — the bootstrap example apps copy. |
|
||||
|
||||
Deferred until a real need: layout persistence/serialization, pane zoom (maximize one pane), a bottom dock, collapse-to-edge docks, floating windows, drag between windows.
|
||||
@@ -377,6 +377,6 @@ Dropped from rev 3: D2, D5, D9 (port-era decisions); the review gate returns in
|
||||
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), 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 landed as §6.3 M7 (2026-11-01).
|
||||
8. Then — Phase G is the migration additions (§6.3): Badge ✅, Select ✅, Segmented control ✅, Context menu ✅, Empty state ✅, password input ✅, multiline textarea ✅, then Avatar image (M8) — each unblocks a concrete signed screen. Pickers (Calendar/DatePicker, TimeField, ColorPicker) stay decided-at-unit-time. `crates/editor` starts when file viewing (M15) is reached.
|
||||
9. Then — the layout crate (§6.4): ResizeHandle ✅, Shell ✅, window frame ✅, Dock ✅, drag-to-split ✅, titlebar + window controls, and panel-declared sidebar auto-open — each through the review gate, with signed's `crates/dock` as the usage reference and the gallery workspace page as the example. Order against M9+ is decided when the crate starts.
|
||||
9. Then — the layout crate (§6.4): ResizeHandle ✅, Shell ✅, window frame ✅, Dock ✅, drag-to-split ✅, titlebar + window controls ✅, and panel-declared sidebar auto-open — each through the review gate, with signed's `crates/dock` as the usage reference and the gallery workspace page as the example. Order against M9+ is decided when the crate starts.
|
||||
|
||||
Batched polish candidates between units: scrollbar auto-hide + horizontal thumb, `Undo`/`Redo` menu events.
|
||||
|
||||
Reference in New Issue
Block a user