Files
OneCAD-Draft/src/app/dock/tests.rs
T
Claude b12a29b233 Dock: review fixes - hover, preview width, stale indexes, config healing
Fixes:
- Hover state no longer drops when the pointer moves up from one strip
  icon, group grip or title bar to the one above: iced handles the
  element above first, so the old element's "left" (None) cleared the
  hover just set. Leave messages now carry the element and only clear
  their own hover.
- The drop preview used the pallets' own widths; it now uses the
  group's width, matching where the pallet lands.
- Group indexes from messages (group grab, splitter) are looked up
  safely, so a message from before a layout change no longer panics.
- Floating placements read from the config are healed (finite, positive,
  at least the minimum size), like the group weights already were.
- The shown group's edge bar is centred in its row: a stack takes its
  height from its first layer, and the icon column was shorter than the
  row. The "Drag to move group" tooltip is removed.

Simplifications (no behaviour change):
- dock_open_at_default / dock_unpeek replace seven copies of "dock on
  the right if unplaced, then reveal" and four of "stop peeking".
- The drop target takes the grab offset as a parameter, so a drag
  update stores its state once.
- dock_set_open, ensure_settings, the shown-index bookkeeping after a
  group is removed and the on-screen width clamp each lose duplication.
- VBarLabel drops options its single caller never varied.
- The drop preview's landing computation is one helper.

Naming and consistency: pallet names in the + menu, the drag ghost and
the floating title bar go through t!(); StripHit::Tab becomes
StripHit::Join; stale comments updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
2026-10-09 20:53:42 +00:00

