diff --git a/src/apprt/gtk/class/split_tree.zig b/src/apprt/gtk/class/split_tree.zig index 36d92ad47..473ae3daf 100644 --- a/src/apprt/gtk/class/split_tree.zig +++ b/src/apprt/gtk/class/split_tree.zig @@ -387,11 +387,6 @@ pub const SplitTree = extern struct { return true; } - pub const MoveSplitError = Allocator.Error || error{ - SourceNotFound, - TargetNotFound, - }; - /// Move the source split onto the target split in a given direction. /// The target split must be located within this tree. pub fn moveSplit( @@ -399,21 +394,27 @@ pub const SplitTree = extern struct { source: *Surface, target: *Surface, dir: Surface.Tree.Split.Direction, - ) MoveSplitError!void { + ) Allocator.Error!void { const alloc = Application.default().allocator(); - const tree = self.getTree() orelse return; - - const source_handle = tree.locate(source) orelse return error.SourceNotFound; + const target_tree = self.getTree() orelse return; // This really shouldn't fail, but just in case - const target_handle = tree.locate(target) orelse return error.TargetNotFound; + const target_handle = target_tree.locate(target) orelse { + log.warn("target is not placed in a split tree", .{}); + return; + }; - // TODO: is it perhaps possible to condense all of this - // into one atomic operation? + // Try to find the source within the current tree. + // If it exists, then it's a local move; otherwise the logic gets more + // complicated + const source_handle = target_tree.locate(source); + + // First add the source into this tree. + // We have to do this no matter what var branch = try Surface.Tree.init(alloc, source); defer branch.deinit(); - var after_split = try tree.split( + var after_split = try target_tree.split( alloc, target_handle, dir, @@ -422,10 +423,36 @@ pub const SplitTree = extern struct { ); defer after_split.deinit(); - var after_remove = try after_split.remove(alloc, source_handle); - defer after_remove.deinit(); + if (source_handle) |handle| { + // Happy path: source and target are located within the same tree. + // In that case, we just remove the existing source from this tree - self.setTree(&after_remove); + var after_remove = try after_split.remove(alloc, handle); + defer after_remove.deinit(); + + self.setTree(&after_remove); + } else { + // :( Cross-tree moves are a bit more complicated. + + // TODO: Find a better way to access the split tree from here + const source_tree_widget = ext.getAncestor( + SplitTree, + source.as(gtk.Widget), + ) orelse { + log.warn("source is not placed in a split tree", .{}); + return; + }; + const source_tree = source_tree_widget.getTree() orelse return; + + // Remove the source from its own tree + const handle = source_tree.locate(source) orelse return; + var new_source_tree = try source_tree.remove(alloc, handle); + defer new_source_tree.deinit(); + + // Finally, set the final tree structures for both tree widgets + source_tree_widget.setTree(&new_source_tree); + self.setTree(&after_split); + } } fn disconnectSurfaceHandlers(self: *Self) void { diff --git a/src/apprt/gtk/class/surface.zig b/src/apprt/gtk/class/surface.zig index 8d0231acb..b9f896c93 100644 --- a/src/apprt/gtk/class/surface.zig +++ b/src/apprt/gtk/class/surface.zig @@ -3773,21 +3773,9 @@ pub const Surface = extern struct { const dir = self.calcDropDirection(x, y); - st.moveSplit(from, self, dir) catch |err| { - switch (err) { - error.OutOfMemory => log.warn("out of memory", .{}), - // FIXME: implement this! - error.SourceNotFound => log.debug( - "cross-tree drag-and-drop not yet supported", - .{}, - ), - error.TargetNotFound => log.warn( - "can't seem to find surface in tree - this shouldn't happen!", - .{}, - ), - } - return; - }; + // The only error that could happen here is an OOM, + // and in that case we're already milliseconds away from crashing, so... + st.moveSplit(from, self, dir) catch return; // Clean up overlay state self.setDropOverlayDirection(null);