diff --git a/crates/ui-egui/src/floating.rs b/crates/ui-egui/src/floating.rs index 2c7247617..2022381c5 100644 --- a/crates/ui-egui/src/floating.rs +++ b/crates/ui-egui/src/floating.rs @@ -138,6 +138,16 @@ pub fn group_of(ui: &UiState, id: &str) -> Option { ui.floating_panels.iter().position(|g| g.panels.iter().any(|p| p == id)) } +/// Reorder tabs in a floating group by index, keeping the active tab on the same panel. +pub(crate) fn reorder_group_tab_indices(g: &mut FloatingPanels, from: usize, to: usize) { + if from != to && from < g.panels.len() && to < g.panels.len() { + let active = g.panels.get(g.active).cloned(); + let moved = g.panels.remove(from); + g.panels.insert(to, moved); + g.active = active.and_then(|id| g.panels.iter().position(|p| *p == id)).unwrap_or(0); + } +} + /// The floating groups drawn with group `gi`, top to bottom: the groups of its set, or it alone. pub fn set_of(ui: &UiState, gi: usize) -> Vec { match ui.floating_panels.get(gi).and_then(|g| g.column) { @@ -679,15 +689,18 @@ fn apply(app: &mut VectorcraftApp, ctx: &egui::Context, action: Option) Some(Action::Detach(gi, left)) => detach(app, ctx, gi, left), Some(Action::Tear(id, left)) => tear(app, ctx, &[id], id, strip_grab(ctx, left)), Some(Action::Reorder(gi, from, to)) => { - if let Some(g) = app.ui.floating_panels.get_mut(gi) - && from != to - && from < g.panels.len() - && to < g.panels.len() + let ids = app.ui.floating_panels.get(gi).and_then(|g| { + let panel = g.panels.get(from)?; + let before = g.panels.get(to)?; + Some((panel.clone(), before.clone())) + }); + if let Some((panel, before)) = ids + && let Err(error) = app.run( + "window.panel.move", + json!({ "panel": panel, "anchor": before, "before": before }), + ) { - let active = g.panels.get(g.active).cloned(); - let moved = g.panels.remove(from); - g.panels.insert(to, moved); - g.active = active.and_then(|id| g.panels.iter().position(|p| *p == id)).unwrap_or(0); + app.ui.status = error; } } Some(Action::Undock(gi, grab)) => { diff --git a/crates/ui-egui/src/panel_docking.rs b/crates/ui-egui/src/panel_docking.rs index 58fe6d11f..38dc95b74 100644 --- a/crates/ui-egui/src/panel_docking.rs +++ b/crates/ui-egui/src/panel_docking.rs @@ -142,38 +142,55 @@ fn number(params: &Value, key: &str, default: f32) -> Result { } } +const LEGACY_REORDER_BAD_DEST: &str = "move requires an existing destination panel"; + +enum LegacyStripReorder { + DockStrip, + FloatingGroup(usize), +} + +fn legacy_strip_reorder_target( + app: &VectorcraftApp, + before: &str, + dock_strip: bool, + panel_group: Option, +) -> Result { + if dock_strip { + return Ok(LegacyStripReorder::DockStrip); + } + let gi = panel_group.ok_or(LEGACY_REORDER_BAD_DEST)?; + if crate::floating::group_of(&app.ui, before) != Some(gi) { + return Err(LEGACY_REORDER_BAD_DEST.into()); + } + Ok(LegacyStripReorder::FloatingGroup(gi)) +} + /// Legacy workspaces (no saved `docking` layout): reorder tabs in the dock strip or a floating /// group without promoting to a shared layout. -fn legacy_same_strip_reorder(app: &mut VectorcraftApp, panel: &str, before: &str) -> Result { +fn legacy_same_strip_reorder( + app: &mut VectorcraftApp, + panel: &str, + before: &str, + target: LegacyStripReorder, +) -> Result { use crate::state::DockTab; - const BAD_DEST: &str = "move requires an existing destination panel"; - if crate::state::DockTab::from_id(panel).is_some() - && DockTab::from_id(before).is_some() - && crate::floating::group_of(&app.ui, panel).is_none() - && crate::floating::group_of(&app.ui, before).is_none() - { - let mut order: Vec = crate::floating::docked_tabs(&app.ui).collect(); - let from = order.iter().position(|t| t.info().0 == panel).ok_or(BAD_DEST)?; - let to = order.iter().position(|t| t.info().0 == before).ok_or(BAD_DEST)?; - if from != to { - let item = order.remove(from); - order.insert(to, item); - crate::floating::set_dock_tab_order(&mut app.ui, order); + match target { + LegacyStripReorder::DockStrip => { + let mut order: Vec = crate::floating::docked_tabs(&app.ui).collect(); + let from = order.iter().position(|t| t.info().0 == panel).ok_or(LEGACY_REORDER_BAD_DEST)?; + let to = order.iter().position(|t| t.info().0 == before).ok_or(LEGACY_REORDER_BAD_DEST)?; + if from != to { + let item = order.remove(from); + order.insert(to, item); + crate::floating::set_dock_tab_order(&mut app.ui, order); + } + } + LegacyStripReorder::FloatingGroup(gi) => { + let g = app.ui.floating_panels.get_mut(gi).ok_or("invalid restored panel group")?; + let from = g.panels.iter().position(|id| id == panel).ok_or(LEGACY_REORDER_BAD_DEST)?; + let to = g.panels.iter().position(|id| id == before).ok_or(LEGACY_REORDER_BAD_DEST)?; + crate::floating::reorder_group_tab_indices(g, from, to); } - return Ok(json!({ "panel": panel, "before": before })); - } - let gi = crate::floating::group_of(&app.ui, panel).ok_or(BAD_DEST)?; - if crate::floating::group_of(&app.ui, before) != Some(gi) { - return Err(BAD_DEST.into()); - } - let g = app.ui.floating_panels.get_mut(gi).ok_or("invalid restored panel group")?; - let from = g.panels.iter().position(|id| id == panel).ok_or(BAD_DEST)?; - let to = g.panels.iter().position(|id| id == before).ok_or(BAD_DEST)?; - if from != to { - let active = g.panels.get(g.active).cloned(); - let moved = g.panels.remove(from); - g.panels.insert(to, moved); - g.active = active.and_then(|id| g.panels.iter().position(|p| *p == id)).unwrap_or(0); } Ok(json!({ "panel": panel, "before": before })) } @@ -332,18 +349,19 @@ fn change(app: &mut VectorcraftApp, operation: &str, params: &Value) -> Result