M-egui037: document shared UI behavior and regression contracts

This commit is contained in:
skylar taylor-barrick committed 2026-10-25 15:31:41 +01:00
1 parent 253c425e2f
commit 4e03c031de
19 files changed
+137

No files matched your search

+2
View File
@@ -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"),
+1
View File
@@ -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))
}
+2
View File
@@ -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();
+1
View File
@@ -14,6 +14,7 @@ impl DockContent<u32> 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}")
}
+6
View File
@@ -177,10 +177,12 @@ fn main() -> Result<(), Box<dyn Error>> {
Ok(())
}
/// Synthetic tab widths from 40 to 119 pt, cycling with `(i * 17) % 80`.
fn tab_widths(count: usize) -> Vec<f32> {
(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<f32> {
(0..count).map(|i| 24.0 + ((i * 13) % 48) as f32).collect()
}
@@ -263,6 +265,8 @@ fn font_definitions() -> Result<(egui::FontDefinitions, Vec<String>), Box<dyn Er
Ok((fonts, names))
}
/// Describe the report's font source: the pinned `CRAFT_UI_PERF_FONT` file when
/// set, otherwise egui's bundled default fonts.
fn font_source() -> &'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<u32> {
}
}
/// Return the benchmark content's logical label, `Panel {panel}`.
fn panel_label(panel: &u32) -> String {
format!("Panel {panel}")
}
+2
View File
@@ -19,6 +19,8 @@ fn observe_repaints(ctx: &Context) -> Arc<Mutex<Vec<ViewportId>>> {
#[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<T: Send + Sync>() {}
assert_send_sync::<CaptureSlot>();
assert_send_sync::<ScreenshotCallback>();
+11
View File
@@ -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]]]| {
+13
View File
@@ -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<u32>, 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<Rect> {
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();
+26
View File
@@ -59,6 +59,8 @@ struct TestContent {
}
impl DockContent<u32> 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<u32> 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<u32>, 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<Event>) -> DockOutput<u32> {
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<u32>]) -> Option<&Target<u32>> {
}
#[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<u32> 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<Vec2>| {
let content = TestContent { fill: true, floating_size, ..Default::default() };
+3
View File
@@ -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();
+7
View File
@@ -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<u32, Content>, 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;
+1
View File
@@ -848,6 +848,7 @@ fn body_input_key() -> Id {
pub(in crate::docking) type InputHook = Box<dyn FnMut(&Context, &mut RawInput) + Send>;
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"
}
+33
View File
@@ -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<u32>, events: Vec<Event>, 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<u32>, events: Vec<Event>) -> Vec<Acti
out.actions
}
/// A primary-button pointer event at `pos` with `pressed` and no modifiers.
fn pointer_button(pos: egui::Pos2, pressed: bool) -> 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<u32> 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<u32> 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<u32>,
@@ -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<u32>]| 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<u32> 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<DropFeedback> {
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 =
+9
View File
@@ -73,6 +73,8 @@ impl DockContent<u32> 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
+2
View File
@@ -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();
+9
View File
@@ -29,6 +29,8 @@ impl PaneContent<Pane> 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());
}
+3
View File
@@ -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();
+2
View File
@@ -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();
+4
View File
@@ -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());