forked from 1b-agent/opencadstudio-upstream
Dock: option B icon strip with an edge-band group grip
The icon strip follows the agreed "option B" concept: - Flat 40 px strip; groups are rows of their pallets' icons separated by hairline dividers, with no grip lines. The + button is a small round button under the last group. - The shown group is marked by a blue bar on its window-side edge and brighter icons. That 8 px edge band is the group grip: hovering it widens and lights the bar, tints the row, shows a grab cursor and a "Drag to move group" tooltip; dragging it moves the whole group. - Icons still drag single pallets: within their group or into another group (between its icons), or onto a divider to start a new group. Hovering an icon gives it a soft background and shows its name. - Drop feedback: a short accent line with end caps between icons means "join this group here" (the target group's bar lights up); a full-width line on a divider with a "+ New group" badge means "new group here". The dragged icon (or whole group) fades in place and a ghost follows the pointer. - The zone around a divider (a few px into the icons on either side) starts a new group; the lower half of a group's last icon still joins that group at the end. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
This commit is contained in:
4 files changed
+438
-89
No files matched your search
@@ -652,6 +652,14 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||
self.save_config();
|
||||
task
|
||||
}
|
||||
DockMsg::GripHover(grip) => {
|
||||
self.dock_grip_hover = grip;
|
||||
iced::Task::none()
|
||||
}
|
||||
DockMsg::IconHover(id) => {
|
||||
self.dock_icon_hover = id;
|
||||
iced::Task::none()
|
||||
}
|
||||
DockMsg::GroupGrab(side, gi) => {
|
||||
if let Some(&id) = self.dock_group_visible(side, gi).first() {
|
||||
self.dock_begin_drag(id);
|
||||
@@ -2232,10 +2240,15 @@ mod tests {
|
||||
app.dock.join_group(PanelId::BlockPalette, DockSide::Left, 0, 1);
|
||||
let (layout, _) = app.dock_strip_layout(DockSide::Left);
|
||||
let top = layout[0].icons_top;
|
||||
// Drag the Properties icon below the Blocks icon.
|
||||
// Drag the Properties icon onto the lower half of the Blocks icon:
|
||||
// below it, still in the group (the divider under it would start a
|
||||
// new group).
|
||||
let _ = app.on_dock(DockMsg::IconPress(PanelId::Properties));
|
||||
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(18.0, top + 10.0)));
|
||||
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(18.0, top + 2.0 * STRIP_CELL_H)));
|
||||
let _ = app.on_dock(DockMsg::DragMove(iced::Point::new(
|
||||
18.0,
|
||||
top + 2.0 * STRIP_CELL_H - 10.0,
|
||||
)));
|
||||
assert_eq!(
|
||||
app.dock_drag_target,
|
||||
Some(DropTarget::Join {
|
||||
@@ -2251,6 +2264,41 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[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!(
|
||||
app.dock_drag_target,
|
||||
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;
|
||||
@@ -2264,7 +2312,7 @@ mod tests {
|
||||
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].grip_top + 2.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]);
|
||||
|
||||
Reference in new issue
Block a user