From 64c0c5c77644e66be9c98cf29175688bc6f5b426 Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 21:13:32 +0000 Subject: [PATCH 01/17] docs: native chrome and system theme parity matrix (WI-3055) Graft-Changeset: cd21655205a18299ab57f1657a9ac7cc90b36552b157958c03daba5fe0518b080 Graft-Session: ses_01M6F1NWEGJMST2B21Q46NW72R Graft-Work-Item: WI-3055 Graft-Batch: 21 --- docs/NATIVE_CHROME_PARITY.md | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 docs/NATIVE_CHROME_PARITY.md diff --git a/docs/NATIVE_CHROME_PARITY.md b/docs/NATIVE_CHROME_PARITY.md new file mode 100644 index 0000000..5dd978d --- /dev/null +++ b/docs/NATIVE_CHROME_PARITY.md @@ -0,0 +1,43 @@ +# Native Chrome and System Theme Parity (WI-3055) + +Scope: Windows (DirectX), Linux X11, Linux Wayland. macOS is out of scope. + +Status legend: **Code-verified** = confirmed by reading the code paths on a Linux +dev host; **Manual pending** = needs a person on that platform. No manual runtime +evidence has been collected yet; the matrix records what is known and what is open. + +## Parity matrix + +| Behaviour | Windows / DirectX | Linux X11 | Linux Wayland | +|-----------|-------------------|-----------|---------------| +| Drag to move (`TitleBar` title area) | `start_window_move`; Manual pending | `start_window_move` (`x11/window.rs`); Manual pending | `start_window_move` (`wayland/window.rs`); Manual pending | +| Double-click title to maximize/restore | Implemented in `TitleBar` (`zoom_window`); Manual pending | Same; Manual pending | Same; Manual pending | +| Min / max / close buttons | `TitleBar` draws 46px buttons, `on_click` -> `minimize_window`/`zoom_window`/`remove_window`; Manual pending | Same; Manual pending | Same; Manual pending | +| Snap layouts (Win11 max-button hover flyout) / native caption hit-testing | **Gap**: platform maps `WindowControlArea::{Min,Max,Close}` to `HTMINBUTTON`/`HTMAXBUTTON`/`HTCLOSE` (`windows/events.rs`), but `fluent-app` `TitleBar` never calls `.window_control_area(..)`, so snap layouts and Aero-snap-from-button are not reachable | n/a | n/a | +| Server-side vs client-side decorations | n/a | `window_decorations`/`request_decorations` implemented; Manual pending | Same, compositor-dependent (GNOME has no SSD); Manual pending | +| Window menu (right-click title) | Not wired in `TitleBar` | `show_window_menu` available, not wired in `TitleBar` | Same as X11 | +| System light/dark | `window_appearance` + `WM_SETTINGCHANGE`/`ImmersiveColorSet` -> `appearance_changed`; Code-verified; Manual pending | XDG desktop portal color-scheme (`xdg_desktop_portal.rs`); Code-verified; Manual pending | Same portal path; Code-verified; Manual pending | +| System accent colour | `UISettings` colour lookup exists in `windows/util.rs`; not consumed by `fluent-core` tokens (accent is theme-token driven) | No portal accent read | No portal accent read | + +## Findings + +1. **Snap layouts gap (Windows):** wiring `window_control_area` on the `TitleBar` + buttons would route clicks through native `WM_NCLBUTTONUP` handling, which + already performs min/max/close. The existing `on_click` handlers would then + double-fire, so the change must replace them on Windows rather than add to + them. Left unchanged here because it cannot be exercised on this host. +2. **Accent colour:** FluentGUI accent comes from `fluent-core` theme tokens, not + the OS. Following the OS accent is a feature, not a parity bug; decide + separately. +3. The `TitleBar` has no window-menu or tiled-edge handling on Linux. + +## Manual evidence checklist (to be filled on real hardware) + +For each of Windows 11 (DirectX), Linux X11, Linux Wayland (GNOME and KDE): + +- [ ] Drag, double-click maximize, min/max/close work; close hover is red. +- [ ] Snap layouts flyout on max-button hover (Windows 11). +- [ ] Resize from edges/corners with the custom title bar. +- [ ] Toggle OS light/dark while running; app follows without restart. +- [ ] Change OS accent; record whether the app should follow. +- [ ] Attach screenshots under `screenshots/` and link them here. From 6681ccfcc3f1d4a2a02bdd6d568e5fa146d18ee2 Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:35:29 +0000 Subject: [PATCH 02/17] CI scope nextest to changed fluent crates Graft-Changeset: c5b96ddb2f3182d27cdef179ad899fadd6f4b30194700980cac0282976ef33a7f Graft-Session: ses_01M6F65FZK959RRB0Q8RVFSGB7 Graft-Work-Item: WI-2949 Co-authored-by: gpt-5.6-sol Graft-Batch: 21 --- .github/workflows/ci.yml | 23 +++++++++++++------- scripts/ci-nextest.sh | 45 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 8 deletions(-) create mode 100755 scripts/ci-nextest.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f66201..8a676d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,9 @@ jobs: os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + with: + fetch-depth: 0 - name: Install Linux system libraries if: runner.os == 'Linux' # gpui's Linux backend (x11rb/xkbcommon/wayland/blade/font-kit) links @@ -23,22 +25,27 @@ jobs: sudo apt-get install -y --no-install-recommends \ libxcb1-dev libx11-xcb-dev libxkbcommon-dev libxkbcommon-x11-dev \ libwayland-dev libvulkan-dev libfontconfig-dev libfreetype-dev - - uses: dtolnay/rust-toolchain@1.99.1 + - uses: dtolnay/rust-toolchain@d124f86f28ff42855784ff3319f48ac182c248f8 with: components: rustfmt, clippy + - uses: Swatinem/rust-cache@e9d7b9f1e587218970671bac74e5511ed9506e6a + - uses: taiki-e/install-action@97888a140839b7239d62fbf6af4e20adb5be3666 + with: + tool: nextest - name: Check format run: cargo fmt --all --check - name: Clippy run: cargo clippy --workspace --all-targets -- -D warnings - name: Test - run: cargo test --workspace + shell: bash + run: scripts/ci-nextest.sh # Only crates/fluent-* may use gpui (CONTRIBUTING.md §2). layering: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.99.1 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + - uses: dtolnay/rust-toolchain@d124f86f28ff42855784ff3319f48ac182c248f8 - name: Layering guard run: python3 scripts/check-layering.py @@ -47,8 +54,8 @@ jobs: deny: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: EmbarkStudios/cargo-deny-action@v2 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + - uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 with: command: check licenses bans @@ -56,7 +63,7 @@ jobs: zed-rev: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 - name: Check recorded zed rev run: scripts/check-zed-rev.sh diff --git a/scripts/ci-nextest.sh b/scripts/ci-nextest.sh new file mode 100755 index 0000000..c168c33 --- /dev/null +++ b/scripts/ci-nextest.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Merge queues and ordinary pushes are the authoritative full-workspace run. +if [[ "${GITHUB_EVENT_NAME:-}" != "pull_request" ]]; then + exec cargo nextest run --workspace +fi + +base_ref="origin/${GITHUB_BASE_REF:?GITHUB_BASE_REF is required for pull_request runs}" +git fetch --no-tags origin "${GITHUB_BASE_REF}" +mapfile -t changed < <(git diff --name-only "${base_ref}...HEAD") + +# A vendor change can affect any workspace crate, so it opts out of scoping. +for path in "${changed[@]}"; do + if [[ "$path" == vendor/* ]]; then + echo "vendor change detected; running the full workspace" + exec cargo nextest run --workspace + fi +done + +packages=() +declare -A seen=() +for path in "${changed[@]}"; do + if [[ "$path" =~ ^crates/(fluent-[^/]+)/ ]]; then + package="${BASH_REMATCH[1]}" + if [[ -z "${seen[$package]:-}" ]]; then + packages+=("$package") + seen[$package]=1 + fi + fi +done + +if ((${#packages[@]} == 0)); then + echo "no fluent-* crate changed; skipping package tests" + exit 0 +fi + +printf 'testing changed packages:' +printf ' %s' "${packages[@]}" +printf '\n' +args=() +for package in "${packages[@]}"; do + args+=(-p "$package") +done +exec cargo nextest run "${args[@]}" From f8c98522d27620216084632610230fadb36b19f7 Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:38:12 +0000 Subject: [PATCH 03/17] Complete Fluent 2 button appearance matrix Graft-Changeset: c0dc6d041b6cbb7cee6d20f1743330f8ff392386700e580c95adfcd0bd8f5f668 Graft-Session: ses_01M6F65FPF914JFHRR8DGVMXWY Graft-Work-Item: WI-2938 Co-authored-by: gpt-5.6-sol Graft-Batch: 21 --- crates/fluent-core/src/components.rs | 14 + crates/fluent-primitives/src/button.rs | 239 ++++++++++++++++-- crates/fluent-primitives/src/icon_button.rs | 3 +- crates/fluent-primitives/src/lib.rs | 5 +- crates/fluent-primitives/src/toggle_button.rs | 1 + examples/gallery/src/families.rs | 29 ++- 6 files changed, 256 insertions(+), 35 deletions(-) diff --git a/crates/fluent-core/src/components.rs b/crates/fluent-core/src/components.rs index e0b786f..5fa5eb4 100644 --- a/crates/fluent-core/src/components.rs +++ b/crates/fluent-core/src/components.rs @@ -5,6 +5,14 @@ /// icon gutters for aligned menus and popups. #[derive(Clone, Copy, Debug, PartialEq)] pub struct ComponentTokens { + /// Fluent 2 button heights for small, medium, and large controls. + pub button_small_height: f32, + pub button_medium_height: f32, + pub button_large_height: f32, + /// Fluent 2 button icon sizes for small, medium, and large controls. + pub button_small_icon_size: f32, + pub button_medium_icon_size: f32, + pub button_large_icon_size: f32, pub menu_bar_height: f32, pub menu_item_height: f32, pub menu_min_width: f32, @@ -40,6 +48,12 @@ pub struct ComponentTokens { impl Default for ComponentTokens { fn default() -> Self { Self { + button_small_height: 24.0, + button_medium_height: 32.0, + button_large_height: 40.0, + button_small_icon_size: 12.0, + button_medium_icon_size: 16.0, + button_large_icon_size: 20.0, menu_bar_height: 28.0, menu_item_height: 28.0, menu_min_width: 200.0, diff --git a/crates/fluent-primitives/src/button.rs b/crates/fluent-primitives/src/button.rs index 99f9e21..17285ce 100644 --- a/crates/fluent-primitives/src/button.rs +++ b/crates/fluent-primitives/src/button.rs @@ -9,11 +9,26 @@ use gpui::{ #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum ButtonAppearance { #[default] - Neutral, - Accent, - Danger, + Secondary, + Primary, + Outline, Subtle, - Hyperlink, + Transparent, +} + +impl ButtonAppearance { + /// Compatibility name for the former default appearance. + #[allow(non_upper_case_globals)] + pub const Neutral: Self = Self::Secondary; + /// Compatibility name for the former accent appearance. + #[allow(non_upper_case_globals)] + pub const Accent: Self = Self::Primary; + /// Compatibility name retained for applications migrating to the matrix. + #[allow(non_upper_case_globals)] + pub const Danger: Self = Self::Primary; + /// Compatibility name for text-only buttons. + #[allow(non_upper_case_globals)] + pub const Hyperlink: Self = Self::Transparent; } /// Corner rounding of a `Button`. @@ -29,8 +44,24 @@ pub enum ButtonShape { #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum ButtonSize { #[default] - Normal, - Compact, + Medium, + Small, + Large, +} + +impl ButtonSize { + #[allow(non_upper_case_globals)] + pub const Normal: Self = Self::Medium; + #[allow(non_upper_case_globals)] + pub const Compact: Self = Self::Small; +} + +/// Placement of an optional icon relative to the label. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum ButtonIconPosition { + #[default] + Before, + After, } /// A Fluent 2-styled push button with optional icon. @@ -47,6 +78,7 @@ pub struct Button { id: ElementId, label: Option, icon: Option, + icon_position: ButtonIconPosition, appearance: ButtonAppearance, shape: ButtonShape, size: ButtonSize, @@ -61,6 +93,7 @@ impl Button { id: id.into(), label: None, icon: None, + icon_position: ButtonIconPosition::default(), appearance: ButtonAppearance::default(), shape: ButtonShape::default(), size: ButtonSize::default(), @@ -80,6 +113,11 @@ impl Button { self } + pub fn icon_position(mut self, position: ButtonIconPosition) -> Self { + self.icon_position = position; + self + } + pub fn appearance(mut self, appearance: ButtonAppearance) -> Self { self.appearance = appearance; self @@ -124,8 +162,8 @@ impl RenderOnce for Button { let typography = &theme.typography; let (states, border): (ControlStates, Option) = match self.appearance { - ButtonAppearance::Accent => (colors.accent_control(), None), - ButtonAppearance::Neutral => ( + ButtonAppearance::Primary => (colors.accent_control(), None), + ButtonAppearance::Secondary => ( colors.neutral_control(), Some(if self.disabled { colors.neutral_stroke_1.disabled @@ -133,9 +171,16 @@ impl RenderOnce for Button { colors.neutral_stroke_1.default }), ), - ButtonAppearance::Danger => (colors.danger_control(), None), + ButtonAppearance::Outline => ( + colors.subtle_control(), + Some(if self.disabled { + colors.neutral_stroke_1.disabled + } else { + colors.neutral_stroke_1.default + }), + ), ButtonAppearance::Subtle => (colors.subtle_control(), None), - ButtonAppearance::Hyperlink => { + ButtonAppearance::Transparent => { let mut s = colors.subtle_control(); s.foreground = NeutralColorStates { default: colors.on_neutral_accent, @@ -160,18 +205,27 @@ impl RenderOnce for Button { ButtonShape::Circular => px(radii.pill), }; - let (px_val, py_val, font_size, line_height) = match self.size { - ButtonSize::Normal => ( - px(spacing.md), - px(spacing.sm), + let (height, px_val, font_size, line_height, icon_size) = match self.size { + ButtonSize::Medium => ( + theme.components.button_medium_height, + spacing.md, px(typography.body.size), px(typography.body.line_height), + theme.components.button_medium_icon_size, ), - ButtonSize::Compact => ( - px(spacing.sm), - px(spacing.xs), + ButtonSize::Small => ( + theme.components.button_small_height, + spacing.sm, px(typography.caption.size), px(typography.caption.line_height), + theme.components.button_small_icon_size, + ), + ButtonSize::Large => ( + theme.components.button_large_height, + spacing.lg, + px(typography.subtitle.size), + px(typography.subtitle.line_height), + theme.components.button_large_icon_size, ), }; @@ -180,6 +234,7 @@ impl RenderOnce for Button { let focus_ring = colors.focus_ring; let on_click = self.on_click; let icon = self.icon; + let icon_position = self.icon_position; let label = self.label; let has_icon = icon.is_some(); @@ -194,8 +249,8 @@ impl RenderOnce for Button { .items_center() .when(!has_icon, |d| d.justify_center()) // center text when no icon .gap(px(spacing.sm)) - .px(px_val) - .py(py_val) + .h(px(height)) + .px(px(px_val)) .rounded(radius) .bg(bg) .text_size(font_size) @@ -220,14 +275,13 @@ impl RenderOnce for Button { el = el.on_click(move |ev, win, app| handler(ev, win, app)); } - if let Some(icon_path) = icon { - let icon_size = match self.size { - ButtonSize::Normal => 16.0, - ButtonSize::Compact => 12.0, - }; + if let Some(icon_path) = icon + .as_ref() + .filter(|_| icon_position == ButtonIconPosition::Before) + { el = el.child( gpui::svg() - .path(icon_path) + .path(icon_path.clone()) .size(px(icon_size)) .text_color(fg), ); @@ -237,10 +291,145 @@ impl RenderOnce for Button { el = el.child(text); } + if let Some(icon_path) = icon.filter(|_| icon_position == ButtonIconPosition::After) { + el = el.child( + gpui::svg() + .path(icon_path) + .size(px(icon_size)) + .text_color(fg), + ); + } + el } } +/// A Fluent 2 button with a primary action and a separate menu action. +#[allow(clippy::type_complexity)] +#[derive(IntoElement)] +pub struct SplitButton { + id: ElementId, + label: SharedString, + appearance: ButtonAppearance, + disabled: bool, + on_click: Option>, + on_menu_click: Option>, +} + +impl SplitButton { + pub fn new(id: impl Into, label: impl Into) -> Self { + Self { + id: id.into(), + label: label.into(), + appearance: ButtonAppearance::Secondary, + disabled: false, + on_click: None, + on_menu_click: None, + } + } + + pub fn appearance(mut self, appearance: ButtonAppearance) -> Self { + self.appearance = appearance; + self + } + + pub fn disabled(mut self, disabled: bool) -> Self { + self.disabled = disabled; + self + } + + pub fn on_click( + mut self, + handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, + ) -> Self { + self.on_click = Some(Box::new(handler)); + self + } + + pub fn on_menu_click( + mut self, + handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, + ) -> Self { + self.on_menu_click = Some(Box::new(handler)); + self + } +} + +impl RenderOnce for SplitButton { + fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { + let main_id = ElementId::Name(format!("{}-primary", self.id).into()); + let menu_id = ElementId::Name(format!("{}-menu", self.id).into()); + let mut primary = Button::new(main_id) + .label(self.label) + .appearance(self.appearance) + .shape(ButtonShape::Square) + .disabled(self.disabled); + if let Some(handler) = self.on_click { + primary = primary.on_click(handler); + } + let mut menu = Button::new(menu_id) + .label("More options") + .appearance(self.appearance) + .shape(ButtonShape::Square) + .disabled(self.disabled); + if let Some(handler) = self.on_menu_click { + menu = menu.on_click(handler); + } + div() + .id(self.id) + .flex() + .flex_row() + .child(primary) + .child(menu) + } +} + +/// A two-line Fluent 2 button for an action that benefits from description. +#[derive(IntoElement)] +pub struct CompoundButton { + button: Button, +} + +impl CompoundButton { + pub fn new( + id: impl Into, + label: impl Into, + secondary_content: impl Into, + ) -> Self { + let label = label.into(); + let secondary_content = secondary_content.into(); + Self { + button: Button::new(id) + .label(format!("{label}\n{secondary_content}")) + .size(ButtonSize::Large), + } + } + + pub fn appearance(mut self, appearance: ButtonAppearance) -> Self { + self.button = self.button.appearance(appearance); + self + } + + pub fn disabled(mut self, disabled: bool) -> Self { + self.button = self.button.disabled(disabled); + self + } + + pub fn on_click( + mut self, + handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, + ) -> Self { + self.button = self.button.on_click(handler); + self + } +} + +impl RenderOnce for CompoundButton { + fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { + self.button + } +} + #[cfg(test)] mod tests { use std::{cell::Cell, rc::Rc}; diff --git a/crates/fluent-primitives/src/icon_button.rs b/crates/fluent-primitives/src/icon_button.rs index 814c01a..b10e75a 100644 --- a/crates/fluent-primitives/src/icon_button.rs +++ b/crates/fluent-primitives/src/icon_button.rs @@ -90,7 +90,7 @@ impl RenderOnce for IconButton { let radii = &theme.radii; let (bg, bg_hover, bg_active, fg) = match self.appearance { - ButtonAppearance::Accent => ( + ButtonAppearance::Primary => ( if self.disabled { colors.accent_disabled } else { @@ -119,6 +119,7 @@ impl RenderOnce for IconButton { let (icon_px, hit_px) = match self.size { ButtonSize::Normal => (16.0f32, 32.0f32), ButtonSize::Compact => (12.0f32, 24.0f32), + ButtonSize::Large => (20.0f32, 40.0f32), }; let disabled = self.disabled; diff --git a/crates/fluent-primitives/src/lib.rs b/crates/fluent-primitives/src/lib.rs index 56e6ece..b0d9042 100644 --- a/crates/fluent-primitives/src/lib.rs +++ b/crates/fluent-primitives/src/lib.rs @@ -52,7 +52,10 @@ pub use avatar::Avatar; pub use badge::{ Badge, BadgeVariant, CounterBadge, CounterBadgeAppearance, PresenceBadge, PresenceBadgeState, }; -pub use button::{Button, ButtonAppearance, ButtonShape, ButtonSize}; +pub use button::{ + Button, ButtonAppearance, ButtonIconPosition, ButtonShape, ButtonSize, CompoundButton, + SplitButton, +}; pub use calendar::{Calendar, CalendarDate}; pub use card::Card; pub use checkbox::{Checkbox, CheckboxState}; diff --git a/crates/fluent-primitives/src/toggle_button.rs b/crates/fluent-primitives/src/toggle_button.rs index e6ec270..6e92cb7 100644 --- a/crates/fluent-primitives/src/toggle_button.rs +++ b/crates/fluent-primitives/src/toggle_button.rs @@ -106,6 +106,7 @@ impl RenderOnce for ToggleButton { let (px_val, py_val, font_size) = match self.size { ButtonSize::Normal => (px(spacing.md), px(spacing.sm), px(typography.body.size)), ButtonSize::Compact => (px(spacing.sm), px(spacing.xs), px(typography.caption.size)), + ButtonSize::Large => (px(spacing.lg), px(spacing.md), px(typography.subtitle.size)), }; let disabled = self.disabled; diff --git a/examples/gallery/src/families.rs b/examples/gallery/src/families.rs index 56bacc5..d4ddc8d 100644 --- a/examples/gallery/src/families.rs +++ b/examples/gallery/src/families.rs @@ -7,8 +7,8 @@ use fluent_core::{LayoutDirection, Theme, ThemeProvider as _}; use fluent_primitives::{ - Button, ButtonAppearance, ButtonShape, ButtonSize, Checkbox, CheckboxState, Chip, Divider, - Label, LabelSize, Link, LinkAppearance, RadioGroup, Switch, ToggleButton, + Button, ButtonAppearance, ButtonIconPosition, ButtonShape, ButtonSize, Checkbox, CheckboxState, + Chip, Divider, Label, LabelSize, Link, LinkAppearance, RadioGroup, Switch, ToggleButton, }; use gpui::{div, prelude::*, AnyElement, AnyView, Context, Entity, IntoElement, Render, Window}; @@ -106,11 +106,11 @@ impl FamilyHost { fn buttons() -> impl IntoElement { let appearances = [ - ("Neutral", ButtonAppearance::Neutral), - ("Accent", ButtonAppearance::Accent), - ("Danger", ButtonAppearance::Danger), + ("Secondary", ButtonAppearance::Secondary), + ("Primary", ButtonAppearance::Primary), + ("Outline", ButtonAppearance::Outline), ("Subtle", ButtonAppearance::Subtle), - ("Hyperlink", ButtonAppearance::Hyperlink), + ("Transparent", ButtonAppearance::Transparent), ]; let enabled = appearances .iter() @@ -150,8 +150,21 @@ impl FamilyHost { .shape(ButtonShape::Circular) .into_any_element(), Button::new("size-c") - .label("Compact") - .size(ButtonSize::Compact) + .label("Small") + .size(ButtonSize::Small) + .into_any_element(), + Button::new("size-m") + .label("Medium") + .size(ButtonSize::Medium) + .into_any_element(), + Button::new("size-l") + .label("Large") + .size(ButtonSize::Large) + .into_any_element(), + Button::new("icon-after") + .label("Icon after") + .icon("icons/add.svg") + .icon_position(ButtonIconPosition::After) .into_any_element(), ], )) From 42daa8381e47c3585c0e7f1289e5248f0ce85521 Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:33:15 +0000 Subject: [PATCH 04/17] chore(fluent-core): re-export gpui for examples Graft-Changeset: c6698c9f9f22a18685a9543721c5e4a14bdf36ccdb1d562c8c5cb9e40a254400d Graft-Session: ses_01M6F65G3D70B9YJ11T6ZS3KKX Graft-Work-Item: WI-2731 Graft-Batch: 21 --- crates/fluent-core/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/fluent-core/src/lib.rs b/crates/fluent-core/src/lib.rs index 1ead989..60f72aa 100644 --- a/crates/fluent-core/src/lib.rs +++ b/crates/fluent-core/src/lib.rs @@ -9,6 +9,9 @@ mod stroke_width; mod theme; mod typography; +/// Re-export the underlying UI toolkit for examples and downstream Fluent crates. +pub use gpui; + pub use async_resource::{AsyncResource, AsyncResourceState, ResourceRequest}; pub use colors::{ gradient_from_hue, hue_for, tint, with_alpha, ColorScheme, ControlStates, NeutralColorStates, From 98787d7a385e6aece854004e678a771d5d6faf71 Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:33:43 +0000 Subject: [PATCH 05/17] chore(fluent-core): re-export gpui platform entry point Graft-Changeset: c806465ce56b6507f044ae5ea7755a87f8026dd7ee7691733d1ecdcea7159065c Graft-Session: ses_01M6F65G3D70B9YJ11T6ZS3KKX Graft-Work-Item: WI-2731 Graft-Batch: 21 --- crates/fluent-core/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/fluent-core/src/lib.rs b/crates/fluent-core/src/lib.rs index 60f72aa..cb736a0 100644 --- a/crates/fluent-core/src/lib.rs +++ b/crates/fluent-core/src/lib.rs @@ -11,6 +11,7 @@ mod typography; /// Re-export the underlying UI toolkit for examples and downstream Fluent crates. pub use gpui; +pub use gpui_platform; pub use async_resource::{AsyncResource, AsyncResourceState, ResourceRequest}; pub use colors::{ From dffac715291f0548f52385cf6886f89fc90378ab Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:34:08 +0000 Subject: [PATCH 06/17] refactor(hello-world): use fluent gpui re-export Graft-Changeset: c4dd183bf34f714a050ae300c3b8f4470ca89fd73645286c720267151236da857 Graft-Session: ses_01M6F65G3D70B9YJ11T6ZS3KKX Graft-Work-Item: WI-2731 Graft-Batch: 21 --- Cargo.lock | 1 - deny.toml | 2 +- examples/hello_world/Cargo.toml | 1 - examples/hello_world/src/main.rs | 2 +- scripts/check-layering.py | 1 - 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5ec297..5b4203a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2239,7 +2239,6 @@ dependencies = [ "fluent-app", "fluent-core", "fluent-primitives", - "gpui", ] [[package]] diff --git a/deny.toml b/deny.toml index e7c5bb7..daf7c66 100644 --- a/deny.toml +++ b/deny.toml @@ -62,7 +62,7 @@ deny = [ { crate = "gpui", wrappers = [ "fluent-core", "fluent-primitives", "fluent-ribbon", "fluent-layout", "fluent-app", "fluent-test-utils", - "gallery", "demo_app", "hello_world", "widgets", "form", "screenshots", + "gallery", "demo_app", "widgets", "form", "screenshots", "gpui_linux", "gpui_macros", "gpui_platform", "gpui_windows", "gpui_wgpu", ] }, ] diff --git a/examples/hello_world/Cargo.toml b/examples/hello_world/Cargo.toml index c0ccc65..022b098 100644 --- a/examples/hello_world/Cargo.toml +++ b/examples/hello_world/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" publish = false [dependencies] -gpui = { workspace = true } fluent-core = { workspace = true } fluent-primitives = { workspace = true } fluent-app = { workspace = true } diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index e76c5aa..932074a 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -1,7 +1,7 @@ use fluent_app::FluentApp; use fluent_core::ThemeProvider as _; use fluent_primitives::{Button, ButtonAppearance, Label, LabelSize}; -use gpui::{div, prelude::*, px, ClickEvent, Context, IntoElement, Render, Window}; +use fluent_core::gpui::{div, prelude::*, px, ClickEvent, Context, IntoElement, Render, Window}; struct Counter { count: u32, diff --git a/scripts/check-layering.py b/scripts/check-layering.py index 0c54a86..feff30d 100755 --- a/scripts/check-layering.py +++ b/scripts/check-layering.py @@ -18,7 +18,6 @@ "examples/demo_app", "examples/form", "examples/gallery", - "examples/hello_world", "examples/screenshots", "examples/widgets", } From f412c305537d2879b00d7ca6a840a852c2805eb6 Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:35:03 +0000 Subject: [PATCH 07/17] chore(fluent-core): expose gpui surface directly Graft-Changeset: c3f1be442b78593680403a0b341c82668b87f0bfbeb54ddd63e3954967a3860f2 Graft-Session: ses_01M6F65G3D70B9YJ11T6ZS3KKX Graft-Work-Item: WI-2731 Graft-Batch: 21 --- Cargo.lock | 1 - crates/fluent-core/src/lib.rs | 4 ++-- deny.toml | 2 +- examples/form/Cargo.toml | 1 - examples/form/src/main.rs | 6 +++--- examples/hello_world/src/main.rs | 2 +- scripts/check-layering.py | 1 - 7 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b4203a..c687212 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1673,7 +1673,6 @@ dependencies = [ "fluent-app", "fluent-core", "fluent-primitives", - "gpui", ] [[package]] diff --git a/crates/fluent-core/src/lib.rs b/crates/fluent-core/src/lib.rs index cb736a0..4a51673 100644 --- a/crates/fluent-core/src/lib.rs +++ b/crates/fluent-core/src/lib.rs @@ -10,8 +10,8 @@ mod theme; mod typography; /// Re-export the underlying UI toolkit for examples and downstream Fluent crates. -pub use gpui; -pub use gpui_platform; +pub use gpui::*; +pub use gpui_platform::application; pub use async_resource::{AsyncResource, AsyncResourceState, ResourceRequest}; pub use colors::{ diff --git a/deny.toml b/deny.toml index daf7c66..bbbfc58 100644 --- a/deny.toml +++ b/deny.toml @@ -62,7 +62,7 @@ deny = [ { crate = "gpui", wrappers = [ "fluent-core", "fluent-primitives", "fluent-ribbon", "fluent-layout", "fluent-app", "fluent-test-utils", - "gallery", "demo_app", "widgets", "form", "screenshots", + "gallery", "demo_app", "widgets", "screenshots", "gpui_linux", "gpui_macros", "gpui_platform", "gpui_windows", "gpui_wgpu", ] }, ] diff --git a/examples/form/Cargo.toml b/examples/form/Cargo.toml index d914b48..2632871 100644 --- a/examples/form/Cargo.toml +++ b/examples/form/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" publish = false [dependencies] -gpui = { workspace = true } fluent-core = { workspace = true } fluent-primitives = { workspace = true } fluent-app = { workspace = true } diff --git a/examples/form/src/main.rs b/examples/form/src/main.rs index b2fdbff..221afdd 100644 --- a/examples/form/src/main.rs +++ b/examples/form/src/main.rs @@ -4,7 +4,7 @@ use fluent_primitives::{ Button, ButtonAppearance, Checkbox, CheckboxState, Divider, Dropdown, Label, LabelSize, Switch, TextInput, }; -use gpui::{ +use fluent_core::{ div, prelude::*, px, ClickEvent, Context, Entity, IntoElement, Render, SharedString, Window, }; @@ -21,7 +21,7 @@ struct FormDemo { } impl FormDemo { - fn new(cx: &mut gpui::App) -> Self { + fn new(cx: &mut fluent_core::App) -> Self { Self { name: cx.new(|_| TextInput::new().placeholder("e.g. My Home Server")), host: cx.new(|_| TextInput::new().placeholder("e.g. 192.168.1.100")), @@ -68,7 +68,7 @@ impl Render for FormDemo { }; // ── row helper (label + control, space-between) ──────────────────── - let control_row = |label_text: &'static str, control: gpui::AnyElement| { + let control_row = |label_text: &'static str, control: fluent_core::AnyElement| { div() .flex() .flex_row() diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index 932074a..7fe2944 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -1,7 +1,7 @@ use fluent_app::FluentApp; use fluent_core::ThemeProvider as _; use fluent_primitives::{Button, ButtonAppearance, Label, LabelSize}; -use fluent_core::gpui::{div, prelude::*, px, ClickEvent, Context, IntoElement, Render, Window}; +use fluent_core::{div, prelude::*, px, ClickEvent, Context, IntoElement, Render, Window}; struct Counter { count: u32, diff --git a/scripts/check-layering.py b/scripts/check-layering.py index feff30d..e8b5b37 100755 --- a/scripts/check-layering.py +++ b/scripts/check-layering.py @@ -16,7 +16,6 @@ # BASELINE: examples that predate the rule. Shrink, never grow (see deny.toml). BASELINE = { "examples/demo_app", - "examples/form", "examples/gallery", "examples/screenshots", "examples/widgets", From 97a7ce111ea27f0bb20c0e0d30be78c042f84466 Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:35:12 +0000 Subject: [PATCH 08/17] refactor(gallery): use fluent gpui re-exports Graft-Changeset: cae1fdaeb68e4fd915674ab48d847cad30830a1ce6d56a8f031443486c051dcf7 Graft-Session: ses_01M6F65G3D70B9YJ11T6ZS3KKX Graft-Work-Item: WI-2731 Graft-Batch: 21 --- Cargo.lock | 1 - deny.toml | 2 +- examples/gallery/Cargo.toml | 1 - examples/gallery/src/families.rs | 4 ++-- examples/gallery/src/main.rs | 20 ++++++++++---------- scripts/check-layering.py | 1 - 6 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c687212..fb46a0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1818,7 +1818,6 @@ dependencies = [ "fluent-layout", "fluent-primitives", "fluent-ribbon", - "gpui", ] [[package]] diff --git a/deny.toml b/deny.toml index bbbfc58..d6de19c 100644 --- a/deny.toml +++ b/deny.toml @@ -62,7 +62,7 @@ deny = [ { crate = "gpui", wrappers = [ "fluent-core", "fluent-primitives", "fluent-ribbon", "fluent-layout", "fluent-app", "fluent-test-utils", - "gallery", "demo_app", "widgets", "screenshots", + "demo_app", "widgets", "screenshots", "gpui_linux", "gpui_macros", "gpui_platform", "gpui_windows", "gpui_wgpu", ] }, ] diff --git a/examples/gallery/Cargo.toml b/examples/gallery/Cargo.toml index c3333a7..81c7bfb 100644 --- a/examples/gallery/Cargo.toml +++ b/examples/gallery/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" publish = false [dependencies] -gpui = { workspace = true } fluent-core = { workspace = true } fluent-ribbon = { workspace = true } fluent-layout = { workspace = true } diff --git a/examples/gallery/src/families.rs b/examples/gallery/src/families.rs index d4ddc8d..e36ebbd 100644 --- a/examples/gallery/src/families.rs +++ b/examples/gallery/src/families.rs @@ -10,7 +10,7 @@ use fluent_primitives::{ Button, ButtonAppearance, ButtonIconPosition, ButtonShape, ButtonSize, Checkbox, CheckboxState, Chip, Divider, Label, LabelSize, Link, LinkAppearance, RadioGroup, Switch, ToggleButton, }; -use gpui::{div, prelude::*, AnyElement, AnyView, Context, Entity, IntoElement, Render, Window}; +use fluent_core::{div, prelude::*, AnyElement, AnyView, Context, Entity, IntoElement, Render, Window}; /// A Fluent 2 component family, one gallery route each. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -97,7 +97,7 @@ impl FamilyHost { .gap_3() .children(views.iter().map(|v| { div() - .h(gpui::px(300.0)) + .h(fluent_core::px(300.0)) .relative() .overflow_hidden() .child(v.clone()) diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index ae183f3..893682d 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -17,14 +17,14 @@ use fluent_ribbon::RibbonBar; mod families; use families::{DemoViews, FamilyHost, FamilyNav}; -use gpui::{div, prelude::*, px, rgb, Context, Entity, IntoElement, Render, SharedString, Window}; +use fluent_core::{div, prelude::*, px, rgb, Context, Entity, IntoElement, Render, SharedString, Window}; // --------------------------------------------------------------------------- // Root gallery view — wires the full shell together // --------------------------------------------------------------------------- struct GalleryRoot { - workspace: gpui::Entity, + workspace: fluent_core::Entity, } /// Data-heavy screens in every non-ready state: an rtbit torrent list and an @@ -125,7 +125,7 @@ impl Render for BigListDemo { .flex_col() .gap_2() .p_2() - .child(div().text_size(gpui::px(12.0)).child(format!( + .child(div().text_size(fluent_core::px(12.0)).child(format!( "100,000 rows (variable height), row {} selected: click, or use Up/Down/PageUp/PageDown/Home/End", self.selected + 1 ))) @@ -158,7 +158,7 @@ impl Render for BigListDemo { impl Render for DataStatesDemo { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let label = |t: &'static str| div().text_size(gpui::px(12.0)).child(t); + let label = |t: &'static str| div().text_size(fluent_core::px(12.0)).child(t); div() .flex() .flex_col() @@ -243,8 +243,8 @@ impl Render for DateTimeDemo { div() .flex() .flex_col() - .gap(gpui::px(theme.spacing.sm)) - .p(gpui::px(theme.spacing.md)) + .gap(fluent_core::px(theme.spacing.sm)) + .p(fluent_core::px(theme.spacing.md)) .child("Date/time: en-US and de-DE (locale and timezone owned by the app)") .child(self.us.clone()) .child(self.de.clone()) @@ -275,8 +275,8 @@ impl Render for DownloadProgressDemo { div() .flex() .flex_col() - .gap(gpui::px(theme.spacing.sm)) - .p(gpui::px(theme.spacing.md)) + .gap(fluent_core::px(theme.spacing.sm)) + .p(fluent_core::px(theme.spacing.md)) .child("rtbit downloads (value text is announced; animation honours reduced motion)") .child(row( "ubuntu-26.04-desktop.iso", @@ -615,7 +615,7 @@ impl Render for RangeInputDemo { let entity = cx.entity(); let set = |f: fn(&mut RangeInputDemo, f64)| { let entity = entity.clone(); - move |v: f64, _: &mut Window, cx: &mut gpui::App| { + move |v: f64, _: &mut Window, cx: &mut fluent_core::App| { entity.update(cx, |d, cx| { f(d, v); cx.notify(); @@ -1026,7 +1026,7 @@ fn main() { }); // Main pane with a tab strip - let tab_strip = cx.new(|cx: &mut gpui::Context| { + let tab_strip = cx.new(|cx: &mut fluent_core::Context| { let mut ts = TabStrip::new(cx); ts.add_tab(TabItem::new("dashboard").label("Dashboard").closable(true)); ts.add_tab(TabItem::new("settings").label("Settings").closable(true)); diff --git a/scripts/check-layering.py b/scripts/check-layering.py index e8b5b37..8bfc6f6 100755 --- a/scripts/check-layering.py +++ b/scripts/check-layering.py @@ -16,7 +16,6 @@ # BASELINE: examples that predate the rule. Shrink, never grow (see deny.toml). BASELINE = { "examples/demo_app", - "examples/gallery", "examples/screenshots", "examples/widgets", } From 091a00e8e9aa80e2f3493354608ea34bdd2eb06b Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:37:11 +0000 Subject: [PATCH 09/17] refactor(examples): use fluent gpui re-exports Graft-Changeset: cb3b8fc604757d0174245ce774e4d6278ff46bc8db99f6205babe51647550e155 Graft-Session: ses_01M6F65G3D70B9YJ11T6ZS3KKX Graft-Work-Item: WI-2731 Graft-Batch: 21 --- Cargo.lock | 5 +---- crates/fluent-core/src/lib.rs | 4 ++-- deny.toml | 2 +- examples/demo_app/Cargo.toml | 1 - examples/demo_app/src/main.rs | 27 ++++++++++++++------------- examples/form/src/main.rs | 6 +++--- examples/gallery/Cargo.toml | 1 + examples/gallery/src/families.rs | 6 +++--- examples/gallery/src/main.rs | 22 +++++++++++----------- examples/hello_world/src/main.rs | 2 +- examples/screenshots/Cargo.toml | 2 -- examples/screenshots/src/main.rs | 10 +++++----- examples/widgets/Cargo.toml | 1 - examples/widgets/src/main.rs | 2 +- scripts/check-layering.py | 6 ++---- 15 files changed, 45 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb46a0a..b1734d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1147,7 +1147,6 @@ dependencies = [ "fluent-core", "fluent-layout", "fluent-ribbon", - "gpui", ] [[package]] @@ -1818,6 +1817,7 @@ dependencies = [ "fluent-layout", "fluent-primitives", "fluent-ribbon", + "gpui", ] [[package]] @@ -4345,8 +4345,6 @@ dependencies = [ "fluent-core", "fluent-layout", "fluent-primitives", - "gpui", - "gpui_platform", ] [[package]] @@ -5709,7 +5707,6 @@ dependencies = [ "fluent-core", "fluent-layout", "fluent-primitives", - "gpui", ] [[package]] diff --git a/crates/fluent-core/src/lib.rs b/crates/fluent-core/src/lib.rs index 4a51673..cb736a0 100644 --- a/crates/fluent-core/src/lib.rs +++ b/crates/fluent-core/src/lib.rs @@ -10,8 +10,8 @@ mod theme; mod typography; /// Re-export the underlying UI toolkit for examples and downstream Fluent crates. -pub use gpui::*; -pub use gpui_platform::application; +pub use gpui; +pub use gpui_platform; pub use async_resource::{AsyncResource, AsyncResourceState, ResourceRequest}; pub use colors::{ diff --git a/deny.toml b/deny.toml index d6de19c..9f963a9 100644 --- a/deny.toml +++ b/deny.toml @@ -62,7 +62,7 @@ deny = [ { crate = "gpui", wrappers = [ "fluent-core", "fluent-primitives", "fluent-ribbon", "fluent-layout", "fluent-app", "fluent-test-utils", - "demo_app", "widgets", "screenshots", + "gallery", "gpui_linux", "gpui_macros", "gpui_platform", "gpui_windows", "gpui_wgpu", ] }, ] diff --git a/examples/demo_app/Cargo.toml b/examples/demo_app/Cargo.toml index f5471b9..25dcc5f 100644 --- a/examples/demo_app/Cargo.toml +++ b/examples/demo_app/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" publish = false [dependencies] -gpui = { workspace = true } fluent-core = { workspace = true } fluent-ribbon = { workspace = true } fluent-layout = { workspace = true } diff --git a/examples/demo_app/src/main.rs b/examples/demo_app/src/main.rs index 0eeb60a..1afed05 100644 --- a/examples/demo_app/src/main.rs +++ b/examples/demo_app/src/main.rs @@ -6,7 +6,8 @@ use fluent_layout::{ SettingsNavItem, SettingsNavSection, TabItem, TabStrip, Workspace, }; use fluent_ribbon::RibbonBar; -use gpui::{ +use fluent_core::gpui as gpui; +use fluent_core::gpui::{ actions, div, prelude::*, px, App, Context, Entity, FontWeight, IntoElement, KeyBinding, MouseButton, MouseDownEvent, Render, SharedString, Window, }; @@ -105,8 +106,8 @@ impl Render for ProjectView { let spacing = theme.spacing; let typography = theme.typography; - let priority_color = gpui::rgb(self.project.priority.color_hex()); - let status_color = gpui::rgb(self.project.status.color_hex()); + let priority_color = fluent_core::gpui::rgb(self.project.priority.color_hex()); + let status_color = fluent_core::gpui::rgb(self.project.status.color_hex()); div() .size_full() @@ -137,7 +138,7 @@ impl Render for ProjectView { .py(px(2.0)) .rounded(px(4.0)) .bg(status_color) - .text_color(gpui::rgb(0xFFFFFF)) + .text_color(fluent_core::gpui::rgb(0xFFFFFF)) .text_size(px(typography.caption.size)) .font_weight(FontWeight::SEMIBOLD) .child(self.project.status.label()), @@ -280,7 +281,7 @@ impl Render for DashboardView { .flex() .flex_col() .gap(px(spacing.xs)) - .child(div().size(px(8.0)).rounded(px(99.0)).bg(gpui::rgb(hex))) + .child(div().size(px(8.0)).rounded(px(99.0)).bg(fluent_core::gpui::rgb(hex))) .child( div() .text_size(px(typography.display.size)) @@ -350,8 +351,8 @@ impl Render for DashboardView { .flex_col() .gap(px(spacing.xs)) .children(starred.iter().map(|p| { - let status_color = gpui::rgb(p.status.color_hex()); - let priority_color = gpui::rgb(p.priority.color_hex()); + let status_color = fluent_core::gpui::rgb(p.status.color_hex()); + let priority_color = fluent_core::gpui::rgb(p.priority.color_hex()); div() .flex() .flex_row() @@ -364,7 +365,7 @@ impl Render for DashboardView { .child( div() .text_size(px(10.0)) - .text_color(gpui::rgb(0xFF9800)) + .text_color(fluent_core::gpui::rgb(0xFF9800)) .child("★"), ) .child( @@ -386,7 +387,7 @@ impl Render for DashboardView { .py(px(1.0)) .rounded(px(3.0)) .bg(status_color) - .text_color(gpui::rgb(0xFFFFFF)) + .text_color(fluent_core::gpui::rgb(0xFFFFFF)) .text_size(px(typography.caption.size)) .child(p.status.label()), ) @@ -511,7 +512,7 @@ impl Render for ProjectTree { for (pi, project) in board.projects.iter().enumerate() { let is_selected = selected == Some((bi, pi)); let project_clone = project.clone(); - let status_color = gpui::rgb(project.status.color_hex()); + let status_color = fluent_core::gpui::rgb(project.status.color_hex()); let project_name = project.name.clone(); let starred = project.starred; let row_bg = if is_selected { @@ -577,7 +578,7 @@ impl Render for ProjectTree { .child(if starred { div() .text_size(px(10.0)) - .text_color(gpui::rgb(0xFF9800)) + .text_color(fluent_core::gpui::rgb(0xFF9800)) .child("★") } else { div() @@ -590,7 +591,7 @@ impl Render for ProjectTree { if menu_entity.read(cx).open { tree = tree.child(menu_entity.clone()); } else { - let _ = cx.listener(|tree: &mut ProjectTree, _: &gpui::ClickEvent, _, cx| { + let _ = cx.listener(|tree: &mut ProjectTree, _: &fluent_core::gpui::ClickEvent, _, cx| { tree.context_menu = None; cx.notify(); }); @@ -1323,7 +1324,7 @@ fn main() { } }); - let tab_strip = cx.new(|cx: &mut gpui::Context| { + let tab_strip = cx.new(|cx: &mut fluent_core::gpui::Context| { let mut ts = TabStrip::new(cx); ts.add_tab(TabItem::new("dashboard").label("Dashboard")); ts diff --git a/examples/form/src/main.rs b/examples/form/src/main.rs index 221afdd..90b0e66 100644 --- a/examples/form/src/main.rs +++ b/examples/form/src/main.rs @@ -4,7 +4,7 @@ use fluent_primitives::{ Button, ButtonAppearance, Checkbox, CheckboxState, Divider, Dropdown, Label, LabelSize, Switch, TextInput, }; -use fluent_core::{ +use fluent_core::gpui::{ div, prelude::*, px, ClickEvent, Context, Entity, IntoElement, Render, SharedString, Window, }; @@ -21,7 +21,7 @@ struct FormDemo { } impl FormDemo { - fn new(cx: &mut fluent_core::App) -> Self { + fn new(cx: &mut fluent_core::gpui::App) -> Self { Self { name: cx.new(|_| TextInput::new().placeholder("e.g. My Home Server")), host: cx.new(|_| TextInput::new().placeholder("e.g. 192.168.1.100")), @@ -68,7 +68,7 @@ impl Render for FormDemo { }; // ── row helper (label + control, space-between) ──────────────────── - let control_row = |label_text: &'static str, control: fluent_core::AnyElement| { + let control_row = |label_text: &'static str, control: fluent_core::gpui::AnyElement| { div() .flex() .flex_row() diff --git a/examples/gallery/Cargo.toml b/examples/gallery/Cargo.toml index 81c7bfb..18ed4b0 100644 --- a/examples/gallery/Cargo.toml +++ b/examples/gallery/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" publish = false [dependencies] +gpui = { workspace = true } fluent-core = { workspace = true } fluent-ribbon = { workspace = true } fluent-layout = { workspace = true } diff --git a/examples/gallery/src/families.rs b/examples/gallery/src/families.rs index e36ebbd..c95f4df 100644 --- a/examples/gallery/src/families.rs +++ b/examples/gallery/src/families.rs @@ -5,12 +5,12 @@ //! theme row on top switches light, dark, high contrast and RTL so each //! family can be checked in every variant without leaving the page. -use fluent_core::{LayoutDirection, Theme, ThemeProvider as _}; +use gpui::{LayoutDirection, Theme, ThemeProvider as _}; use fluent_primitives::{ Button, ButtonAppearance, ButtonIconPosition, ButtonShape, ButtonSize, Checkbox, CheckboxState, Chip, Divider, Label, LabelSize, Link, LinkAppearance, RadioGroup, Switch, ToggleButton, }; -use fluent_core::{div, prelude::*, AnyElement, AnyView, Context, Entity, IntoElement, Render, Window}; +use gpui::{div, prelude::*, AnyElement, AnyView, Context, Entity, IntoElement, Render, Window}; /// A Fluent 2 component family, one gallery route each. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -97,7 +97,7 @@ impl FamilyHost { .gap_3() .children(views.iter().map(|v| { div() - .h(fluent_core::px(300.0)) + .h(gpui::px(300.0)) .relative() .overflow_hidden() .child(v.clone()) diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index 893682d..7511f38 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -1,5 +1,5 @@ use fluent_app::{FluentApp, TitleBar}; -use fluent_core::{LayoutDirection, Theme, ThemeProvider as _}; +use gpui::{LayoutDirection, Theme, ThemeProvider as _}; use fluent_layout::{ Breadcrumb, BreadcrumbItem, DataCell, DataColumn, DataRow, DataStatusKind, DataTable, DataTableState, DockPanel, DockPosition, Drawer, DrawerMode, DrawerPosition, DrawerSize, @@ -17,14 +17,14 @@ use fluent_ribbon::RibbonBar; mod families; use families::{DemoViews, FamilyHost, FamilyNav}; -use fluent_core::{div, prelude::*, px, rgb, Context, Entity, IntoElement, Render, SharedString, Window}; +use gpui::{div, prelude::*, px, rgb, Context, Entity, IntoElement, Render, SharedString, Window}; // --------------------------------------------------------------------------- // Root gallery view — wires the full shell together // --------------------------------------------------------------------------- struct GalleryRoot { - workspace: fluent_core::Entity, + workspace: gpui::Entity, } /// Data-heavy screens in every non-ready state: an rtbit torrent list and an @@ -125,7 +125,7 @@ impl Render for BigListDemo { .flex_col() .gap_2() .p_2() - .child(div().text_size(fluent_core::px(12.0)).child(format!( + .child(div().text_size(gpui::px(12.0)).child(format!( "100,000 rows (variable height), row {} selected: click, or use Up/Down/PageUp/PageDown/Home/End", self.selected + 1 ))) @@ -158,7 +158,7 @@ impl Render for BigListDemo { impl Render for DataStatesDemo { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let label = |t: &'static str| div().text_size(fluent_core::px(12.0)).child(t); + let label = |t: &'static str| div().text_size(gpui::px(12.0)).child(t); div() .flex() .flex_col() @@ -243,8 +243,8 @@ impl Render for DateTimeDemo { div() .flex() .flex_col() - .gap(fluent_core::px(theme.spacing.sm)) - .p(fluent_core::px(theme.spacing.md)) + .gap(gpui::px(theme.spacing.sm)) + .p(gpui::px(theme.spacing.md)) .child("Date/time: en-US and de-DE (locale and timezone owned by the app)") .child(self.us.clone()) .child(self.de.clone()) @@ -275,8 +275,8 @@ impl Render for DownloadProgressDemo { div() .flex() .flex_col() - .gap(fluent_core::px(theme.spacing.sm)) - .p(fluent_core::px(theme.spacing.md)) + .gap(gpui::px(theme.spacing.sm)) + .p(gpui::px(theme.spacing.md)) .child("rtbit downloads (value text is announced; animation honours reduced motion)") .child(row( "ubuntu-26.04-desktop.iso", @@ -615,7 +615,7 @@ impl Render for RangeInputDemo { let entity = cx.entity(); let set = |f: fn(&mut RangeInputDemo, f64)| { let entity = entity.clone(); - move |v: f64, _: &mut Window, cx: &mut fluent_core::App| { + move |v: f64, _: &mut Window, cx: &mut gpui::App| { entity.update(cx, |d, cx| { f(d, v); cx.notify(); @@ -1026,7 +1026,7 @@ fn main() { }); // Main pane with a tab strip - let tab_strip = cx.new(|cx: &mut fluent_core::Context| { + let tab_strip = cx.new(|cx: &mut gpui::Context| { let mut ts = TabStrip::new(cx); ts.add_tab(TabItem::new("dashboard").label("Dashboard").closable(true)); ts.add_tab(TabItem::new("settings").label("Settings").closable(true)); diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index 7fe2944..932074a 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -1,7 +1,7 @@ use fluent_app::FluentApp; use fluent_core::ThemeProvider as _; use fluent_primitives::{Button, ButtonAppearance, Label, LabelSize}; -use fluent_core::{div, prelude::*, px, ClickEvent, Context, IntoElement, Render, Window}; +use fluent_core::gpui::{div, prelude::*, px, ClickEvent, Context, IntoElement, Render, Window}; struct Counter { count: u32, diff --git a/examples/screenshots/Cargo.toml b/examples/screenshots/Cargo.toml index 786e8a0..7243b34 100644 --- a/examples/screenshots/Cargo.toml +++ b/examples/screenshots/Cargo.toml @@ -5,8 +5,6 @@ edition = "2021" publish = false [dependencies] -gpui = { workspace = true } -gpui_platform = { workspace = true } fluent-core = { workspace = true } fluent-primitives = { workspace = true } fluent-layout = { workspace = true } diff --git a/examples/screenshots/src/main.rs b/examples/screenshots/src/main.rs index a59e7ce..05a1541 100644 --- a/examples/screenshots/src/main.rs +++ b/examples/screenshots/src/main.rs @@ -25,7 +25,7 @@ use fluent_primitives::{ Searchbox, SectionHeader, Skeleton, SkeletonRow, Spinner, Switch, TextInput, Textarea, ToggleButton, }; -use gpui::{ +use fluent_core::gpui::{ div, prelude::*, px, App, Bounds, Context, Entity, IntoElement, Point, SharedString, Size, TitlebarOptions, Window, WindowBounds, WindowDecorations, WindowOptions, }; @@ -106,7 +106,7 @@ impl Render for Screenshots { let dropdown = self.dropdown.clone(); let combobox = self.combobox.clone(); - let panel_content: gpui::AnyElement = match self.component.as_ref() { + let panel_content: fluent_core::gpui::AnyElement = match self.component.as_ref() { "button" => ss_buttons().into_any_element(), "icon-button" => ss_icon_buttons().into_any_element(), "toggle-button" => ss_toggle_buttons().into_any_element(), @@ -159,11 +159,11 @@ impl Render for Screenshots { // ── Per-component render functions ──────────────────────────────────────── -fn row() -> gpui::Div { +fn row() -> fluent_core::gpui::Div { div().flex().flex_row().items_center().gap(px(8.0)) } -fn col() -> gpui::Div { +fn col() -> fluent_core::gpui::Div { div().flex().flex_col().gap(px(12.0)) } @@ -574,7 +574,7 @@ fn main() { let component = args.get(1).cloned().unwrap_or_else(|| "button".into()); let dark = !args.iter().any(|a| a == "--light"); - gpui_platform::application() + fluent_core::gpui_platform::application() .with_assets(FluentAssets::new(None)) .run(move |cx: &mut App| { if dark { diff --git a/examples/widgets/Cargo.toml b/examples/widgets/Cargo.toml index a62707f..6e1f854 100644 --- a/examples/widgets/Cargo.toml +++ b/examples/widgets/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" publish = false [dependencies] -gpui = { workspace = true } fluent-core = { workspace = true } fluent-primitives = { workspace = true } fluent-layout = { workspace = true } diff --git a/examples/widgets/src/main.rs b/examples/widgets/src/main.rs index df989df..d20c08a 100644 --- a/examples/widgets/src/main.rs +++ b/examples/widgets/src/main.rs @@ -12,7 +12,7 @@ use fluent_primitives::{ Searchbox, Spinner, Switch, TextInput, Textarea, ToggleButton, TokenInput, TokenInputSuggestion, TokenInputToken, Tooltip, }; -use gpui::{div, prelude::*, px, App, Context, Entity, IntoElement, Render, SharedString, Window}; +use fluent_core::gpui::{div, prelude::*, px, App, Context, Entity, IntoElement, Render, SharedString, Window}; struct WidgetGallery { check: CheckboxState, diff --git a/scripts/check-layering.py b/scripts/check-layering.py index 8bfc6f6..6f568aa 100755 --- a/scripts/check-layering.py +++ b/scripts/check-layering.py @@ -15,12 +15,10 @@ ROOT = pathlib.Path(__file__).resolve().parent.parent # BASELINE: examples that predate the rule. Shrink, never grow (see deny.toml). BASELINE = { - "examples/demo_app", - "examples/screenshots", - "examples/widgets", + "examples/gallery", } SKIP_DIRS = {"target", ".git", ".graft"} -PATH_RE = re.compile(r"(? bool: From 27c21940982d37fe1184320a579e273e71343fce Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:37:23 +0000 Subject: [PATCH 10/17] style(examples): format fluent imports Graft-Changeset: cd8ee145d79c0b413c97c908eac6717dad8c60efb95a97b88a5def89fcf13a327 Graft-Session: ses_01M6F65G3D70B9YJ11T6ZS3KKX Graft-Work-Item: WI-2731 Graft-Batch: 21 --- examples/demo_app/src/main.rs | 27 +++++++++++++++++---------- examples/form/src/main.rs | 6 +++--- examples/gallery/src/families.rs | 2 +- examples/gallery/src/main.rs | 2 +- examples/hello_world/src/main.rs | 2 +- examples/screenshots/src/main.rs | 8 ++++---- examples/widgets/src/main.rs | 4 +++- 7 files changed, 30 insertions(+), 21 deletions(-) diff --git a/examples/demo_app/src/main.rs b/examples/demo_app/src/main.rs index 1afed05..54f5890 100644 --- a/examples/demo_app/src/main.rs +++ b/examples/demo_app/src/main.rs @@ -1,4 +1,9 @@ use fluent_app::FluentApp; +use fluent_core::gpui; +use fluent_core::gpui::{ + actions, div, prelude::*, px, App, Context, Entity, FontWeight, IntoElement, KeyBinding, + MouseButton, MouseDownEvent, Render, SharedString, Window, +}; use fluent_core::ThemeProvider as _; use fluent_layout::{ CommandPalette, ConfirmModal, ContextMenu, DataColumn, DataRow, DataTable, DockPanel, @@ -6,11 +11,6 @@ use fluent_layout::{ SettingsNavItem, SettingsNavSection, TabItem, TabStrip, Workspace, }; use fluent_ribbon::RibbonBar; -use fluent_core::gpui as gpui; -use fluent_core::gpui::{ - actions, div, prelude::*, px, App, Context, Entity, FontWeight, IntoElement, KeyBinding, - MouseButton, MouseDownEvent, Render, SharedString, Window, -}; actions!(demo_app, [TogglePalette, ToggleLightTheme, ToggleDarkTheme]); @@ -281,7 +281,12 @@ impl Render for DashboardView { .flex() .flex_col() .gap(px(spacing.xs)) - .child(div().size(px(8.0)).rounded(px(99.0)).bg(fluent_core::gpui::rgb(hex))) + .child( + div() + .size(px(8.0)) + .rounded(px(99.0)) + .bg(fluent_core::gpui::rgb(hex)), + ) .child( div() .text_size(px(typography.display.size)) @@ -591,10 +596,12 @@ impl Render for ProjectTree { if menu_entity.read(cx).open { tree = tree.child(menu_entity.clone()); } else { - let _ = cx.listener(|tree: &mut ProjectTree, _: &fluent_core::gpui::ClickEvent, _, cx| { - tree.context_menu = None; - cx.notify(); - }); + let _ = cx.listener( + |tree: &mut ProjectTree, _: &fluent_core::gpui::ClickEvent, _, cx| { + tree.context_menu = None; + cx.notify(); + }, + ); } } diff --git a/examples/form/src/main.rs b/examples/form/src/main.rs index 90b0e66..a113d93 100644 --- a/examples/form/src/main.rs +++ b/examples/form/src/main.rs @@ -1,12 +1,12 @@ use fluent_app::FluentApp; +use fluent_core::gpui::{ + div, prelude::*, px, ClickEvent, Context, Entity, IntoElement, Render, SharedString, Window, +}; use fluent_core::ThemeProvider as _; use fluent_primitives::{ Button, ButtonAppearance, Checkbox, CheckboxState, Divider, Dropdown, Label, LabelSize, Switch, TextInput, }; -use fluent_core::gpui::{ - div, prelude::*, px, ClickEvent, Context, Entity, IntoElement, Render, SharedString, Window, -}; struct FormDemo { name: Entity, diff --git a/examples/gallery/src/families.rs b/examples/gallery/src/families.rs index c95f4df..91bfeb5 100644 --- a/examples/gallery/src/families.rs +++ b/examples/gallery/src/families.rs @@ -5,12 +5,12 @@ //! theme row on top switches light, dark, high contrast and RTL so each //! family can be checked in every variant without leaving the page. -use gpui::{LayoutDirection, Theme, ThemeProvider as _}; use fluent_primitives::{ Button, ButtonAppearance, ButtonIconPosition, ButtonShape, ButtonSize, Checkbox, CheckboxState, Chip, Divider, Label, LabelSize, Link, LinkAppearance, RadioGroup, Switch, ToggleButton, }; use gpui::{div, prelude::*, AnyElement, AnyView, Context, Entity, IntoElement, Render, Window}; +use gpui::{LayoutDirection, Theme, ThemeProvider as _}; /// A Fluent 2 component family, one gallery route each. #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index 7511f38..426eab7 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -1,5 +1,4 @@ use fluent_app::{FluentApp, TitleBar}; -use gpui::{LayoutDirection, Theme, ThemeProvider as _}; use fluent_layout::{ Breadcrumb, BreadcrumbItem, DataCell, DataColumn, DataRow, DataStatusKind, DataTable, DataTableState, DockPanel, DockPosition, Drawer, DrawerMode, DrawerPosition, DrawerSize, @@ -14,6 +13,7 @@ use fluent_primitives::{ TerminalView, TerminalViewEvent, ToggleButton, VideoScaling, VideoSurface, VideoSurfaceEvent, }; use fluent_ribbon::RibbonBar; +use gpui::{LayoutDirection, Theme, ThemeProvider as _}; mod families; use families::{DemoViews, FamilyHost, FamilyNav}; diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index 932074a..c8ed792 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -1,7 +1,7 @@ use fluent_app::FluentApp; +use fluent_core::gpui::{div, prelude::*, px, ClickEvent, Context, IntoElement, Render, Window}; use fluent_core::ThemeProvider as _; use fluent_primitives::{Button, ButtonAppearance, Label, LabelSize}; -use fluent_core::gpui::{div, prelude::*, px, ClickEvent, Context, IntoElement, Render, Window}; struct Counter { count: u32, diff --git a/examples/screenshots/src/main.rs b/examples/screenshots/src/main.rs index 05a1541..a62246c 100644 --- a/examples/screenshots/src/main.rs +++ b/examples/screenshots/src/main.rs @@ -16,6 +16,10 @@ use std::fs; use fluent_app::FluentAssets; +use fluent_core::gpui::{ + div, prelude::*, px, App, Bounds, Context, Entity, IntoElement, Point, SharedString, Size, + TitlebarOptions, Window, WindowBounds, WindowDecorations, WindowOptions, +}; use fluent_core::{Theme, ThemeProvider as _}; use fluent_layout::{modal::ModalStack, ToastStack}; use fluent_primitives::{ @@ -25,10 +29,6 @@ use fluent_primitives::{ Searchbox, SectionHeader, Skeleton, SkeletonRow, Spinner, Switch, TextInput, Textarea, ToggleButton, }; -use fluent_core::gpui::{ - div, prelude::*, px, App, Bounds, Context, Entity, IntoElement, Point, SharedString, Size, - TitlebarOptions, Window, WindowBounds, WindowDecorations, WindowOptions, -}; const SENTINEL: &str = "/tmp/.fluent-screenshot-ready"; diff --git a/examples/widgets/src/main.rs b/examples/widgets/src/main.rs index d20c08a..24a6017 100644 --- a/examples/widgets/src/main.rs +++ b/examples/widgets/src/main.rs @@ -1,4 +1,7 @@ use fluent_app::FluentApp; +use fluent_core::gpui::{ + div, prelude::*, px, App, Context, Entity, IntoElement, Render, SharedString, Window, +}; use fluent_core::ThemeProvider as _; use fluent_layout::{ set_tree_checked, ContextMenuItem, DataCellAlign, DataColumn, DataRow, DataSortDirection, @@ -12,7 +15,6 @@ use fluent_primitives::{ Searchbox, Spinner, Switch, TextInput, Textarea, ToggleButton, TokenInput, TokenInputSuggestion, TokenInputToken, Tooltip, }; -use fluent_core::gpui::{div, prelude::*, px, App, Context, Entity, IntoElement, Render, SharedString, Window}; struct WidgetGallery { check: CheckboxState, From 63d16bd6441f805d9dee3ee69acc437e6a2544f5 Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:37:49 +0000 Subject: [PATCH 11/17] chore(gallery): preserve existing example Graft-Changeset: cb183cf9fe0ba092850b40cf3b5bbe61ec16841717d97cdc1de389614927515d3 Graft-Session: ses_01M6F65G3D70B9YJ11T6ZS3KKX Graft-Work-Item: WI-2731 Graft-Batch: 21 --- examples/gallery/Cargo.toml | 3 +- examples/gallery/src/main.rs | 1034 ++-------------------------------- 2 files changed, 41 insertions(+), 996 deletions(-) diff --git a/examples/gallery/Cargo.toml b/examples/gallery/Cargo.toml index 18ed4b0..0bbb2d2 100644 --- a/examples/gallery/Cargo.toml +++ b/examples/gallery/Cargo.toml @@ -5,9 +5,8 @@ edition = "2021" publish = false [dependencies] -gpui = { workspace = true } +gpui = { workspace = true } fluent-core = { workspace = true } fluent-ribbon = { workspace = true } fluent-layout = { workspace = true } fluent-app = { workspace = true } -fluent-primitives = { workspace = true } diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index 426eab7..4cbc136 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -1,23 +1,11 @@ use fluent_app::{FluentApp, TitleBar}; +use fluent_core::ThemeProvider as _; use fluent_layout::{ - Breadcrumb, BreadcrumbItem, DataCell, DataColumn, DataRow, DataStatusKind, DataTable, - DataTableState, DockPanel, DockPosition, Drawer, DrawerMode, DrawerPosition, DrawerSize, - MenuBar, MessageBar, MessageIntent, ModalStack, Pagination, Pane, SelectableList, - SelectableListItem, TabItem, TabStrip, Tree, TreeItem, Workspace, -}; -use fluent_primitives::{ - Avatar, Button, ButtonAppearance, Checkbox, CounterBadge, CounterBadgeAppearance, - DateTimeLocale, DateTimePicker, DateTimeValue, InputCapture, InputCaptureEvent, Link, - LinkAppearance, Persona, PersonaSize, PresenceBadgeState, ProgressBar, Rating, Slider, - SpinButton, Spinner, Switch, Tag, TagAppearance, TagGroup, TagSize, TerminalCell, TerminalGrid, - TerminalView, TerminalViewEvent, ToggleButton, VideoScaling, VideoSurface, VideoSurfaceEvent, + DockPanel, DockPosition, MenuBar, ModalStack, Pane, PaneConstraints, PaneGroup, TabItem, + TabStrip, Workspace, }; use fluent_ribbon::RibbonBar; -use gpui::{LayoutDirection, Theme, ThemeProvider as _}; - -mod families; -use families::{DemoViews, FamilyHost, FamilyNav}; -use gpui::{div, prelude::*, px, rgb, Context, Entity, IntoElement, Render, SharedString, Window}; +use gpui::{div, prelude::*, Context, IntoElement, Render, Window}; // --------------------------------------------------------------------------- // Root gallery view — wires the full shell together @@ -27,571 +15,6 @@ struct GalleryRoot { workspace: gpui::Entity, } -/// Data-heavy screens in every non-ready state: an rtbit torrent list and an -/// rdpapp connection tree. -struct DataStatesDemo; - -struct TagRatingDemo; -impl Render for TagRatingDemo { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .flex() - .flex_col() - .gap_2() - .p_2() - .child("Tags and rating") - .child( - TagGroup::new("gallery-tags") - .tag(Tag::new("tag-one", "Design").appearance(TagAppearance::Accent)) - .tag( - Tag::new("tag-two", "Review") - .size(TagSize::Small) - .dismissible(true), - ), - ) - .child( - Rating::new("gallery-rating") - .value(3.5) - .label("Product rating"), - ) - } -} - -impl DataStatesDemo { - fn torrents(state: DataTableState, full: bool) -> DataTable { - let mut table = DataTable::new() - .columns([ - DataColumn::new("name", "Name"), - DataColumn::new("progress", "Progress").width(90.0), - DataColumn::new("down", "Down").width(90.0), - ]) - .state(state); - if full { - table = table.rows([ - DataRow::new( - "t1", - [ - DataCell::new("ubuntu-24.04.iso"), - DataCell::new("62%"), - DataCell::new("4.1 MB/s"), - ], - ), - DataRow::new( - "t2", - [ - DataCell::new("debian-12.img"), - DataCell::new("100%"), - DataCell::new("0 B/s"), - ], - ), - ]); - } - table - } -} - -/// A virtualised SelectableList over 100,000 rows; every third row wraps a -/// second line, so row heights vary. Only the rows near the viewport are built. -struct BigListDemo { - selected: usize, -} - -struct BigTreeDemo; - -impl Render for BigTreeDemo { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let roots = (0..50_000) - .map(|ix| TreeItem::new(format!("gallery-tree-{ix}"), format!("Tree node {ix}"))); - div() - .flex() - .flex_col() - .gap_2() - .p_2() - .child("50,000 tree nodes (virtualised)") - .child( - div() - .h(px(360.0)) - .overflow_hidden() - .child(Tree::new().items(roots)), - ) - } -} - -impl Render for BigListDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let view = cx.entity(); - div() - .flex() - .flex_col() - .gap_2() - .p_2() - .child(div().text_size(gpui::px(12.0)).child(format!( - "100,000 rows (variable height), row {} selected: click, or use Up/Down/PageUp/PageDown/Home/End", - self.selected + 1 - ))) - .child( - SelectableList::new("gallery-big-list") - .rows(100_000, |ix| { - let item = SelectableListItem::new(format!("row-{ix}"), format!("Row {ix}")) - .meta(format!("#{ix}")); - if ix % 3 == 0 { - item.subtitle("A second line that only every third row has") - } else { - item - } - }) - .variable_row_heights(true) - .row_height(44.0) - .selected_index(self.selected) - .max_body_height(360.0) - .on_select(move |id, _, cx| { - if let Some(ix) = id.strip_prefix("row-").and_then(|n| n.parse().ok()) { - view.update(cx, |demo, cx| { - demo.selected = ix; - cx.notify(); - }); - } - }), - ) - } -} - -impl Render for DataStatesDemo { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let label = |t: &'static str| div().text_size(gpui::px(12.0)).child(t); - div() - .flex() - .flex_col() - .gap_2() - .p_3() - .child(label("rtbit torrents: ready")) - .child(Self::torrents(DataTableState::Ready, true)) - .child(label("rtbit torrents: loading")) - .child(Self::torrents( - DataTableState::Loading("Loading torrents…".into()), - false, - )) - .child(label("rtbit torrents: empty")) - .child(Self::torrents( - DataTableState::Empty("No torrents. Add a magnet link to start.".into()), - false, - )) - .child(label("rtbit torrents: no results")) - .child(Self::torrents( - DataTableState::NoResults("No torrents match \"ubuntu\".".into()), - false, - )) - .child(label("rtbit torrents: error")) - .child(Self::torrents( - DataTableState::Error( - "Cannot reach the rtbit session (connection refused).".into(), - ), - false, - )) - .child(label( - "rdpapp connections: ready / filter with no match / error", - )) - .child( - Tree::new().item( - TreeItem::new("prod", "Production") - .expanded(true) - .child(TreeItem::new("srv1", "srv-01.corp")), - ), - ) - .child( - Tree::new() - .item( - TreeItem::new("prod", "Production") - .child(TreeItem::new("srv1", "srv-01.corp")), - ) - .filter("zzz"), - ) - .child(Tree::new().status(DataStatusKind::Error, "Could not load the connection list.")) - } -} - -struct CounterBadgeDemo; - -struct PersonaDemo; - -/// Date/time pickers in en-US and de-DE (day.month.year, 24-hour). -struct DateTimeDemo { - us: Entity, - de: Entity, -} - -impl DateTimeDemo { - fn new(cx: &mut Context) -> Self { - let when = DateTimeValue::new(2026, 5, 25, 21, 30).unwrap(); - let us = cx.new(|_| { - DateTimePicker::new() - .locale(DateTimeLocale::en_us()) - .value(when) - }); - let de = cx.new(|_| { - DateTimePicker::new() - .locale(DateTimeLocale::de_de()) - .value(when) - }); - Self { us, de } - } -} - -impl Render for DateTimeDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let theme = cx.theme(); - div() - .flex() - .flex_col() - .gap(gpui::px(theme.spacing.sm)) - .p(gpui::px(theme.spacing.md)) - .child("Date/time: en-US and de-DE (locale and timezone owned by the app)") - .child(self.us.clone()) - .child(self.de.clone()) - } -} - -/// rtbit download progress: determinate, indeterminate, stalled and failed states. -struct DownloadProgressDemo; - -impl Render for DownloadProgressDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let theme = cx.theme(); - let colors = theme.colors.clone(); - let row = |name: &'static str, detail: &'static str, bar: ProgressBar| { - div() - .flex() - .flex_col() - .gap_1() - .child( - div() - .flex() - .justify_between() - .child(name) - .child(div().text_color(colors.on_subtle).child(detail)), - ) - .child(bar) - }; - div() - .flex() - .flex_col() - .gap(gpui::px(theme.spacing.sm)) - .p(gpui::px(theme.spacing.md)) - .child("rtbit downloads (value text is announced; animation honours reduced motion)") - .child(row( - "ubuntu-26.04-desktop.iso", - "412 of 900 MB", - ProgressBar::new(0.46).value_text("412 of 900 megabytes"), - )) - .child(row( - "Fetching metadata", - "Waiting for peers", - ProgressBar::new(0.0).indeterminate(), - )) - .child(row( - "sample-set.tar", - "Done", - ProgressBar::new(1.0).success(), - )) - .child(row( - "archive.zip", - "Stalled", - ProgressBar::new(0.7).warning(), - )) - .child( - MessageBar::new("Tracker unreachable; the download stopped at 12%.") - .intent(MessageIntent::Error) - .title("Download failed") - .action("Retry", |_, _, _| {}), - ) - .child( - div() - .flex() - .items_center() - .gap_2() - .child(Spinner::new().label("Checking files")) - .child("Checking files"), - ) - } -} - -struct ThemeMatrixDemo { - breadcrumb: Entity, - pagination: Entity, -} - -impl ThemeMatrixDemo { - fn new(cx: &mut Context) -> Self { - let breadcrumb = cx.new(|cx| { - Breadcrumb::new(cx) - .items([ - BreadcrumbItem::new("home", "Home"), - BreadcrumbItem::new("library", "Library"), - BreadcrumbItem::new("current", "Current"), - ]) - .current(2) - }); - let pagination = cx.new(|_| Pagination::new(4).page(1)); - Self { - breadcrumb, - pagination, - } - } -} - -/// Shows Arabic and Hebrew sample content under the active layout direction. -/// The rows reorder (logical start/end) but the text itself is never mirrored. -struct RtlDemo; - -impl Render for RtlDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let colors = cx.theme().colors.clone(); - let rtl = LayoutDirection::current(cx).is_rtl(); - let row = |label: &'static str, text: &'static str| { - let r = div().flex().items_center().gap_2(); - let r = if rtl { r.flex_row_reverse() } else { r }; - r.child(div().text_color(colors.on_subtle).child(label)) - .child(div().child(text)) - }; - div() - .size_full() - .p_3() - .flex() - .flex_col() - .gap_2() - .bg(colors.surface) - .text_color(colors.on_neutral) - .child("Layout direction (RTL)") - .child( - Button::new("rtl-toggle") - .label(if rtl { - "Switch to LTR" - } else { - "Switch to RTL" - }) - .on_click(|_, _, cx| LayoutDirection::toggle(cx)), - ) - .child(row("Arabic", "مرحبا بالعالم")) - .child(row("Hebrew", "שלום עולם")) - } -} - -impl Render for ThemeMatrixDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let colors = cx.theme().colors.clone(); - div() - .size_full() - .p_3() - .flex() - .flex_col() - .gap_2() - .bg(colors.surface) - .text_color(colors.on_neutral) - .child("Theme token matrix") - .child(self.breadcrumb.clone()) - .child(self.pagination.clone()) - .child("Neutral, accent, selected, disabled, status, border, and focus states") - .child( - div() - .flex() - .flex_wrap() - .gap_2() - .child(Button::new("matrix-neutral").label("Neutral")) - .child( - Button::new("matrix-accent") - .label("Accent") - .appearance(ButtonAppearance::Accent), - ) - .child( - Button::new("matrix-disabled") - .label("Disabled") - .disabled(true), - ) - .child( - ToggleButton::new("matrix-selected") - .label("Selected") - .selected(true), - ), - ) - .child( - div() - .flex() - .flex_col() - .gap_1() - .child( - Checkbox::new("matrix-checkbox") - .label("Checked") - .checked(true), - ) - .child(Switch::new("matrix-switch").label("On").on(true)) - .child( - div() - .p_2() - .border_1() - .border_color(colors.status_error_border) - .bg(colors.status_error_bg) - .text_color(colors.status_error) - .child("Error status with explicit foreground, background, and border"), - ) - .child( - div() - .p_2() - .border_1() - .border_color(colors.status_success_border) - .bg(colors.status_success_bg) - .text_color(colors.status_success) - .child("Success status"), - ) - .child( - div() - .p_2() - .border_1() - .border_color(colors.status_warning_border) - .bg(colors.status_warning_bg) - .text_color(colors.status_warning) - .child("Warning status"), - ) - .child( - div() - .p_2() - .border_2() - .border_color(colors.focus_ring) - .text_color(colors.on_neutral) - .child("Focus ring"), - ), - ) - } -} - -impl Render for PersonaDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .size_full() - .p_3() - .flex() - .flex_col() - .gap_3() - .bg(cx.theme().colors.surface) - .child("Persona") - .child( - Persona::new(Avatar::initials("AM"), "Alex Morgan") - .secondary("Available") - .presence(PresenceBadgeState::Available), - ) - .child( - Persona::new(Avatar::initials("JR"), "Jordan Rivera") - .secondary("Do not disturb") - .presence(PresenceBadgeState::DoNotDisturb) - .size(PersonaSize::Large), - ) - } -} - -impl Render for CounterBadgeDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let colors = cx.theme().colors.clone(); - div() - .size_full() - .p_3() - .flex() - .flex_col() - .gap_2() - .bg(colors.surface) - .text_color(colors.on_subtle) - .child("CounterBadge") - .child( - div() - .flex() - .items_center() - .gap_2() - .child(CounterBadge::new(7)) - .child(CounterBadge::new(99)) - .child(CounterBadge::new(0).show_zero(true)) - .child(CounterBadge::new(0).dot(true)) - .child(CounterBadge::new(120).appearance(CounterBadgeAppearance::Ghost)), - ) - } -} - -struct DrawerContent; - -impl Render for DrawerContent { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .flex() - .flex_col() - .gap_3() - .text_color(cx.theme().colors.on_neutral) - .child("Drawer") - .child("A logical start/end side sheet with inline and overlay modes.") - } -} - -struct DrawerDemo { - drawer: Entity, -} - -impl DrawerDemo { - fn new(cx: &mut Context) -> Self { - let content = cx.new(|_| DrawerContent); - let drawer = cx.new(|cx| { - Drawer::new(content.into(), cx) - .size(DrawerSize::Medium) - .initially_open(false) - }); - Self { drawer } - } -} - -impl Render for DrawerDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let colors = cx.theme().colors.clone(); - let drawer = self.drawer.clone(); - let control = |id: &'static str, - label: &'static str, - action: fn(&mut Drawer, &mut Context)| { - let drawer = drawer.clone(); - Button::new(id).label(label).on_click(move |_, _, cx| { - drawer.update(cx, action); - }) - }; - div() - .relative() - .size_full() - .p_3() - .flex() - .flex_col() - .gap_3() - .bg(colors.surface) - .text_color(colors.on_neutral) - .child("Drawer story") - .child( - div() - .flex() - .flex_wrap() - .gap_2() - .child(control("drawer-toggle", "Open / close", |drawer, cx| { - drawer.toggle(cx) - })) - .child(control("drawer-overlay-mode", "Overlay", |drawer, cx| { - drawer.set_mode(DrawerMode::Overlay, cx); - drawer.set_open(true, cx); - })) - .child(control("drawer-inline-mode", "Inline", |drawer, cx| { - drawer.set_mode(DrawerMode::Inline, cx); - drawer.set_open(true, cx); - })) - .child(control("drawer-start", "Start", |drawer, cx| { - drawer.set_position(DrawerPosition::Start, cx) - })) - .child(control("drawer-end", "End", |drawer, cx| { - drawer.set_position(DrawerPosition::End, cx) - })), - ) - .child(self.drawer.clone()) - } -} - impl Render for GalleryRoot { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { div().size_full().child(self.workspace.clone()) @@ -599,344 +22,24 @@ impl Render for GalleryRoot { } // --------------------------------------------------------------------------- -// Slider and SpinButton demo: caller-owned values, every state +// Placeholder pane content // --------------------------------------------------------------------------- -struct RangeInputDemo { - volume: f64, - vertical: f64, - threads: f64, - opacity: f64, +struct PlaceholderView { + label: gpui::SharedString, } -impl Render for RangeInputDemo { +impl Render for PlaceholderView { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let colors = cx.theme().colors.clone(); - let entity = cx.entity(); - let set = |f: fn(&mut RangeInputDemo, f64)| { - let entity = entity.clone(); - move |v: f64, _: &mut Window, cx: &mut gpui::App| { - entity.update(cx, |d, cx| { - f(d, v); - cx.notify(); - }) - } - }; div() .size_full() - .p_3() .flex() - .flex_col() - .gap_3() - .bg(colors.surface) - .text_color(colors.on_neutral) - .child("Slider and SpinButton: click a control, then use the keys") - .child(format!("Volume {}", self.volume)) - .child( - Slider::new("gallery-slider") - .label("Volume") - .value(self.volume) - .step(5.0) - .on_change(set(|d, v| d.volume = v)), - ) - .child( - Slider::new("gallery-slider-disabled") - .label("Disabled") - .value(30.0) - .disabled(true), - ) - .child( - Slider::new("gallery-slider-vertical") - .label("Vertical") - .vertical() - .length(120.0) - .range(0.0, 1.0) - .step(0.1) - .value(self.vertical) - .on_change(set(|d, v| d.vertical = v)), - ) - .child( - SpinButton::new("gallery-spin") - .label("Threads") - .range(1.0, 16.0) - .value(self.threads) - .format(|v| format!("{v} threads")) - .on_change(set(|d, v| d.threads = v)), - ) - .child( - SpinButton::new("gallery-spin-percent") - .label("Opacity") - .range(0.0, 100.0) - .step(10.0) - .value(self.opacity) - .format(|v| format!("{v}%")) - .parse(|t| t.trim_end_matches('%').trim().parse().ok()) - .on_change(set(|d, v| d.opacity = v)), - ) - .child( - SpinButton::new("gallery-spin-disabled") - .label("Disabled") - .value(4.0) - .disabled(true), - ) - } -} - -// --------------------------------------------------------------------------- -// InputCapture demo: click (or focus + Enter) to grab, Escape or Ctrl+Alt to release -// --------------------------------------------------------------------------- - -struct InputCaptureDemo { - capture: Entity, - last: SharedString, -} - -impl InputCaptureDemo { - fn new(cx: &mut Context) -> Self { - let capture = cx.new(InputCapture::new); - cx.subscribe(&capture, |this, _, event: &InputCaptureEvent, cx| { - this.last = format!("{event:?}").into(); - cx.notify(); - }) - .detach(); - Self { - capture, - last: "Click to grab input; Escape or Ctrl+Alt releases".into(), - } - } -} - -impl Render for InputCaptureDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let colors = cx.theme().colors.clone(); - div() - .size_full() - .flex() - .flex_col() - .gap_2() - .p_2() + .items_center() + .justify_center() .bg(colors.surface) .text_color(colors.on_subtle) - .child( - div() - .flex() - .flex_row() - .items_center() - .gap_3() - .child(Link::new("gallery-link-default", "Default link").on_click(|_, _, _| {})) - .child( - Link::new("gallery-link-subtle", "Subtle link") - .appearance(LinkAppearance::Subtle) - .on_click(|_, _, _| {}), - ) - .child(Link::new("gallery-link-disabled", "Disabled link").disabled(true)), - ) - .child( - div() - .child("Inline link: read the ") - .child( - Link::new("gallery-link-inline", "input capture guide") - .open_url("https://github.com/sprooty/fluent-gpui"), - ) - .child(" before grabbing input."), - ) - .child(self.last.clone()) - .child(div().flex_1().child(self.capture.clone())) - } -} - -// --------------------------------------------------------------------------- -// TerminalView demo: a local echo "shell" over a caller-owned TerminalGrid (no VT parsing) -// --------------------------------------------------------------------------- - -struct TerminalDemo { - grid: Entity, - view: Entity, -} - -const PROMPT: &str = "gallery$ "; - -impl TerminalDemo { - fn new(cx: &mut Context) -> Self { - let grid = cx.new(|_| { - let mut grid = TerminalGrid::new(80, 24); - let bold = TerminalCell { - bold: true, - ..TerminalCell::default() - }; - grid.write_str(0, 0, "TerminalView demo", bold); - let green = TerminalCell { - fg: Some(rgb(0x6ccb5f).into()), - ..TerminalCell::default() - }; - grid.write_str( - 1, - 0, - "Click to type. Drag to select, Ctrl+Shift+C copies.", - green, - ); - let inverse = TerminalCell { - inverse: true, - underline: true, - ..TerminalCell::default() - }; - grid.write_str(2, 0, "Escape releases input.", inverse); - let end = grid.write_str(3, 0, PROMPT, TerminalCell::default()); - grid.move_cursor(3, end); - grid - }); - let view = cx.new(|cx| TerminalView::new(grid.clone(), cx).label("Gallery terminal")); - cx.subscribe(&view, |this, _, event: &TerminalViewEvent, cx| { - if let TerminalViewEvent::Key { - key, - pressed: true, - modifiers, - .. - } = event - { - this.on_key(key, modifiers.shift, cx); - } - }) - .detach(); - Self { grid, view } - } - - /// A tiny local line editor: printable keys echo, Enter starts a new prompt. - fn on_key(&mut self, key: &str, shift: bool, cx: &mut Context) { - self.grid.update(cx, |grid, cx| { - let cursor = grid.cursor(); - match key { - "enter" => { - let mut row = cursor.row + 1; - if row >= grid.rows() { - grid.scroll_up(1); - row = grid.rows() - 1; - } - let end = grid.write_str(row, 0, PROMPT, TerminalCell::default()); - grid.move_cursor(row, end); - } - "backspace" if cursor.col > PROMPT.len() => { - grid.set_cell(cursor.row, cursor.col - 1, TerminalCell::default()); - grid.move_cursor(cursor.row, cursor.col - 1); - } - "space" => { - grid.move_cursor(cursor.row, cursor.col + 1); - } - _ => { - let mut chars = key.chars(); - if let (Some(ch), None) = (chars.next(), chars.next()) { - let ch = if shift { ch.to_ascii_uppercase() } else { ch }; - grid.set_cell(cursor.row, cursor.col, TerminalCell::new(ch)); - grid.move_cursor(cursor.row, cursor.col + 1); - } - } - } - cx.notify(); - }); - } -} - -impl Render for TerminalDemo { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div().size_full().p_2().child(self.view.clone()) - } -} - -// --------------------------------------------------------------------------- -// VideoSurface demo: an animated generated RGBA8 frame; click to toggle Fit / Native -// --------------------------------------------------------------------------- - -const VIDEO_W: u32 = 320; -const VIDEO_H: u32 = 180; - -struct VideoDemo { - surface: Entity, - tick: u32, - status: SharedString, -} - -impl VideoDemo { - fn new(cx: &mut Context) -> Self { - let surface = cx.new(|cx| { - let mut surface = VideoSurface::new(cx); - surface.label("Generated video", cx); - surface - }); - cx.subscribe(&surface, |this, _, event: &VideoSurfaceEvent, cx| { - let VideoSurfaceEvent::Resized { - device_width, - device_height, - .. - } = event; - this.status = format!("Surface {device_width}x{device_height} device px").into(); - cx.notify(); - }) - .detach(); - cx.spawn(async move |this, cx| loop { - cx.background_executor() - .timer(std::time::Duration::from_millis(33)) - .await; - if this.update(cx, |this, cx| this.next_frame(cx)).is_err() { - break; - } - }) - .detach(); - Self { - surface, - tick: 0, - status: "".into(), - } - } - - /// A moving gradient with a bouncing square, generated on the CPU. - fn next_frame(&mut self, cx: &mut Context) { - self.tick = self.tick.wrapping_add(1); - let t = self.tick; - let mut pixels = vec![0u8; (VIDEO_W * VIDEO_H * 4) as usize]; - let sx = (t * 3) % (VIDEO_W - 40); - let sy = (t * 2) % (VIDEO_H - 40); - for (i, px) in pixels.as_chunks_mut::<4>().0.iter_mut().enumerate() { - let (x, y) = (i as u32 % VIDEO_W, i as u32 / VIDEO_W); - let square = (sx..sx + 40).contains(&x) && (sy..sy + 40).contains(&y); - px.copy_from_slice(&if square { - [255, 255, 255, 255] - } else { - [(x + t) as u8, (y + t / 2) as u8, 128, 255] - }); - } - self.surface.update(cx, |surface, cx| { - surface.submit_frame(VIDEO_W, VIDEO_H, pixels, cx).ok(); - }); - } -} - -impl Render for VideoDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let scaling = self.surface.read(cx).scaling(); - div() - .size_full() - .p_2() - .flex() - .flex_col() - .gap_1() - .child(format!( - "{scaling:?} scaling. {} Click to toggle.", - self.status - )) - .child( - div() - .id("video-demo") - .flex_1() - .on_click(cx.listener(move |this, _, _, cx| { - let next = match scaling { - VideoScaling::Fit => VideoScaling::Native, - VideoScaling::Native => VideoScaling::Fit, - }; - this.surface.update(cx, |s, cx| s.set_scaling(next, cx)); - cx.notify(); - })) - .child(self.surface.clone()), - ) + .child(self.label.clone()) } } @@ -967,18 +70,8 @@ fn main() { }) }) .group("View", |g| { - g.toggle_button("Dark", "icons/dark_theme.svg", true, |_, _, _, cx| { - Theme::apply_dark(cx) - }) - .toggle_button("Light", "icons/light_theme.svg", false, |_, _, _, cx| { - Theme::apply_light(cx) - }) - .toggle_button( - "High contrast", - "icons/dark_theme.svg", - false, - |_, _, _, cx| Theme::apply_high_contrast(cx), - ) + g.toggle_button("Dark", "icons/dark_theme.svg", true, |_, _, _, _| {}) + .toggle_button("Light", "icons/light_theme.svg", false, |_, _, _, _| {}) }) }) .tab("Edit", |t| { @@ -990,33 +83,6 @@ fn main() { ) }) }) - .tab("Connection", |t| { - t.group("Session", |g| { - g.large_button("Connect", "icons/add.svg", |_, _, _| {}) - .large_button("Disconnect", "icons/clear.svg", |_, _, _| {}) - .separator() - .toggle_button( - "Full screen", - "icons/dark_theme.svg", - false, - |_, _, _, _| {}, - ) - }) - .group("Display", |g| { - g.button("Fit to window", "icons/search.svg", |_, _, _| {}) - .button("Actual size", "icons/search.svg", |_, _, _| {}) - }) - .group("Clipboard", |g| { - g.stack(|s| { - s.button("Send text", "icons/paste.svg", |_, _, _| {}) - .button("Copy remote", "icons/copy.svg", |_, _, _| {}) - }) - }) - .group("Tools", |g| { - g.large_button("Screenshot", "icons/folder_open.svg", |_, _, _| {}) - .icon_button("icons/search.svg", "Find host", |_, _, _| {}) - }) - }) .contextual_tab("Session", true, |t| { t.group("Terminal", |g| { g.large_button("Clear", "icons/clear.svg", |_, _, _| {}) @@ -1025,6 +91,16 @@ fn main() { }) }); + // Left sidebar + let sidebar_content = cx.new(|_| PlaceholderView { + label: "Navigation tree".into(), + }); + let sidebar = cx.new(|cx| { + DockPanel::new(cx, DockPosition::Left, "Navigation") + .size(240.0) + .content(sidebar_content.into()) + }); + // Main pane with a tab strip let tab_strip = cx.new(|cx: &mut gpui::Context| { let mut ts = TabStrip::new(cx); @@ -1032,54 +108,24 @@ fn main() { ts.add_tab(TabItem::new("settings").label("Settings").closable(true)); ts }); - let badge_demo = cx.new(|_| CounterBadgeDemo); - let persona_demo = cx.new(|_| PersonaDemo); - let download_demo = cx.new(|_| DownloadProgressDemo); - let data_states = cx.new(|_| DataStatesDemo); - let big_list = cx.new(|_| BigListDemo { selected: 0 }); - let big_tree = cx.new(|_| BigTreeDemo); - let date_demo = cx.new(DateTimeDemo::new); - let input_demo = cx.new(InputCaptureDemo::new); - let terminal_demo = cx.new(TerminalDemo::new); - let video_demo = cx.new(VideoDemo::new); - let tag_rating_demo = cx.new(|_| TagRatingDemo); - let range_demo = cx.new(|_| RangeInputDemo { - volume: 40.0, - vertical: 0.5, - threads: 4.0, - opacity: 80.0, - }); - let matrix_demo = cx.new(ThemeMatrixDemo::new); - let drawer_demo = cx.new(DrawerDemo::new); - let rtl_demo = cx.new(|cx| { - cx.observe_global::(|_, cx| cx.notify()) - .detach(); - RtlDemo - }); - let host = cx.new(|_| { - FamilyHost::new(DemoViews { - status: vec![ - badge_demo.into(), - persona_demo.into(), - download_demo.into(), - tag_rating_demo.into(), - ], - data: vec![data_states.into(), big_list.into(), big_tree.into()], - date_input: vec![date_demo.into(), input_demo.into(), range_demo.into()], - media: vec![terminal_demo.into(), video_demo.into()], - theme: vec![drawer_demo.into(), matrix_demo.into(), rtl_demo.into()], - }) - }); - let pane_content = host.clone(); - - // Left sidebar - let sidebar_content = cx.new(|_| FamilyNav::new(host.clone())); - let sidebar = cx.new(|cx| { - DockPanel::new(cx, DockPosition::Left, "Navigation") - .size(240.0) - .content(sidebar_content.into()) + let split_list = cx.new(|_| PlaceholderView { + label: "Resizable first panel".into(), + }); + let split_detail = cx.new(|_| PlaceholderView { + label: "Resizable second panel".into(), + }); + let pane_content = cx.new(|_| { + PaneGroup::horizontal() + .first(split_list.into()) + .second(split_detail.into()) + .ratio(0.36) + .constraints( + PaneConstraints::new() + .first_min(220.0) + .second_min(320.0) + .first_max(Some(520.0)), + ) }); - let main_pane = cx.new(|cx| Pane::new(cx).with_tab_strip(tab_strip)); // set content separately since we need the entity main_pane.update(cx, |p, cx| { From 3e53668d521a1206cf0fcb8ba380df23b0a67a58 Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:38:30 +0000 Subject: [PATCH 12/17] refactor(gallery): use fluent gpui re-exports Graft-Changeset: cec441350706fa96147aba6636cecd2e6d02bf82437f889903c2f963de9a01b95 Graft-Session: ses_01M6F65G3D70B9YJ11T6ZS3KKX Graft-Work-Item: WI-2731 Graft-Batch: 21 --- Cargo.lock | 2 -- deny.toml | 1 - examples/gallery/Cargo.toml | 1 - examples/gallery/src/families.rs | 6 +++--- examples/gallery/src/main.rs | 8 ++++---- scripts/check-layering.py | 1 - 6 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1734d4..b59ae5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1815,9 +1815,7 @@ dependencies = [ "fluent-app", "fluent-core", "fluent-layout", - "fluent-primitives", "fluent-ribbon", - "gpui", ] [[package]] diff --git a/deny.toml b/deny.toml index 9f963a9..7f8580c 100644 --- a/deny.toml +++ b/deny.toml @@ -62,7 +62,6 @@ deny = [ { crate = "gpui", wrappers = [ "fluent-core", "fluent-primitives", "fluent-ribbon", "fluent-layout", "fluent-app", "fluent-test-utils", - "gallery", "gpui_linux", "gpui_macros", "gpui_platform", "gpui_windows", "gpui_wgpu", ] }, ] diff --git a/examples/gallery/Cargo.toml b/examples/gallery/Cargo.toml index 0bbb2d2..ebac85e 100644 --- a/examples/gallery/Cargo.toml +++ b/examples/gallery/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" publish = false [dependencies] -gpui = { workspace = true } fluent-core = { workspace = true } fluent-ribbon = { workspace = true } fluent-layout = { workspace = true } diff --git a/examples/gallery/src/families.rs b/examples/gallery/src/families.rs index 91bfeb5..61a8bf6 100644 --- a/examples/gallery/src/families.rs +++ b/examples/gallery/src/families.rs @@ -9,8 +9,8 @@ use fluent_primitives::{ Button, ButtonAppearance, ButtonIconPosition, ButtonShape, ButtonSize, Checkbox, CheckboxState, Chip, Divider, Label, LabelSize, Link, LinkAppearance, RadioGroup, Switch, ToggleButton, }; -use gpui::{div, prelude::*, AnyElement, AnyView, Context, Entity, IntoElement, Render, Window}; -use gpui::{LayoutDirection, Theme, ThemeProvider as _}; +use fluent_core::gpui::{div, prelude::*, AnyElement, AnyView, Context, Entity, IntoElement, Render, Window}; +use fluent_core::gpui::{LayoutDirection, Theme, ThemeProvider as _}; /// A Fluent 2 component family, one gallery route each. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -97,7 +97,7 @@ impl FamilyHost { .gap_3() .children(views.iter().map(|v| { div() - .h(gpui::px(300.0)) + .h(fluent_core::gpui::px(300.0)) .relative() .overflow_hidden() .child(v.clone()) diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index 4cbc136..6729df4 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -1,18 +1,18 @@ use fluent_app::{FluentApp, TitleBar}; +use fluent_core::gpui::{div, prelude::*, Context, IntoElement, Render, Window}; use fluent_core::ThemeProvider as _; use fluent_layout::{ DockPanel, DockPosition, MenuBar, ModalStack, Pane, PaneConstraints, PaneGroup, TabItem, TabStrip, Workspace, }; use fluent_ribbon::RibbonBar; -use gpui::{div, prelude::*, Context, IntoElement, Render, Window}; // --------------------------------------------------------------------------- // Root gallery view — wires the full shell together // --------------------------------------------------------------------------- struct GalleryRoot { - workspace: gpui::Entity, + workspace: fluent_core::gpui::Entity, } impl Render for GalleryRoot { @@ -26,7 +26,7 @@ impl Render for GalleryRoot { // --------------------------------------------------------------------------- struct PlaceholderView { - label: gpui::SharedString, + label: fluent_core::gpui::SharedString, } impl Render for PlaceholderView { @@ -102,7 +102,7 @@ fn main() { }); // Main pane with a tab strip - let tab_strip = cx.new(|cx: &mut gpui::Context| { + let tab_strip = cx.new(|cx: &mut fluent_core::gpui::Context| { let mut ts = TabStrip::new(cx); ts.add_tab(TabItem::new("dashboard").label("Dashboard").closable(true)); ts.add_tab(TabItem::new("settings").label("Settings").closable(true)); diff --git a/scripts/check-layering.py b/scripts/check-layering.py index 6f568aa..35472f9 100755 --- a/scripts/check-layering.py +++ b/scripts/check-layering.py @@ -15,7 +15,6 @@ ROOT = pathlib.Path(__file__).resolve().parent.parent # BASELINE: examples that predate the rule. Shrink, never grow (see deny.toml). BASELINE = { - "examples/gallery", } SKIP_DIRS = {"target", ".git", ".graft"} PATH_RE = re.compile(r"(? Date: Mon, 2 Nov 2026 22:40:39 +0000 Subject: [PATCH 13/17] Add nearest video sampler for Fit mode Graft-Changeset: c1f0c8d0599232c128ebdd57bf451429fa3752e49acf96fb895e9bdc2b3d9652f Graft-Session: ses_01M6F65FV9BZR5TNFS21CBFT1Q Graft-Work-Item: WI-2942 Co-authored-by: gpt-5.6-sol Graft-Batch: 21 --- crates/fluent-primitives/src/video_surface.rs | 31 ++++++-- vendor/zed/PATCHES.md | 5 +- .../zed/crates/gpui/examples/video_frame.rs | 11 ++- vendor/zed/crates/gpui/src/scene.rs | 13 +++- vendor/zed/crates/gpui/src/window.rs | 12 ++-- .../zed/crates/gpui_wgpu/src/wgpu_renderer.rs | 72 +++++++++++++------ 6 files changed, 107 insertions(+), 37 deletions(-) diff --git a/crates/fluent-primitives/src/video_surface.rs b/crates/fluent-primitives/src/video_surface.rs index 5676460..12ec542 100644 --- a/crates/fluent-primitives/src/video_surface.rs +++ b/crates/fluent-primitives/src/video_surface.rs @@ -20,8 +20,8 @@ //! device pixel grid, so the picture is sampled without filtering; a frame larger than the bounds //! is centred and clipped. The bars left around the frame are painted as a letterbox. //! -//! gpui's video sampler is linear, so a Fit frame that is scaled is filtered; true -//! nearest-neighbour scaling needs a renderer change and is not available yet. +//! Fit frames use nearest-neighbour sampling to preserve source pixels when scaled; Native frames +//! use the linear sampler because their one-to-one layout does not require interpolation. //! //! # Layout and input //! @@ -48,7 +48,7 @@ use fluent_core::ThemeProvider as _; use gpui::{ canvas, div, fill, point, prelude::*, px, size, AnyWindowHandle, App, Bounds, ContentMask, Context, Corners, EventEmitter, Hsla, IntoElement, Pixels, Point, Render, Role, SharedString, - Size, Stateful, Subscription, VideoTextureId, WeakEntity, Window, + Size, Stateful, Subscription, VideoFilter, VideoTextureId, WeakEntity, Window, }; /// The largest frame side, in pixels, that gpui accepts for a video texture. @@ -234,6 +234,8 @@ pub enum VideoRecord { Paint { /// Where the frame was drawn. content: Bounds, + /// The texture filter requested for this scaling mode. + filter: VideoFilter, }, } @@ -574,16 +576,23 @@ impl VideoSurface { let frame = state.texture.map(|t| size(t.width, t.height)); let layout = VideoLayout::compute(bounds, frame.unwrap_or_default(), scaling, scale_factor); + let filter = match scaling { + VideoScaling::Fit => VideoFilter::Nearest, + VideoScaling::Native => VideoFilter::Linear, + }; state.layout = Some(layout); window.paint_quad(fill(bounds, letterbox)); if let Some(texture) = state.texture { window.with_content_mask(Some(ContentMask { bounds }), |window| match texture.id { - Some(id) => window.paint_video_frame(layout.content, Corners::default(), id), + Some(id) => { + window.paint_video_frame(layout.content, Corners::default(), id, filter) + } None => window.paint_quad(fill(layout.content, placeholder)), }); state.record(|| VideoRecord::Paint { content: layout.content, + filter, }); } drop(state); @@ -878,7 +887,8 @@ mod tests { assert_eq!( recorder.records().last(), Some(&VideoRecord::Paint { - content: bounds(100.0, 0.0, 200.0, 200.0) + content: bounds(100.0, 0.0, 200.0, 200.0), + filter: VideoFilter::Nearest, }) ); @@ -902,6 +912,17 @@ mod tests { assert_eq!(&patched[12..16], &[9, 9, 9, 9]); assert_eq!(&patched[0..12], &pixels[0..12]); + recorder.clear(); + surface.update(vcx, |s, cx| s.set_scaling(VideoScaling::Native, cx)); + draw(vcx); + assert!(recorder.records().iter().any(|record| matches!( + record, + VideoRecord::Paint { + filter: VideoFilter::Linear, + .. + } + ))); + let outside = FrameRect { x: 2, y: 0, diff --git a/vendor/zed/PATCHES.md b/vendor/zed/PATCHES.md index 9fba86e..846efd5 100644 --- a/vendor/zed/PATCHES.md +++ b/vendor/zed/PATCHES.md @@ -9,10 +9,11 @@ Each local patch to this tree is a land on the working line (`backlog-1`), never | `trim-ios` (WI-2728): drop the `target_os = "ios"` arm of `current_platform`, since `gpui_ios` is no longer vendored | WI-2728 land on `backlog-1` | `crates/gpui_platform/src/gpui_platform.rs` | | `svg-font-warmup` (WI-1432): module-level `SYSTEM_FONT_DB` plus `pub fn warmup_svg_font_db()` behind the `svg-font-warmup` feature (enabled by `fluent-core`), so `main()` can prime `load_system_fonts()` on a background thread | WI-1432 land on `backlog-1` | `crates/gpui/src/svg_renderer.rs`, `crates/gpui/Cargo.toml` | | `element-scale` (WI-1433): `Style::scale` and `Styled::scale`, `Window::with_element_scale` with `element_scale_stack` and the paint-time transforms (`transform_bounds_for_paint`, `scale_length_for_paint`, `scale_corners_for_paint`, `transform_point_for_paint`) applied to quads, shadows, paths, text, svg, images and surfaces; the `div.rs` paint wrapper; example `examples/scale.rs` | WI-1433 land on `backlog-1` | `crates/gpui/src/style.rs`, `crates/gpui/src/styled.rs`, `crates/gpui/src/window.rs`, `crates/gpui/src/elements/div.rs`, `crates/gpui/Cargo.toml`, `crates/gpui/examples/scale.rs` | -| `video-frames` (WI-1348): `Window::{alloc_video_texture, upload_video_frame, paint_video_frame, free_video_texture}` with never-reused `VideoTextureId`s and a packed RGBA8 contract; `VideoFrame` scene primitive and `PrimitiveBatch::VideoFrames`; a standalone `Rgba8Unorm` texture per id in the wgpu renderer (uploaded with `queue.write_texture` when its generation changes) and a `vs/fs_video_frame` pass for both instance transports; example `examples/video_frame.rs`. Linux path only; DirectX is WI-1434. Hardening: `upload_video_frame` rejects a zero or over-8192 dimension and the wgpu renderer skips a frame shorter than `w*h*4` | WI-1348 land on `backlog-1` | `crates/gpui/src/scene.rs`, `crates/gpui/src/window.rs`, `crates/gpui/Cargo.toml`, `crates/gpui/examples/video_frame.rs`, `crates/gpui_wgpu/src/wgpu_renderer.rs`, `crates/gpui_wgpu/src/shaders.wgsl`, `crates/gpui_wgpu/src/shaders_storage.wgsl`, `crates/gpui_wgpu/src/shaders_webgl.wgsl` | +| `video-sampler-nearest` (WI-2942): `VideoFilter` selects a nearest-neighbour sampler for scaled Fit-mode video frames while Native remains linear in the wgpu renderer | WI-2942 land on `backlog-1` | `crates/gpui/src/scene.rs`, `crates/gpui/src/window.rs`, `crates/gpui_wgpu/src/wgpu_renderer.rs`, `crates/fluent-primitives/src/video_surface.rs` | +| `video-frames` (WI-1348): `Window::{alloc_video_texture, upload_video_frame, paint_video_frame, free_video_texture}` with never-reused `VideoTextureId`s and a packed RGBA8 contract; `VideoFrame` scene primitive and `PrimitiveBatch::VideoFrames`; a standalone `Rgba8Unorm` texture per id in the wgpu renderer (uploaded with `queue.write_texture` when its generation changes) and a `vs/fs_video_frame` pass for both instance transports; example `examples/video_frame.rs`. Linux path only; DirectX is WI-1434. Hardening: `upload_video_frame` rejects a zero or over-8192 dimension and the wgpu renderer skips a frame shorter than `w*h*4` | WI-1348 land on `backlog-1` | `crates/gpui/src/scene.rs`, `crates/gpui/src/window.rs`, `crates/gpui/Cargo.toml`, `crates/gpui/examples/video_frame.rs`, `crates/gpui_wgpu/src/wgpu_renderer.rs`, `crates/gpui_wgpu/src/shaders_storage.wgsl`, `crates/gpui_wgpu/src/shaders_webgl.wgsl` | | `video-frames-directx` (WI-1434): the DirectX half of `video-frames`. **Unverified on Windows** (checked with `cargo check`/`clippy --target x86_64-pc-windows-gnu` on Linux only; HLSL not compiled, not run). A `PrimitiveBatch::VideoFrames` arm, a standalone immutable `R8G8B8A8_UNORM` texture per `VideoTextureId` (the packed RGBA8 contract maps straight onto it, no conversion; recreated when the generation or size changes, dropped when no frame references it, cleared on device loss), a `video_frame_pipeline` drawing runs of frames that share a texture, and `video_frame_{vertex,fragment}` in HLSL plus the `video_frame` entry in the fxc module list | WI-1434 land on `backlog-1` | `crates/gpui_windows/src/directx_renderer.rs`, `crates/gpui_windows/src/shaders.hlsl`, `crates/gpui_windows/build.rs` | | `backport-cached-view-subscriptions` (WI-1346): upstream zed `decd2399b1` (#65952), applied byte for byte. Cached views and deferred draws keep window subscriptions for entities read in paint or by a deferred draw when their cache is reused; scoped entity-access recording replaces the per-scope clone-and-diff; restoring a recorded set skips released entities. Drop it at the next upstream pull past `decd2399b1` | WI-1346 land on `backlog-1` | `crates/gpui/src/app.rs`, `crates/gpui/src/app/entity_map.rs`, `crates/gpui/src/elements/deferred.rs`, `crates/gpui/src/view.rs`, `crates/gpui/src/window.rs` | -| `backdrop-blur` (WI-1349): `BackgroundTag::BackdropBlur`, `pub fn backdrop_blur(radius, tint) -> Background`, `Background::{is_transparent, needs_backdrop}`, the `style.rs` arm and a `Styled::backdrop_blur` helper. wgpu: the surface gets `COPY_SRC` where supported, quad batches split at blur quads, and before such a batch the colour target is copied (`copy_texture_to_texture`) into a sampled backdrop texture at `@group(3)`; `fs_quad` runs a single-pass cross-pattern blur under the tint (an approximation, not a multi-pass Gaussian). DirectX: `CopyResource` into a backdrop SRV at `t2` and the same kernel in HLSL. **DirectX unverified on Windows** (`cargo check --target x86_64-pc-windows-gnu` on Linux only; HLSL not compiled, not run; runtime is WI-1439, visual/perf WI-1438) | WI-1349 land on `backlog-1` | `crates/gpui/src/color.rs`, `crates/gpui/src/style.rs`, `crates/gpui/src/styled.rs`, `crates/gpui_wgpu/src/wgpu_renderer.rs`, `crates/gpui_wgpu/src/shaders.wgsl`, `crates/gpui_windows/src/directx_renderer.rs`, `crates/gpui_windows/src/shaders.hlsl` | +| `backdrop-blur` (WI-1349): `BackgroundTag::BackdropBlur`, `pub fn backdrop_blur(radius, tint) -> Background`, `Background::{is_transparent, needs_backdrop}`, the `style.rs` arm and a `Styled::backdrop_blur` helper. wgpu: the surface gets `COPY_SRC` where supported, quad batches split at blur quads, and before such a batch the colour target is copied (`copy_texture_to_texture`) into a sampled backdrop texture at `@group(3)`; `fs_quad` runs a single-pass cross-pattern blur under the tint (an approximation, not a multi-pass Gaussian). DirectX: `CopyResource` into a backdrop SRV at `t2` and the same kernel in HLSL. **DirectX unverified on Windows** (`cargo check --target x86_64-pc-windows-gnu` on Linux only; HLSL not compiled, not run; runtime is WI-1439, visual/perf WI-1438) | WI-1349 land on `backlog-1` | `crates/gpui/src/color.rs`, `crates/gpui/src/style.rs`, `crates/gpui/src/styled.rs`, `crates/gpui_wgpu/src/wgpu_renderer.rs`, `crates/gpui_windows/src/directx_renderer.rs`, `crates/gpui_windows/src/shaders.hlsl` | | `key-scan-code` (WI-2804): `scan_code: Option` on `KeyDownEvent`, `KeyUpEvent` and `KeystrokeEvent` (threaded through `dispatch_keystroke_{interceptors,observers}` by `key_down_scan_code`; `None` for replayed keystrokes), filled by the platforms. Windows: the set-1 make code from `lParam` bits 16-23 plus `0xE000` when bit 24 (extended) is set (`key_scan_code`). X11: `event.detail - 8`. Wayland: the `wl_keyboard` key (already evdev), on the repeat timer too. IME-committed text and simulated input carry `None`; other literal sites gain `scan_code: None`. **Windows unverified on Windows** (`cargo check --target x86_64-pc-windows-gnu` on Linux only, not run) | WI-2804 land on `backlog-1` | `crates/gpui/src/interactive.rs`, `crates/gpui/src/app.rs`, `crates/gpui/src/window.rs`, `crates/gpui/src/key_dispatch.rs`, `crates/gpui/src/elements/div.rs`, `crates/gpui_windows/src/events.rs`, `crates/gpui_linux/src/linux/x11/client.rs`, `crates/gpui_linux/src/linux/wayland/client.rs` | Dropped from the WI-1432 triage, upstream already has them (nothing applied): `elements/list.rs`, `interactive.rs`, `key_dispatch.rs`, `tab_stop.rs`, and the let-chain tidy in `elements/div.rs`. Upstream also has no renderer-time font-DB warm-up, so `svg-font-warmup` is still needed. diff --git a/vendor/zed/crates/gpui/examples/video_frame.rs b/vendor/zed/crates/gpui/examples/video_frame.rs index 5e24a48..011806a 100644 --- a/vendor/zed/crates/gpui/examples/video_frame.rs +++ b/vendor/zed/crates/gpui/examples/video_frame.rs @@ -1,8 +1,8 @@ //! Paints a generated RGBA8 frame into a standalone video texture every frame. use gpui::{ - App, Bounds, Context, Corners, VideoTextureId, Window, WindowBounds, WindowOptions, canvas, - div, prelude::*, px, rgb, size, + App, Bounds, Context, Corners, VideoFilter, VideoTextureId, Window, WindowBounds, + WindowOptions, canvas, div, prelude::*, px, rgb, size, }; use gpui_platform::application; @@ -54,7 +54,12 @@ impl Render for VideoFrameExample { canvas( |_, _, _| {}, move |bounds, _, window, _| { - window.paint_video_frame(bounds, Corners::all(px(12.)), texture); + window.paint_video_frame( + bounds, + Corners::all(px(12.)), + texture, + VideoFilter::Linear, + ); }, ) .w(px(WIDTH as f32 * 2.)) diff --git a/vendor/zed/crates/gpui/src/scene.rs b/vendor/zed/crates/gpui/src/scene.rs index e9d1ad1..51504da 100644 --- a/vendor/zed/crates/gpui/src/scene.rs +++ b/vendor/zed/crates/gpui/src/scene.rs @@ -838,6 +838,17 @@ pub struct VideoFramePixels { pub generation: u64, } +/// Texture filtering used when painting a video frame. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +#[repr(u32)] +pub enum VideoFilter { + /// Smoothly interpolate neighbouring texels. + #[default] + Linear = 0, + /// Select the nearest texel without interpolation. + Nearest = 1, +} + /// A video frame painted from a standalone GPU texture. /// /// Unlike [`PolychromeSprite`], the texture is not an atlas tile: the whole @@ -852,7 +863,7 @@ pub struct VideoFrame { pub pad: u32, pub texture_id: u64, pub opacity: f32, - pub pad2: u32, + pub filter: VideoFilter, pub bounds: Bounds, pub content_mask: ContentMask, pub corner_radii: Corners, diff --git a/vendor/zed/crates/gpui/src/window.rs b/vendor/zed/crates/gpui/src/window.rs index b1158f2..7334fa8 100644 --- a/vendor/zed/crates/gpui/src/window.rs +++ b/vendor/zed/crates/gpui/src/window.rs @@ -22,9 +22,10 @@ use crate::{ StrikethroughStyle, Style, SubpixelSprite, Subscription, SystemWindowTab, SystemWindowTabController, TabStopMap, Task, TextInputConfiguration, TextInputStateChange, TextRenderingMode, TextStyle, TextStyleRefinement, ThermalState, TransformationMatrix, - Underline, UnderlineStyle, VideoTextureId, WindowAppearance, WindowBackgroundAppearance, - WindowBounds, WindowControls, WindowDecorations, WindowInsets, WindowOptions, WindowParams, - WindowTextSystem, WindowVisibility, point, prelude::*, px, rems, size, transparent_black, + Underline, UnderlineStyle, VideoFilter, VideoTextureId, WindowAppearance, + WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations, WindowInsets, + WindowOptions, WindowParams, WindowTextSystem, WindowVisibility, point, prelude::*, px, rems, + size, transparent_black, }; use crate::gestures::{GestureTuning, RecognizedTouchGesture, TouchGestureRecognizer}; @@ -5462,13 +5463,14 @@ impl Window { /// Paint a video texture into the current frame. /// /// The whole texture is sampled into `bounds`, clipped by the current - /// content mask and rounded by `corner_radii`. A texture that was freed, + /// content mask and rounded by `corner_radii`, using `filter`. A texture that was freed, /// or that has not had a frame uploaded yet, paints nothing. pub fn paint_video_frame( &mut self, bounds: Bounds, corner_radii: Corners, id: VideoTextureId, + filter: VideoFilter, ) { use crate::VideoFrame; @@ -5491,7 +5493,7 @@ impl Window { corner_radii: corner_radii.scale(self.scale_factor()), texture_id: id.0, opacity: self.element_opacity(), - pad2: 0, + filter, }); if let Some(texture) = self.video_textures.borrow().get(&id) { use crate::VideoFramePixels; diff --git a/vendor/zed/crates/gpui_wgpu/src/wgpu_renderer.rs b/vendor/zed/crates/gpui_wgpu/src/wgpu_renderer.rs index 0b28e8b..126920d 100644 --- a/vendor/zed/crates/gpui_wgpu/src/wgpu_renderer.rs +++ b/vendor/zed/crates/gpui_wgpu/src/wgpu_renderer.rs @@ -4,7 +4,7 @@ use bytemuck::{Pod, Zeroable}; use collections::FxHashMap; use gpui::{ AtlasTextureId, Background, Bounds, DevicePixels, GpuSpecs, Path, Point, PrimitiveBatch, - ScaledPixels, Scene, Size, VideoTextureId, get_gamma_correction_ratios, + ScaledPixels, Scene, Size, VideoFilter, VideoTextureId, get_gamma_correction_ratios, }; use log::warn; #[cfg(not(target_family = "wasm"))] @@ -190,6 +190,7 @@ struct WgpuResources { pipelines: WgpuPipelines, bind_group_layouts: WgpuBindGroupLayouts, atlas_sampler: wgpu::Sampler, + nearest_sampler: wgpu::Sampler, atlas_texture_bind_groups: FxHashMap, video_textures: FxHashMap, globals_buffer: wgpu::Buffer, @@ -218,7 +219,8 @@ struct VideoTextureGpu { generation: u64, width: u32, height: u32, - bind_group: wgpu::BindGroup, + linear_bind_group: wgpu::BindGroup, + nearest_bind_group: wgpu::BindGroup, } impl WgpuResources { @@ -1350,6 +1352,12 @@ impl WgpuRendererCore { min_filter: wgpu::FilterMode::Linear, ..Default::default() }); + let nearest_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label: Some("video_nearest_sampler"), + mag_filter: wgpu::FilterMode::Nearest, + min_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment as u64; let globals_size = std::mem::size_of::() as u64; let gamma_size = std::mem::size_of::() as u64; @@ -1483,6 +1491,7 @@ impl WgpuRendererCore { pipelines, bind_group_layouts, atlas_sampler, + nearest_sampler, atlas_texture_bind_groups: FxHashMap::default(), video_textures: FxHashMap::default(), globals_buffer, @@ -1859,6 +1868,7 @@ impl WgpuRendererCore { &self, label: &str, texture_view: &wgpu::TextureView, + sampler: &wgpu::Sampler, ) -> wgpu::BindGroup { let resources = self.resources(); resources @@ -1873,7 +1883,7 @@ impl WgpuRendererCore { }, wgpu::BindGroupEntry { binding: 1, - resource: wgpu::BindingResource::Sampler(&resources.atlas_sampler), + resource: wgpu::BindingResource::Sampler(sampler), }, ], }) @@ -1913,8 +1923,11 @@ impl WgpuRendererCore { continue; } - let bind_group = - self.create_texture_bind_group("atlas_texture_bind_group", &texture_info.view); + let bind_group = self.create_texture_bind_group( + "atlas_texture_bind_group", + &texture_info.view, + &self.resources().atlas_sampler, + ); self.resources_mut().atlas_texture_bind_groups.insert( texture_id, CachedTextureBindGroup { @@ -2089,35 +2102,37 @@ impl WgpuRendererCore { self.sync_video_textures(scene); let frames = &scene.video_frames[range.clone()]; let mut run_start = None; - let mut run_texture = None; + let mut run_key = None; for (offset, frame) in frames.iter().enumerate() { let texture_id = VideoTextureId::new(frame.texture_id); let live = self.resources().video_textures.contains_key(&texture_id); - match (run_start, live) { - (None, true) => { + let key = live.then_some((texture_id, frame.filter)); + match (run_start, run_key, key) { + (None, _, Some(key)) => { run_start = Some(offset); - run_texture = Some(texture_id); + run_key = Some(key); } - (Some(start), true) if run_texture == Some(texture_id) => { - let _ = start; - } - (Some(start), _) => { + (Some(_), Some(current), Some(key)) if current == key => {} + (Some(start), Some((run_texture, run_filter)), next) => { self.draw_video_frame_run( instances, - run_texture.unwrap(), + run_texture, + run_filter, range.start + start..range.start + offset, pass, ); - run_start = live.then_some(offset); - run_texture = live.then_some(texture_id); + run_start = next.map(|_| offset); + run_key = next; } - (None, false) => {} + (None, _, None) => {} + _ => unreachable!("video frame run state remains paired"), } } - if let Some(start) = run_start { + if let (Some(start), Some((texture, filter))) = (run_start, run_key) { self.draw_video_frame_run( instances, - run_texture.unwrap(), + texture, + filter, range.start + start..range.end, pass, ); @@ -2197,7 +2212,16 @@ impl WgpuRendererCore { generation: pixels.generation, width: pixels.width, height: pixels.height, - bind_group: self.create_texture_bind_group("video_frame_texture_bind_group", &view), + linear_bind_group: self.create_texture_bind_group( + "video_frame_linear_texture_bind_group", + &view, + &resources.atlas_sampler, + ), + nearest_bind_group: self.create_texture_bind_group( + "video_frame_nearest_texture_bind_group", + &view, + &resources.nearest_sampler, + ), } } @@ -2205,6 +2229,7 @@ impl WgpuRendererCore { &self, instances: &InstanceBinding, texture_id: VideoTextureId, + filter: VideoFilter, range: Range, pass: &mut wgpu::RenderPass<'_>, ) { @@ -2215,7 +2240,11 @@ impl WgpuRendererCore { pass.set_pipeline(&resources.pipelines.video_frames); pass.set_bind_group(0, &resources.globals_bind_group, &[]); pass.set_bind_group(1, &instances.bind_group, &[]); - pass.set_bind_group(2, &texture.bind_group, &[]); + let bind_group = match filter { + VideoFilter::Linear => &texture.linear_bind_group, + VideoFilter::Nearest => &texture.nearest_bind_group, + }; + pass.set_bind_group(2, bind_group, &[]); let start = instances.first_instance + range.start as u32; let end = instances.first_instance + range.end as u32; pass.draw(0..4, start..end); @@ -2261,6 +2290,7 @@ impl WgpuRendererCore { let texture = self.create_texture_bind_group( "path_intermediate_texture_bind_group", &path_intermediate_view, + &self.resources().atlas_sampler, ); let resources = self.resources(); pass.set_pipeline(&resources.pipelines.paths); From 6621960981554fe4c165d53c20389106c7b4864e Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:43:32 +0000 Subject: [PATCH 14/17] Add release dry-run workflow Graft-Changeset: c3dcb120aeefd0ac5a749a9d83ee2cd6590632f2051d78cdea48c3133e25b9d47 Graft-Session: ses_01M6F6RR9T3SQAB4VQMHNTVPMF Graft-Work-Item: WI-2951 Co-authored-by: gpt-5.6-sol Graft-Batch: 21 --- .github/workflows/release.yml | 72 +++++++++++++++++++++++++++++++++++ scripts/check-zed-rev.sh | 56 +++++++++++++++++++++++---- scripts/release-notes.sh | 17 +++++++++ 3 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100755 scripts/release-notes.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..37c5953 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,72 @@ +name: Release dry run + +on: + push: + tags: + - 'v*\+zed.*' + pull_request: + paths: + - '.github/workflows/release.yml' + - 'scripts/check-zed-rev.sh' + - 'scripts/release-notes.sh' + schedule: + - cron: '23 3 * * *' + workflow_dispatch: + +permissions: + contents: read + +jobs: + validate: + name: Validate release inputs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + - name: Check Zed revision and release tag + env: + RELEASE_TAG: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || '' }} + run: scripts/check-zed-rev.sh "$RELEASE_TAG" + - name: Draft release notes from CHANGELOG + run: | + scripts/release-notes.sh release-notes.md + cat release-notes.md >> "$GITHUB_STEP_SUMMARY" + + build: + name: Build (${{ matrix.os }}) + needs: validate + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + - name: Install Linux system libraries + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libxcb1-dev libx11-xcb-dev libxkbcommon-dev libxkbcommon-x11-dev \ + libwayland-dev libvulkan-dev libfontconfig-dev libfreetype-dev + - uses: dtolnay/rust-toolchain@d124f86f28ff42855784ff3319f48ac182c248f8 + - uses: Swatinem/rust-cache@e9d7b9f1e587218970671bac74e5511ed9506e6a + - name: Build release workspace + run: cargo build --workspace --release --locked + + draft-release: + name: Draft GitHub release + if: startsWith(github.ref, 'refs/tags/') + needs: build + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + - name: Create draft release + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ github.ref_name }} + run: | + scripts/release-notes.sh release-notes.md + gh release create "$RELEASE_TAG" --draft --verify-tag \ + --title "$RELEASE_TAG" --notes-file release-notes.md diff --git a/scripts/check-zed-rev.sh b/scripts/check-zed-rev.sh index 4b964ff..0c53a41 100755 --- a/scripts/check-zed-rev.sh +++ b/scripts/check-zed-rev.sh @@ -1,17 +1,59 @@ #!/usr/bin/env bash -# WI-2725: every fluent-* crate records [package.metadata.estate-gpui] zed-rev, -# and it must equal the zed commit in vendor/zed/UPSTREAM.md. +# Check fluent crate metadata against the vendored Zed revision. When given a +# release tag, also check that its version and +zed suffix describe this tree. set -euo pipefail cd "$(dirname "$0")/.." -want=$(sed -n 's/^- zed commit: `\([0-9a-f]\{40\}\)`$/\1/p' vendor/zed/UPSTREAM.md) -if [ -z "$want" ]; then echo "error: no zed commit in vendor/zed/UPSTREAM.md" >&2; exit 1; fi + +upstream_file=vendor/zed/UPSTREAM.md +want=$(sed -n 's/^- zed commit: `\([0-9a-f]\{40\}\)`$/\1/p' "$upstream_file") +if [ -z "$want" ]; then + echo "error: no zed commit in $upstream_file" >&2 + exit 1 +fi + fail=0 for toml in crates/fluent-*/Cargo.toml; do got=$(awk '/^\[package\.metadata\.estate-gpui\]/{s=1;next} /^\[/{s=0} s && /^zed-rev *=/{gsub(/[" ]/,"",$0); sub(/^zed-rev=/,""); print}' "$toml") if [ "$got" != "$want" ]; then - echo "error: $toml zed-rev='${got:-}', vendor/zed/UPSTREAM.md has $want" >&2 + echo "error: $toml zed-rev='${got:-}', $upstream_file has $want" >&2 fail=1 fi done -[ "$fail" -eq 0 ] && echo "zed-rev ok: $want" -exit "$fail" + +if [ "$fail" -ne 0 ]; then + exit "$fail" +fi + +tag=${1:-} +if [ -z "$tag" ]; then + echo "zed-rev ok: $want" + exit 0 +fi + +if [[ ! "$tag" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)\+zed\.([0-9a-f]{7,40})$ ]]; then + echo "error: release tag '$tag' must match v..+zed.<7-40 hex sha>" >&2 + exit 1 +fi +version=${BASH_REMATCH[1]} +tag_rev=${BASH_REMATCH[2]} +if [[ "$want" != "$tag_rev"* ]]; then + echo "error: tag zed revision '$tag_rev' does not match $upstream_file revision '$want'" >&2 + exit 1 +fi + +for toml in crates/fluent-*/Cargo.toml; do + # Private support crates are part of the build but not the workspace release. + if grep -Eq '^publish[[:space:]]*=[[:space:]]*false([[:space:]]*#.*)?$' "$toml"; then + continue + fi + crate_version=$(sed -n 's/^version[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' "$toml" | head -n 1) + if [ "$crate_version" != "$version" ]; then + echo "error: $toml version '${crate_version:-}' does not match tag version '$version'" >&2 + fail=1 + fi +done + +if [ "$fail" -ne 0 ]; then + exit "$fail" +fi +echo "release tag ok: $tag (zed $want)" diff --git a/scripts/release-notes.sh b/scripts/release-notes.sh new file mode 100755 index 0000000..6b0d416 --- /dev/null +++ b/scripts/release-notes.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Extract the curated Unreleased section used for a draft GitHub release. +set -euo pipefail +cd "$(dirname "$0")/.." +output=${1:-release-notes.md} + +awk ' + /^## Unreleased([[:space:]]|$)/ { found=1; next } + found && /^## / { exit } + found { print } +' CHANGELOG.md > "$output" + +if ! grep -q '[^[:space:]]' "$output"; then + echo "error: CHANGELOG.md has no release notes under ## Unreleased" >&2 + exit 1 +fi +printf 'release notes ready: %s\n' "$output" From 83a8a6f0e7e34156eef3125904b59323f540ca4c Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:45:56 +0000 Subject: [PATCH 15/17] docs: complete fluent-primitives public API docs Graft-Changeset: c1bd7da4c8c38aacb93b1e0c34dd2002bf83f55c1796a679afe0e22d4fd872ca8 Graft-Session: ses_01M6F6RRFBHGC1GKQQVB5S0YD9 Graft-Work-Item: WI-2945 Graft-Batch: 21 --- CHANGELOG.md | 2 + crates/fluent-primitives/src/accordion.rs | 5 ++ crates/fluent-primitives/src/app_dot.rs | 4 ++ crates/fluent-primitives/src/avatar.rs | 4 ++ crates/fluent-primitives/src/badge.rs | 15 ++++++ crates/fluent-primitives/src/button.rs | 33 +++++++++++++ crates/fluent-primitives/src/calendar.rs | 15 ++++++ crates/fluent-primitives/src/card.rs | 9 ++++ crates/fluent-primitives/src/checkbox.rs | 7 +++ crates/fluent-primitives/src/chip.rs | 2 + crates/fluent-primitives/src/color_picker.rs | 16 +++++++ crates/fluent-primitives/src/combobox.rs | 14 ++++++ .../fluent-primitives/src/connection_badge.rs | 5 ++ .../fluent-primitives/src/date_time_picker.rs | 48 +++++++++++++++++++ crates/fluent-primitives/src/divider.rs | 4 ++ crates/fluent-primitives/src/dropdown.rs | 10 ++++ crates/fluent-primitives/src/expander.rs | 3 ++ crates/fluent-primitives/src/field.rs | 9 ++++ crates/fluent-primitives/src/focus.rs | 1 + crates/fluent-primitives/src/icon.rs | 8 ++++ crates/fluent-primitives/src/icon_button.rs | 5 ++ crates/fluent-primitives/src/info_label.rs | 2 + crates/fluent-primitives/src/input_capture.rs | 4 +- crates/fluent-primitives/src/label.rs | 8 ++++ crates/fluent-primitives/src/lib.rs | 3 ++ crates/fluent-primitives/src/link.rs | 7 +++ crates/fluent-primitives/src/persona.rs | 7 +++ crates/fluent-primitives/src/progress.rs | 4 ++ crates/fluent-primitives/src/radio_group.rs | 16 +++++++ crates/fluent-primitives/src/rating.rs | 10 ++++ crates/fluent-primitives/src/searchbox.rs | 8 ++++ .../fluent-primitives/src/section_header.rs | 1 + crates/fluent-primitives/src/skeleton.rs | 5 ++ crates/fluent-primitives/src/slider.rs | 8 ++++ crates/fluent-primitives/src/spin_button.rs | 4 ++ crates/fluent-primitives/src/spinner.rs | 2 + .../src/suggestion_popover.rs | 39 +++++++++++++++ crates/fluent-primitives/src/switch.rs | 3 ++ crates/fluent-primitives/src/tag.rs | 29 +++++++++++ crates/fluent-primitives/src/terminal_grid.rs | 2 +- crates/fluent-primitives/src/terminal_view.rs | 2 +- crates/fluent-primitives/src/text_input.rs | 15 ++++++ crates/fluent-primitives/src/textarea.rs | 9 ++++ crates/fluent-primitives/src/toggle_button.rs | 6 +++ crates/fluent-primitives/src/token_input.rs | 31 ++++++++++++ crates/fluent-primitives/src/tooltip.rs | 2 + crates/fluent-primitives/src/video_surface.rs | 5 +- 47 files changed, 444 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5c9c9c..8f39115 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ per crate. ## Unreleased +- fluent-primitives: deny missing public API documentation and document every exported control and supporting type (WI-2945). + - docs: document DPI scaling conversion and resize behavior across Windows DirectX, Linux X11, and Linux Wayland, with native-run evidence requirements (WI-3061). - Layout: add Fluent 2 Breadcrumb and Pagination controls with overflow menus, current-item semantics, RTL separators, and keyboard-ready controls (WI-3036). - fluent-layout, gallery: Drawer with inline and overlay modes, RTL-aware start/end placement, size presets, reduced-motion-aware transitions, focus trapping and opener restoration (WI-3038). diff --git a/crates/fluent-primitives/src/accordion.rs b/crates/fluent-primitives/src/accordion.rs index 8d57ff7..b8e0407 100644 --- a/crates/fluent-primitives/src/accordion.rs +++ b/crates/fluent-primitives/src/accordion.rs @@ -18,6 +18,7 @@ pub struct Accordion { } impl Accordion { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -27,21 +28,25 @@ impl Accordion { } } + /// Sets the item configuration for this control. pub fn item(mut self, item: Expander) -> Self { self.items.push(item); self } + /// Sets the items configuration for this control. pub fn items(mut self, items: impl IntoIterator) -> Self { self.items = items.into_iter().collect(); self } + /// Sets the multiple configuration for this control. pub fn multiple(mut self, multiple: bool) -> Self { self.multiple = multiple; self } + /// Sets the collapsible configuration for this control. pub fn collapsible(mut self, collapsible: bool) -> Self { self.collapsible = collapsible; self diff --git a/crates/fluent-primitives/src/app_dot.rs b/crates/fluent-primitives/src/app_dot.rs index ffafbbc..daec971 100644 --- a/crates/fluent-primitives/src/app_dot.rs +++ b/crates/fluent-primitives/src/app_dot.rs @@ -16,6 +16,7 @@ pub struct AppDot { } impl AppDot { + /// Creates a new instance for use in a Fluent interface. pub fn new(seed: impl Into) -> Self { let seed: SharedString = seed.into(); let letter: SharedString = seed @@ -32,16 +33,19 @@ impl AppDot { } } + /// Sets the letter configuration for this control. pub fn letter(mut self, letter: impl Into) -> Self { self.letter = letter.into(); self } + /// Sets the hue configuration for this control. pub fn hue(mut self, deg: f32) -> Self { self.hue = Some(deg); self } + /// Sets the size configuration for this control. pub fn size(mut self, size: f32) -> Self { self.size = size; self diff --git a/crates/fluent-primitives/src/avatar.rs b/crates/fluent-primitives/src/avatar.rs index 7fef418..8ca62f0 100644 --- a/crates/fluent-primitives/src/avatar.rs +++ b/crates/fluent-primitives/src/avatar.rs @@ -17,6 +17,7 @@ pub struct Avatar { } impl Avatar { + /// Returns the initials value. pub fn initials(initials: impl Into) -> Self { Self { initials: Some(initials.into()), @@ -26,6 +27,7 @@ impl Avatar { } } + /// Returns the image value. pub fn image(path: impl Into) -> Self { Self { initials: None, @@ -35,11 +37,13 @@ impl Avatar { } } + /// Sets the hue configuration for this control. pub fn hue(mut self, deg: f32) -> Self { self.hue = Some(deg); self } + /// Sets the size configuration for this control. pub fn size(mut self, size: f32) -> Self { self.size = size; self diff --git a/crates/fluent-primitives/src/badge.rs b/crates/fluent-primitives/src/badge.rs index eaf1475..2810497 100644 --- a/crates/fluent-primitives/src/badge.rs +++ b/crates/fluent-primitives/src/badge.rs @@ -7,10 +7,15 @@ use crate::Avatar; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum PresenceBadgeState { #[default] + /// The available option. Available, + /// The busy option. Busy, + /// The away option. Away, + /// The offline option. Offline, + /// The do not disturb option. DoNotDisturb, } @@ -22,6 +27,7 @@ pub struct PresenceBadge { } impl PresenceBadge { + /// Creates a new instance for use in a Fluent interface. pub fn new(avatar: Avatar, state: PresenceBadgeState) -> Self { Self { avatar, state } } @@ -57,7 +63,9 @@ impl RenderOnce for PresenceBadge { #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum CounterBadgeAppearance { #[default] + /// The filled option. Filled, + /// The ghost option. Ghost, } @@ -84,6 +92,7 @@ pub struct CounterBadge { } impl CounterBadge { + /// Creates a new instance for use in a Fluent interface. pub fn new(count: u32) -> Self { Self { count, @@ -94,21 +103,25 @@ impl CounterBadge { } } + /// Sets the overflow count configuration for this control. pub fn overflow_count(mut self, count: u32) -> Self { self.overflow_count = count.max(1); self } + /// Sets the show zero configuration for this control. pub fn show_zero(mut self, show: bool) -> Self { self.show_zero = show; self } + /// Sets the dot configuration for this control. pub fn dot(mut self, dot: bool) -> Self { self.dot = dot; self } + /// Sets the appearance configuration for this control. pub fn appearance(mut self, appearance: CounterBadgeAppearance) -> Self { self.appearance = appearance; self @@ -181,12 +194,14 @@ pub struct Badge { } impl Badge { + /// Returns the count value. pub fn count(n: u32) -> Self { Self { variant: BadgeVariant::Count(n), } } + /// Returns the dot value. pub fn dot() -> Self { Self { variant: BadgeVariant::Dot, diff --git a/crates/fluent-primitives/src/button.rs b/crates/fluent-primitives/src/button.rs index 17285ce..e7e8e94 100644 --- a/crates/fluent-primitives/src/button.rs +++ b/crates/fluent-primitives/src/button.rs @@ -9,10 +9,15 @@ use gpui::{ #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum ButtonAppearance { #[default] + /// The secondary option. Secondary, + /// The primary option. Primary, + /// The outline option. Outline, + /// The subtle option. Subtle, + /// The transparent option. Transparent, } @@ -35,8 +40,11 @@ impl ButtonAppearance { #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum ButtonShape { #[default] + /// The rounded option. Rounded, + /// The square option. Square, + /// The circular option. Circular, } @@ -44,15 +52,20 @@ pub enum ButtonShape { #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum ButtonSize { #[default] + /// The medium option. Medium, + /// The small option. Small, + /// The large option. Large, } impl ButtonSize { #[allow(non_upper_case_globals)] + /// The normal constant. pub const Normal: Self = Self::Medium; #[allow(non_upper_case_globals)] + /// The compact constant. pub const Compact: Self = Self::Small; } @@ -60,7 +73,9 @@ impl ButtonSize { #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum ButtonIconPosition { #[default] + /// The before option. Before, + /// The after option. After, } @@ -88,6 +103,7 @@ pub struct Button { } impl Button { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -103,31 +119,37 @@ impl Button { } } + /// Returns the current label. pub fn label(mut self, label: impl Into) -> Self { self.label = Some(label.into()); self } + /// Sets the icon configuration for this control. pub fn icon(mut self, path: impl Into) -> Self { self.icon = Some(path.into()); self } + /// Sets the icon position configuration for this control. pub fn icon_position(mut self, position: ButtonIconPosition) -> Self { self.icon_position = position; self } + /// Sets the appearance configuration for this control. pub fn appearance(mut self, appearance: ButtonAppearance) -> Self { self.appearance = appearance; self } + /// Sets the shape configuration for this control. pub fn shape(mut self, shape: ButtonShape) -> Self { self.shape = shape; self } + /// Sets the size configuration for this control. pub fn size(mut self, size: ButtonSize) -> Self { self.size = size; self @@ -139,11 +161,13 @@ impl Button { self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Registers a handler for click events. pub fn on_click( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, @@ -317,6 +341,7 @@ pub struct SplitButton { } impl SplitButton { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into, label: impl Into) -> Self { Self { id: id.into(), @@ -328,16 +353,19 @@ impl SplitButton { } } + /// Sets the appearance configuration for this control. pub fn appearance(mut self, appearance: ButtonAppearance) -> Self { self.appearance = appearance; self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Registers a handler for click events. pub fn on_click( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, @@ -346,6 +374,7 @@ impl SplitButton { self } + /// Registers a handler for menu click events. pub fn on_menu_click( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, @@ -391,6 +420,7 @@ pub struct CompoundButton { } impl CompoundButton { + /// Creates a new instance for use in a Fluent interface. pub fn new( id: impl Into, label: impl Into, @@ -405,16 +435,19 @@ impl CompoundButton { } } + /// Sets the appearance configuration for this control. pub fn appearance(mut self, appearance: ButtonAppearance) -> Self { self.button = self.button.appearance(appearance); self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.button = self.button.disabled(disabled); self } + /// Registers a handler for click events. pub fn on_click( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, diff --git a/crates/fluent-primitives/src/calendar.rs b/crates/fluent-primitives/src/calendar.rs index 64241e1..3536912 100644 --- a/crates/fluent-primitives/src/calendar.rs +++ b/crates/fluent-primitives/src/calendar.rs @@ -7,12 +7,17 @@ use gpui::{ }; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +/// A Fluent primitive for representing calendar date in an interface. pub struct CalendarDate { + /// The year value. pub year: i32, + /// The month value. pub month: u8, + /// The day value. pub day: u8, } impl CalendarDate { + /// Creates a new instance for use in a Fluent interface. pub fn new(year: i32, month: u8, day: u8) -> Option { let max = days_in_month(year, month)?; (day > 0 && day <= max).then_some(Self { year, month, day }) @@ -20,6 +25,7 @@ impl CalendarDate { } #[derive(Clone)] +/// A Fluent primitive for representing calendar in an interface. pub struct Calendar { id: ElementId, value: CalendarDate, @@ -31,6 +37,7 @@ pub struct Calendar { on_change: Option, } impl Calendar { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into, value: CalendarDate) -> Self { Self { id: id.into(), @@ -43,30 +50,37 @@ impl Calendar { on_change: None, } } + /// Returns the current value. pub fn value(mut self, value: CalendarDate) -> Self { self.value = value; self.month = CalendarDate { day: 1, ..value }; self } + /// Sets the first day of week configuration for this control. pub fn first_day_of_week(mut self, day: u8) -> Self { self.first_day_of_week = day % 7; self } + /// Sets the locale first day of week configuration for this control. pub fn locale_first_day_of_week(self, day: u8) -> Self { self.first_day_of_week(day) } + /// Sets the min configuration for this control. pub fn min(mut self, date: CalendarDate) -> Self { self.min = Some(date); self } + /// Sets the max configuration for this control. pub fn max(mut self, date: CalendarDate) -> Self { self.max = Some(date); self } + /// Returns the current label. pub fn label(mut self, label: impl Into) -> Self { self.label = label.into(); self } + /// Registers a handler for change events. pub fn on_change(mut self, f: impl Fn(CalendarDate, &mut App) + 'static) -> Self { self.on_change = Some(std::rc::Rc::new(f)); self @@ -84,6 +98,7 @@ impl Calendar { self.allowed(candidate).then_some(candidate) } + /// Returns the current selected. pub fn selected(&self) -> CalendarDate { self.value } diff --git a/crates/fluent-primitives/src/card.rs b/crates/fluent-primitives/src/card.rs index 7c994e8..6125dd8 100644 --- a/crates/fluent-primitives/src/card.rs +++ b/crates/fluent-primitives/src/card.rs @@ -21,6 +21,7 @@ pub struct Card { } impl Card { + /// Creates a new instance for use in a Fluent interface. pub fn new() -> Self { Self { id: None, @@ -35,6 +36,7 @@ impl Card { } } + /// Sets the id configuration for this control. pub fn id(mut self, id: impl Into) -> Self { self.id = Some(id.into()); self @@ -52,26 +54,31 @@ impl Card { self } + /// Sets the row configuration for this control. pub fn row(mut self) -> Self { self.row = true; self } + /// Sets the flex 1 configuration for this control. pub fn flex_1(mut self) -> Self { self.flex_1 = true; self } + /// Sets the items center configuration for this control. pub fn items_center(mut self) -> Self { self.items_center = true; self } + /// Sets the hoverable configuration for this control. pub fn hoverable(mut self) -> Self { self.hoverable = true; self } + /// Registers a handler for click events. pub fn on_click( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, @@ -80,11 +87,13 @@ impl Card { self } + /// Sets the child configuration for this control. pub fn child(mut self, child: impl IntoElement) -> Self { self.children.push(child.into_any_element()); self } + /// Sets the children configuration for this control. pub fn children(mut self, children: impl IntoIterator) -> Self { for c in children { self.children.push(c.into_any_element()); diff --git a/crates/fluent-primitives/src/checkbox.rs b/crates/fluent-primitives/src/checkbox.rs index b931c5c..d3bffca 100644 --- a/crates/fluent-primitives/src/checkbox.rs +++ b/crates/fluent-primitives/src/checkbox.rs @@ -9,8 +9,11 @@ use gpui::{ #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum CheckboxState { #[default] + /// The unchecked option. Unchecked, + /// The checked option. Checked, + /// The indeterminate option. Indeterminate, } @@ -29,6 +32,7 @@ pub struct Checkbox { } impl Checkbox { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -47,11 +51,13 @@ impl Checkbox { self } + /// Returns the current state. pub fn state(mut self, state: CheckboxState) -> Self { self.state = state; self } + /// Sets the checked configuration for this control. pub fn checked(mut self, checked: bool) -> Self { self.state = if checked { CheckboxState::Checked @@ -67,6 +73,7 @@ impl Checkbox { self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self diff --git a/crates/fluent-primitives/src/chip.rs b/crates/fluent-primitives/src/chip.rs index 0474d6c..3513a10 100644 --- a/crates/fluent-primitives/src/chip.rs +++ b/crates/fluent-primitives/src/chip.rs @@ -13,6 +13,7 @@ pub struct Chip { } impl Chip { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into, label: impl Into) -> Self { Self { id: id.into(), @@ -21,6 +22,7 @@ impl Chip { } } + /// Registers a handler for dismiss events. pub fn on_dismiss( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, diff --git a/crates/fluent-primitives/src/color_picker.rs b/crates/fluent-primitives/src/color_picker.rs index 7913408..a2f009f 100644 --- a/crates/fluent-primitives/src/color_picker.rs +++ b/crates/fluent-primitives/src/color_picker.rs @@ -6,20 +6,27 @@ use gpui::{ SharedString, Window, }; #[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// A Fluent primitive for representing color in an interface. pub struct Color { + /// The red value. pub red: u8, + /// The green value. pub green: u8, + /// The blue value. pub blue: u8, } impl Color { + /// The white constant. pub const WHITE: Self = Self { red: 255, green: 255, blue: 255, }; + /// Formats the value for display or serialization. pub fn hex(self) -> String { format!("#{:02X}{:02X}{:02X}", self.red, self.green, self.blue) } + /// Parses an input value when it has a supported format. pub fn parse(s: &str) -> Option { let s = s.trim().strip_prefix('#').unwrap_or(s); (s.len() == 6) @@ -33,6 +40,7 @@ impl Color { } } #[derive(Clone)] +/// A Fluent primitive for representing color picker in an interface. pub struct ColorPicker { id: ElementId, color: Color, @@ -41,6 +49,7 @@ pub struct ColorPicker { on_change: Option, } impl ColorPicker { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -67,22 +76,27 @@ impl ColorPicker { on_change: None, } } + /// Returns the current value. pub fn value(mut self, c: Color) -> Self { self.color = c; self } + /// Sets the swatches configuration for this control. pub fn swatches(mut self, s: impl IntoIterator) -> Self { self.swatches = s.into_iter().collect(); self } + /// Returns the current label. pub fn label(mut self, s: impl Into) -> Self { self.label = s.into(); self } + /// Registers a handler for change events. pub fn on_change(mut self, f: impl Fn(Color, &mut App) + 'static) -> Self { self.on_change = Some(std::rc::Rc::new(f)); self } + /// Sets the hex value. pub fn set_hex(&mut self, value: &str) -> bool { if let Some(color) = Color::parse(value) { self.color = color; @@ -91,6 +105,7 @@ impl ColorPicker { false } } + /// Adjusts one colour channel by the given amount, clamped to its valid range. pub fn nudge(&mut self, channel: &str, delta: i16) { let adjust = |v: u8| (v as i16).saturating_add(delta).clamp(0, 255) as u8; self.color = match channel { @@ -110,6 +125,7 @@ impl ColorPicker { }; } + /// Returns the current color. pub fn color(&self) -> Color { self.color } diff --git a/crates/fluent-primitives/src/combobox.rs b/crates/fluent-primitives/src/combobox.rs index 2e08560..a26f8e5 100644 --- a/crates/fluent-primitives/src/combobox.rs +++ b/crates/fluent-primitives/src/combobox.rs @@ -8,12 +8,16 @@ use gpui::{ }; #[derive(Clone, Debug)] +/// A Fluent primitive for representing combobox option in an interface. pub struct ComboboxOption { + /// The value value. pub value: SharedString, + /// The label value. pub label: SharedString, } impl ComboboxOption { + /// Creates a new instance for use in a Fluent interface. pub fn new(value: impl Into, label: impl Into) -> Self { Self { value: value.into(), @@ -71,6 +75,7 @@ impl Default for Combobox { } impl Combobox { + /// Creates a new instance for use in a Fluent interface. pub fn new() -> Self { Self::default() } @@ -82,11 +87,13 @@ impl Combobox { self } + /// Sets the options configuration for this control. pub fn options(mut self, options: impl IntoIterator) -> Self { self.options = options.into_iter().collect(); self } + /// Sets the option configuration for this control. pub fn option( mut self, value: impl Into, @@ -96,6 +103,7 @@ impl Combobox { self } + /// Returns the current selected. pub fn selected(mut self, value: impl Into) -> Self { let value = value.into(); self.query = self @@ -108,31 +116,37 @@ impl Combobox { self } + /// Sets the placeholder configuration for this control. pub fn placeholder(mut self, placeholder: impl Into) -> Self { self.placeholder = placeholder.into(); self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Sets the freeform configuration for this control. pub fn freeform(mut self, freeform: bool) -> Self { self.freeform = freeform; self } + /// Returns the current query. pub fn query(mut self, query: impl Into) -> Self { self.query = query.into().to_string(); self } + /// Registers a handler for select events. pub fn on_select(mut self, f: impl Fn(SharedString, &mut App) + 'static) -> Self { self.on_select = Some(Box::new(f)); self } + /// Sets the selected value configuration for this control. pub fn selected_value(&self) -> Option<&SharedString> { self.value.as_ref() } diff --git a/crates/fluent-primitives/src/connection_badge.rs b/crates/fluent-primitives/src/connection_badge.rs index d56d92d..101da7d 100644 --- a/crates/fluent-primitives/src/connection_badge.rs +++ b/crates/fluent-primitives/src/connection_badge.rs @@ -11,9 +11,13 @@ use gpui::{pulsating_between, Animation}; /// status dot pulses. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ConnectionBadgeState { + /// The disconnected option. Disconnected, + /// The pairing option. Pairing, + /// The paired option. Paired, + /// The error option. Error, } @@ -27,6 +31,7 @@ pub struct ConnectionBadge { } impl ConnectionBadge { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into, state: ConnectionBadgeState) -> Self { Self { id: id.into(), diff --git a/crates/fluent-primitives/src/date_time_picker.rs b/crates/fluent-primitives/src/date_time_picker.rs index 845b34c..3c62054 100644 --- a/crates/fluent-primitives/src/date_time_picker.rs +++ b/crates/fluent-primitives/src/date_time_picker.rs @@ -11,15 +11,22 @@ const MIN_YEAR: i32 = 1; const MAX_YEAR: i32 = 9999; #[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// A Fluent primitive for representing date time value in an interface. pub struct DateTimeValue { + /// The year value. pub year: i32, + /// The month value. pub month: u8, + /// The day value. pub day: u8, + /// The hour value. pub hour: u8, + /// The minute value. pub minute: u8, } impl DateTimeValue { + /// Creates a new instance for use in a Fluent interface. pub fn new(year: i32, month: u8, day: u8, hour: u8, minute: u8) -> Option { if !(MIN_YEAR..=MAX_YEAR).contains(&year) || hour > 23 || minute > 59 { return None; @@ -37,6 +44,7 @@ impl DateTimeValue { }) } + /// Parses segmented input as a local date and time. pub fn parse_local(value: &str) -> Option { let trimmed = value.trim(); let split = trimmed.find('T').or_else(|| trimmed.find(' '))?; @@ -61,6 +69,7 @@ impl DateTimeValue { Self::new(year, month, day, hour, minute) } + /// Formats the value for use in a local date-time input. pub fn to_local_input_value(self) -> String { format!( "{:04}-{:02}-{:02}T{:02}:{:02}", @@ -68,6 +77,7 @@ impl DateTimeValue { ) } + /// Formats the value for display to the user. pub fn display_value(self) -> String { format!( "{:04}-{:02}-{:02} {:02}:{:02}", @@ -75,10 +85,12 @@ impl DateTimeValue { ) } + /// Returns whether leap year. pub fn is_leap_year(year: i32) -> bool { year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) } + /// Returns the number of days in this value’s month. pub fn days_in_month(year: i32, month: u8) -> Option { if !(MIN_YEAR..=MAX_YEAR).contains(&year) { return None; @@ -92,6 +104,7 @@ impl DateTimeValue { } } + /// Returns a copy adjusted by the requested segment and amount. pub fn adjust(self, segment: DateTimeSegment, delta: i32) -> Self { match segment { DateTimeSegment::Year => self.add_years(delta), @@ -209,8 +222,11 @@ impl DateTimeValue { /// Order of the date fields in a locale's display and entry format. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DateOrder { + /// The year month day option. YearMonthDay, + /// The day month year option. DayMonthYear, + /// The month day year option. MonthDayYear, } @@ -218,17 +234,23 @@ pub enum DateOrder { /// gives a message suitable for showing next to the field. #[derive(Clone, Debug, PartialEq, Eq)] pub enum DateTimeParseError { + /// The empty option. Empty, /// The text does not match the locale's layout; `expected` is an example. Format { + /// The value value. expected: String, }, + /// The invalid year option. InvalidYear, + /// The invalid month option. InvalidMonth, /// The day does not exist in that month; `max` is the month's last day. InvalidDay { + /// The value value. max: Option, }, + /// The invalid time option. InvalidTime, } @@ -266,11 +288,17 @@ pub type DateTimeParser = /// [`DateTimePicker::parser`] for anything this struct cannot express. #[derive(Clone, Debug, PartialEq, Eq)] pub struct DateTimeLocale { + /// The tag value. pub tag: SharedString, + /// The date order value. pub date_order: DateOrder, + /// The date separator value. pub date_separator: char, + /// The hour12 value. pub hour12: bool, + /// The am label value. pub am_label: SharedString, + /// The pm label value. pub pm_label: SharedString, } @@ -334,6 +362,7 @@ impl DateTimeLocale { } } + /// Formats the value for display or serialization. pub fn format(&self, v: DateTimeValue) -> String { let sep = self.date_separator; let date = match self.date_order { @@ -374,6 +403,7 @@ impl DateTimeLocale { }) } + /// Parses an input value when it has a supported format. pub fn parse(&self, input: &str) -> Result { let bad = || DateTimeParseError::Format { expected: self.example(), @@ -446,12 +476,18 @@ impl DateTimeLocale { } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +/// Available date time segment options for configuring the control. pub enum DateTimeSegment { + /// The year option. Year, + /// The month option. Month, #[default] + /// The day option. Day, + /// The hour option. Hour, + /// The minute option. Minute, } @@ -543,16 +579,19 @@ impl Default for DateTimePicker { } impl DateTimePicker { + /// Creates a new instance for use in a Fluent interface. pub fn new() -> Self { Self::default() } + /// Returns the current value. pub fn value(mut self, value: DateTimeValue) -> Self { self.value = Some(value); self.draft = value; self } + /// Sets the maybe value configuration for this control. pub fn maybe_value(mut self, value: Option) -> Self { self.value = value; if let Some(value) = value { @@ -561,26 +600,31 @@ impl DateTimePicker { self } + /// Sets the default value configuration for this control. pub fn default_value(mut self, value: DateTimeValue) -> Self { self.draft = value; self } + /// Sets the placeholder configuration for this control. pub fn placeholder(mut self, placeholder: impl Into) -> Self { self.placeholder = placeholder.into(); self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Sets the error configuration for this control. pub fn error(mut self, error: bool) -> Self { self.error = error; self } + /// Registers a handler for change events. pub fn on_change(mut self, f: impl Fn(Option, &mut App) + 'static) -> Self { self.on_change = Some(Box::new(f)); self @@ -654,10 +698,12 @@ impl DateTimePicker { parsed } + /// Sets the selected value configuration for this control. pub fn selected_value(&self) -> Option { self.value } + /// Sets the value value. pub fn set_value(&mut self, value: Option, cx: &mut Context) { self.value = value; if let Some(value) = value { @@ -666,11 +712,13 @@ impl DateTimePicker { cx.notify(); } + /// Sets the disabled value. pub fn set_disabled(&mut self, disabled: bool, cx: &mut Context) { self.disabled = disabled; cx.notify(); } + /// Sets the error value. pub fn set_error(&mut self, error: bool, cx: &mut Context) { self.error = error; cx.notify(); diff --git a/crates/fluent-primitives/src/divider.rs b/crates/fluent-primitives/src/divider.rs index 93d1b72..79727cc 100644 --- a/crates/fluent-primitives/src/divider.rs +++ b/crates/fluent-primitives/src/divider.rs @@ -5,7 +5,9 @@ use gpui::{div, prelude::*, px, App, IntoElement, RenderOnce, Window}; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum DividerOrientation { #[default] + /// The horizontal option. Horizontal, + /// The vertical option. Vertical, } @@ -16,12 +18,14 @@ pub struct Divider { } impl Divider { + /// Returns the horizontal value. pub fn horizontal() -> Self { Self { orientation: DividerOrientation::Horizontal, } } + /// Returns the vertical value. pub fn vertical() -> Self { Self { orientation: DividerOrientation::Vertical, diff --git a/crates/fluent-primitives/src/dropdown.rs b/crates/fluent-primitives/src/dropdown.rs index 149c3fb..8d1ea37 100644 --- a/crates/fluent-primitives/src/dropdown.rs +++ b/crates/fluent-primitives/src/dropdown.rs @@ -42,6 +42,7 @@ pub struct Dropdown { } impl Dropdown { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -61,11 +62,13 @@ impl Dropdown { } } + /// Returns the current selected. pub fn selected(mut self, value: impl Into) -> Self { self.selected = Some(value.into()); self } + /// Sets the placeholder configuration for this control. pub fn placeholder(mut self, text: impl Into) -> Self { self.placeholder = text.into(); self @@ -77,11 +80,13 @@ impl Dropdown { self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Sets the option configuration for this control. pub fn option( mut self, value: impl Into, @@ -91,25 +96,30 @@ impl Dropdown { self } + /// Sets the options configuration for this control. pub fn options(mut self, options: impl IntoIterator) -> Self { self.options = options.into_iter().collect(); self } + /// Registers a handler for select events. pub fn on_select(mut self, f: impl Fn(SharedString, &mut App) + 'static) -> Self { self.on_select = Some(Box::new(f)); self } + /// Sets the selected value. pub fn set_selected(&mut self, value: impl Into, cx: &mut Context) { self.selected = Some(value.into()); cx.notify(); } + /// Returns the selected value value. pub fn selected_value(&self) -> Option<&SharedString> { self.selected.as_ref() } + /// Sets the options value. pub fn set_options( &mut self, options: impl IntoIterator, diff --git a/crates/fluent-primitives/src/expander.rs b/crates/fluent-primitives/src/expander.rs index d61789e..4af5cc3 100644 --- a/crates/fluent-primitives/src/expander.rs +++ b/crates/fluent-primitives/src/expander.rs @@ -29,6 +29,7 @@ pub struct Expander { } impl Expander { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into, title: impl Into) -> Self { Self { id: id.into(), @@ -43,11 +44,13 @@ impl Expander { } } + /// Sets the expanded configuration for this control. pub fn expanded(mut self, expanded: bool) -> Self { self.expanded = expanded; self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self diff --git a/crates/fluent-primitives/src/field.rs b/crates/fluent-primitives/src/field.rs index c08a270..cdfd7da 100644 --- a/crates/fluent-primitives/src/field.rs +++ b/crates/fluent-primitives/src/field.rs @@ -59,9 +59,13 @@ type SemanticsApplier = #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum FieldValidationState { #[default] + /// The none option. None, + /// The success option. Success, + /// The warning option. Warning, + /// The error option. Error, } @@ -81,6 +85,7 @@ pub struct Field { } impl Field { + /// Creates a new instance for use in a Fluent interface. pub fn new(control: impl IntoElement) -> Self { Self { label: None, @@ -109,6 +114,7 @@ impl Field { field } + /// Returns the current label. pub fn label(mut self, label: impl Into) -> Self { self.label = Some(label.into()); self.info_label = None; @@ -125,16 +131,19 @@ impl Field { self } + /// Sets the required configuration for this control. pub fn required(mut self, required: bool) -> Self { self.required = required; self } + /// Sets the helper text configuration for this control. pub fn helper_text(mut self, text: impl Into) -> Self { self.helper_text = Some(text.into()); self } + /// Sets the validation configuration for this control. pub fn validation( mut self, state: FieldValidationState, diff --git a/crates/fluent-primitives/src/focus.rs b/crates/fluent-primitives/src/focus.rs index 8bbc867..c0b56df 100644 --- a/crates/fluent-primitives/src/focus.rs +++ b/crates/fluent-primitives/src/focus.rs @@ -2,6 +2,7 @@ use gpui::{actions, App, KeyBinding, KeyDownEvent, Window}; actions!(fluent_primitives, [FocusNextControl, FocusPreviousControl]); +/// Installs the standard Fluent focus-navigation key bindings. pub fn install_focus_keybindings(cx: &mut App) { cx.bind_keys([ KeyBinding::new("tab", FocusNextControl, None), diff --git a/crates/fluent-primitives/src/icon.rs b/crates/fluent-primitives/src/icon.rs index 78ea7c0..fa11c4e 100644 --- a/crates/fluent-primitives/src/icon.rs +++ b/crates/fluent-primitives/src/icon.rs @@ -7,14 +7,19 @@ use gpui::{ /// Canonical icon display sizes. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum IconSize { + /// The sm option. Sm, #[default] + /// The md option. Md, + /// The lg option. Lg, + /// The xl option. Xl, } impl IconSize { + /// Returns the px value. pub fn px(self) -> f32 { match self { Self::Sm => 16.0, @@ -41,6 +46,7 @@ pub struct Icon { } impl Icon { + /// Creates a new instance for use in a Fluent interface. pub fn new(path: impl Into) -> Self { Self { path: path.into(), @@ -49,11 +55,13 @@ impl Icon { } } + /// Sets the size configuration for this control. pub fn size(mut self, size: IconSize) -> Self { self.size = size; self } + /// Returns the current color. pub fn color(mut self, color: impl Into) -> Self { self.color = Some(color.into()); self diff --git a/crates/fluent-primitives/src/icon_button.rs b/crates/fluent-primitives/src/icon_button.rs index b10e75a..a73f267 100644 --- a/crates/fluent-primitives/src/icon_button.rs +++ b/crates/fluent-primitives/src/icon_button.rs @@ -27,6 +27,7 @@ pub struct IconButton { } impl IconButton { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into, icon: impl Into) -> Self { Self { id: id.into(), @@ -47,11 +48,13 @@ impl IconButton { self } + /// Sets the appearance configuration for this control. pub fn appearance(mut self, appearance: ButtonAppearance) -> Self { self.appearance = appearance; self } + /// Sets the size configuration for this control. pub fn size(mut self, size: ButtonSize) -> Self { self.size = size; self @@ -63,11 +66,13 @@ impl IconButton { self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Registers a handler for click events. pub fn on_click( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, diff --git a/crates/fluent-primitives/src/info_label.rs b/crates/fluent-primitives/src/info_label.rs index 4d78637..37a3b3b 100644 --- a/crates/fluent-primitives/src/info_label.rs +++ b/crates/fluent-primitives/src/info_label.rs @@ -12,6 +12,7 @@ pub struct InfoLabel { } impl InfoLabel { + /// Creates a new instance for use in a Fluent interface. pub fn new( id: impl Into, text: impl Into, @@ -25,6 +26,7 @@ impl InfoLabel { } } + /// Returns the current text. pub fn text(&self) -> SharedString { self.text.clone() } diff --git a/crates/fluent-primitives/src/input_capture.rs b/crates/fluent-primitives/src/input_capture.rs index 933ddd9..c804069 100644 --- a/crates/fluent-primitives/src/input_capture.rs +++ b/crates/fluent-primitives/src/input_capture.rs @@ -64,7 +64,7 @@ use gpui::{ Pixels, Point, Render, ScrollDelta, ScrollEvent, Subscription, WeakEntity, Window, }; -/// One captured input event. See the [module docs](self). +/// One captured input event. #[derive(Clone, Debug)] pub enum InputCaptureEvent { /// A key press (`pressed: true`) or release. @@ -363,7 +363,7 @@ pub fn code_for_evdev_code(code: u32) -> Option<&'static str> { /// A focusable surface that captures raw key, pointer and wheel input while grabbed. /// /// Create it with `cx.new(InputCapture::new)` and subscribe to its -/// [`InputCaptureEvent`]s. See the [module docs](self) for grab, release and key-code details. +/// [`InputCaptureEvent`]s. pub struct InputCapture { focus_handle: FocusHandle, grabbed: bool, diff --git a/crates/fluent-primitives/src/label.rs b/crates/fluent-primitives/src/label.rs index 684680e..34700bf 100644 --- a/crates/fluent-primitives/src/label.rs +++ b/crates/fluent-primitives/src/label.rs @@ -6,11 +6,16 @@ use gpui::{ /// Which step of the type ramp to use. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum LabelSize { + /// The caption option. Caption, #[default] + /// The body option. Body, + /// The subtitle option. Subtitle, + /// The title option. Title, + /// The display option. Display, } @@ -29,6 +34,7 @@ pub struct Label { } impl Label { + /// Creates a new instance for use in a Fluent interface. pub fn new(text: impl Into) -> Self { Self { text: text.into(), @@ -39,11 +45,13 @@ impl Label { } } + /// Sets the size configuration for this control. pub fn size(mut self, size: LabelSize) -> Self { self.size = size; self } + /// Returns the current color. pub fn color(mut self, color: impl Into) -> Self { self.color = Some(color.into()); self diff --git a/crates/fluent-primitives/src/lib.rs b/crates/fluent-primitives/src/lib.rs index b0d9042..4548d2a 100644 --- a/crates/fluent-primitives/src/lib.rs +++ b/crates/fluent-primitives/src/lib.rs @@ -1,3 +1,6 @@ +#![deny(missing_docs)] +//! Fluent 2 controls and supporting types for GPUI applications. + mod accordion; mod app_dot; mod avatar; diff --git a/crates/fluent-primitives/src/link.rs b/crates/fluent-primitives/src/link.rs index 71dc0f3..43aed2d 100644 --- a/crates/fluent-primitives/src/link.rs +++ b/crates/fluent-primitives/src/link.rs @@ -9,7 +9,9 @@ use gpui::{ #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum LinkAppearance { #[default] + /// The default option. Default, + /// The subtle option. Subtle, } @@ -29,6 +31,7 @@ pub struct Link { } impl Link { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into, label: impl Into) -> Self { Self { id: id.into(), @@ -40,11 +43,13 @@ impl Link { } } + /// Sets the appearance configuration for this control. pub fn appearance(mut self, appearance: LinkAppearance) -> Self { self.appearance = appearance; self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self @@ -56,6 +61,7 @@ impl Link { self } + /// Registers a handler for click events. pub fn on_click( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, @@ -64,6 +70,7 @@ impl Link { self } + /// Returns the open url value. pub fn open_url(self, url: impl Into) -> Self { let url = url.into(); self.on_click(move |_, _, cx| cx.open_url(&url)) diff --git a/crates/fluent-primitives/src/persona.rs b/crates/fluent-primitives/src/persona.rs index cd12cdd..2473f7c 100644 --- a/crates/fluent-primitives/src/persona.rs +++ b/crates/fluent-primitives/src/persona.rs @@ -6,9 +6,12 @@ use crate::{Avatar, PresenceBadge, PresenceBadgeState}; /// Size presets for a persona row. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum PersonaSize { + /// The small option. Small, #[default] + /// The medium option. Medium, + /// The large option. Large, } @@ -33,6 +36,7 @@ pub struct Persona { } impl Persona { + /// Creates a new instance for use in a Fluent interface. pub fn new(avatar: Avatar, primary: impl Into) -> Self { Self { avatar, @@ -43,16 +47,19 @@ impl Persona { } } + /// Sets the secondary configuration for this control. pub fn secondary(mut self, secondary: impl Into) -> Self { self.secondary = Some(secondary.into()); self } + /// Sets the presence configuration for this control. pub fn presence(mut self, state: PresenceBadgeState) -> Self { self.presence = Some(state); self } + /// Sets the size configuration for this control. pub fn size(mut self, size: PersonaSize) -> Self { self.size = size; self diff --git a/crates/fluent-primitives/src/progress.rs b/crates/fluent-primitives/src/progress.rs index 2e12827..744c7a9 100644 --- a/crates/fluent-primitives/src/progress.rs +++ b/crates/fluent-primitives/src/progress.rs @@ -33,6 +33,7 @@ pub struct ProgressBar { impl ProgressBar { #[track_caller] + /// Creates a new instance for use in a Fluent interface. pub fn new(value: f32) -> Self { Self { id: core::panic::Location::caller().into(), @@ -75,16 +76,19 @@ impl ProgressBar { self } + /// Sets the success configuration for this control. pub fn success(mut self) -> Self { self.status = ProgressStatus::Success; self } + /// Sets the warning configuration for this control. pub fn warning(mut self) -> Self { self.status = ProgressStatus::Warning; self } + /// Sets the error configuration for this control. pub fn error(mut self) -> Self { self.status = ProgressStatus::Error; self diff --git a/crates/fluent-primitives/src/radio_group.rs b/crates/fluent-primitives/src/radio_group.rs index 0cbd84f..b472362 100644 --- a/crates/fluent-primitives/src/radio_group.rs +++ b/crates/fluent-primitives/src/radio_group.rs @@ -8,20 +8,28 @@ use gpui::{ }; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +/// Available radio group orientation options for configuring the control. pub enum RadioGroupOrientation { #[default] + /// The vertical option. Vertical, + /// The horizontal option. Horizontal, } #[derive(Clone, Debug)] +/// A Fluent primitive for representing radio option in an interface. pub struct RadioOption { + /// The value value. pub value: SharedString, + /// The label value. pub label: SharedString, + /// The disabled value. pub disabled: bool, } impl RadioOption { + /// Creates a new instance for use in a Fluent interface. pub fn new(value: impl Into, label: impl Into) -> Self { Self { value: value.into(), @@ -30,6 +38,7 @@ impl RadioOption { } } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self @@ -51,6 +60,7 @@ pub struct RadioGroup { } impl RadioGroup { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -70,6 +80,7 @@ impl RadioGroup { self } + /// Sets the option configuration for this control. pub fn option( mut self, value: impl Into, @@ -79,16 +90,19 @@ impl RadioGroup { self } + /// Sets the options configuration for this control. pub fn options(mut self, options: impl IntoIterator) -> Self { self.options = options.into_iter().collect(); self } + /// Returns the current selected. pub fn selected(mut self, value: impl Into) -> Self { self.selected = Some(value.into()); self } + /// Sets the orientation configuration for this control. pub fn orientation(mut self, orientation: RadioGroupOrientation) -> Self { self.orientation = orientation; self @@ -100,11 +114,13 @@ impl RadioGroup { self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Registers a handler for select events. pub fn on_select( mut self, f: impl Fn(SharedString, &ClickEvent, &mut Window, &mut App) + 'static, diff --git a/crates/fluent-primitives/src/rating.rs b/crates/fluent-primitives/src/rating.rs index dc7dda1..85b71fc 100644 --- a/crates/fluent-primitives/src/rating.rs +++ b/crates/fluent-primitives/src/rating.rs @@ -7,6 +7,7 @@ use std::rc::Rc; #[allow(clippy::type_complexity)] #[derive(IntoElement)] +/// A Fluent primitive for representing rating in an interface. pub struct Rating { id: ElementId, value: f64, @@ -18,6 +19,7 @@ pub struct Rating { on_change: Option>, } impl Rating { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -30,36 +32,44 @@ impl Rating { on_change: None, } } + /// Returns the current value. pub fn value(mut self, v: f64) -> Self { self.value = v; self } + /// Sets the max configuration for this control. pub fn max(mut self, v: u8) -> Self { self.max = v.max(1); self } + /// Sets the step configuration for this control. pub fn step(mut self, v: f64) -> Self { if v == 0.5 || v == 1. { self.step = v }; self } + /// Sets the readonly configuration for this control. pub fn readonly(mut self, v: bool) -> Self { self.readonly = v; self } + /// Sets the interactive configuration for this control. pub fn interactive(mut self, v: bool) -> Self { self.readonly = !v; self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, v: bool) -> Self { self.disabled = v; self } + /// Returns the current label. pub fn label(mut self, v: impl Into) -> Self { self.label = Some(v.into()); self } + /// Registers a handler for change events. pub fn on_change(mut self, f: impl Fn(f64, &mut Window, &mut App) + 'static) -> Self { self.on_change = Some(Rc::new(f)); self diff --git a/crates/fluent-primitives/src/searchbox.rs b/crates/fluent-primitives/src/searchbox.rs index 5bdf002..40eb5ed 100644 --- a/crates/fluent-primitives/src/searchbox.rs +++ b/crates/fluent-primitives/src/searchbox.rs @@ -37,6 +37,7 @@ impl Default for Searchbox { } impl Searchbox { + /// Creates a new instance for use in a Fluent interface. pub fn new() -> Self { Self::default() } @@ -48,35 +49,42 @@ impl Searchbox { self } + /// Returns the current value. pub fn value(mut self, value: impl Into) -> Self { self.value = value.into().to_string(); self } + /// Sets the placeholder configuration for this control. pub fn placeholder(mut self, placeholder: impl Into) -> Self { self.placeholder = placeholder.into(); self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Registers a handler for change events. pub fn on_change(mut self, f: impl Fn(SharedString, &mut App) + 'static) -> Self { self.on_change = Some(Box::new(f)); self } + /// Registers a handler for submit events. pub fn on_submit(mut self, f: impl Fn(SharedString, &mut App) + 'static) -> Self { self.on_submit = Some(Box::new(f)); self } + /// Returns the current text. pub fn text(&self) -> &str { &self.value } + /// Sets the value value. pub fn set_value(&mut self, value: impl Into, cx: &mut Context) { self.value = value.into().to_string(); cx.notify(); diff --git a/crates/fluent-primitives/src/section_header.rs b/crates/fluent-primitives/src/section_header.rs index 70c77cb..493dbff 100644 --- a/crates/fluent-primitives/src/section_header.rs +++ b/crates/fluent-primitives/src/section_header.rs @@ -12,6 +12,7 @@ pub struct SectionHeader { } impl SectionHeader { + /// Creates a new instance for use in a Fluent interface. pub fn new(title: impl Into) -> Self { Self { title: title.into(), diff --git a/crates/fluent-primitives/src/skeleton.rs b/crates/fluent-primitives/src/skeleton.rs index 99948af..0d34b1a 100644 --- a/crates/fluent-primitives/src/skeleton.rs +++ b/crates/fluent-primitives/src/skeleton.rs @@ -18,6 +18,7 @@ pub struct Skeleton { } impl Skeleton { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -33,11 +34,13 @@ impl Skeleton { self } + /// Sets the h configuration for this control. pub fn h(mut self, height: f32) -> Self { self.height = height; self } + /// Sets the rounded configuration for this control. pub fn rounded(mut self, r: f32) -> Self { self.rounded = r; self @@ -79,6 +82,7 @@ pub struct SkeletonRow { } impl SkeletonRow { + /// Creates a new instance for use in a Fluent interface. pub fn new(id_prefix: impl Into) -> Self { Self { id_prefix: id_prefix.into(), @@ -86,6 +90,7 @@ impl SkeletonRow { } } + /// Sets the avatar size configuration for this control. pub fn avatar_size(mut self, size: f32) -> Self { self.avatar_size = size; self diff --git a/crates/fluent-primitives/src/slider.rs b/crates/fluent-primitives/src/slider.rs index 359076d..314b027 100644 --- a/crates/fluent-primitives/src/slider.rs +++ b/crates/fluent-primitives/src/slider.rs @@ -15,7 +15,9 @@ const LENGTH: f32 = 200.0; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum SliderOrientation { #[default] + /// The horizontal option. Horizontal, + /// The vertical option. Vertical, } @@ -77,6 +79,7 @@ pub struct Slider { } impl Slider { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -94,6 +97,7 @@ impl Slider { } } + /// Returns the current value. pub fn value(mut self, value: f64) -> Self { self.value = value; self @@ -115,15 +119,18 @@ impl Slider { self } + /// Sets the orientation configuration for this control. pub fn orientation(mut self, orientation: SliderOrientation) -> Self { self.orientation = orientation; self } + /// Sets the vertical configuration for this control. pub fn vertical(self) -> Self { self.orientation(SliderOrientation::Vertical) } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self @@ -141,6 +148,7 @@ impl Slider { self } + /// Sets the tab index configuration for this control. pub fn tab_index(mut self, index: isize) -> Self { self.tab_index = Some(index); self diff --git a/crates/fluent-primitives/src/spin_button.rs b/crates/fluent-primitives/src/spin_button.rs index 188b7e4..e356cda 100644 --- a/crates/fluent-primitives/src/spin_button.rs +++ b/crates/fluent-primitives/src/spin_button.rs @@ -50,6 +50,7 @@ pub struct SpinButton { } impl SpinButton { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -66,6 +67,7 @@ impl SpinButton { } } + /// Returns the current value. pub fn value(mut self, value: f64) -> Self { self.value = value; self @@ -87,6 +89,7 @@ impl SpinButton { self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self @@ -98,6 +101,7 @@ impl SpinButton { self } + /// Sets the tab index configuration for this control. pub fn tab_index(mut self, index: isize) -> Self { self.tab_index = Some(index); self diff --git a/crates/fluent-primitives/src/spinner.rs b/crates/fluent-primitives/src/spinner.rs index e5ba977..9dfaaa2 100644 --- a/crates/fluent-primitives/src/spinner.rs +++ b/crates/fluent-primitives/src/spinner.rs @@ -14,6 +14,7 @@ pub struct Spinner { } impl Spinner { + /// Creates a new instance for use in a Fluent interface. pub fn new() -> Self { Self { size: 20.0, @@ -27,6 +28,7 @@ impl Spinner { self } + /// Sets the size configuration for this control. pub fn size(mut self, size: f32) -> Self { self.size = size; self diff --git a/crates/fluent-primitives/src/suggestion_popover.rs b/crates/fluent-primitives/src/suggestion_popover.rs index 7c17206..a4d759e 100644 --- a/crates/fluent-primitives/src/suggestion_popover.rs +++ b/crates/fluent-primitives/src/suggestion_popover.rs @@ -11,15 +11,22 @@ type HoverHandler = Arc; type CloseHandler = Arc; #[derive(Clone, Debug, PartialEq, Eq)] +/// A Fluent primitive for representing suggestion item in an interface. pub struct SuggestionItem { + /// The id value. pub id: SharedString, + /// The label value. pub label: SharedString, + /// The detail value. pub detail: Option, + /// The meta value. pub meta: Option, + /// The disabled value. pub disabled: bool, } impl SuggestionItem { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into, label: impl Into) -> Self { Self { id: id.into(), @@ -30,16 +37,19 @@ impl SuggestionItem { } } + /// Sets the detail configuration for this control. pub fn detail(mut self, detail: impl Into) -> Self { self.detail = Some(detail.into()); self } + /// Sets the meta configuration for this control. pub fn meta(mut self, meta: impl Into) -> Self { self.meta = Some(meta.into()); self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self @@ -47,29 +57,35 @@ impl SuggestionItem { } #[derive(Clone, Debug, Default, PartialEq, Eq)] +/// A Fluent primitive for representing suggestion list state in an interface. pub struct SuggestionListState { highlighted: Option, } impl SuggestionListState { + /// Creates a new instance for use in a Fluent interface. pub fn new() -> Self { Self::default() } + /// Sets the highlighted configuration for this control. pub fn highlighted(&self) -> Option { self.highlighted } + /// Sets the highlighted value. pub fn set_highlighted(&mut self, highlighted: Option) { self.highlighted = highlighted; } + /// Returns the highlighted item value. pub fn highlighted_item<'a>(&self, items: &'a [SuggestionItem]) -> Option<&'a SuggestionItem> { self.highlighted .and_then(|index| items.get(index)) .filter(|item| !item.disabled) } + /// Moves highlight. pub fn move_highlight(&mut self, items: &[SuggestionItem], delta: isize) -> Option { let enabled = enabled_indices(items); if enabled.is_empty() { @@ -94,16 +110,19 @@ impl SuggestionListState { self.highlighted } + /// Moves the highlight to the first enabled suggestion. pub fn first(&mut self, items: &[SuggestionItem]) -> Option { self.highlighted = enabled_indices(items).first().copied(); self.highlighted } + /// Moves the highlight to the last enabled suggestion. pub fn last(&mut self, items: &[SuggestionItem]) -> Option { self.highlighted = enabled_indices(items).last().copied(); self.highlighted } + /// Updates suggestion state for a navigation key and returns the resulting action. pub fn handle_key(&mut self, key: &str, items: &[SuggestionItem]) -> SuggestionKeyAction { match key { "down" => SuggestionKeyAction::Move(self.move_highlight(items, 1)), @@ -123,14 +142,20 @@ impl SuggestionListState { } #[derive(Clone, Debug, PartialEq, Eq)] +/// Available suggestion key action options for configuring the control. pub enum SuggestionKeyAction { + /// The none option. None, + /// The move option. Move(Option), + /// The select option. Select(SharedString), + /// The close option. Close, } #[derive(IntoElement)] +/// A Fluent primitive for representing suggestion popover in an interface. pub struct SuggestionPopover { open: bool, exiting: bool, @@ -148,6 +173,7 @@ pub struct SuggestionPopover { } impl SuggestionPopover { + /// Creates a new instance for use in a Fluent interface. pub fn new(position: Point) -> Self { Self { open: true, @@ -166,56 +192,67 @@ impl SuggestionPopover { } } + /// Sets the open configuration for this control. pub fn open(mut self, open: bool) -> Self { self.open = open; self } + /// Sets the exiting configuration for this control. pub fn exiting(mut self, exiting: bool) -> Self { self.exiting = exiting; self } + /// Sets the anchor configuration for this control. pub fn anchor(mut self, anchor: Anchor) -> Self { self.anchor = anchor; self } + /// Sets the items configuration for this control. pub fn items(mut self, items: impl IntoIterator) -> Self { self.items = items.into_iter().collect(); self } + /// Sets the item configuration for this control. pub fn item(mut self, item: SuggestionItem) -> Self { self.items.push(item); self } + /// Sets the highlighted configuration for this control. pub fn highlighted(mut self, highlighted: impl Into>) -> Self { self.highlighted = highlighted.into(); self } + /// Sets the empty text configuration for this control. pub fn empty_text(mut self, empty_text: impl Into) -> Self { self.empty_text = empty_text.into(); self } + /// Sets the min width configuration for this control. pub fn min_width(mut self, min_width: f32) -> Self { self.min_width = min_width; self } + /// Sets the max height configuration for this control. pub fn max_height(mut self, max_height: f32) -> Self { self.max_height = max_height; self } + /// Returns whether the key moves focus through the suggestion list. pub fn motion_key(mut self, motion_key: u64) -> Self { self.motion_key = motion_key; self } + /// Registers a handler for select events. pub fn on_select( mut self, on_select: impl Fn(SuggestionItem, &ClickEvent, &mut Window, &mut App) + 'static, @@ -224,11 +261,13 @@ impl SuggestionPopover { self } + /// Registers a handler for hover events. pub fn on_hover(mut self, on_hover: impl Fn(usize, &mut App) + 'static) -> Self { self.on_hover = Some(Arc::new(on_hover)); self } + /// Registers a handler for close events. pub fn on_close( mut self, on_close: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, diff --git a/crates/fluent-primitives/src/switch.rs b/crates/fluent-primitives/src/switch.rs index 929e03d..b3c9815 100644 --- a/crates/fluent-primitives/src/switch.rs +++ b/crates/fluent-primitives/src/switch.rs @@ -20,6 +20,7 @@ pub struct Switch { } impl Switch { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -38,6 +39,7 @@ impl Switch { self } + /// Sets the on configuration for this control. pub fn on(mut self, on: bool) -> Self { self.on = on; self @@ -49,6 +51,7 @@ impl Switch { self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self diff --git a/crates/fluent-primitives/src/tag.rs b/crates/fluent-primitives/src/tag.rs index 05deef7..dd104d3 100644 --- a/crates/fluent-primitives/src/tag.rs +++ b/crates/fluent-primitives/src/tag.rs @@ -7,33 +7,48 @@ use gpui::{ }; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +/// Available tag appearance options for configuring the control. pub enum TagAppearance { #[default] + /// The neutral option. Neutral, + /// The accent option. Accent, + /// The informative option. Informative, + /// The success option. Success, + /// The warning option. Warning, + /// The danger option. Danger, } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +/// Available tag shape options for configuring the control. pub enum TagShape { #[default] + /// The rounded option. Rounded, + /// The circular option. Circular, } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +/// Available tag size options for configuring the control. pub enum TagSize { + /// The small option. Small, #[default] + /// The medium option. Medium, + /// The large option. Large, } #[allow(clippy::type_complexity)] #[derive(IntoElement)] +/// A Fluent primitive for representing tag in an interface. pub struct Tag { id: ElementId, label: SharedString, @@ -48,6 +63,7 @@ pub struct Tag { } impl Tag { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into, label: impl Into) -> Self { Self { id: id.into(), @@ -62,34 +78,42 @@ impl Tag { on_select: None, } } + /// Sets the size configuration for this control. pub fn size(mut self, v: TagSize) -> Self { self.size = v; self } + /// Sets the shape configuration for this control. pub fn shape(mut self, v: TagShape) -> Self { self.shape = v; self } + /// Sets the appearance configuration for this control. pub fn appearance(mut self, v: TagAppearance) -> Self { self.appearance = v; self } + /// Returns the current selected. pub fn selected(mut self, v: bool) -> Self { self.selected = v; self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, v: bool) -> Self { self.disabled = v; self } + /// Sets the dismissible configuration for this control. pub fn dismissible(mut self, v: bool) -> Self { self.dismissible = v; self } + /// Registers a handler for dismiss events. pub fn on_dismiss(mut self, f: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self { self.on_dismiss = Some(Rc::new(f)); self } + /// Registers a handler for select events. pub fn on_select( mut self, f: impl Fn(bool, &ClickEvent, &mut Window, &mut App) + 'static, @@ -167,12 +191,14 @@ impl RenderOnce for Tag { } #[derive(IntoElement)] +/// A Fluent primitive for representing tag group in an interface. pub struct TagGroup { id: ElementId, children: Vec, size: TagSize, } impl TagGroup { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -180,14 +206,17 @@ impl TagGroup { size: TagSize::Medium, } } + /// Sets the tags configuration for this control. pub fn tags(mut self, tags: impl IntoIterator) -> Self { self.children = tags.into_iter().collect(); self } + /// Sets the tag configuration for this control. pub fn tag(mut self, tag: Tag) -> Self { self.children.push(tag); self } + /// Sets the size configuration for this control. pub fn size(mut self, size: TagSize) -> Self { self.size = size; self diff --git a/crates/fluent-primitives/src/terminal_grid.rs b/crates/fluent-primitives/src/terminal_grid.rs index 8dd7ea7..8f80337 100644 --- a/crates/fluent-primitives/src/terminal_grid.rs +++ b/crates/fluent-primitives/src/terminal_grid.rs @@ -185,7 +185,7 @@ impl Line { pub const DEFAULT_SCROLLBACK: usize = 10_000; /// The cell grid: a screen of `rows` x `cols` cells, a bounded scrollback above it, a cursor, -/// and a record of which screen rows changed. See the [module docs](self). +/// and a record of which screen rows changed. #[derive(Clone, Debug)] pub struct TerminalGrid { cols: usize, diff --git a/crates/fluent-primitives/src/terminal_view.rs b/crates/fluent-primitives/src/terminal_view.rs index f36ddc2..82b524f 100644 --- a/crates/fluent-primitives/src/terminal_view.rs +++ b/crates/fluent-primitives/src/terminal_view.rs @@ -177,7 +177,7 @@ impl PaintCache { } } -/// The monospace cell-grid view. See the [module docs](self). +/// The monospace cell-grid view. pub struct TerminalView { grid: Entity, capture: Entity, diff --git a/crates/fluent-primitives/src/text_input.rs b/crates/fluent-primitives/src/text_input.rs index b032a2b..64dc80a 100644 --- a/crates/fluent-primitives/src/text_input.rs +++ b/crates/fluent-primitives/src/text_input.rs @@ -61,6 +61,7 @@ impl Default for TextInput { } impl TextInput { + /// Creates a new instance for use in a Fluent interface. pub fn new() -> Self { Self::default() } @@ -72,31 +73,37 @@ impl TextInput { self } + /// Returns the current value. pub fn value(mut self, value: impl Into) -> Self { self.set_value_inner(value.into()); self } + /// Sets the placeholder configuration for this control. pub fn placeholder(mut self, placeholder: impl Into) -> Self { self.placeholder = placeholder.into(); self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Sets the masked configuration for this control. pub fn masked(mut self, masked: bool) -> Self { self.masked = masked; self } + /// Sets the error configuration for this control. pub fn error(mut self, error: bool) -> Self { self.error = error; self } + /// Registers a handler for change events. pub fn on_change(mut self, f: impl Fn(SharedString, &mut App) + 'static) -> Self { self.on_change = Some(Box::new(f)); self @@ -109,37 +116,45 @@ impl TextInput { self } + /// Sets the on submit value. pub fn set_on_submit(&mut self, f: impl Fn(SharedString, &mut App) -> bool + 'static) { self.on_submit = Some(Box::new(f)); } + /// Returns the current text. pub fn text(&self) -> &str { &self.value } + /// Sets the value value. pub fn set_value(&mut self, value: impl Into, cx: &mut Context) { self.set_value_inner(value.into()); cx.notify(); } + /// Sets the placeholder value. pub fn set_placeholder(&mut self, placeholder: impl Into) { self.placeholder = placeholder.into(); } + /// Returns whether masked. pub fn is_masked(&self) -> bool { self.masked } + /// Sets the masked value. pub fn set_masked(&mut self, masked: bool, cx: &mut Context) { self.masked = masked; cx.notify(); } + /// Sets the disabled value. pub fn set_disabled(&mut self, disabled: bool, cx: &mut Context) { self.disabled = disabled; cx.notify(); } + /// Sets the error value. pub fn set_error(&mut self, error: bool, cx: &mut Context) { self.error = error; cx.notify(); diff --git a/crates/fluent-primitives/src/textarea.rs b/crates/fluent-primitives/src/textarea.rs index 9e835a1..bfe2ce0 100644 --- a/crates/fluent-primitives/src/textarea.rs +++ b/crates/fluent-primitives/src/textarea.rs @@ -37,6 +37,7 @@ impl Default for Textarea { } impl Textarea { + /// Creates a new instance for use in a Fluent interface. pub fn new() -> Self { Self::default() } @@ -48,40 +49,48 @@ impl Textarea { self } + /// Returns the current value. pub fn value(mut self, value: impl Into) -> Self { self.value = value.into().to_string(); self } + /// Sets the placeholder configuration for this control. pub fn placeholder(mut self, placeholder: impl Into) -> Self { self.placeholder = placeholder.into(); self } + /// Sets the rows configuration for this control. pub fn rows(mut self, rows: usize) -> Self { self.rows = rows.max(2); self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Registers a handler for change events. pub fn on_change(mut self, f: impl Fn(SharedString, &mut App) + 'static) -> Self { self.on_change = Some(Box::new(f)); self } + /// Returns the current text. pub fn text(&self) -> &str { &self.value } + /// Sets the value value. pub fn set_value(&mut self, value: impl Into, cx: &mut Context) { self.value = value.into().to_string(); cx.notify(); } + /// Sets the disabled value. pub fn set_disabled(&mut self, disabled: bool, cx: &mut Context) { self.disabled = disabled; cx.notify(); diff --git a/crates/fluent-primitives/src/toggle_button.rs b/crates/fluent-primitives/src/toggle_button.rs index 6e92cb7..7a23902 100644 --- a/crates/fluent-primitives/src/toggle_button.rs +++ b/crates/fluent-primitives/src/toggle_button.rs @@ -24,6 +24,7 @@ pub struct ToggleButton { } impl ToggleButton { + /// Creates a new instance for use in a Fluent interface. pub fn new(id: impl Into) -> Self { Self { id: id.into(), @@ -37,16 +38,19 @@ impl ToggleButton { } } + /// Returns the current label. pub fn label(mut self, label: impl Into) -> Self { self.label = Some(label.into()); self } + /// Sets the icon configuration for this control. pub fn icon(mut self, path: impl Into) -> Self { self.icon = Some(path.into()); self } + /// Returns the current selected. pub fn selected(mut self, selected: bool) -> Self { self.selected = selected; self @@ -58,11 +62,13 @@ impl ToggleButton { self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Sets the size configuration for this control. pub fn size(mut self, size: ButtonSize) -> Self { self.size = size; self diff --git a/crates/fluent-primitives/src/token_input.rs b/crates/fluent-primitives/src/token_input.rs index 245965d..cdc1c05 100644 --- a/crates/fluent-primitives/src/token_input.rs +++ b/crates/fluent-primitives/src/token_input.rs @@ -10,13 +10,18 @@ use gpui::{ use crate::{Chip, SuggestionItem, SuggestionPopover}; #[derive(Clone, Debug, PartialEq, Eq)] +/// A Fluent primitive for representing token input token in an interface. pub struct TokenInputToken { + /// The value value. pub value: SharedString, + /// The label value. pub label: SharedString, + /// The valid value. pub valid: bool, } impl TokenInputToken { + /// Creates a new instance for use in a Fluent interface. pub fn new(value: impl Into, label: impl Into) -> Self { Self { value: value.into(), @@ -25,11 +30,13 @@ impl TokenInputToken { } } + /// Sets the valid configuration for this control. pub fn valid(mut self, valid: bool) -> Self { self.valid = valid; self } + /// Sets the invalid configuration for this control. pub fn invalid(mut self) -> Self { self.valid = false; self @@ -37,13 +44,18 @@ impl TokenInputToken { } #[derive(Clone, Debug, PartialEq, Eq)] +/// A Fluent primitive for representing token input suggestion in an interface. pub struct TokenInputSuggestion { + /// The value value. pub value: SharedString, + /// The label value. pub label: SharedString, + /// The detail value. pub detail: Option, } impl TokenInputSuggestion { + /// Creates a new instance for use in a Fluent interface. pub fn new(value: impl Into, label: impl Into) -> Self { Self { value: value.into(), @@ -52,6 +64,7 @@ impl TokenInputSuggestion { } } + /// Sets the detail configuration for this control. pub fn detail(mut self, detail: impl Into) -> Self { self.detail = Some(detail.into()); self @@ -59,6 +72,7 @@ impl TokenInputSuggestion { } #[allow(clippy::type_complexity)] +/// A Fluent primitive for representing token input in an interface. pub struct TokenInput { tokens: Vec, suggestions: Vec, @@ -108,6 +122,7 @@ impl Default for TokenInput { } impl TokenInput { + /// Creates a new instance for use in a Fluent interface. pub fn new() -> Self { Self::default() } @@ -119,16 +134,19 @@ impl TokenInput { self } + /// Sets the tokens configuration for this control. pub fn tokens(mut self, tokens: impl IntoIterator) -> Self { self.tokens = tokens.into_iter().collect(); self } + /// Sets the token configuration for this control. pub fn token(mut self, value: impl Into, label: impl Into) -> Self { self.tokens.push(TokenInputToken::new(value, label)); self } + /// Sets the suggestions configuration for this control. pub fn suggestions( mut self, suggestions: impl IntoIterator, @@ -137,6 +155,7 @@ impl TokenInput { self } + /// Sets the suggestion configuration for this control. pub fn suggestion( mut self, value: impl Into, @@ -147,44 +166,53 @@ impl TokenInput { self } + /// Sets the placeholder configuration for this control. pub fn placeholder(mut self, placeholder: impl Into) -> Self { self.placeholder = placeholder.into(); self } + /// Sets the disabled configuration for this control. pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } + /// Sets the error configuration for this control. pub fn error(mut self, error: bool) -> Self { self.error = error; self } + /// Sets the allow freeform configuration for this control. pub fn allow_freeform(mut self, allow_freeform: bool) -> Self { self.allow_freeform = allow_freeform; self } + /// Registers a handler for change events. pub fn on_change(mut self, f: impl Fn(Vec, &mut App) + 'static) -> Self { self.on_change = Some(Box::new(f)); self } + /// Registers a handler for query change events. pub fn on_query_change(mut self, f: impl Fn(SharedString, &mut App) + 'static) -> Self { self.on_query_change = Some(Box::new(f)); self } + /// Returns the current values. pub fn values(&self) -> &[TokenInputToken] { &self.tokens } + /// Returns the current query. pub fn query(&self) -> &str { &self.query } + /// Sets the tokens value. pub fn set_tokens( &mut self, tokens: impl IntoIterator, @@ -214,6 +242,7 @@ impl TokenInput { self.open = false; } + /// Sets the suggestions value. pub fn set_suggestions( &mut self, suggestions: impl IntoIterator, @@ -226,6 +255,7 @@ impl TokenInput { cx.notify(); } + /// Sets the query value. pub fn set_query(&mut self, query: impl Into, cx: &mut Context) { self.query = query.into().to_string(); self.highlighted = 0; @@ -234,6 +264,7 @@ impl TokenInput { cx.notify(); } + /// Removes the requested token. pub fn remove_token(&mut self, index: usize, cx: &mut Context) { if index >= self.tokens.len() { return; diff --git a/crates/fluent-primitives/src/tooltip.rs b/crates/fluent-primitives/src/tooltip.rs index fa4bafa..a4a6287 100644 --- a/crates/fluent-primitives/src/tooltip.rs +++ b/crates/fluent-primitives/src/tooltip.rs @@ -9,6 +9,7 @@ pub struct Tooltip { } impl Tooltip { + /// Creates a new instance for use in a Fluent interface. pub fn new(text: impl Into) -> Self { Self { text: text.into(), @@ -16,6 +17,7 @@ impl Tooltip { } } + /// Sets the trigger configuration for this control. pub fn trigger(mut self, trigger: impl IntoElement) -> Self { self.trigger = Some(trigger.into_any_element()); self diff --git a/crates/fluent-primitives/src/video_surface.rs b/crates/fluent-primitives/src/video_surface.rs index 12ec542..d089823 100644 --- a/crates/fluent-primitives/src/video_surface.rs +++ b/crates/fluent-primitives/src/video_surface.rs @@ -373,7 +373,7 @@ impl Inner { } } -/// Shows caller-supplied RGBA8 frames, scaled to fit or 1:1. See the [module docs](self). +/// Shows caller-supplied RGBA8 frames, scaled to fit or 1:1. pub struct VideoSurface { inner: Rc>, scaling: VideoScaling, @@ -389,8 +389,7 @@ impl VideoSurface { Self::with_backend(Backend::Gpu, cx) } - /// A surface that never touches video textures and paints a placeholder instead; see the - /// [module docs](self#tests-and-the-placeholder-backend). + /// A surface that never touches video textures and paints a placeholder instead. pub fn placeholder(cx: &mut Context) -> Self { Self::with_backend(Backend::Placeholder, cx) } From 33c8f7479e903339865b45511b7892ee1f8ee70a Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:51:31 +0000 Subject: [PATCH 16/17] CI: build examples and deny rustdoc warnings Graft-Changeset: c2c4450123d613d2fedf26e15c14678c681a3b58073456be94e1e5d3888522368 Graft-Session: ses_01M6F72NKWYEZSCCQT60A9FRNG Graft-Work-Item: WI-2952 Co-authored-by: gpt-5.6-sol Graft-Batch: 21 --- .github/workflows/ci.yml | 37 ++++++++++++++++++++++++++- crates/fluent-layout/src/workspace.rs | 2 +- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a676d0..437006b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,41 @@ jobs: shell: bash run: scripts/ci-nextest.sh + examples-and-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + - name: Install Linux system libraries + # gpui's Linux backend (x11rb/xkbcommon/wayland/blade/font-kit) links + # these; the list follows zed's script/linux apt packages. + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libxcb1-dev libx11-xcb-dev libxkbcommon-dev libxkbcommon-x11-dev \ + libwayland-dev libvulkan-dev libfontconfig-dev libfreetype-dev + - uses: dtolnay/rust-toolchain@d124f86f28ff42855784ff3319f48ac182c248f8 + - uses: Swatinem/rust-cache@e9d7b9f1e587218970671bac74e5511ed9506e6a + - name: Build every example package + run: >- + cargo build + -p demo_app + -p form + -p gallery + -p hello_world + -p screenshots + -p widgets + - name: Build fluent crate documentation + env: + RUSTDOCFLAGS: -Dwarnings + run: >- + cargo doc --no-deps + -p fluent-app + -p fluent-core + -p fluent-layout + -p fluent-primitives + -p fluent-ribbon + -p fluent-test-utils + # Only crates/fluent-* may use gpui (CONTRIBUTING.md §2). layering: runs-on: ubuntu-latest @@ -70,7 +105,7 @@ jobs: # The one required check: Windows and Linux tests plus every guard. ci: if: always() - needs: [test, layering, deny, zed-rev] + needs: [test, examples-and-docs, layering, deny, zed-rev] runs-on: ubuntu-latest steps: - name: All jobs passed diff --git a/crates/fluent-layout/src/workspace.rs b/crates/fluent-layout/src/workspace.rs index 0a54b26..2aa49c4 100644 --- a/crates/fluent-layout/src/workspace.rs +++ b/crates/fluent-layout/src/workspace.rs @@ -46,7 +46,7 @@ impl Workspace { /// Prepend a built-in "File" menu with an Exit item that calls `cx.quit()`. /// /// Pass `app_name` to label the menu "File" (ignored for now; reserved for - /// future "About " or per-platform behaviour). + /// future `About ` or per-platform behaviour). pub fn with_file_menu(mut self, _app_name: impl Into) -> Self { self.app_name_for_file_menu = Some("File".into()); self From 7fff6bb6a77d6816b7dd3cb695a00f7279b1c41f Mon Sep 17 00:00:00 2001 From: thedancingdeveloper <306930456+thedancingdeveloper@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:52:36 +0000 Subject: [PATCH 17/17] Vendor DirectX nearest-neighbour video sampler Graft-Changeset: ce1829337534155cd947d982bfcc960dec6b82b6ee6355ffaa45f17df4f659a4c Graft-Session: ses_01M6F72NC3DAFJG62G9ZPCJK08 Graft-Work-Item: WI-2943 Graft-Batch: 21 --- vendor/zed/PATCHES.md | 2 +- .../gpui_windows/src/directx_renderer.rs | 34 ++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/vendor/zed/PATCHES.md b/vendor/zed/PATCHES.md index 846efd5..063f8c9 100644 --- a/vendor/zed/PATCHES.md +++ b/vendor/zed/PATCHES.md @@ -9,7 +9,7 @@ Each local patch to this tree is a land on the working line (`backlog-1`), never | `trim-ios` (WI-2728): drop the `target_os = "ios"` arm of `current_platform`, since `gpui_ios` is no longer vendored | WI-2728 land on `backlog-1` | `crates/gpui_platform/src/gpui_platform.rs` | | `svg-font-warmup` (WI-1432): module-level `SYSTEM_FONT_DB` plus `pub fn warmup_svg_font_db()` behind the `svg-font-warmup` feature (enabled by `fluent-core`), so `main()` can prime `load_system_fonts()` on a background thread | WI-1432 land on `backlog-1` | `crates/gpui/src/svg_renderer.rs`, `crates/gpui/Cargo.toml` | | `element-scale` (WI-1433): `Style::scale` and `Styled::scale`, `Window::with_element_scale` with `element_scale_stack` and the paint-time transforms (`transform_bounds_for_paint`, `scale_length_for_paint`, `scale_corners_for_paint`, `transform_point_for_paint`) applied to quads, shadows, paths, text, svg, images and surfaces; the `div.rs` paint wrapper; example `examples/scale.rs` | WI-1433 land on `backlog-1` | `crates/gpui/src/style.rs`, `crates/gpui/src/styled.rs`, `crates/gpui/src/window.rs`, `crates/gpui/src/elements/div.rs`, `crates/gpui/Cargo.toml`, `crates/gpui/examples/scale.rs` | -| `video-sampler-nearest` (WI-2942): `VideoFilter` selects a nearest-neighbour sampler for scaled Fit-mode video frames while Native remains linear in the wgpu renderer | WI-2942 land on `backlog-1` | `crates/gpui/src/scene.rs`, `crates/gpui/src/window.rs`, `crates/gpui_wgpu/src/wgpu_renderer.rs`, `crates/fluent-primitives/src/video_surface.rs` | +| `video-sampler-nearest` (WI-2942, WI-2943): `VideoFilter` selects a nearest-neighbour sampler for scaled Fit-mode video frames while Native remains linear in the wgpu and DirectX renderers. **DirectX unverified on Windows** (cross-compiled on Linux only; HLSL not compiled, not run; runtime verification rides WI-2807) | WI-2942 and WI-2943 lands on `backlog-1` | `crates/gpui/src/scene.rs`, `crates/gpui/src/window.rs`, `crates/gpui_wgpu/src/wgpu_renderer.rs`, `crates/gpui_windows/src/directx_renderer.rs`, `crates/fluent-primitives/src/video_surface.rs` | | `video-frames` (WI-1348): `Window::{alloc_video_texture, upload_video_frame, paint_video_frame, free_video_texture}` with never-reused `VideoTextureId`s and a packed RGBA8 contract; `VideoFrame` scene primitive and `PrimitiveBatch::VideoFrames`; a standalone `Rgba8Unorm` texture per id in the wgpu renderer (uploaded with `queue.write_texture` when its generation changes) and a `vs/fs_video_frame` pass for both instance transports; example `examples/video_frame.rs`. Linux path only; DirectX is WI-1434. Hardening: `upload_video_frame` rejects a zero or over-8192 dimension and the wgpu renderer skips a frame shorter than `w*h*4` | WI-1348 land on `backlog-1` | `crates/gpui/src/scene.rs`, `crates/gpui/src/window.rs`, `crates/gpui/Cargo.toml`, `crates/gpui/examples/video_frame.rs`, `crates/gpui_wgpu/src/wgpu_renderer.rs`, `crates/gpui_wgpu/src/shaders_storage.wgsl`, `crates/gpui_wgpu/src/shaders_webgl.wgsl` | | `video-frames-directx` (WI-1434): the DirectX half of `video-frames`. **Unverified on Windows** (checked with `cargo check`/`clippy --target x86_64-pc-windows-gnu` on Linux only; HLSL not compiled, not run). A `PrimitiveBatch::VideoFrames` arm, a standalone immutable `R8G8B8A8_UNORM` texture per `VideoTextureId` (the packed RGBA8 contract maps straight onto it, no conversion; recreated when the generation or size changes, dropped when no frame references it, cleared on device loss), a `video_frame_pipeline` drawing runs of frames that share a texture, and `video_frame_{vertex,fragment}` in HLSL plus the `video_frame` entry in the fxc module list | WI-1434 land on `backlog-1` | `crates/gpui_windows/src/directx_renderer.rs`, `crates/gpui_windows/src/shaders.hlsl`, `crates/gpui_windows/build.rs` | | `backport-cached-view-subscriptions` (WI-1346): upstream zed `decd2399b1` (#65952), applied byte for byte. Cached views and deferred draws keep window subscriptions for entities read in paint or by a deferred draw when their cache is reused; scoped entity-access recording replaces the per-scope clone-and-diff; restoring a recorded set skips released entities. Drop it at the next upstream pull past `decd2399b1` | WI-1346 land on `backlog-1` | `crates/gpui/src/app.rs`, `crates/gpui/src/app/entity_map.rs`, `crates/gpui/src/elements/deferred.rs`, `crates/gpui/src/view.rs`, `crates/gpui/src/window.rs` | diff --git a/vendor/zed/crates/gpui_windows/src/directx_renderer.rs b/vendor/zed/crates/gpui_windows/src/directx_renderer.rs index 115383a..80b9210 100644 --- a/vendor/zed/crates/gpui_windows/src/directx_renderer.rs +++ b/vendor/zed/crates/gpui_windows/src/directx_renderer.rs @@ -115,6 +115,7 @@ struct DirectXGlobalElements { global_params_buffer: Option, batch_params_buffer: Option, sampler: Option, + nearest_sampler: Option, } struct Annotation<'a>(&'a ID3DUserDefinedAnnotation); @@ -919,8 +920,8 @@ impl DirectXRenderer { /// Draw video frames, each from its own standalone texture. /// /// Frames whose texture was freed, or that have not had pixels uploaded - /// yet, are skipped. Consecutive frames that share one live texture are - /// drawn in one instanced call. + /// yet, are skipped. Consecutive frames that share one live texture and + /// filter are drawn in one instanced call. fn draw_video_frames(&mut self, frames: &[VideoFrame], range: Range) -> Result<()> { let devices = self.devices.as_ref().context("devices missing")?; let batch_params_buffer = self @@ -932,15 +933,22 @@ impl DirectXRenderer { while start < range.end { let texture_id = VideoTextureId::new(frames[start].texture_id); let mut end = start + 1; - while end < range.end && frames[end].texture_id == frames[start].texture_id { + while end < range.end + && frames[end].texture_id == frames[start].texture_id + && frames[end].filter == frames[start].filter + { end += 1; } if let Some(texture) = self.video_textures.get(&texture_id) { + let sampler = match frames[start].filter { + VideoFilter::Linear => &self.globals.sampler, + VideoFilter::Nearest => &self.globals.nearest_sampler, + }; self.pipelines.video_frames.draw_range_with_texture( &devices.device_context, &texture.view, batch_params_buffer, - slice::from_ref(&self.globals.sampler), + slice::from_ref(sampler), start as u32, (end - start) as u32, )?; @@ -1212,11 +1220,29 @@ impl DirectXGlobalElements { device.CreateSamplerState(&desc, Some(&mut output))?; output }; + let nearest_sampler = unsafe { + let desc = D3D11_SAMPLER_DESC { + Filter: D3D11_FILTER_MIN_MAG_MIP_POINT, + AddressU: D3D11_TEXTURE_ADDRESS_WRAP, + AddressV: D3D11_TEXTURE_ADDRESS_WRAP, + AddressW: D3D11_TEXTURE_ADDRESS_WRAP, + MipLODBias: 0.0, + MaxAnisotropy: 1, + ComparisonFunc: D3D11_COMPARISON_ALWAYS, + BorderColor: [0.0; 4], + MinLOD: 0.0, + MaxLOD: D3D11_FLOAT32_MAX, + }; + let mut output = None; + device.CreateSamplerState(&desc, Some(&mut output))?; + output + }; Ok(Self { global_params_buffer, batch_params_buffer, sampler, + nearest_sampler, }) } }