From e2b14389771c50d856fe6d22901b30bee32fa81f Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 4 May 2023 09:40:40 +0200 Subject: [PATCH] Improve dragging around tab labels --- crates/egui_extras/src/dock/branch/linear.rs | 58 +++++++++++------ crates/egui_extras/src/dock/branch/tabs.rs | 65 ++++++++++---------- 2 files changed, 72 insertions(+), 51 deletions(-) diff --git a/crates/egui_extras/src/dock/branch/linear.rs b/crates/egui_extras/src/dock/branch/linear.rs index a49717a2b..8931b1414 100644 --- a/crates/egui_extras/src/dock/branch/linear.rs +++ b/crates/egui_extras/src/dock/branch/linear.rs @@ -122,7 +122,7 @@ impl Linear { } } - drop_zones(ui.ctx(), nodes, &self.children, self.dir, |rect, i| { + linear_drop_zones(ui.ctx(), nodes, &self.children, self.dir, |rect, i| { drop_context.suggest_rect( InsertionPoint::new(parent_id, LayoutInsertion::Horizontal(i)), rect, @@ -185,7 +185,7 @@ impl Linear { } } - drop_zones(ui.ctx(), nodes, &self.children, self.dir, |rect, i| { + linear_drop_zones(ui.ctx(), nodes, &self.children, self.dir, |rect, i| { drop_context.suggest_rect( InsertionPoint::new(parent_id, LayoutInsertion::Vertical(i)), rect, @@ -315,25 +315,18 @@ fn shrink_shares( total_shares_lost } -fn drop_zones( +fn linear_drop_zones( egui_ctx: &egui::Context, nodes: &Nodes, children: &[NodeId], dir: LinearDir, - mut add_drop_drect: impl FnMut(Rect, usize), + add_drop_drect: impl FnMut(Rect, usize), ) { let preview_thickness = 12.0; + let dragged_index = children + .iter() + .position(|&child| is_being_dragged(egui_ctx, child)); - let before_rect = |rect: Rect| match dir { - LinearDir::Horizontal => Rect::from_min_max( - rect.left_top(), - rect.left_bottom() + vec2(preview_thickness, 0.0), - ), - LinearDir::Vertical => Rect::from_min_max( - rect.left_top(), - rect.right_top() + vec2(0.0, preview_thickness), - ), - }; let afer_rect = |rect: Rect| match dir { LinearDir::Horizontal => Rect::from_min_max( rect.right_top() - vec2(preview_thickness, 0.0), @@ -344,6 +337,37 @@ fn drop_zones( rect.right_bottom(), ), }; + + drop_zones( + preview_thickness, + children, + dragged_index, + dir, + |node_id| nodes.rect(node_id), + add_drop_drect, + afer_rect, + ); +} + +pub fn drop_zones( + preview_thickness: f32, + children: &[NodeId], + dragged_index: Option, + dir: LinearDir, + get_rect: impl Fn(NodeId) -> Rect, + mut add_drop_drect: impl FnMut(Rect, usize), + afer_rect: impl Fn(Rect) -> Rect, +) { + let before_rect = |rect: Rect| match dir { + LinearDir::Horizontal => Rect::from_min_max( + rect.left_top(), + rect.left_bottom() + vec2(preview_thickness, 0.0), + ), + LinearDir::Vertical => Rect::from_min_max( + rect.left_top(), + rect.right_top() + vec2(0.0, preview_thickness), + ), + }; let between_rects = |a: Rect, b: Rect| match dir { LinearDir::Horizontal => Rect::from_center_size( a.right_center().lerp(b.left_center(), 0.5), @@ -355,15 +379,11 @@ fn drop_zones( ), }; - let dragged_index = children - .iter() - .position(|&child| is_being_dragged(egui_ctx, child)); - let mut prev_rect: Option = None; let mut insertion_index = 0; // skips over drag-source, if any, beacuse it will be removed before its re-inserted for (i, &child) in children.iter().enumerate() { - let rect = nodes.rect(child); + let rect = get_rect(child); if Some(i) == dragged_index { // Suggest hole as a drop-target: diff --git a/crates/egui_extras/src/dock/branch/tabs.rs b/crates/egui_extras/src/dock/branch/tabs.rs index 63cd276d3..43445b61a 100644 --- a/crates/egui_extras/src/dock/branch/tabs.rs +++ b/crates/egui_extras/src/dock/branch/tabs.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use egui::{vec2, Rect}; use crate::dock::{ @@ -54,8 +56,8 @@ impl Tabs { // Show tab bar: tab_bar_ui.horizontal(|ui| { - let mut prev_tab_rect: Option = None; - let mut insertion_index = 0; // skips over drag-source, if any, beacuse it will be removed then re-inserted + let mut button_rects = HashMap::new(); + let mut dragged_index = None; for (i, &child_id) in self.children.iter().enumerate() { let is_being_dragged = is_being_dragged(ui.ctx(), child_id); @@ -76,38 +78,37 @@ impl Tabs { } } - let rect = response.rect; - - { - // suggest dropping before this tab: - let before_point = if let Some(prev_tab_rect) = prev_tab_rect { - // between - prev_tab_rect.right_center().lerp(rect.left_center(), 0.5) - } else { - // before first - rect.left_center() - }; - - drop_context.suggest_rect( - InsertionPoint::new(node_id, LayoutInsertion::Tabs(insertion_index)), - Rect::from_center_size(before_point, vec2(4.0, rect.height())), - ); - } - - if i + 1 == self.children.len() { - // suggest dropping after last tab: - drop_context.suggest_rect( - InsertionPoint::new(node_id, LayoutInsertion::Tabs(insertion_index + 1)), - Rect::from_center_size(rect.right_center(), vec2(4.0, rect.height())), - ); - } - - prev_tab_rect = Some(rect); - - if !is_being_dragged { - insertion_index += 1; + button_rects.insert(child_id, response.rect); + if is_being_dragged { + dragged_index = Some(i); } } + + let preview_thickness = 6.0; + let afer_rect = |rect: Rect| { + let dragged_size = if let Some(dragged_index) = dragged_index { + // We actually know the size of this thing + button_rects[&self.children[dragged_index]].size() + } else { + rect.size() // guess that the size is the same as the last button + }; + Rect::from_min_size( + rect.right_top() + vec2(ui.spacing().item_spacing.x, 0.0), + dragged_size, + ) + }; + super::linear::drop_zones( + preview_thickness, + &self.children, + dragged_index, + super::LinearDir::Horizontal, + |node_id| button_rects[&node_id], + |rect, i| { + drop_context + .suggest_rect(InsertionPoint::new(node_id, LayoutInsertion::Tabs(i)), rect); + }, + afer_rect, + ); }); // When dragged, don't show it (it is "being held")