From 4e03c031de26137a2af8d48f57d57efd0b91e9fd Mon Sep 17 00:00:00 2001 From: skylar taylor-barrick Date: Sun, 25 Oct 2026 15:31:41 +0100 Subject: [PATCH] M-egui037: document shared UI behavior and regression contracts --- crates/keymap/src/ui/review.rs | 2 ++ crates/keymap/tests/ui_dialog.rs | 1 + crates/keymap/tests/ui_snapshot.rs | 2 ++ crates/ui/examples/docking_native.rs | 1 + crates/ui/examples/ui_hotpaths.rs | 6 +++++ crates/ui/src/capture/tests.rs | 2 ++ crates/ui/src/docking/dock_tests.rs | 11 +++++++++ crates/ui/src/docking/floating_tests.rs | 13 ++++++++++ crates/ui/src/docking/group_tests.rs | 26 +++++++++++++++++++ crates/ui/src/docking/iconic_tests.rs | 3 +++ crates/ui/src/docking/native_tests.rs | 7 ++++++ crates/ui/src/docking/render/native.rs | 1 + crates/ui/src/docking/tests.rs | 33 +++++++++++++++++++++++++ crates/ui/src/docking/trial_tests.rs | 9 +++++++ crates/ui/src/layout/tests.rs | 2 ++ crates/ui/src/panes/tests.rs | 9 +++++++ crates/ui/src/printing/tests.rs | 3 +++ crates/ui/src/tabs/tests.rs | 2 ++ crates/ui/tests/docking_workflows.rs | 4 +++ 19 files changed, 137 insertions(+) diff --git a/crates/keymap/src/ui/review.rs b/crates/keymap/src/ui/review.rs index 132a810d..231bafeb 100644 --- a/crates/keymap/src/ui/review.rs +++ b/crates/keymap/src/ui/review.rs @@ -108,6 +108,8 @@ impl Draw<'_, '_> { } } + /// Translate the note shown for an approximated imported shortcut; currently + /// only the numeric-keypad read-as-main-key note exists. fn read_note(&self, n: ReadNote) -> String { match n { ReadNote::Numpad => self.ui("numeric keypad key read as main key"), diff --git a/crates/keymap/tests/ui_dialog.rs b/crates/keymap/tests/ui_dialog.rs index cf7ceab3..77330d58 100644 --- a/crates/keymap/tests/ui_dialog.rs +++ b/crates/keymap/tests/ui_dialog.rs @@ -61,6 +61,7 @@ impl Fx { self.km.bindings(&CommandRef::from(id)).iter().map(|b| b.to_string()).collect() } + /// Whether any outcome recorded by the dialog harness is `Outcome::Cancel`. fn canceled(&self) -> bool { self.outcomes.iter().any(|o| matches!(o, Outcome::Cancel)) } diff --git a/crates/keymap/tests/ui_snapshot.rs b/crates/keymap/tests/ui_snapshot.rs index 39113ead..c9779cda 100644 --- a/crates/keymap/tests/ui_snapshot.rs +++ b/crates/keymap/tests/ui_snapshot.rs @@ -117,6 +117,8 @@ fn click(h: &mut Harness<'_, Fx>, label: &str) { h.run(); } +/// Click the widget carrying `label` and run a frame; used to put a command row +/// into the snapshot's selected state. fn select(h: &mut Harness<'_, Fx>, label: &str) { h.get_by_label(label).click(); h.run(); diff --git a/crates/ui/examples/docking_native.rs b/crates/ui/examples/docking_native.rs index 94b3d19a..753a4d57 100644 --- a/crates/ui/examples/docking_native.rs +++ b/crates/ui/examples/docking_native.rs @@ -14,6 +14,7 @@ impl DockContent for Panels { ui.label(format!("Body of panel {panel}")); } + /// Return the example content's logical and accessibility label, `Panel {panel}`. fn logical_label(&self, panel: &u32) -> String { format!("Panel {panel}") } diff --git a/crates/ui/examples/ui_hotpaths.rs b/crates/ui/examples/ui_hotpaths.rs index 705136b4..9deb6fa5 100644 --- a/crates/ui/examples/ui_hotpaths.rs +++ b/crates/ui/examples/ui_hotpaths.rs @@ -177,10 +177,12 @@ fn main() -> Result<(), Box> { Ok(()) } +/// Synthetic tab widths from 40 to 119 pt, cycling with `(i * 17) % 80`. fn tab_widths(count: usize) -> Vec { (0..count).map(|i| 40.0 + ((i * 17) % 80) as f32).collect() } +/// Synthetic toolbar widths from 24 to 71 pt, cycling with `(i * 13) % 48`. fn toolbar_widths(count: usize) -> Vec { (0..count).map(|i| 24.0 + ((i * 13) % 48) as f32).collect() } @@ -263,6 +265,8 @@ fn font_definitions() -> Result<(egui::FontDefinitions, Vec), Box &'static str { if env::var_os("CRAFT_UI_PERF_FONT").is_some() { "CRAFT_UI_PERF_FONT (pinned fixture file)" } else { "egui default_fonts (bundled)" } } @@ -281,6 +285,7 @@ fn context(scale: f32, fonts: &egui::FontDefinitions) -> Context { ctx } +/// A headless screen rectangle from `(0, 0)` to `viewport[0] × viewport[1]`. fn screen_rect(viewport: [f32; 2]) -> Rect { Rect::from_min_size(pos2(0.0, 0.0), vec2(viewport[0], viewport[1])) } @@ -424,6 +429,7 @@ fn dock_layout(panels: u32, with_floating: bool) -> Layout { } } +/// Return the benchmark content's logical label, `Panel {panel}`. fn panel_label(panel: &u32) -> String { format!("Panel {panel}") } diff --git a/crates/ui/src/capture/tests.rs b/crates/ui/src/capture/tests.rs index 9170125a..dd666d71 100644 --- a/crates/ui/src/capture/tests.rs +++ b/crates/ui/src/capture/tests.rs @@ -19,6 +19,8 @@ fn observe_repaints(ctx: &Context) -> Arc>> { #[test] /// Compile-time assertion that `CaptureSlot` and `ScreenshotCallback` are `Send + Sync`. fn slot_is_send_and_sync() { + /// Compile-time helper requiring `T: Send + Sync`; the enclosing test uses it to + /// assert the public capture types are thread-safe. fn assert_send_sync() {} assert_send_sync::(); assert_send_sync::(); diff --git a/crates/ui/src/docking/dock_tests.rs b/crates/ui/src/docking/dock_tests.rs index 7abdce16..25d43491 100644 --- a/crates/ui/src/docking/dock_tests.rs +++ b/crates/ui/src/docking/dock_tests.rs @@ -100,6 +100,8 @@ impl Driver { }) } + /// Run three empty frames so floating areas finish their sizing pass, then + /// return the driver. fn start(mut driver: Self) -> Self { // Floating areas settle after their sizing pass. for _ in 0..3 { @@ -144,10 +146,13 @@ impl Driver { output } + /// Read the response rectangle for the dock widget named `name` and scoped to + /// `panel`. fn rect(&self, name: &str, panel: u32) -> Rect { self.ctx.read_response(Id::unique(AREA).with((name, Some(&panel)))).unwrap().rect } + /// Read the response rectangle for `panel`'s tab. fn tab(&self, panel: u32) -> Rect { self.ctx.read_response(Id::unique(AREA).with(("tab", &panel))).unwrap().rect } @@ -413,6 +418,8 @@ fn tab_dragged_to_the_window_edge_without_a_dock_makes_a_dock_of_that_panel() { } #[test] +/// A group dropped on a column's tab strip joins as a tab, on a group body +/// appends as its last tab, and in the empty space below starts a new group. fn a_column_takes_groups_anywhere_in_it_and_as_tabs() { let windows = [(5, [400.0, 40.0, 150.0, 120.0]), (6, [400.0, 220.0, 150.0, 120.0]), (7, [400.0, 400.0, 150.0, 120.0])]; let mut d = Driver::new(with_floating(docked(Side::Start, &[&[&[2], &[3]]]), &windows)); @@ -461,6 +468,8 @@ fn a_column_too_tall_for_the_window_scrolls() { } #[test] +/// Dragging a column bar to the window edge moves the column first; dragging it +/// away from the dock floats every group in the column and shows a follow ghost. fn column_bar_drag_moves_the_column_and_away_from_a_dock_floats_its_groups() { let mut d = Driver::new(docked(Side::Start, &[&[&[2], &[3]], &[&[5]]])); // The second column to the window edge: it becomes the first. @@ -619,6 +628,8 @@ fn right_to_left_mirrors_docks_their_edges_and_new_docks() { } #[test] +/// Dropping a column's first tab just past its own edge anchors it after the +/// other column's panel; a panel alone in its column stays in place. fn a_column_s_first_tab_dropped_on_its_own_column_edge_anchors_on_another_panel() { // Two columns, 0..240 and 240..480; the dragged tab is the first panel of the last one. let wide = |columns: &[&[&[u32]]]| { diff --git a/crates/ui/src/docking/floating_tests.rs b/crates/ui/src/docking/floating_tests.rs index 3137ccbb..e853bc20 100644 --- a/crates/ui/src/docking/floating_tests.rs +++ b/crates/ui/src/docking/floating_tests.rs @@ -109,6 +109,7 @@ struct Driver { } impl Driver { + /// Create a floating test driver and run two empty frames to settle sizing. fn new(layout: Layout, split_tree: bool) -> Self { let mut driver = Self { ctx: Context::default(), @@ -172,10 +173,12 @@ impl Driver { self.ctx.read_response(id).map(|r| r.rect) } + /// Read the response rectangle of the move bar whose first panel is `first`. fn title(&self, first: u32) -> Rect { self.response(Id::unique(AREA).with(("move-group", Some(&first)))).unwrap() } + /// Read `panel`'s tab response rectangle, or `None` when it is not rendered. fn tab(&self, panel: u32) -> Option { self.response(Id::unique(AREA).with(("tab", &panel))) } @@ -261,6 +264,8 @@ fn title_bar_drag_moves_every_stacked_group_and_drops_into_a_dock_as_stacked_gro } #[test] +/// Releasing a floating window's title bar on a dock seam stacks its group +/// between the target groups; releasing on the edge band makes an end dock. fn title_bar_drag_onto_dock_targets_docks_the_window() { // Released on the seam between two docked groups: the window's group stacks between them. let mut d = Driver::new(workspace(), false); @@ -398,6 +403,8 @@ fn outer_edge_band_splits_at_the_root() { } #[test] +/// With split-tree chrome, a drop 7 pt inside the outer edge targets the edge +/// dock, while a drop at 10 pt targets frame [1]'s split zone. fn the_split_tree_outer_edge_band_is_8_pt() { let mut d = Driver::new(frames(), true); let tab = d.tab(3).unwrap().center(); @@ -481,6 +488,8 @@ fn close_other_panels_keeps_only_the_chosen_tab() { } #[test] +/// Hiding every panel draws no tabs and leaves the layout unchanged; the +/// `Except` set keeps only its panels, with a retained window below the resize band. fn hide_panels_keeps_protected_panels_and_the_except_set() { let mut d = Driver::new(workspace(), false); d.hide = Hide::All; @@ -521,6 +530,8 @@ fn a_canvas_that_accepts_splits_is_still_protected() { } #[test] +/// When viewports are embedded, a floating window marked native draws in the +/// main window at its usual rect without emitting actions. fn native_window_falls_back_in_window_when_viewports_are_embedded() { let mut layout = workspace(); layout.floating[0].native = Some([1200.0, 900.0, 240.0, 300.0]); @@ -663,6 +674,8 @@ fn hidden_panels_leave_their_split_and_resizing_names_the_layout_path() { } #[test] +/// Hiding floating window 5 removes its coverage of the dock seam, so dragging +/// window 8 there stacks its group between panels 3 and 4. fn a_hidden_floating_window_does_not_cover_dock_targets() { // Window 5 covers the column's seam at x 150..240 until it is hidden. let mut layout = workspace(); diff --git a/crates/ui/src/docking/group_tests.rs b/crates/ui/src/docking/group_tests.rs index ef246549..46be48c4 100644 --- a/crates/ui/src/docking/group_tests.rs +++ b/crates/ui/src/docking/group_tests.rs @@ -59,6 +59,8 @@ struct TestContent { } impl DockContent for TestContent { + /// Test body that fills its rect when `fill` is set, otherwise takes the + /// configured height or draws a one-line `Body` label. fn body(&mut self, ui: &mut egui::Ui, _: &u32) { if self.fill { ui.allocate_rect(ui.max_rect(), egui::Sense::hover()); @@ -72,6 +74,8 @@ impl DockContent for TestContent { fn logical_label(&self, panel: &u32) -> String { format!("Panel {panel}") } + /// Mark panels in `protected` as protected and leave the rest with default + /// permissions. fn permissions(&self, panel: &u32) -> Permissions { if self.protected.contains(panel) { Permissions::PROTECTED } else { Permissions::default() } } @@ -114,6 +118,8 @@ impl Driver { Self::with(layout, TestContent::default(), Rect::from_min_size(pos2(0.0, 0.0), vec2(600.0, 600.0))) } + /// Create a group test driver for `layout`, `content` and `area`, then settle + /// two frames. fn with(layout: Layout, content: TestContent, area: Rect) -> Self { let mut driver = Self { ctx: Context::default(), @@ -162,6 +168,8 @@ impl Driver { output } + /// Run one frame, assert it reports no error, and apply each returned action + /// with the content's permissions. fn step(&mut self, events: Vec) -> DockOutput { let output = self.frame(events); assert!(output.error.is_none(), "{output:?}"); @@ -172,10 +180,13 @@ impl Driver { output } + /// Read the response rectangle for the dock widget named `name` and scoped to + /// `panel`. fn rect(&self, name: &str, panel: u32) -> Rect { self.ctx.read_response(Id::unique(AREA).with((name, Some(&panel)))).unwrap().rect } + /// Read the response rectangle for `panel`'s tab. fn tab(&self, panel: u32) -> Rect { self.ctx.read_response(Id::unique(AREA).with(("tab", &panel))).unwrap().rect } @@ -222,6 +233,9 @@ fn moved_group(actions: &[Action]) -> Option<&Target> { } #[test] +/// A group bar dropped on another strip emits one `MoveGroup` and keeps panel +/// order and the active panel; dropped on empty space it floats at its docked +/// width and 600 pt height; inside a column it joins the group below. fn docked_two_tab_group_drags_by_its_bar_and_keeps_order_and_active_tab() { // Onto another group's strip: one MoveGroup, both panels in order, 3 still selected. let mut d = Driver::new(frames()); @@ -376,6 +390,8 @@ fn group_drop_on_a_frame_edge_splits_and_on_a_protected_canvas_floats() { } #[test] +/// Holding Ctrl or Command at release turns either a group-bar drop or a tab +/// drop into a float instead of a dock. fn ctrl_or_cmd_held_at_release_always_floats() { for modifiers in [Modifiers::CTRL, Modifiers::MAC_CMD, Modifiers::COMMAND] { let mut d = Driver::new(frames()); @@ -528,6 +544,8 @@ fn floating_border_and_drag_bar_move_and_cancel_as_a_group() { } #[test] +/// Moving a floating window by eight fractional pointer steps lands within one +/// point of the accumulated 80.3 × 60.7 pt offset, without step-rounding drift. fn floating_border_movement_does_not_accumulate_fractional_step_rounding() { let layout = Layout { root: Some(tabs(&[1], 0)), floating: vec![Floating::single(vec![5, 6], 1, [80.0, 70.0, 300.0, 220.0])] }; let mut d = Driver::new(layout); @@ -542,6 +560,8 @@ fn floating_border_movement_does_not_accumulate_fractional_step_rounding() { } #[test] +/// With host-owned headers, dragging either the border or the bar reaches +/// `whole_group`, emits no action or group drop, and leaves the layout unchanged. fn docked_header_drag_reaches_the_host_hook_and_preserves_the_group() { for name in ["drag-border", "drag-bar"] { let content = TestContent { host_owned: true, ..Default::default() }; @@ -559,7 +579,9 @@ fn docked_header_drag_reaches_the_host_hook_and_preserves_the_group() { fn a_lone_protected_panel_draws_no_strip_and_no_bar() { struct Bare; impl DockContent for Bare { + /// A bare content body that draws nothing. fn body(&mut self, _: &mut egui::Ui, _: &u32) {} + /// Mark panel 1 protected and leave every other panel with default permissions. fn permissions(&self, panel: &u32) -> Permissions { if *panel == 1 { Permissions::PROTECTED } else { Permissions::default() } } @@ -615,6 +637,8 @@ fn drag_bar_menu_has_the_group_items_and_opens_from_the_keyboard() { } #[test] +/// Dragging one group out of a two-group floating stack floats only that group +/// at the stack width and its recorded height in the stack. fn a_group_dragged_out_of_a_floating_stack_tears_out_alone() { let stack = Floating::stack(vec![Group::new(vec![5], 0), Group::new(vec![6, 7], 1)], [80.0, 70.0, 300.0, 400.0]).unwrap(); let layout = Layout { root: Some(tabs(&[1], 0)), floating: vec![stack] }; @@ -671,6 +695,8 @@ fn a_short_docked_panel_floats_at_its_content_height_up_to_its_floating_size() { } #[test] +/// A fill-height panel floats at the 320 pt docked cap without a host size +/// and at the host's height when one is supplied. fn a_panel_that_fills_its_frame_floats_at_its_floating_size_or_320() { let float = |floating_size: Option| { let content = TestContent { fill: true, floating_size, ..Default::default() }; diff --git a/crates/ui/src/docking/iconic_tests.rs b/crates/ui/src/docking/iconic_tests.rs index 68644d5c..58a85fa5 100644 --- a/crates/ui/src/docking/iconic_tests.rs +++ b/crates/ui/src/docking/iconic_tests.rs @@ -103,6 +103,9 @@ fn widening_an_iconic_column_shows_labels() { } #[test] +/// An icon click activates its panel and opens a canvas-side flyout; another +/// icon switches it, and the same icon closes it even with auto-collapse off. Escape +/// and outside presses close it only with auto-collapse on; a programmatic raise opens it. fn icon_click_opens_a_flyout_on_the_canvas_side_and_auto_collapse_closes_it() { let mut d = DockDriver::new(docked(&[&[1, 2], &[3]], true), Content); let icon = hit(&d, Hit::Icon(2)).center(); diff --git a/crates/ui/src/docking/native_tests.rs b/crates/ui/src/docking/native_tests.rs index 5f02b748..4b69e1f4 100644 --- a/crates/ui/src/docking/native_tests.rs +++ b/crates/ui/src/docking/native_tests.rs @@ -1491,6 +1491,8 @@ fn the_bars_close_button_wins_over_the_resize_bands() { } #[test] +/// Resize bands avoid the close button, are at most 6 pt deep, use 16 pt +/// corners along each edge, and leave the bar below the top band inert. fn resize_bands_are_a_few_points_deep_and_leave_out_the_bar_buttons() { let rect = Rect::from_min_size(Pos2::ZERO, vec2(240.0, 300.0)); let close = Rect::from_center_size(pos2(224.0, 9.0), vec2(18.0, 18.0)); @@ -1624,6 +1626,9 @@ fn dimmed(d: &DockDriver, viewport: egui::ViewportId) -> bool { } #[test] +/// While input is blocked, a native window is dimmed, loses always-on-top, +/// ignores clicks, drags, key presses and close requests, but still forwards key +/// releases; unblocking restores normal input and activation. fn a_native_window_takes_no_input_while_the_app_blocks_it() { let mut d = blocked_driver(); let id = id_of(&d); @@ -2238,6 +2243,8 @@ fn escape_and_cancel_drag_end_the_routing_of_an_app_drag() { } #[test] +/// Dragging a tab onto a native window's body emits a tab `Move` rather than +/// an app body drag and leaves the content's drops empty. fn a_tab_drag_onto_a_native_window_is_no_app_drag() { let mut d = app_drag(layout(true), 1, &[5]); let id = window(&d, 5).id; diff --git a/crates/ui/src/docking/render/native.rs b/crates/ui/src/docking/render/native.rs index 82c788a9..12ef37a8 100644 --- a/crates/ui/src/docking/render/native.rs +++ b/crates/ui/src/docking/render/native.rs @@ -848,6 +848,7 @@ fn body_input_key() -> Id { pub(in crate::docking) type InputHook = Box; impl egui::Plugin for NativeInput { + /// The egui plugin's diagnostic name, `craft-ui docking native input`. fn debug_name(&self) -> &'static str { "craft-ui docking native input" } diff --git a/crates/ui/src/docking/tests.rs b/crates/ui/src/docking/tests.rs index 12ffe2fb..0e363276 100644 --- a/crates/ui/src/docking/tests.rs +++ b/crates/ui/src/docking/tests.rs @@ -472,6 +472,8 @@ fn saved_layout_and_hidden_location_round_trip() { #[cfg(feature = "serde")] #[test] +/// Deserialize an older hidden `Location` without a `sibling` field; the +/// missing list stays empty and the anchor panel is preserved. fn old_hidden_locations_accept_panel_ids_without_a_default_identity() { #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] enum Panel { @@ -515,6 +517,8 @@ fn frame(ctx: &Context, layout: &Layout, events: Vec, focused: bool) } #[test] +/// Moving a panel from a root tab into a floating window keeps its body scope +/// id across frames. fn renderer_keeps_content_identity_when_panel_moves_into_a_float() { let ctx = Context::default(); let mut layout = Layout { root: Some(tabs(&[1], 0)), floating: Vec::new() }; @@ -564,6 +568,7 @@ fn step(ctx: &Context, layout: &mut Layout, events: Vec) -> Vec Event { Event::PointerButton { pos, button: PointerButton::Primary, pressed, modifiers: Modifiers::NONE } } @@ -812,6 +817,8 @@ fn split_inner_edge_remains_draggable_beside_a_scrollbar() { } #[test] +/// Closing a panel in a nested vertical/horizontal layout and restoring its +/// saved location reproduces the complete layout. fn restoring_a_panel_beside_a_nested_subtree_preserves_the_complete_workspace() { let mut layout = Layout { root: Some(Node::Split { @@ -968,6 +975,8 @@ fn floating_group_touch_release_reports_drop_after_pointer_gone() { } #[test] +/// A floating group drag cancelled by pointer loss, with or without Escape, restores the +/// original floating rect and emits no group drag or drop. fn floating_group_exit_with_escape_or_focus_loss_restores_original_geometry() { for escape in [false, true] { let ctx = Context::default(); @@ -1035,13 +1044,17 @@ impl DockContent for CustomContent { self.labels.push(logical.to_owned()); ui.painter().layout_no_wrap("Custom visual label".into(), font.clone(), color).into() } + /// Custom visual action label that prefixes the logical text with + /// `Action visual: `. fn action_label(&mut self, ui: &egui::Ui, logical: &str, font: &egui::FontId, color: egui::Color32) -> egui::WidgetText { ui.painter().layout_no_wrap(format!("Action visual: {logical}"), font.clone(), color).into() } + /// Record an app menu draw and emit a clickable `App menu` button. fn panel_menu(&mut self, ui: &mut egui::Ui, _: &u32) { self.menus += 1; ui.button("App menu").clicked(); } + /// Test custom chrome declares a 40 pt group header width. fn group_header_width(&self, _: &[u32], _: &u32, _: bool, _: f32) -> f32 { 40.0 } @@ -1054,6 +1067,8 @@ impl DockContent for CustomContent { } } +/// Run one 800×600 customized dock frame with optional blockers and a +/// programmatic raise of panel 5; returns the output with texture deltas cleared. fn custom_frame( ctx: &Context, layout: &Layout, @@ -1077,6 +1092,9 @@ fn custom_frame( } #[test] +/// Custom chrome receives logical labels, draws custom headers and bodies and app-menu +/// popups, while a programmatic raise sets no user focus or focus ring and emits +/// no action. fn custom_chrome_shaped_labels_and_programmatic_raise_are_available() { let ctx = Context::default(); let layout = sample(); @@ -1128,6 +1146,7 @@ fn overlay_appearing_mid_drag_blocks_release_and_float_fallback() { } #[test] +/// Escape during a floating group move restores the original floating rect. fn group_move_escape_restores_original_geometry() { let ctx = Context::default(); let mut layout = sample(); @@ -1248,6 +1267,8 @@ fn resize_floating_group(permissions: impl Fn(&u32) -> Permissions + Copy) -> Ve } #[test] +/// A floating group's grip resizes a movable group, but a group containing +/// protected panel 6 blocks the `MoveFloating` action. fn protected_group_member_blocks_floating_group_resize() { let moved = |actions: &[Action]| actions.iter().any(|a| matches!(a, Action::MoveFloating { .. })); assert!(moved(&resize_floating_group(|_| Permissions::default())), "the grip resizes a movable group"); @@ -1455,6 +1476,8 @@ fn workspace_cancel_preserves_other_area_and_body_drag_ownership() { /// logical accessibility names; the visual text is not exposed as an accessibility label. fn popup_action_labels_use_custom_visual_text_and_logical_accessibility() { use egui_kittest::{Harness, kittest::Queryable}; + /// Recursively search a shape or shape vector for a text galley whose text is + /// exactly `text`. fn paints(shape: &egui::Shape, text: &str) -> bool { match shape { egui::Shape::Text(shape) => shape.galley.text() == text, @@ -1658,6 +1681,8 @@ fn collapsed_floating_height_cap_does_not_disable_keyboard_width_resizing() { } #[test] +/// Dropping a panel back on its own singleton or multi-tab source body is an +/// explicit no-op with no insertion marker, with or without split permission. fn returning_to_a_singleton_or_floating_source_body_is_an_explicit_noop() { for panels in [vec![1u32], vec![1, 2, 3]] { for allow_splits in [false, true] { @@ -1722,6 +1747,8 @@ fn pointer_floating_resize_stops_at_edges_and_waits_for_cursor_to_return() { } #[test] +/// Dropping a panel within its existing tab slots is a no-op whose insertion +/// marker stays inside the strip; positions outside its own slots move it. fn unchanged_tab_slots_are_explicit_noops_with_bounded_insertion_markers() { let rect = Rect::from_min_size(pos2(0.0, 0.0), vec2(300.0, 200.0)); let target = DropTarget { @@ -1758,6 +1785,8 @@ fn host_owned_noop_consumes_release_without_a_fallback_float() { struct Content(usize); impl DockContent for Content { fn body(&mut self, _ui: &mut egui::Ui, _panel: &u32) {} + /// Test drop handler that counts releases and accepts every request with + /// default feedback, consuming host-owned drops. fn resolve_drag(&mut self, _ctx: &Context, _style: &DockStyle, request: &DropRequest<'_, u32>) -> Option { if request.released { self.0 += 1; @@ -1828,6 +1857,8 @@ fn overflow_noop_detection_uses_logical_order_including_hidden_tabs() { } #[test] +/// For clipped and singleton strips of several widths, a no-op drop keeps its +/// insertion marker inside the visible strip and at most 2 pt wide. fn insertion_markers_stay_inside_clipped_and_singleton_strips() { for width in [0.5, 1.0, 20.0, 140.0] { let rect = Rect::from_min_size(pos2(10.0, 20.0), vec2(width, 200.0)); @@ -1854,6 +1885,8 @@ fn insertion_markers_stay_inside_clipped_and_singleton_strips() { } #[test] +/// A subpixel-wide tab strip crossing zero still yields a no-op insertion +/// marker contained by the strip. fn insertion_marker_handles_subpixel_strip_crossing_zero_without_panicking() { let rect = Rect::from_min_max(pos2(-0.001, 0.0), pos2(0.002, 30.0)); let target = diff --git a/crates/ui/src/docking/trial_tests.rs b/crates/ui/src/docking/trial_tests.rs index 56bf03a6..da55949f 100644 --- a/crates/ui/src/docking/trial_tests.rs +++ b/crates/ui/src/docking/trial_tests.rs @@ -73,6 +73,8 @@ impl DockContent for GalleyLabels { fn logical_label(&self, panel: &u32) -> String { format!("Character Styles {panel}") } + /// Host-provided galley label that lays the logical text out without wrapping, + /// letting the dock handle pre-shaped widget text. fn label(&mut self, ui: &egui::Ui, _: &u32, logical: &str, font: &egui::FontId, color: Color32) -> egui::WidgetText { egui::WidgetText::Galley(ui.painter().layout_no_wrap(logical.to_owned(), font.clone(), color)) } @@ -162,6 +164,8 @@ fn double_click_on_a_tab_of_a_minimized_group_ends_expanded() { } #[test] +/// Double-clicking the active tab of a minimized floating window or docked +/// group expands it; a second double-click minimizes the docked group again. fn double_click_on_the_active_tab_of_a_minimized_group_or_window_ends_expanded() { let window = Floating { minimized: true, ..Floating::single(vec![5, 6], 1, [80.0, 70.0, 300.0, 220.0]) }; let mut d = Driver::new(Layout { root: Some(tabs(&[1], 0)), floating: vec![window] }); @@ -268,6 +272,9 @@ fn a_press_in_a_stripless_panel_reports_it_focused() { } #[test] +/// In a right-to-left strip the first tab sits at the right edge and the bar +/// lies to its left; drops just right of the first tab insert before panel 2, +/// and insertion slots are mirrored at the strip edges. fn right_to_left_mirrors_the_tab_strip_and_its_insertion_slots() { let mut d = Driver::with(frames(), TestContent { header: 30.0, ..Default::default() }, AREA_600); d.tweak = |style| style.right_to_left = true; @@ -360,6 +367,8 @@ fn hidden_close_controls_leave_closing_to_the_host() { } #[test] +/// `apply_all` returns every panel closed by close-others and close-window in +/// order, leaving the remaining panel open until a later `apply` closes it. fn apply_all_returns_every_panel_its_actions_closed() { let mut layout = Layout { root: Some(tabs(&[1, 2, 3], 0)), floating: vec![Floating::single(vec![5, 6], 0, [80.0, 70.0, 300.0, 220.0])] }; let closed = layout diff --git a/crates/ui/src/layout/tests.rs b/crates/ui/src/layout/tests.rs index d2a12e65..f306f7a6 100644 --- a/crates/ui/src/layout/tests.rs +++ b/crates/ui/src/layout/tests.rs @@ -107,6 +107,8 @@ struct HandleTest { } impl HandleTest { + /// Create a handle test with horizontal axis, a 4×140 pt rect, the label + /// `Resize panels`, enabled state and value 100. fn new() -> Self { let ctx = Context::default(); ctx.enable_accesskit(); diff --git a/crates/ui/src/panes/tests.rs b/crates/ui/src/panes/tests.rs index e6cdeca4..2be008ac 100644 --- a/crates/ui/src/panes/tests.rs +++ b/crates/ui/src/panes/tests.rs @@ -29,6 +29,8 @@ impl PaneContent for Content { fn body(&mut self, ui: &mut Ui, pane: &Pane) { ui.label(format!("{pane:?} body")); } + /// Default docked pane state: end for Comments and Pivot, start otherwise, + /// with a 250 pt size. fn default_state(&self, pane: &Pane) -> PaneState { let edge = if matches!(pane, Pane::Comments | Pane::Pivot) { Edge::End } else { Edge::Start }; PaneState::docked(edge, 250.0) @@ -53,6 +55,8 @@ struct App { } impl App { + /// Create an app with `open` panes, empty state and log, no focused pane, no + /// document-focus requests, left-to-right layout and column side policy. fn new(open: &[Pane]) -> Self { Self { open: open.to_vec(), states: HashMap::new(), log: Vec::new(), focused: None, to_document: 0, rtl: false, policy: SidePolicy::Columns } } @@ -85,6 +89,8 @@ impl App { } } +/// Build an 800×600 harness around `App::frame`, mark input focused, and run +/// two frames before returning it. fn harness(app: App) -> Harness<'static, App> { let mut h = Harness::builder().with_size(vec2(800.0, 600.0)).build_ui_state(|ui, app: &mut App| app.frame(ui), app); h.input_mut().focused = true; @@ -324,6 +330,8 @@ fn float_rect_is_finite_and_inside_the_bounds() { #[cfg(feature = "serde")] #[test] +/// A `PaneState` with a docked edge, size and float rect serializes to JSON and +/// deserializes back unchanged. fn pane_state_round_trips_through_json() { let state = PaneState { place: Place::Docked(Edge::End), size: Some(280.0), float_rect: Some([1.0, 2.0, 270.0, 480.0]) }; let json = serde_json::to_string(&state).unwrap(); @@ -339,6 +347,7 @@ fn a_body_wider_than_its_pane_is_clipped_and_the_document_starts_at_the_pane_edg fn title(&self, pane: &Pane) -> String { format!("{pane:?}") } + /// A pane body that requests 400×40 pt inside its pane to exercise clipping. fn body(&mut self, ui: &mut Ui, _: &Pane) { ui.allocate_exact_size(vec2(400.0, 40.0), Sense::hover()); } diff --git a/crates/ui/src/printing/tests.rs b/crates/ui/src/printing/tests.rs index 5da489ca..28ac98b9 100644 --- a/crates/ui/src/printing/tests.rs +++ b/crates/ui/src/printing/tests.rs @@ -144,6 +144,9 @@ fn failed_discovery_cannot_reuse_a_previously_available_queue() { } #[test] +/// Only one print job can be active: a second `begin` fails, the receiver stays +/// with the job, and submission, queue refusal and a dropped worker report +/// `Submitted`, `Failed` and `WorkerStopped` while returning to ready. fn active_jobs_keep_their_receiver_and_report_receipt_refusal_or_worker_failure() { let ctx = egui::Context::default(); let mut state = State::default(); diff --git a/crates/ui/src/tabs/tests.rs b/crates/ui/src/tabs/tests.rs index eb4accbb..5a5b4e3d 100644 --- a/crates/ui/src/tabs/tests.rs +++ b/crates/ui/src/tabs/tests.rs @@ -127,6 +127,8 @@ struct TabTest { } impl TabTest { + /// Create a two-tab test with duplicate `Layers` labels, the first selected and + /// enabled, click sense, a 90×26 pt rect and zero counters. fn new() -> Self { let ctx = Context::default(); ctx.enable_accesskit(); diff --git a/crates/ui/tests/docking_workflows.rs b/crates/ui/tests/docking_workflows.rs index 571265c6..26f50d89 100644 --- a/crates/ui/tests/docking_workflows.rs +++ b/crates/ui/tests/docking_workflows.rs @@ -40,6 +40,9 @@ struct Harness { } impl Harness { + /// Create a workflow harness with the requested theme, width and pixel scale, + /// zero animation and no cursor blink, and a canvas plus side tabs and floating + /// brushes layout, then settle it. fn new(dark: bool, width: f32, scale: f32) -> Self { let ctx = Context::default(); ctx.set_theme(if dark { egui::Theme::Dark } else { egui::Theme::Light }); @@ -177,6 +180,7 @@ impl Harness { self.layout.validate().unwrap(); } + /// Run four empty workflow frames to settle sizing. fn settle(&mut self) { for _ in 0..4 { self.step(Vec::new());