From f4f9e5b206a721d28ea1214ef8bf85b8e30bb779 Mon Sep 17 00:00:00 2001 From: aiongg Date: Mon, 19 Oct 2026 12:26:31 +0000 Subject: [PATCH] docking: one drop preview painter for docking and panes chrome::drop_preview draws the accent outline, filled or not, for every docking destination and drag ghost and for the pane host's drop preview. --- crates/ui/src/docking/chrome.rs | 7 +++++++ crates/ui/src/docking/render/preview.rs | 20 +++++++------------- crates/ui/src/panes/mod.rs | 11 ++--------- docs/ui-docking.md | 3 ++- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/crates/ui/src/docking/chrome.rs b/crates/ui/src/docking/chrome.rs index f5b6bb6b..3aeab529 100644 --- a/crates/ui/src/docking/chrome.rs +++ b/crates/ui/src/docking/chrome.rs @@ -245,6 +245,13 @@ impl<'a> FloatingFrame<'a> { } } +/// A drop preview: a 2 pt accent outline inside `rect`, filled with 15% accent when `filled`. +/// Docking and the pane host draw every destination and drag ghost with it. +pub fn drop_preview(painter: &egui::Painter, rect: Rect, accent: Color32, filled: bool) { + let fill = if filled { accent.gamma_multiply(0.15) } else { Color32::TRANSPARENT }; + painter.rect(rect, 0.0, fill, Stroke::new(2.0, accent), StrokeKind::Inside); +} + /// Keep `rect` inside `bounds`, shrinking it when it is larger. pub fn clamp(rect: Rect, bounds: Rect) -> Rect { if !super::targets::usable(bounds) { diff --git a/crates/ui/src/docking/render/preview.rs b/crates/ui/src/docking/render/preview.rs index 985b4dd7..2ab0a836 100644 --- a/crates/ui/src/docking/render/preview.rs +++ b/crates/ui/src/docking/render/preview.rs @@ -1,8 +1,9 @@ use std::{fmt::Debug, hash::Hash}; -use egui::{Color32, Context, Key, Pos2, Rect, Stroke, StrokeKind, Ui, Vec2, pos2, vec2}; +use egui::{Context, Key, Pos2, Rect, Ui, Vec2, pos2, vec2}; use super::{Renderer, clamp_float, dock::group_action}; +use crate::docking::chrome::drop_preview; use crate::docking::{ Action, Drag, DropProposal, DropRequest, GroupDrag, GroupGesture, Placement, Target, Zone, cancel_drag, dock_model::Place, resolve_drop, surface::TAB_PADDING, @@ -137,7 +138,7 @@ impl Renderer<'_, P> { let painter = self.preview_painter(ctx); if drag.gesture != GroupGesture::Window { let ghost = self.group_ghost(ctx, drag, pointer); - painter.rect(ghost, 0.0, self.style.accent.gamma_multiply(0.15), Stroke::new(2.0, self.style.accent), StrokeKind::Inside); + drop_preview(&painter, ghost, self.style.accent, true); let strip = Rect::from_min_size(ghost.min, vec2(ghost.width(), self.style.tab_height.min(ghost.height()))); let labels = painter.with_clip_rect(strip.shrink(2.0)); let mut x = strip.left() + TAB_PADDING * 0.5; @@ -164,9 +165,8 @@ impl Renderer<'_, P> { } if let Some(proposal) = self.group_proposal(drag, pointer).filter(|p| !p.noop) { let split = matches!(proposal.placement, Placement::Split(zone) | Placement::Edge(zone) if zone != Zone::Center); - let fill = if split { self.style.accent.gamma_multiply(0.15) } else { Color32::TRANSPARENT }; if crate::docking::targets::usable(proposal.preview) { - painter.rect(proposal.preview.shrink(1.0), 0.0, fill, Stroke::new(2.0, self.style.accent), StrokeKind::Inside); + drop_preview(&painter, proposal.preview.shrink(1.0), self.style.accent, split); } if let Some(marker) = proposal.insertion { painter.rect_filled(marker, 0.0, self.style.accent); @@ -256,19 +256,13 @@ impl Renderer<'_, P> { let same_group = proposal.insertion.is_some() && self.targets.iter().any(|target| target.rect == proposal.preview && target.tabs.iter().any(|(panel, _)| panel == &drag.panel)); if !same_group && !proposal.noop && crate::docking::targets::usable(proposal.preview) { - painter.rect( - proposal.preview.shrink(1.0), - 0.0, - self.style.accent.gamma_multiply(0.15), - Stroke::new(2.0, self.style.accent), - StrokeKind::Inside, - ); + drop_preview(&painter, proposal.preview.shrink(1.0), self.style.accent, true); } if let Some(marker) = proposal.insertion { painter.rect_filled(marker, 0.0, self.style.accent); } } else if let Some(rect) = floating_rect { - painter.rect_stroke(rect, 0.0, Stroke::new(2.0, self.style.accent), StrokeKind::Inside); + drop_preview(&painter, rect, self.style.accent, false); } } @@ -348,7 +342,7 @@ impl Renderer<'_, P> { if !released { let painter = self.preview_painter(ctx); if let Some(rect) = feedback.preview.filter(|rect| crate::docking::targets::usable(*rect)) { - painter.rect(rect, 0.0, self.style.accent.gamma_multiply(0.15), Stroke::new(2.0, self.style.accent), StrokeKind::Inside); + drop_preview(&painter, rect, self.style.accent, true); } if let Some(rect) = feedback.insertion.filter(|rect| crate::docking::targets::usable(*rect)) { painter.rect_filled(rect, 0.0, self.style.accent); diff --git a/crates/ui/src/panes/mod.rs b/crates/ui/src/panes/mod.rs index 961c8053..9e2aeca4 100644 --- a/crates/ui/src/panes/mod.rs +++ b/crates/ui/src/panes/mod.rs @@ -11,10 +11,7 @@ use std::fmt::Debug; use std::hash::Hash; use std::ops::RangeInclusive; -use egui::{ - Color32, Context, Frame, Id, Key, LayerId, Margin, Modifiers, Order, Pos2, Rect, Response, Sense, Stroke, StrokeKind, Ui, UiBuilder, Vec2, pos2, - vec2, -}; +use egui::{Context, Frame, Id, Key, LayerId, Margin, Modifiers, Order, Pos2, Rect, Response, Sense, Ui, UiBuilder, Vec2, pos2, vec2}; use crate::docking::PanelLimits; use crate::docking::chrome::{self, ChromeStyle, FloatingFrame, MoveStep, TitleBar}; @@ -599,7 +596,7 @@ impl PaneHost { Some(_) => Rect::from_x_y_ranges(bounds.right() - rect.width()..=bounds.right(), bounds.y_range()), None => float, }; - paint_preview(preview, shape, style.accent); + chrome::drop_preview(preview, shape.shrink(1.0), style.accent, true); } #[allow(clippy::too_many_arguments)] @@ -714,10 +711,6 @@ impl PaneHost { } } -fn paint_preview(painter: &egui::Painter, rect: Rect, accent: Color32) { - painter.rect(rect.shrink(1.0), 0.0, accent.gamma_multiply(0.15), Stroke::new(2.0, accent), StrokeKind::Inside); -} - fn rect_array(rect: Rect) -> [f32; 4] { [rect.min.x, rect.min.y, rect.width(), rect.height()] } diff --git a/docs/ui-docking.md b/docs/ui-docking.md index 96ea7ae3..35c8612a 100644 --- a/docs/ui-docking.md +++ b/docs/ui-docking.md @@ -131,7 +131,8 @@ by the app's own Close items and commands. `DockArea::chrome_has_focus` tells whether keyboard focus is on docking chrome rather than on a panel body. `begin_drag` hands a drag that started in the app's own chrome to the area. -`docking::chrome` has the floating frame and title bar for apps that draw their own windows. +`docking::chrome` has the floating frame, the title bar and `drop_preview` (the drop preview +docking and `craft_ui::panes` both draw) for apps that draw their own windows. ## Gestures