571 lines
22 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Dock behaviour through the app: drags, hover, menus, and the real view.
use crate::app::{Message, OpenCADStudio};
fn fresh() -> OpenCADStudio {
let mut app = OpenCADStudio::new_for_test();
app.automation_op(r#"{"op":"new"}"#);
app
}
#[test]
fn blockpalette_pin_toggles_autocollapse_and_close_hides() {
let mut app = fresh();
app.show_block_palette = true;
let id = crate::ui::dock::PanelId::BlockPalette;
let _ = app.on_dock(crate::ui::dock::DockMsg::AutoCollapseToggle(id));
assert!(app.dock.auto_hides(id), "pin enables auto-hide");
let _ = app.on_dock(crate::ui::dock::DockMsg::AutoCollapseToggle(id));
assert!(!app.dock.auto_hides(id), "second pin disables auto-hide");
let _ = app.on_dock(crate::ui::dock::DockMsg::Close(id));
assert!(!app.show_block_palette, "close dismisses the sidebar");
}
/// A hermetic app with a 1600×900 workspace and the default dock layout
/// (Properties left, block palette right), both shown.
fn dock_app() -> OpenCADStudio {
let mut app = fresh();
// Tests load the user's persisted config; reset the dock to a known
// state so this stays hermetic.
app.dock = Default::default();
app.dock.ensure_settings();
app.show_properties = true;
app.show_block_palette = true;
let i = app.active_tab;
app.tabs[i].scene.selection.borrow_mut().view.vp_size = (1600.0, 900.0);
app.win_size = (1600.0, 900.0).into();
app
}
/// Where the current dock drag would land, if it has started.
fn drag_target(app: &OpenCADStudio) -> Option<crate::ui::dock::DropTarget> {
app.dock_drag.and_then(|d| d.movement()).and_then(|(_, _, target)| target)
}
/// Press `id`'s title bar at `from`, drag to `to`, and return the target
/// shown just before release.
fn drag_panel(
app: &mut OpenCADStudio,
id: crate::ui::dock::PanelId,
from: iced::Point,
to: iced::Point,
) -> Option<crate::ui::dock::DropTarget> {
use crate::ui::dock::DockMsg;
let _ = app.on_dock(DockMsg::DockGrab(id));
let _ = app.on_dock(DockMsg::DragMove(from));
let _ = app.on_dock(DockMsg::DragMove(to));
let target = drag_target(app);
let _ = app.on_dock(DockMsg::DragRelease);
target
}
#[test]
fn dropping_on_a_pallets_top_or_bottom_half_stacks_above_or_below_it() {
use crate::app::config::DockSide;
use crate::ui::dock::{DropTarget, PanelId};
let mut app = dock_app();
let id = PanelId::BlockPalette;
assert_eq!(app.dock.location(id), Some((DockSide::Right, 0)));
let target = drag_panel(
&mut app,
id,
iced::Point::new(1500.0, 10.0),
iced::Point::new(150.0, 300.0),
);
assert_eq!(
target,
Some(DropTarget::Join {
side: DockSide::Left,
group: 0,
index: 0
})
);
assert_eq!(
app.dock.left[0].panels,
vec![PanelId::BlockPalette, PanelId::Properties]
);
assert!(app.dock.right.is_empty());
// The bottom half of a pallet stacks below it.
let target = drag_panel(
&mut app,
id,
iced::Point::new(150.0, 100.0),
iced::Point::new(150.0, 800.0),
);
assert_eq!(
target,
Some(DropTarget::Join {
side: DockSide::Left,
group: 0,
index: 2
})
);
assert_eq!(
app.dock.left[0].panels,
vec![PanelId::Properties, PanelId::BlockPalette]
);
}
#[test]
fn dock_dragging_a_lone_pallet_over_its_own_group_is_a_no_op() {
use crate::app::config::DockSide;
use crate::ui::dock::{DropTarget, PanelId};
let mut app = dock_app();
let before = app.dock.clone();
// The right edge's only group holds just the block palette; drag it
// around over itself.
let target = drag_panel(
&mut app,
PanelId::BlockPalette,
iced::Point::new(1500.0, 10.0),
iced::Point::new(1450.0, 600.0),
);
assert_eq!(
target,
Some(DropTarget::Join {
side: DockSide::Right,
group: 0,
index: 1
})
);
assert_eq!(app.dock, before);
}
#[test]
fn dock_click_without_drag_changes_nothing() {
let mut app = dock_app();
let before = app.dock.clone();
let id = crate::ui::dock::PanelId::BlockPalette;
let p = iced::Point::new(1500.0, 10.0);
let target = drag_panel(&mut app, id, p, iced::Point::new(1502.0, 11.0));
assert_eq!(target, None, "movement under the threshold is a click");
assert_eq!(app.dock, before);
}
#[test]
fn dock_drop_below_the_strip_groups_starts_a_new_shown_group() {
use crate::app::config::DockSide;
use crate::ui::dock::{DropTarget, PanelId};
let mut app = dock_app();
let (_, plus_top) = app.dock_strip_layout(DockSide::Left);
let target = drag_panel(
&mut app,
PanelId::BlockPalette,
iced::Point::new(1500.0, 10.0),
iced::Point::new(18.0, plus_top + 10.0),
);
assert_eq!(
target,
Some(DropTarget::Edge {
side: DockSide::Left,
index: 1
})
);
assert_eq!(app.dock.left.len(), 2);
assert_eq!(app.dock.left[1].panels, vec![PanelId::BlockPalette]);
// The new group is the one shown: the column holds only the palette.
assert_eq!(app.dock_shown_group(DockSide::Left), Some(1));
assert_eq!(app.dock_slot_spans(DockSide::Left), vec![(0, 0.0, 900.0)]);
}
#[test]
fn dock_icon_press_switches_the_shown_group() {
use crate::app::config::DockSide;
use crate::ui::dock::{DockGroup, DockMsg, PanelId};
let mut app = dock_app();
app.show_browser = true;
app.dock.left = vec![
DockGroup::stack(vec![PanelId::Properties, PanelId::BlockPalette]),
DockGroup::single(PanelId::Browser),
];
app.dock.right.clear();
// Group 0 shows both stacked pallets.
assert_eq!(app.dock_slot_spans(DockSide::Left).len(), 2);
let _ = app.on_dock(DockMsg::IconPress(PanelId::Browser));
let _ = app.on_dock(DockMsg::DragRelease);
assert_eq!(app.dock_shown_group(DockSide::Left), Some(1));
assert_eq!(app.dock_slot_spans(DockSide::Left), vec![(0, 0.0, 900.0)]);
// Each group keeps its own width.
app.dock.set_width(PanelId::Browser, 400.0);
assert_eq!(app.dock_column_width(DockSide::Left), 400.0);
let _ = app.on_dock(DockMsg::IconPress(PanelId::BlockPalette));
let _ = app.on_dock(DockMsg::DragRelease);
assert_eq!(app.dock_column_width(DockSide::Left), 260.0);
}
#[test]
fn dock_drop_over_the_viewport_floats_the_panel() {
use crate::ui::dock::{DropTarget, PanelId};
let mut app = dock_app();
let target = drag_panel(
&mut app,
PanelId::BlockPalette,
iced::Point::new(1500.0, 10.0),
iced::Point::new(800.0, 400.0),
);
assert!(matches!(target, Some(DropTarget::Float { .. })));
assert!(app.dock.right.is_empty());
let f = app.dock.float_rect(PanelId::BlockPalette).expect("floating");
assert!(f.x > 600.0 && f.x < 800.0 && f.y > 350.0 && f.y < 400.0);
// Dragging a floating panel keeps the grab point under the pointer.
let target = drag_panel(
&mut app,
PanelId::BlockPalette,
iced::Point::new(f.x + 20.0, f.y + 10.0),
iced::Point::new(f.x + 120.0, f.y + 60.0),
);
assert_eq!(target, Some(DropTarget::Float { x: f.x + 100.0, y: f.y + 50.0 }));
// Opening a floating panel must not re-dock it.
assert!(app.dock.is_placed(PanelId::BlockPalette));
}
#[test]
fn dock_dragging_an_icon_into_another_group_lands_between_its_icons() {
use crate::app::config::DockSide;
use crate::ui::dock::{DockGroup, DockMsg, DropTarget, PanelId, STRIP_CELL_H};
let mut app = dock_app();
app.show_browser = true;
app.dock.left = vec![
DockGroup::stack(vec![PanelId::Properties, PanelId::Browser]),
DockGroup::single(PanelId::BlockPalette),
];
app.dock.right.clear();
let (layout, _) = app.dock_strip_layout(DockSide::Left);
// Drag the Blocks icon (group 1) to between Properties and Browser.
let _ = app.on_dock(DockMsg::IconPress(PanelId::BlockPalette));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(20.0, layout[1].icons_top + 10.0)));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(
20.0,
layout[0].icons_top + STRIP_CELL_H + 2.0,
)));
assert_eq!(
drag_target(&app),
Some(DropTarget::Join {
side: DockSide::Left,
group: 0,
index: 1
})
);
let _ = app.on_dock(DockMsg::DragRelease);
assert_eq!(app.dock.left.len(), 1, "the emptied group goes away");
assert_eq!(
app.dock.left[0].panels,
vec![PanelId::Properties, PanelId::BlockPalette, PanelId::Browser]
);
}
#[test]
fn dock_grip_drag_moves_a_whole_group() {
use crate::app::config::DockSide;
use crate::ui::dock::{DockGroup, DockMsg, PanelId};
let mut app = dock_app();
app.show_browser = true;
app.dock.left = vec![
DockGroup::stack(vec![PanelId::Properties, PanelId::Browser]),
DockGroup::single(PanelId::BlockPalette),
];
app.dock.right.clear();
let (layout, plus_top) = app.dock_strip_layout(DockSide::Left);
let _ = app.on_dock(DockMsg::GroupGrab(DockSide::Left, 0));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(18.0, layout[0].top + 2.0)));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(18.0, plus_top + 10.0)));
let _ = app.on_dock(DockMsg::DragRelease);
assert_eq!(app.dock.left[0].panels, vec![PanelId::BlockPalette]);
assert_eq!(
app.dock.left[1].panels,
vec![PanelId::Properties, PanelId::Browser]
);
// A group dropped over the viewport does not float or split up.
let before = app.dock.clone();
let _ = app.on_dock(DockMsg::GroupGrab(DockSide::Left, 1));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(18.0, 300.0)));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(800.0, 400.0)));
assert_eq!(drag_target(&app), None);
let _ = app.on_dock(DockMsg::DragRelease);
assert_eq!(app.dock, before);
}
#[test]
fn dock_closed_pallets_take_no_room_and_empty_groups_hide() {
use crate::app::config::DockSide;
use crate::ui::dock::{DockGroup, PanelId};
let mut app = dock_app();
app.dock.left = vec![
DockGroup::stack(vec![PanelId::BlockPalette, PanelId::Properties]),
DockGroup::single(PanelId::Browser),
];
app.show_block_palette = false;
app.show_browser = false;
// Only Properties is open: one listed group, full height.
assert_eq!(app.dock_visible_groups(DockSide::Left), vec![0]);
assert_eq!(app.dock_slot_spans(DockSide::Left), vec![(1, 0.0, 900.0)]);
// A shown group with nothing open falls back to one that has.
app.dock.show_group(DockSide::Left, 1);
assert_eq!(app.dock_shown_group(DockSide::Left), Some(0));
}
#[test]
fn dock_splitter_drag_changes_pallet_heights() {
use crate::app::config::DockSide;
use crate::ui::dock::{DockGroup, DockMsg, PanelId};
let mut app = dock_app();
app.dock.left = vec![DockGroup::stack(vec![PanelId::BlockPalette, PanelId::Properties])];
app.dock.right.clear();
let _ = app.on_dock(DockMsg::SplitGrab(DockSide::Left, 0, 0, 1));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(100.0, 450.0)));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(100.0, 675.0)));
let _ = app.on_dock(DockMsg::DragRelease);
// 225 px of a 900 px edge holding 2 weight units = 0.5 units.
let w = &app.dock.left[0].weights;
assert!((w[0] - 1.5).abs() < 1e-4 && (w[1] - 0.5).abs() < 1e-4);
let _ = app.on_dock(DockMsg::SplitReset(DockSide::Left, 0));
assert_eq!(app.dock.left[0].weights, vec![1.0, 1.0]);
}
#[test]
fn dock_hover_reveals_groups_only_on_an_auto_hiding_edge() {
use crate::app::config::DockSide;
use crate::ui::dock::{DockGroup, DockMsg, PanelId};
let mut app = dock_app();
app.show_browser = true;
app.dock.left.push(DockGroup::single(PanelId::Browser));
// The pin on any docked pallet turns auto-hide on for its edge.
let _ = app.on_dock(DockMsg::AutoCollapseToggle(PanelId::Properties));
assert!(app.dock.edge_auto_hide(DockSide::Left));
assert!(!app.dock.edge_auto_hide(DockSide::Right));
assert!(!app.dock_edge_expanded(DockSide::Left));
// Hovering an icon flies its group out.
let _ = app.on_dock(DockMsg::Hover(PanelId::Browser));
assert!(app.dock_edge_expanded(DockSide::Left));
assert_eq!(app.dock_shown_group(DockSide::Left), Some(1));
let _ = app.on_dock(DockMsg::HoverExit);
assert!(!app.dock_edge_expanded(DockSide::Left));
// Without auto-hide, hovering does not switch groups (clicks do).
let _ = app.on_dock(DockMsg::AutoCollapseToggle(PanelId::Properties));
let _ = app.on_dock(DockMsg::Hover(PanelId::Properties));
assert_eq!(app.dock_shown_group(DockSide::Left), Some(1));
assert!(app.dock_edge_expanded(DockSide::Left));
}
#[test]
fn dock_float_resize_grows_the_panel() {
use crate::ui::dock::{DockMsg, FloatPanel, PanelId};
let mut app = dock_app();
app.dock.float(FloatPanel {
id: PanelId::BlockPalette,
x: 100.0,
y: 100.0,
w: 260.0,
h: 300.0,
});
let _ = app.on_dock(DockMsg::FloatResizeGrab(PanelId::BlockPalette, false));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(360.0, 400.0)));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(400.0, 450.0)));
let _ = app.on_dock(DockMsg::DragRelease);
let f = app.dock.float_rect(PanelId::BlockPalette).unwrap();
assert_eq!((f.w, f.h), (300.0, 350.0));
// The bottom-left grip grows the panel leftward, keeping its right edge.
let _ = app.on_dock(DockMsg::FloatResizeGrab(PanelId::BlockPalette, true));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(100.0, 450.0)));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(60.0, 450.0)));
let _ = app.on_dock(DockMsg::DragRelease);
let g = app.dock.float_rect(PanelId::BlockPalette).unwrap();
assert_eq!((g.x, g.w, g.h), (f.x - 40.0, 340.0, 350.0));
}
#[test]
fn dock_edge_menu_adds_and_hides_pallets() {
use crate::app::config::DockSide;
use crate::ui::dock::{DockMsg, PanelId};
let mut app = dock_app();
app.show_browser = false;
let _ = app.on_dock(DockMsg::EdgeMenu(Some(DockSide::Left)));
// Checking a closed pallet opens it as a new, shown group here.
let _ = app.on_dock(DockMsg::EdgeMenuToggle(DockSide::Left, PanelId::Browser));
assert!(app.show_browser);
assert_eq!(app.dock.left.len(), 2);
assert_eq!(app.dock.left[1].panels, vec![PanelId::Browser]);
assert_eq!(app.dock_shown_group(DockSide::Left), Some(1));
assert_eq!(app.dock_edge_menu, None, "the menu closes after adding");
let _ = app.on_dock(DockMsg::EdgeMenu(Some(DockSide::Left)));
// Unchecking hides it but keeps its place.
let _ = app.on_dock(DockMsg::EdgeMenuToggle(DockSide::Left, PanelId::Browser));
assert!(!app.show_browser);
assert_eq!(app.dock.location(PanelId::Browser), Some((DockSide::Left, 1)));
// Opening it again from the same edge reuses that place.
let _ = app.on_dock(DockMsg::EdgeMenuToggle(DockSide::Left, PanelId::Browser));
assert_eq!(app.dock.left.len(), 2);
// A pallet open on the other edge is not checked here: picking it
// moves it over instead of hiding it.
let _ = app.on_dock(DockMsg::EdgeMenuToggle(DockSide::Left, PanelId::BlockPalette));
assert!(app.show_block_palette);
assert!(app.dock.right.is_empty());
assert_eq!(app.dock.location(PanelId::BlockPalette), Some((DockSide::Left, 2)));
}
#[test]
fn dock_divider_resizes_only_its_group() {
use crate::app::config::DockSide;
use crate::ui::dock::{DockGroup, DockMsg, PanelId};
let mut app = dock_app();
app.dock.left = vec![
DockGroup::stack(vec![PanelId::Properties, PanelId::BlockPalette]),
DockGroup::single(PanelId::Count),
];
app.dock.right.clear();
let _ = app.on_dock(DockMsg::ResizeGrab(PanelId::Properties));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(300.0, 100.0)));
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(330.0, 100.0)));
let _ = app.on_dock(DockMsg::DragRelease);
// The group was 260 wide (its widest pallet); it is now 290, while
// the other group and the pallets' own widths are untouched.
assert_eq!(app.dock_group_width(DockSide::Left, 0), 290.0);
assert_eq!(app.dock_group_width(DockSide::Left, 1), 280.0);
assert_eq!(app.dock.settings(PanelId::Properties).width, 250.0);
}
#[test]
fn layers_command_opens_a_floating_pallet_and_toggles_it_closed() {
use crate::ui::dock::PanelId;
let mut app = dock_app();
let _ = app.update(Message::ToggleLayers);
assert!(app.show_layers);
assert!(app.active_modal.is_none(), "no modal backdrop");
let f = app.dock.float_rect(PanelId::Layers).expect("floats on first use");
assert!(f.x > 0.0 && f.y >= 0.0);
let _ = app.update(Message::ToggleLayers);
assert!(!app.show_layers);
// Its place is kept for next time.
assert!(app.dock.float_rect(PanelId::Layers).is_some());
}
/// A headless renderer for driving the real view.
fn headless_renderer() -> iced::Renderer {
iced_test::futures::futures::executor::block_on(
<iced::Renderer as iced_test::core::renderer::Headless>::new(
iced_test::core::renderer::Settings::default(),
None,
),
)
.expect("headless renderer")
}
/// Size the real view is laid out at.
const VIEW_SIZE: iced::Size = iced::Size::new(1600.0, 900.0);
/// Centre of the first text `label` in the real view.
fn find_in_view(app: &OpenCADStudio, label: &str) -> iced::Point {
use iced_test::core::widget;
use iced_test::runtime::user_interface::{Cache, UserInterface};
use iced_test::Selector;
let mut renderer = headless_renderer();
let mut ui = UserInterface::build(app.view_main(), VIEW_SIZE, Cache::default(), &mut renderer);
let mut find = Selector::find(label);
ui.operate(&renderer, &mut widget::operation::black_box(&mut find));
match widget::Operation::finish(&find) {
widget::operation::Outcome::Some(Some(target)) => target
.visible_bounds()
.expect("visible")
.center(),
_ => panic!("{label} not found"),
}
}
/// Drive the real view through iced's `UserInterface` with the pointer at
/// `at`: feed `events` one at a time, and between events hand the
/// published messages to the app and rebuild the view while keeping the
/// widget-tree cache, as the runtime does.
fn drive_view(app: &mut OpenCADStudio, at: iced::Point, events: &[iced::Event]) {
use iced_test::core::{mouse, shell, window};
use iced_test::runtime::user_interface::{Cache, UserInterface};
let mut renderer = headless_renderer();
let mut cache = Cache::default();
let cursor = mouse::Cursor::Available(at);
for event in events {
let mut messages = Vec::new();
{
let mut ui = UserInterface::build(app.view_main(), VIEW_SIZE, cache, &mut renderer);
let _ = ui.update(
&window::Headless,
&shell::Waker::noop(),
std::slice::from_ref(event),
cursor,
&mut renderer,
&mut messages,
);
cache = ui.into_cache();
}
for message in messages {
let _ = app.update(message);
}
}
}
#[test]
fn docked_title_bar_double_click_floats_the_pallet_in_the_real_view() {
use crate::ui::dock::PanelId;
let mut app = dock_app();
let press = iced::Event::Mouse(iced::mouse::Event::ButtonPressed(iced::mouse::Button::Left));
let release =
iced::Event::Mouse(iced::mouse::Event::ButtonReleased(iced::mouse::Button::Left));
// The block palette's title text sits in its docked title bar.
let title = find_in_view(&app, "Blocks");
drive_view(&mut app, title, &[press.clone(), release.clone(), press, release]);
assert!(
app.dock.float_rect(PanelId::BlockPalette).is_some(),
"a double-click on a docked title bar floats the pallet"
);
}
#[test]
fn hovering_a_strip_icon_reveals_an_auto_hiding_edge_in_the_real_view() {
use crate::app::config::DockSide;
use crate::ui::dock::{DockMsg, PanelId, STRIP_CELL_H, STRIP_PAD};
let mut app = dock_app();
// Mirror the layout onto the left so the block palette's title marks
// where the workspace starts, then auto-hide the right edge.
let title = find_in_view(&app, "Blocks");
let _ = app.on_dock(DockMsg::AutoCollapseToggle(PanelId::BlockPalette));
assert!(!app.dock_edge_expanded(DockSide::Right));
// The title bar text sits ~13 px below the workspace top; the strip's
// first icon is centred STRIP_PAD + half a cell below it.
let workspace_top = title.y - 13.0;
let icon = iced::Point::new(
VIEW_SIZE.width - crate::ui::dock::DOCK_STRIP_W / 2.0,
workspace_top + STRIP_PAD + STRIP_CELL_H / 2.0,
);
drive_view(
&mut app,
icon,
&[iced::Event::Mouse(iced::mouse::Event::CursorMoved { position: icon })],
);
assert!(
app.dock_edge_expanded(DockSide::Right),
"hovering the icon reveals the auto-hiding edge"
);
}
#[test]
fn dock_double_clicks_float_and_dock_panels() {
use crate::app::config::DockSide;
use crate::ui::dock::{DockMsg, PanelId};
let mut app = dock_app();
let _ = app.on_dock(DockMsg::FloatOut(PanelId::Properties));
assert!(app.dock.left.is_empty());
let f = app.dock.float_rect(PanelId::Properties).expect("floating");
assert!(f.x > 0.0, "floats clear of the left edge");
let _ = app.on_dock(DockMsg::DockTo(PanelId::Properties, DockSide::Right));
assert!(app.dock.floating.is_empty());
assert_eq!(app.dock.location(PanelId::Properties), Some((DockSide::Right, 1)));
}
#[test]
fn blockpalette_width_reset() {
use crate::app::config::DockSide;
let mut app = dock_app();
let id = crate::ui::dock::PanelId::BlockPalette;
app.dock.set_group_width(DockSide::Right, 0, 500.0);
let _ = app.on_dock(crate::ui::dock::DockMsg::WidthReset(id));
assert_eq!(app.dock.group_width(DockSide::Right, 0), 260.0);
}