diff --git a/crates/ui-egui/src/layers_component_rows_tests.rs b/crates/ui-egui/src/layers_component_rows_tests.rs index 8b659eac7..7bb867862 100644 --- a/crates/ui-egui/src/layers_component_rows_tests.rs +++ b/crates/ui-egui/src/layers_component_rows_tests.rs @@ -91,3 +91,37 @@ fn rows_inside_a_selected_component_take_a_tone_of_its_row() { assert!(!rect_under(&shapes, t.selected_row_child, label), "not the plain selection's child tone (dark {dark})"); } } + +/// The Layers panel with the component "Button" hidden and nothing selected. +fn drawn_hidden(dark: bool) -> (Vec, Tokens) { + let mut s = Session::new(); + let f = id(s.execute("frame.create", &json!({"x": 0, "y": 0, "width": 100, "height": 40, "props": {"name": "Button"}})).unwrap()); + let c = id(s.execute("component.create", &json!({"ids": [f]})).unwrap()); + s.execute(if dark { "view.themeDark" } else { "view.themeLight" }, &json!({})).unwrap(); + s.execute("node.set", &json!({"id": c, "props": {"visible": false}})).unwrap(); + s.execute("select.set", &json!({"ids": []})).unwrap(); + let mut u = Ui::new(s); + u.frame(vec![]); + let shapes = crate::live_tests::flat(&u.frame_output(vec![]).shapes); + (shapes, Tokens::get(&u.ctx)) +} + +/// `fg` (premultiplied, as egui paints it) composited over the opaque `bg`. +fn over(fg: Color32, bg: Color32) -> Color32 { + let k = 1.0 - f32::from(fg.a()) / 255.0; + let mix = |f: u8, b: u8| (f32::from(f) + f32::from(b) * k).round().min(255.0) as u8; + Color32::from_rgb(mix(fg.r(), bg.r()), mix(fg.g(), bg.g()), mix(fg.b(), bg.b())) +} + +#[test] +fn a_hidden_component_name_keeps_the_dim_text_floor_of_3_to_1() { + for dark in [false, true] { + let (shapes, t) = drawn_hidden(dark); + assert_eq!(t.dark, dark); + let col = name_colour(&shapes, "Button"); + for (what, bg) in [("the panel", t.panel), ("a hovered row", t.hover)] { + let ratio = color(over(col, bg)).contrast(color(bg)); + assert!(ratio >= 3.0, "hidden component name {col:?} is {ratio:.2}:1 on {what} (dark {dark})"); + } + } +} diff --git a/crates/ui-egui/src/panels/layers.rs b/crates/ui-egui/src/panels/layers.rs index 13e40aacb..d400eafd1 100644 --- a/crates/ui-egui/src/panels/layers.rs +++ b/crates/ui-egui/src/panels/layers.rs @@ -746,16 +746,17 @@ fn is_component(kind: &NodeKind) -> bool { matches!(kind, NodeKind::Component { .. } | NodeKind::ComponentSet { .. } | NodeKind::Instance { .. }) } -/// A row's icon or name colour: the theme's component tint (faded when hidden; it reads at AA on -/// the panel and on the selected row, where the canvas purple doesn't, #1903), else `normal` or -/// `faded`. +/// A row's icon or name colour: the theme's component tint when shown (it reads at AA on the +/// panel and on the selected row, where the canvas purple doesn't, #1903), else `normal` or, when +/// hidden, `faded` (halving the tint's alpha would drop it to about 2.2:1, under the 3:1 floor +/// for dim text). fn row_color(t: &Tokens, component: bool, shown: bool, normal: Color32, faded: Color32) -> Color32 { - if component { - t.component.gamma_multiply(if shown { 1.0 } else { 0.5 }) - } else if shown { - normal - } else { + if !shown { faded + } else if component { + t.component + } else { + normal } } diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 728e54d36..c58335ad3 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -16,7 +16,7 @@ interaction feel not yet compared. | Item | Status | Notes | |---|---|---| | UI3 layout: tab strip as title bar, icon rail, file header, pages + layers, floating bottom toolbar with flyouts, Design / Prototype / Inspect panels | done | frameless window on Windows/Linux with drawn buttons; on a Wayland compositor that offers no server-side decorations (GNOME) the window gets winit's Adwaita controls instead (#592; checked by its author on GNOME, KDE not run) | -| Light and dark themes | done | colours from `theme::Tokens`; component, variant and instance rows in the Layers panel take the theme's component tint, at AA on the panel and on their selected row, and the rows inside a selected component a tone of its row (`layers_component_rows_tests`, #1903); a file or a page made while the dark theme shows gets a `#1E1E1E` page (`#F5F5F5` in the light theme), its own from then on: changing the theme repaints no page and opened files keep theirs; the file the app opens with takes the theme once it is known; the CLI, MCP and exports have no theme and keep `#F5F5F5`; text drawn with the Text tool is white on a dark page and black on a light one (so is text a command makes in the app without naming a fill; headless sessions keep black) (`theme_pages_tests`, `dark_page_text_tests`) | +| Light and dark themes | done | colours from `theme::Tokens`; component, variant and instance rows in the Layers panel take the theme's component tint, at AA on the panel and on their selected row (hidden ones in the faded text colour, at least 3:1), and the rows inside a selected component a tone of its row (`layers_component_rows_tests`, #1903); a file or a page made while the dark theme shows gets a `#1E1E1E` page (`#F5F5F5` in the light theme), its own from then on: changing the theme repaints no page and opened files keep theirs; the file the app opens with takes the theme once it is known; the CLI, MCP and exports have no theme and keep `#F5F5F5`; text drawn with the Text tool is white on a dark page and black on a light one (so is text a command makes in the app without naming a fill; headless sessions keep black) (`theme_pages_tests`, `dark_page_text_tests`) | | UI3 layout: tab strip as title bar, icon rail, file header, pages + layers, floating bottom toolbar with flyouts, Design / Prototype / Inspect panels | done | frameless window on Windows/Linux with drawn buttons; tabs reorder by dragging along the strip, and closing one whose file has unsaved changes asks Don't Save / Cancel / Save (Alt+close drops the changes at once, #2032); on a Wayland compositor that offers no server-side decorations (GNOME) the window gets winit's Adwaita controls instead (#592; checked by its author on GNOME, KDE not run) | | Light and dark themes | done | colours from `theme::Tokens`; a file or a page made while the dark theme shows gets a `#1E1E1E` page (`#F5F5F5` in the light theme), its own from then on: changing the theme repaints no page and opened files keep theirs; the file the app opens with takes the theme once it is known; the CLI, MCP and exports have no theme and keep `#F5F5F5`; text drawn with the Text tool is white on a dark page and black on a light one (so is text a command makes in the app without naming a fill; headless sessions keep black) (`theme_pages_tests`, `dark_page_text_tests`) | | Minimize UI (Shift+Cmd/Ctrl+\\, the Minimize button by the file name) and Show/Hide UI (Cmd/Ctrl+\\) | done | Minimize collapses the navigation bar and both sidebars and keeps the toolbar and the panel widths; "If you select an object while the panels are minimized, the left sidebar remains minimized while the right sidebar expands [...] Once all objects are deselected, the right panel minimizes again" (Explore the navigation bar and left sidebar); a small Expand button at the top left of the canvas puts them back (FigCraft's own, the article names no control for it). Hide UI takes the toolbar too (`panel_width_tests`, `shortcuts_tests`; #1835) |