diff --git a/CHANGELOG.md b/CHANGELOG.md index 02ec77ef..d8f1b466 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # egui_dock changelog +## 0.12.0 - 2024-04-03 + +### Breaking changes + +- Upgraded to egui 0.27. + +### Changed + +- All `Style` structs are now serializable with `serde`. ([#227](https://github.com/Adanos020/egui_dock/pull/227)) + +### Fixed + +- Dragging tabs around should no longer cause the `DockArea` to resize a tiny bit on every frame. +- Dragged tabs should now always follow the mouse exactly. +- Button overlay now correctly renders split buttons when allowed splits are either `LeftRightOnly` or `TopBottomOnly`. + ## 0.11.4 - 2024-03-11 ### Fixed @@ -29,17 +45,17 @@ From [#225](https://github.com/Adanos020/egui_dock/pull/225): ### Fixed - Bug where tabs couldn't be re-docked onto the main surface if it's - empty ([#222](https://github.com/Adanos020/egui_dock/pull/222)) + empty. ([#222](https://github.com/Adanos020/egui_dock/pull/222)) ## 0.11.0 - 2024-02-06 ### Added -- `filter_map_tabs`, `filter_tabs`, and `retain_tabs` ([#217](https://github.com/Adanos020/egui_dock/pull/217)) +- `filter_map_tabs`, `filter_tabs`, and `retain_tabs`. ([#217](https://github.com/Adanos020/egui_dock/pull/217)) ### Breaking changes -- Upgraded to egui 0.26 +- Upgraded to egui 0.26. ## 0.10.0 - 2024-01-09 diff --git a/Cargo.toml b/Cargo.toml index 679b9984..3fe8bec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "egui_dock" description = "Docking system for egui - an immediate-mode GUI library for Rust" authors = ["lain-dono", "Adam Gąsior (Adanos020)"] -version = "0.11.4" +version = "0.12.0" edition = "2021" rust-version = "1.72" license = "MIT" @@ -18,14 +18,14 @@ default = [] serde = ["dep:serde", "egui/serde"] [dependencies] -egui = { version = "0.26", default-features = false } +egui = { version = "0.27", default-features = false } serde = { version = "1", optional = true, features = ["derive"] } duplicate = "1.0" paste = "1.0" [dev-dependencies] -eframe = { version = "0.26", default-features = false, features = [ +eframe = { version = "0.27", default-features = false, features = [ "default_fonts", "glow", ] } diff --git a/README.md b/README.md index de46c554..6f89c80d 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Add `egui` and `egui_dock` to your project's dependencies. ```toml [dependencies] egui = "0.26" -egui_dock = "0.11" +egui_dock = "0.12" ``` Then proceed by setting up `egui`, following its [quick start guide](https://github.com/emilk/egui#quick-start). diff --git a/examples/hello.rs b/examples/hello.rs index 8b1c6b93..9097fa16 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; -use eframe::{egui, NativeOptions}; +use eframe::NativeOptions; use egui::{ color_picker::{color_edit_button_srgba, Alpha}, vec2, CentralPanel, ComboBox, Frame, Rounding, Slider, TopBottomPanel, Ui, ViewportBuilder, @@ -197,6 +197,10 @@ impl MyContext { Alpha::OnlyBlend, ); ui.end_row(); + + ui.label("Rounding:"); + rounding_ui(ui, &mut style.main_surface_border_rounding); + ui.end_row(); }); }); diff --git a/src/style.rs b/src/style.rs index 93042ad3..a307b769 100644 --- a/src/style.rs +++ b/src/style.rs @@ -46,6 +46,7 @@ pub enum TabAddAlign { /// # /// ``` #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[allow(missing_docs)] pub struct Style { /// Sets padding to indent from the edges of the window. By `Default` it's `None`. @@ -63,6 +64,7 @@ pub struct Style { /// Specifies the look and feel of buttons. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct ButtonsStyle { /// Color of the close tab button. pub close_tab_color: Color32, @@ -91,6 +93,7 @@ pub struct ButtonsStyle { /// Specifies the look and feel of node separators. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct SeparatorStyle { /// Width of the rectangle separator between nodes. By `Default` it's `1.0`. pub width: f32, @@ -115,6 +118,7 @@ pub struct SeparatorStyle { /// Specifies the look and feel of tab bars. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct TabBarStyle { /// Background color of tab bar. By `Default` it's [`Color32::WHITE`]. pub bg_fill: Color32, @@ -138,6 +142,7 @@ pub struct TabBarStyle { /// Specifies the look and feel of an individual tab. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct TabStyle { /// Style of the tab when it is active. pub active: TabInteractionStyle, @@ -177,6 +182,7 @@ pub struct TabStyle { /// Specifies the look and feel of individual tabs while they are being interacted with. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct TabInteractionStyle { /// Color of the outline around tabs. By `Default` it's [`Color32::BLACK`]. pub outline_color: Color32, @@ -193,6 +199,7 @@ pub struct TabInteractionStyle { /// Specifies the look and feel of the tab body. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct TabBodyStyle { /// Inner margin of tab body. By `Default` it's `Margin::same(4.0)`. pub inner_margin: Margin, @@ -209,6 +216,7 @@ pub struct TabBodyStyle { /// Specifies the look and feel of the tab drop overlay. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct OverlayStyle { /// Sets selection color for the placing area of the tab where this tab targeted on it. /// By `Default` it's `(0, 191, 255)` (light blue) with `0.5` capacity. @@ -246,6 +254,7 @@ pub struct OverlayStyle { /// Specifies the feel of the tab drop overlay, i.e anything non visual about the overlay. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct OverlayFeel { /// range is `0.0..=1.0`. pub window_drop_coverage: f32, @@ -264,7 +273,8 @@ pub struct OverlayFeel { } /// Specifies the type of overlay used. -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum OverlayType { /// Shows highlighted areas predicting where a dropped tab would land were it to be dropped this frame. /// @@ -279,6 +289,7 @@ pub enum OverlayType { /// Highlighting on the currently hovered leaf. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct LeafHighlighting { /// Fill color. pub color: Color32, diff --git a/src/widgets/dock_area/drag_and_drop.rs b/src/widgets/dock_area/drag_and_drop.rs index 49c358ef..b42ed9ea 100644 --- a/src/widgets/dock_area/drag_and_drop.rs +++ b/src/widgets/dock_area/drag_and_drop.rs @@ -4,7 +4,8 @@ use crate::{ AllowedSplits, NodeIndex, Split, Style, SurfaceIndex, TabDestination, TabIndex, TabInsert, }; use egui::{ - emath::inverse_lerp, vec2, Context, Id, LayerId, NumExt, Order, Pos2, Rect, Stroke, Ui, Vec2, + emath::inverse_lerp, vec2, Context, Id, LayerId, NumExt, Order, Painter, Pos2, Rect, Stroke, + Ui, Vec2, }; #[derive(Debug, Clone)] @@ -67,8 +68,15 @@ impl TreeComponent { } } +fn make_overlay_painter(ui: &Ui) -> Painter { + let id = Id::new("overlay"); + let layer_id = LayerId::new(Order::Foreground, id); + ui.ctx().layer_painter(layer_id) +} + fn draw_highlight_rect(rect: Rect, ui: &Ui, style: &Style) { - ui.painter().rect( + let painter = make_overlay_painter(ui); + painter.rect( rect.expand(style.overlay.hovered_leaf_highlight.expansion), style.overlay.hovered_leaf_highlight.rounding, style.overlay.hovered_leaf_highlight.color, @@ -87,7 +95,7 @@ fn button_ui( ) -> bool { let visuals = &style.overlay; let button_stroke = Stroke::new(1.0, visuals.button_color); - let painter = ui.painter(); + let painter = make_overlay_painter(ui); painter.rect_stroke(rect, 0.0, visuals.button_border_stroke); let rect = rect.shrink(rect.width() * 0.1); painter.rect_stroke(rect, 0.0, button_stroke); @@ -175,15 +183,9 @@ impl DragDropState { let shortest_side = ((rect.width() - total_button_spacing) / 3.0) .min((rect.height() - total_button_spacing) / 3.0) .min(style.overlay.max_button_size); - let mut offset_vector = vec2(0.0, shortest_side + style.overlay.button_spacing); - - let mut destination: Option = match windows_allowed { - true => Some(TabDestination::Window(Rect::from_min_size( - pointer, - self.drag.rect.size(), - ))), - false => None, - }; + + let mut destination: Option = windows_allowed + .then(|| TabDestination::Window(Rect::from_min_size(pointer, self.drag.rect.size()))); let center = rect.center(); let rect = Rect::from_center_size(center, Vec2::splat(shortest_side)); @@ -202,10 +204,17 @@ impl DragDropState { for split in [Split::Below, Split::Right, Split::Above, Split::Left] { match allowed_splits { - AllowedSplits::TopBottomOnly if split.is_top_bottom() => continue, - AllowedSplits::LeftRightOnly if split.is_left_right() => continue, + AllowedSplits::TopBottomOnly if !split.is_top_bottom() => continue, + AllowedSplits::LeftRightOnly if !split.is_left_right() => continue, AllowedSplits::None => continue, _ => { + let offset_value = shortest_side + style.overlay.button_spacing; + let offset_vector = match split { + Split::Above => vec2(0.0, -offset_value), + Split::Below => vec2(0.0, offset_value), + Split::Left => vec2(-offset_value, 0.0), + Split::Right => vec2(offset_value, 0.0), + }; if button_ui( Rect::from_center_size(center + offset_vector, Vec2::splat(shortest_side)), ui, @@ -219,7 +228,6 @@ impl DragDropState { Some(TabDestination::Node(surface, node, TabInsert::Split(split))) } } - offset_vector = offset_vector.rot90(); } } } @@ -384,7 +392,7 @@ impl DragDropState { pub(super) fn is_locked(&self, style: &Style, ctx: &Context) -> bool { match self.locked.as_ref() { Some(lock_time) => { - let elapsed = (ctx.input(|i| i.time) - lock_time) as f32; + let elapsed = ctx.input(|i| (i.time - lock_time) as f32); ctx.request_repaint(); elapsed < style.overlay.feel.max_preference_time } @@ -413,18 +421,14 @@ const fn lerp_vec(split: Split, alpha: f32) -> Vec2 { // Draws a filled rect describing where a tab will be dropped. #[inline(always)] fn draw_drop_rect(rect: Rect, ui: &Ui, style: &Style) { - let id = Id::new("overlay"); - let layer_id = LayerId::new(Order::Foreground, id); - let painter = ui.ctx().layer_painter(layer_id); + let painter = make_overlay_painter(ui); painter.rect_filled(rect, 0.0, style.overlay.selection_color); } // Draws a stroked rect describing where a tab will be dropped. #[inline(always)] fn draw_window_rect(rect: Rect, ui: &Ui, style: &Style) { - let id = Id::new("overlay"); - let layer_id = LayerId::new(Order::Foreground, id); - let painter = ui.ctx().layer_painter(layer_id); + let painter = make_overlay_painter(ui); painter.rect_stroke( rect, 0.0, diff --git a/src/widgets/dock_area/mod.rs b/src/widgets/dock_area/mod.rs index 660ae8b4..15de2a0f 100644 --- a/src/widgets/dock_area/mod.rs +++ b/src/widgets/dock_area/mod.rs @@ -11,7 +11,6 @@ mod tab_removal; use crate::{dock_state::DockState, NodeIndex, Style, SurfaceIndex, TabIndex}; pub use allowed_splits::AllowedSplits; -use drag_and_drop::{DragData, HoverData}; use tab_removal::TabRemoval; use egui::{emath::*, Id}; @@ -32,8 +31,6 @@ pub struct DockArea<'tree, Tab> { allowed_splits: AllowedSplits, window_bounds: Option, - drag_data: Option, - hover_data: Option, to_remove: Vec, to_detach: Vec<(SurfaceIndex, NodeIndex, TabIndex)>, new_focused: Option<(SurfaceIndex, NodeIndex)>, @@ -56,8 +53,6 @@ impl<'tree, Tab> DockArea<'tree, Tab> { draggable_tabs: true, show_tab_name_on_hover: false, allowed_splits: AllowedSplits::default(), - drag_data: None, - hover_data: None, to_remove: Vec::new(), to_detach: Vec::new(), new_focused: None, diff --git a/src/widgets/dock_area/show/leaf.rs b/src/widgets/dock_area/show/leaf.rs index e0da77c8..8a242bfe 100644 --- a/src/widgets/dock_area/show/leaf.rs +++ b/src/widgets/dock_area/show/leaf.rs @@ -1,5 +1,6 @@ use std::ops::RangeInclusive; +use egui::emath::TSTransform; use egui::{ epaint::TextShape, lerp, pos2, vec2, Align, Align2, Button, CursorIcon, Frame, Id, Key, LayerId, Layout, NumExt, Order, Rect, Response, Rounding, ScrollArea, Sense, Stroke, TextStyle, @@ -209,7 +210,7 @@ impl<'tree, Tab> DockArea<'tree, Tab> { .with((node_index, "node")) .with((tab_index, "tab")); let tab_index = TabIndex(tab_index); - let is_being_dragged = tabs_ui.memory(|mem| mem.is_being_dragged(id)) + let is_being_dragged = tabs_ui.ctx().is_being_dragged(id) && tabs_ui.input(|i| i.pointer.is_decidedly_dragging()) && self.draggable_tabs; @@ -261,11 +262,20 @@ impl<'tree, Tab> DockArea<'tree, Tab> { let start = *state.drag_start.get_or_insert(pointer_pos); let delta = pointer_pos - start; if delta.x.abs() > 30.0 || delta.y.abs() > 6.0 { - tabs_ui.ctx().translate_layer(layer_id, delta); - - self.drag_data = Some(DragData { - src: TreeComponent::Tab(surface_index, node_index, tab_index), - rect: self.dock_state[surface_index][node_index].rect().unwrap(), + tabs_ui + .ctx() + .transform_layer_shapes(layer_id, TSTransform::new(delta, 1.0)); + + tabs_ui.memory_mut(|mem| { + mem.data.insert_temp( + self.id.with("drag_data"), + Some(DragData { + src: TreeComponent::Tab(surface_index, node_index, tab_index), + rect: self.dock_state[surface_index][node_index] + .rect() + .unwrap(), + }), + ); }); } } @@ -285,17 +295,7 @@ impl<'tree, Tab> DockArea<'tree, Tab> { fade, ); let title_id = response.id; - - let (close_hovered, close_clicked) = close_response - .map(|res| (res.hovered(), res.clicked())) - .unwrap_or_default(); - - let sense = if close_hovered { - Sense::click() - } else { - Sense::click_and_drag() - }; - + let close_clicked = close_response.is_some_and(|res| res.clicked()); let is_lonely_tab = self.dock_state[surface_index].num_tabs() == 1; if self.show_tab_name_on_hover { @@ -321,7 +321,6 @@ impl<'tree, Tab> DockArea<'tree, Tab> { }; let tab = &mut tabs[tab_index.0]; - let response = tabs_ui.interact(response.rect, id, Sense::click()); response.context_menu(|ui| { tab_viewer.context_menu(ui, tab, surface_index, node_index); if (surface_index.is_main() || !is_lonely_tab) @@ -361,14 +360,12 @@ impl<'tree, Tab> DockArea<'tree, Tab> { } } - let response = tabs_ui.interact(response.rect, id, sense); if let Some(pos) = state.last_hover_pos { // Use response.rect.contains instead of // response.hovered as the dragged tab covers // the underlying tab if state.drag_start.is_some() && response.rect.contains(pos) { self.tab_hover_rect = Some((response.rect, tab_index)); - state.drag_start = None; } } @@ -501,7 +498,7 @@ impl<'tree, Tab> DockArea<'tree, Tab> { focused: bool, active: bool, is_being_dragged: bool, - prefered_width: Option, + preferred_width: Option, show_close_button: bool, fade: Option<&Style>, ) -> (Response, Option) { @@ -520,13 +517,11 @@ impl<'tree, Tab> DockArea<'tree, Tab> { .minimum_width .unwrap_or(0.0) .at_least(text_width + close_button_size); - let tab_width = prefered_width.unwrap_or(0.0).at_least(minimum_width); + let tab_width = preferred_width.unwrap_or(0.0).at_least(minimum_width); - let (rect, mut response) = ui.allocate_exact_size( - vec2(tab_width, ui.available_height()), - Sense::focusable_noninteractive(), - ); - if !ui.memory(|mem| mem.is_anything_being_dragged()) && self.draggable_tabs { + let (_, tab_rect) = ui.allocate_space(vec2(tab_width, ui.available_height())); + let mut response = ui.interact(tab_rect, id, Sense::click_and_drag()); + if ui.ctx().dragged_id().is_none() && self.draggable_tabs { response = response.on_hover_cursor(CursorIcon::PointingHand); } @@ -553,8 +548,8 @@ impl<'tree, Tab> DockArea<'tree, Tab> { // Draw the full tab first and then the stroke on top to avoid the stroke // mixing with the background color. ui.painter() - .rect_filled(rect, tab_style.rounding, tab_style.bg_fill); - let stroke_rect = rect_stroke_box(rect, 1.0); + .rect_filled(tab_rect, tab_style.rounding, tab_style.bg_fill); + let stroke_rect = rect_stroke_box(tab_rect, 1.0); ui.painter().rect_stroke( stroke_rect, tab_style.rounding, @@ -572,9 +567,8 @@ impl<'tree, Tab> DockArea<'tree, Tab> { ); } - let mut text_rect = rect; - text_rect.set_width(tab_width - close_button_size); - + let mut text_rect = tab_rect; + text_rect.set_width(text_rect.width() - close_button_size); let text_pos = { let pos = Align2::CENTER_CENTER.pos_in_rect(&text_rect.shrink2(vec2(x_spacing, 0.0))); pos - galley.size() / 2.0 @@ -584,22 +578,22 @@ impl<'tree, Tab> DockArea<'tree, Tab> { .add(TextShape::new(text_pos, galley, tab_style.text_color)); let close_response = show_close_button.then(|| { - let mut close_button_rect = rect; + let mut close_button_rect = tab_rect; close_button_rect.set_left(text_rect.right()); close_button_rect = Rect::from_center_size(close_button_rect.center(), Vec2::splat(close_button_size)); - let response = ui - .interact(close_button_rect, id, Sense::click()) + let close_response = ui + .interact(close_button_rect, id.with("close-button"), Sense::click()) .on_hover_cursor(CursorIcon::PointingHand); - let color = if response.hovered() || response.has_focus() { + let color = if close_response.hovered() || close_response.has_focus() { style.buttons.close_tab_active_color } else { style.buttons.close_tab_color }; - if response.hovered() || response.has_focus() { + if close_response.hovered() || close_response.has_focus() { let mut rounding = tab_style.rounding; rounding.nw = 0.0; rounding.sw = 0.0; @@ -622,7 +616,7 @@ impl<'tree, Tab> DockArea<'tree, Tab> { Stroke::new(1.0, color), ); - response + close_response }); (response, close_response) @@ -852,7 +846,12 @@ impl<'tree, Tab> DockArea<'tree, Tab> { } }; - self.hover_data = Some(HoverData { rect, dst, tab }); + ui.memory_mut(|mem| { + mem.data.insert_temp( + self.id.with("hover_data"), + Some(HoverData { rect, dst, tab }), + ); + }); } } } diff --git a/src/widgets/dock_area/show/main_surface.rs b/src/widgets/dock_area/show/main_surface.rs index fb8de141..30792cb0 100644 --- a/src/widgets/dock_area/show/main_surface.rs +++ b/src/widgets/dock_area/show/main_surface.rs @@ -21,11 +21,16 @@ impl<'tree, Tab> DockArea<'tree, Tab> { let rect = ui.available_rect_before_wrap(); let response = ui.allocate_rect(rect, Sense::hover()); if response.contains_pointer() { - self.hover_data = Some(HoverData { - rect, - dst: TreeComponent::Surface(surf_index), - tab: None, - }) + ui.memory_mut(|mem| { + mem.data.insert_temp( + self.id.with("hover_data"), + Some(HoverData { + rect, + dst: TreeComponent::Surface(surf_index), + tab: None, + }), + ); + }); } return; } diff --git a/src/widgets/dock_area/show/mod.rs b/src/widgets/dock_area/show/mod.rs index 1d31f34b..c9c85b12 100644 --- a/src/widgets/dock_area/show/mod.rs +++ b/src/widgets/dock_area/show/mod.rs @@ -1,6 +1,6 @@ use egui::{ - CentralPanel, Color32, Context, CursorIcon, EventFilter, Frame, Key, LayerId, Order, Pos2, - Rect, Rounding, Sense, Ui, Vec2, + CentralPanel, Color32, Context, CursorIcon, EventFilter, Frame, Key, Pos2, Rect, Rounding, + Sense, Ui, Vec2, }; use duplicate::duplicate; @@ -66,12 +66,46 @@ impl<'tree, Tab> DockArea<'tree, Tab> { self.style .get_or_insert(Style::from_egui(ui.style().as_ref())); self.window_bounds.get_or_insert(ui.ctx().screen_rect()); + let mut state = State::load(ui.ctx(), self.id); + // Delay hover position one frame. On touch screens hover_pos() is None when any_released() if !ui.input(|i| i.pointer.any_released()) { state.last_hover_pos = ui.input(|i| i.pointer.hover_pos()); } + let (drag_data, hover_data) = ui.memory_mut(|mem| { + ( + mem.data.remove_temp(self.id.with("drag_data")).flatten(), + mem.data.remove_temp(self.id.with("hover_data")).flatten(), + ) + }); + + if let (Some(source), Some(hover)) = (drag_data, hover_data) { + let style = self.style.as_ref().unwrap(); + state.set_drag_and_drop(source, hover, ui.ctx(), style); + let tab_dst = self.show_drag_drop_overlay(ui, &mut state, tab_viewer); + if ui.input(|i| i.pointer.primary_released()) { + if let Some(destination) = tab_dst { + let source = { + match state.dnd.as_ref().unwrap().drag.src { + TreeComponent::Tab(src_surf, src_node, src_tab) => { + (src_surf, src_node, src_tab) + } + _ => todo!( + "collections of tabs, like nodes and surfaces can't be docked (yet)" + ), + } + }; + self.dock_state.move_tab(source, destination); + } + } + } + + if ui.input(|i| i.pointer.primary_released()) { + state.reset_drag(); + } + let style = self.style.as_ref().unwrap(); let fade_surface = self.hovered_window_surface(&mut state, style.overlay.feel.fade_hold_time, ui.ctx()); @@ -126,33 +160,6 @@ impl<'tree, Tab> DockArea<'tree, Tab> { self.dock_state.set_focused_node_and_surface(focused); } - if let (Some(source), Some(hover)) = (self.drag_data.take(), self.hover_data.take()) { - let style = self.style.as_ref().unwrap(); - state.set_drag_and_drop(source, hover, ui.ctx(), style); - let tab_dst = { - let layer_id = LayerId::new(Order::Foreground, "foreground".into()); - ui.with_layer_id(layer_id, |ui| { - self.show_drag_drop_overlay(ui, &mut state, tab_viewer) - }) - .inner - }; - if ui.input(|i| i.pointer.primary_released()) { - let source = { - match state.dnd.as_ref().unwrap().drag.src { - TreeComponent::Tab(src_surf, src_node, src_tab) => { - (src_surf, src_node, src_tab) - } - _ => todo!( - "collections of tabs, like nodes and surfaces can't be docked (yet)" - ), - } - }; - if let Some(destination) = tab_dst { - self.dock_state.move_tab(source, destination); - } - state.reset_drag(); - } - } state.store(ui.ctx(), self.id); } @@ -224,24 +231,21 @@ impl<'tree, Tab> DockArea<'tree, Tab> { } let window_bounds = self.window_bounds.unwrap(); - if drag_state.is_on_title_bar() - || style.overlay.overlay_type == OverlayType::HighlightedAreas - { - drag_state.resolve_traditional( + match (style.overlay.overlay_type, drag_state.is_on_title_bar()) { + (OverlayType::HighlightedAreas, _) | (_, true) => drag_state.resolve_traditional( ui, style, allowed_splits, allowed_in_window, window_bounds, - ) - } else { - drag_state.resolve_icon_based( + ), + (OverlayType::Widgets, false) => drag_state.resolve_icon_based( ui, style, allowed_splits, allowed_in_window, window_bounds, - ) + ), } }