mirror of
https://github.com/storytold/craft-libs.git
synced 2026-10-31 15:49:54 +00:00
craft-ui: a tab dragged out of the window floats where it left
On X11 (including XWayland) and Windows, the pointer leaving the window
reaches egui as PointerGone, and egui-winit then drops the button release
because it has no pointer position. finish_drag never saw the drag end:
the panel stayed in its dock with the drop outline following the pointer
until the next click inside the window.
Leaving the window now ends the drag as a drop. interact_pos still holds
the last position on that frame, and the panel floats there (clamped to
the window) instead of docking into whatever target sits at the edge.
Test: tab_dragged_out_of_the_window_floats_where_it_left. It fails without
the fix (no action at all).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit aecdf819ba)
This commit is contained in:
1 parent
ae2f796b67
commit
8db84c54f1
2 files changed
+32
-4
No files matched your search
@@ -829,8 +829,12 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
|
||||
|
||||
fn finish_drag(&mut self, ctx: &Context) {
|
||||
let Some(drag) = ctx.data(|d| d.get_temp::<Drag<P>>(self.id.with("drag"))) else { return };
|
||||
let (pointer, down, released, focused) =
|
||||
ctx.input(|i| (i.pointer.interact_pos(), i.pointer.primary_down(), i.pointer.primary_released(), i.focused));
|
||||
let (pointer, down, released, focused, gone) =
|
||||
ctx.input(|i| (i.pointer.interact_pos(), i.pointer.primary_down(), i.pointer.primary_released(), i.focused, !i.pointer.has_pointer()));
|
||||
// Leaving the window is the drop: on X11 and Windows the release outside never reaches
|
||||
// egui, so the drag would otherwise stay on the pointer. `interact_pos` still holds the
|
||||
// last position this frame, and the panel floats there rather than docking at the edge.
|
||||
let released = released || gone;
|
||||
let escape = ctx.input_mut(|i| i.consume_key(egui::Modifiers::NONE, Key::Escape));
|
||||
if escape || !focused || !self.layout.contains(&drag.panel) || (!down && !released) {
|
||||
cancel_drag::<P>(ctx, self.id);
|
||||
@@ -854,10 +858,10 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
|
||||
if pointer.distance(drag.origin) < 2.0 {
|
||||
return;
|
||||
}
|
||||
let action = if let Some(proposal) = proposal {
|
||||
let action = if let Some(proposal) = proposal.filter(|_| !gone) {
|
||||
Some(Action::Move { panel: drag.panel.clone(), anchor: proposal.anchor, placement: proposal.placement })
|
||||
} else if (self.permissions)(&drag.panel).floatable
|
||||
&& (!drag.source.contains(pointer) || self.layout.floating.iter().any(|g| g.panels.contains(&drag.panel)))
|
||||
&& (gone || !drag.source.contains(pointer) || self.layout.floating.iter().any(|g| g.panels.contains(&drag.panel)))
|
||||
{
|
||||
let rect = clamp_float(Rect::from_min_size(pointer - drag.grab, drag.source.size()), ctx.content_rect());
|
||||
Some(Action::Float { panel: drag.panel.clone(), rect: [rect.left(), rect.top(), rect.width(), rect.height()] })
|
||||
|
||||
@@ -395,6 +395,30 @@ fn actual_pointer_drag_docks_a_floating_panel_into_the_main_tab_strip() {
|
||||
assert_eq!(panels.get(*active), Some(&5));
|
||||
}
|
||||
|
||||
/// On X11 and Windows a release outside the window never reaches egui (egui-winit drops a
|
||||
/// button event once the pointer has left), so a tab dragged out of the window used to stay on
|
||||
/// the pointer for good. Leaving the window detaches the panel where it left, even over a dock
|
||||
/// target, and ends the drag.
|
||||
#[test]
|
||||
fn tab_dragged_out_of_the_window_floats_where_it_left() {
|
||||
let ctx = Context::default();
|
||||
let mut layout = sample();
|
||||
for _ in 0..3 {
|
||||
step(&ctx, &mut layout, Vec::new());
|
||||
}
|
||||
let tab = ctx.read_response(Id::new("test-area").with(("tab", &3u32))).unwrap().rect.center();
|
||||
let exit = pos2(4.0, 300.0);
|
||||
step(&ctx, &mut layout, vec![Event::PointerMoved(tab), pointer_button(tab, true)]);
|
||||
step(&ctx, &mut layout, vec![Event::PointerMoved(tab + vec2(-8.0, 0.0))]);
|
||||
step(&ctx, &mut layout, vec![Event::PointerMoved(exit)]);
|
||||
let actions = step(&ctx, &mut layout, vec![Event::PointerGone]);
|
||||
assert!(actions.iter().any(|a| matches!(a, Action::Float { panel: 3, .. })), "{actions:?}");
|
||||
assert!(layout.floating.iter().any(|g| g.panels == [3]), "{layout:?}");
|
||||
let back = pos2(400.0, 300.0);
|
||||
assert!(step(&ctx, &mut layout, vec![Event::PointerMoved(back)]).is_empty());
|
||||
assert!(step(&ctx, &mut layout, vec![pointer_button(back, false)]).is_empty(), "the drag ended when the pointer left");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panel_size_limits_clamp_geometry_without_overwriting_preferred_widths() {
|
||||
let layout = Layout {
|
||||
|
||||
Reference in new issue
Block a user