From 4327c17fd73ecd3ac238966f3e23fd7eb69323ec Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 26 Aug 2026 14:03:49 -0400 Subject: [PATCH] Fix webview dragging. --- src/Pane.zig | 17 +++++++++++++++++ src/View.zig | 11 +++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/Pane.zig b/src/Pane.zig index ef74998..5245492 100644 --- a/src/Pane.zig +++ b/src/Pane.zig @@ -395,6 +395,12 @@ pub fn grabFocus(self: *Pane) void { self.content.grabFocus(); } +/// Whether the content is allowed to be picked as the target of a pointer or a +/// drop. Off for the length of a pane drag — see `installDropTarget`. +pub fn setContentTargetable(self: *Pane, targetable: bool) void { + self.content.widget().setCanTarget(@intFromBool(targetable)); +} + /// The terminal this pane holds, or null if it holds something else. Callers /// that only make sense for a terminal — pasting a VT sequence, say — use this /// to opt out on a web pane. @@ -585,6 +591,17 @@ fn installDropTarget(self: *Pane) void { _ = gtk.DropTarget.signals.motion.connect(target, *Pane, &onDropMotion, self, .{}); _ = gtk.DropTarget.signals.leave.connect(target, *Pane, &onDropLeave, self, .{}); _ = gtk.DropTarget.signals.drop.connect(target, *Pane, &onDrop, self, .{}); + + // On the pane rather than the header, so the whole pane is a target and + // `targetAt` has an area to pick a side from. That puts it above the + // content in the widget tree, which is fine for a terminal — it takes no + // drops — but a web view is a drop target in its own right and an + // innermost one wins. Since the preview slides the dragged pane in under + // the pointer, the drop at the end of a drag of a browser or a review pane + // would land in that pane's own page: no `drop` here, so `endDrag` would + // find the drag uncommitted and undo the whole move. `View.beginDrag` + // takes every pane's content out of the picking for the length of a drag + // so that the pane is what the drop reaches. self.widget().addController(target.as(gtk.EventController)); } diff --git a/src/View.zig b/src/View.zig index b3b4c5f..4752a38 100644 --- a/src/View.zig +++ b/src/View.zig @@ -786,6 +786,13 @@ pub fn beginDrag(self: *View, pane: *Pane) bool { .origin_ratio = position.ratio, }; pane.widget().addCssClass("dragging"); + + // For the length of the drag, a pane's content is not a drop target and + // the pane itself is. Without this a web view — a browser or a review + // pane — swallows the drop that lands on it, including the drop on the + // dragged pane itself, which is where the preview has just put it. See + // `Pane.installDropTarget`. + for (self.panes.items) |p| p.setContentTargetable(false); return true; } @@ -808,6 +815,10 @@ pub fn commitDrag(self: *View) void { /// End of a drag. If no drop was accepted, undo whatever the preview did. pub fn endDrag(self: *View) void { + // Before the early return: a drag whose pane was closed under it has + // already cleared `drag`, and the content would stay unpickable for good. + for (self.panes.items) |p| p.setContentTargetable(true); + const drag = self.drag orelse return; self.drag = null; drag.pane.widget().removeCssClass("dragging");