From 8cf9d97ac315cf1346f0542552c623be1e13d62b Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Thu, 23 Nov 2023 19:45:19 +0100 Subject: [PATCH] gtk: fix replacing of splits, remove dead code --- src/apprt/gtk/Paned.zig | 205 ------------------------------------- src/apprt/gtk/Split.zig | 159 +++++----------------------- src/apprt/gtk/Surface.zig | 39 +++++-- src/apprt/gtk/Tab.zig | 40 +------- src/apprt/gtk/Window.zig | 71 ------------- src/apprt/gtk/relation.zig | 40 -------- 6 files changed, 61 insertions(+), 493 deletions(-) delete mode 100644 src/apprt/gtk/Paned.zig delete mode 100644 src/apprt/gtk/relation.zig diff --git a/src/apprt/gtk/Paned.zig b/src/apprt/gtk/Paned.zig deleted file mode 100644 index ac699f960..000000000 --- a/src/apprt/gtk/Paned.zig +++ /dev/null @@ -1,205 +0,0 @@ -const Paned = @This(); - -const std = @import("std"); -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const font = @import("../../font/main.zig"); -const input = @import("../../input.zig"); -const CoreSurface = @import("../../Surface.zig"); - -const Window = @import("Window.zig"); -const Surface = @import("Surface.zig"); -const Tab = @import("Tab.zig"); -const Position = @import("relation.zig").Position; -const Parent = @import("relation.zig").Parent; -const Child = @import("relation.zig").Child; -const c = @import("c.zig"); - -const log = std.log.scoped(.gtk); - -/// Our actual GtkPaned widget -paned: *c.GtkPaned, - -// We have two children, each of which can be either a Surface, another pane, -// or empty. We're going to keep track of which each child is here. -child1: Child, -child2: Child, - -// We also hold a reference to our parent widget, so that when we close we can either -// maximize the parent pane, or close the tab. -parent: Parent, - -pub fn create(alloc: Allocator, sibling: *Surface, direction: input.SplitDirection) !*Paned { - var paned = try alloc.create(Paned); - errdefer alloc.destroy(paned); - try paned.init(sibling, direction); - return paned; -} - -pub fn init(self: *Paned, sibling: *Surface, direction: input.SplitDirection) !void { - self.* = .{ - .paned = undefined, - .child1 = .none, - .child2 = .none, - .parent = undefined, - }; - errdefer self.* = undefined; - - const orientation: c_uint = switch (direction) { - .right => c.GTK_ORIENTATION_HORIZONTAL, - .down => c.GTK_ORIENTATION_VERTICAL, - }; - - const paned = c.gtk_paned_new(orientation); - errdefer c.g_object_unref(paned); - - const gtk_paned: *c.GtkPaned = @ptrCast(paned); - self.paned = gtk_paned; - - const alloc = sibling.app.core_app.alloc; - var surface = try Surface.create(alloc, sibling.app, .{ - .parent2 = &sibling.core_surface, - .parent = .{ .paned = .{ self, .end } }, - }); - errdefer surface.destroy(alloc); - surface.container = sibling.container; // TODO - - self.addChild1(.{ .surface = sibling }); - self.addChild2(.{ .surface = surface }); -} - -/// Set the parent of Paned. -pub fn setParent(self: *Paned, parent: Parent) void { - self.parent = parent; -} - -/// Focus on first Surface that can be found in given position. If there's a -/// Paned in the position, it will focus on the first surface in that position. -pub fn focusFirstSurfaceInPosition(self: *Paned, position: Position) void { - const child = self.childInPosition(position); - switch (child) { - .surface => |s| s.grabFocus(), - .paned => |p| p.focusFirstSurfaceInPosition(position), - .none => { - log.warn("attempted to focus on first surface, found none", .{}); - return; - }, - } -} - -/// Split the Surface in the given position into a Paned with two surfaces. -pub fn splitSurfaceInPosition(self: *Paned, position: Position, direction: input.SplitDirection) !void { - const surface: *Surface = self.surfaceInPosition(position) orelse return; - - // Keep explicit reference to surface gl_area before we remove it. - const object: *c.GObject = @ptrCast(surface.gl_area); - _ = c.g_object_ref(object); - defer c.g_object_unref(object); - - // Keep position of divider - const parent_paned_position_before = c.gtk_paned_get_position(self.paned); - // Now remove it - self.removeChildInPosition(position); - - // Create new Paned - // NOTE: We cannot use `replaceChildInPosition` here because we need to - // first remove the surface before we create a new pane. - const paned = try Paned.create(surface.app.core_app.alloc, surface, direction); - switch (position) { - .start => self.addChild1(.{ .paned = paned }), - .end => self.addChild2(.{ .paned = paned }), - } - // Restore position - c.gtk_paned_set_position(self.paned, parent_paned_position_before); - - // Focus on new surface - paned.focusFirstSurfaceInPosition(.end); -} - -/// Replace the existing .start or .end Child with the given new Child. -pub fn replaceChildInPosition(self: *Paned, child: Child, position: Position) void { - // Keep position of divider - const parent_paned_position_before = c.gtk_paned_get_position(self.paned); - - // Focus on the sibling, otherwise we'll get a GTK warning - self.focusFirstSurfaceInPosition(if (position == .start) .end else .start); - - // Now we can remove the other one - self.removeChildInPosition(position); - - switch (position) { - .start => self.addChild1(child), - .end => self.addChild2(child), - } - - // Restore position - c.gtk_paned_set_position(self.paned, parent_paned_position_before); -} - -/// Remove both children, setting *c.GtkPaned start/end children to null. -pub fn removeChildren(self: *Paned) void { - self.removeChildInPosition(.start); - self.removeChildInPosition(.end); -} - -/// Deinit the Paned by deiniting its child Paneds, if they exist. -pub fn deinit(self: *Paned, alloc: Allocator) void { - for ([_]Child{ self.child1, self.child2 }) |child| { - switch (child) { - .none, .surface => continue, - .paned => |paned| { - paned.deinit(alloc); - alloc.destroy(paned); - }, - } - } -} - -fn removeChildInPosition(self: *Paned, position: Position) void { - switch (position) { - .start => { - assert(self.child1 != .none); - self.child1 = .none; - c.gtk_paned_set_start_child(@ptrCast(self.paned), null); - }, - .end => { - assert(self.child2 != .none); - self.child2 = .none; - c.gtk_paned_set_end_child(@ptrCast(self.paned), null); - }, - } -} - -fn addChild1(self: *Paned, child: Child) void { - assert(self.child1 == .none); - - const widget = child.widget() orelse return; - c.gtk_paned_set_start_child(@ptrCast(self.paned), widget); - - self.child1 = child; - child.setParent(.{ .paned = .{ self, .start } }); -} - -fn addChild2(self: *Paned, child: Child) void { - assert(self.child2 == .none); - - const widget = child.widget() orelse return; - c.gtk_paned_set_end_child(@ptrCast(self.paned), widget); - - self.child2 = child; - child.setParent(.{ .paned = .{ self, .end } }); -} - -fn childInPosition(self: *Paned, position: Position) Child { - return switch (position) { - .start => self.child1, - .end => self.child2, - }; -} - -fn surfaceInPosition(self: *Paned, position: Position) ?*Surface { - return switch (self.childInPosition(position)) { - .surface => |surface| surface, - else => null, - }; -} diff --git a/src/apprt/gtk/Split.zig b/src/apprt/gtk/Split.zig index ee997af64..ab7580f17 100644 --- a/src/apprt/gtk/Split.zig +++ b/src/apprt/gtk/Split.zig @@ -11,8 +11,6 @@ const CoreSurface = @import("../../Surface.zig"); const Surface = @import("Surface.zig"); const Tab = @import("Tab.zig"); -const Position = @import("relation.zig").Position; -const Child = @import("relation.zig").Child; const c = @import("c.zig"); const log = std.log.scoped(.gtk); @@ -92,113 +90,38 @@ pub fn init( surface.grabFocus(); } -/// Focus on first Surface that can be found in given position. If there's a -/// Split in the position, it will focus on the first surface in that position. -pub fn focusFirstSurfaceInPosition(self: *Split, position: Position) void { - const child = self.childInPosition(position); - switch (child) { - .surface => |s| s.grabFocus(), - .paned => |p| p.focusFirstSurfaceInPosition(position), - .none => { - log.warn("attempted to focus on first surface, found none", .{}); - return; - }, - } -} - -/// Split the Surface in the given position into a Split with two surfaces. -pub fn splitSurfaceInPosition(self: *Split, position: Position, direction: input.SplitDirection) !void { - const surface: *Surface = self.surfaceInPosition(position) orelse return; - - // Keep explicit reference to surface gl_area before we remove it. - const object: *c.GObject = @ptrCast(surface.gl_area); - _ = c.g_object_ref(object); - defer c.g_object_unref(object); - - // Keep position of divider - const parent_paned_position_before = c.gtk_paned_get_position(self.paned); - // Now remove it - self.removeChildInPosition(position); - - // Create new Split - // NOTE: We cannot use `replaceChildInPosition` here because we need to - // first remove the surface before we create a new pane. - const paned = try Split.create(surface.app.core_app.alloc, surface, direction); - switch (position) { - .start => self.addChild1(.{ .paned = paned }), - .end => self.addChild2(.{ .paned = paned }), - } - // Restore position - c.gtk_paned_set_position(self.paned, parent_paned_position_before); - - // Focus on new surface - paned.focusFirstSurfaceInPosition(.end); -} - -/// Replace the existing .start or .end Child with the given new Child. -pub fn replaceChildInPosition(self: *Split, child: Child, position: Position) void { - // Keep position of divider - const parent_paned_position_before = c.gtk_paned_get_position(self.paned); - - // Focus on the sibling, otherwise we'll get a GTK warning - self.focusFirstSurfaceInPosition(if (position == .start) .end else .start); - - // Now we can remove the other one - self.removeChildInPosition(position); - - switch (position) { - .start => self.addChild1(child), - .end => self.addChild2(child), - } - - // Restore position - c.gtk_paned_set_position(self.paned, parent_paned_position_before); -} - -/// Remove both children, setting *c.GtkSplit start/end children to null. -// pub fn removeChildren(self: *Split) void { -// self.removeChildInPosition(.start); -// self.removeChildInPosition(.end); -//} - -/// Deinit the Split by deiniting its child Split, if they exist. -pub fn deinit(self: *Split, alloc: Allocator) void { - for ([_]Child{ self.child1, self.child2 }) |child| { - switch (child) { - .none, .surface => continue, - .paned => |paned| { - paned.deinit(alloc); - alloc.destroy(paned); - }, - } - } -} - -fn removeChildInPosition(self: *Split, position: Position) void { - switch (position) { - .start => { - assert(self.child1 != .none); - self.child1 = .none; - c.gtk_paned_set_start_child(@ptrCast(self.paned), null); - }, - .end => { - assert(self.child2 != .none); - self.child2 = .none; - c.gtk_paned_set_end_child(@ptrCast(self.paned), null); - }, - } +/// Remove the top left child. +pub fn removeTopLeft(self: *Split) void { + self.removeChild(self.top_left, self.bottom_right); } /// Remove the top left child. -pub fn removeTopLeft(self: *Split) void { +pub fn removeBottomRight(self: *Split) void { + self.removeChild(self.bottom_right, self.top_left); +} + +// TODO: Is this Zig-y? +inline fn removeChild(self: *Split, remove: Surface.Container.Elem, keep: Surface.Container.Elem) void { + const window = self.container.window() orelse return; + + // TODO: Grab focus + + // Keep a reference to the side that we want to keep, so it doesn't get + // destroyed when it's removed from our underlying GtkPaned. + const keep_object: *c.GObject = @ptrCast(keep.widget()); + _ = c.g_object_ref(keep_object); + defer c.g_object_unref(keep_object); + // Remove our children since we are going to no longer be // a split anyways. This prevents widgets with multiple parents. self.removeChildren(); - // Our container must become whatever our bottom right is - self.container.replace(self.bottom_right); + // Our container must become whatever our top left is + self.container.replace(keep); - // TODO: memory management of top left + // TODO: is this correct? + remove.shutdown(); + window.app.core_app.alloc.destroy(self); } // TODO: ehhhhhh @@ -242,37 +165,3 @@ fn removeChildren(self: *const Split) void { c.gtk_paned_set_start_child(@ptrCast(self.paned), null); c.gtk_paned_set_end_child(@ptrCast(self.paned), null); } - -fn addChild1(self: *Split, child: Child) void { - assert(self.child1 == .none); - - const widget = child.widget() orelse return; - c.gtk_paned_set_start_child(@ptrCast(self.paned), widget); - - self.child1 = child; - child.setParent(.{ .paned = .{ self, .start } }); -} - -fn addChild2(self: *Split, child: Child) void { - assert(self.child2 == .none); - - const widget = child.widget() orelse return; - c.gtk_paned_set_end_child(@ptrCast(self.paned), widget); - - self.child2 = child; - child.setParent(.{ .paned = .{ self, .end } }); -} - -fn childInPosition(self: *Split, position: Position) Child { - return switch (position) { - .start => self.child1, - .end => self.child2, - }; -} - -fn surfaceInPosition(self: *Split, position: Position) ?*Surface { - return switch (self.childInPosition(position)) { - .surface => |surface| surface, - else => null, - }; -} diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 848dd02a5..7695224cd 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -17,7 +17,6 @@ const Split = @import("Split.zig"); const Tab = @import("Tab.zig"); const Window = @import("Window.zig"); const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig"); -const Parent = @import("relation.zig").Parent; const inspector = @import("inspector.zig"); const gtk_key = @import("key.zig"); const c = @import("c.zig"); @@ -79,6 +78,22 @@ pub const Container = union(enum) { .split => |s| &s.container, }; } + + pub fn shutdown(self: Elem) void { + switch (self) { + .surface => |s| s.shutdown(), + .split => { + @panic("TODO: shutdownsplit"); + }, + } + } + + pub fn debugName(self: Elem) []const u8 { + return switch (self) { + .surface => "surface", + .split => "split", + }; + } }; /// Returns the window that this surface is attached to. @@ -119,10 +134,14 @@ pub const Container = union(enum) { /// from a surface to a split or a split back to a surface or /// a split to a nested split and so on. pub fn replace(self: Container, elem: Elem) void { + log.debug("Container.replace. self={s}, elem={s}", .{ self.debugName(), elem.debugName() }); + // Move the element into the container switch (self) { .none => {}, - .tab_ => |t| t.replaceElem(elem), + .tab_ => |t| { + t.replaceElem(elem); + }, inline .split_tl, .split_br => |ptr| { const s = self.split().?; s.replace(ptr, elem); @@ -137,13 +156,23 @@ pub const Container = union(enum) { /// children to effectively notify they're containing that /// all children at this level are exiting. pub fn remove(self: Container) void { + log.warn("Container.remove", .{}); switch (self) { .none => {}, .tab_ => |t| t.closeElem(), .split_tl => self.split().?.removeTopLeft(), - else => @panic("TOOD"), + .split_br => self.split().?.removeBottomRight(), } } + + pub fn debugName(self: Container) []const u8 { + return switch (self) { + .none => "none", + .tab_ => "tab", + .split_tl => "split_tl", + .split_br => "split_br", + }; + } }; /// Whether the surface has been realized or not yet. When a surface is @@ -664,10 +693,6 @@ pub fn setTitle(self: *Surface, slice: [:0]const u8) !void { } } -pub fn setParent(self: *Surface, parent: Parent) void { - self.parent = parent; -} - pub fn setMouseShape( self: *Surface, shape: terminal.MouseShape, diff --git a/src/apprt/gtk/Tab.zig b/src/apprt/gtk/Tab.zig index d1d33893a..afa3383b1 100644 --- a/src/apprt/gtk/Tab.zig +++ b/src/apprt/gtk/Tab.zig @@ -10,9 +10,6 @@ const font = @import("../../font/main.zig"); const input = @import("../../input.zig"); const CoreSurface = @import("../../Surface.zig"); -const Paned = @import("Paned.zig"); -const Parent = @import("relation.zig").Parent; -const Child = @import("relation.zig").Child; const Surface = @import("Surface.zig"); const Window = @import("Window.zig"); const c = @import("c.zig"); @@ -34,9 +31,6 @@ box: *c.GtkBox, /// The element of this tab so that we can handle splits and so on. elem: Surface.Container.Elem, -// The child can be either a Surface if the tab is not split or a Paned -child: Child, - // We'll update this every time a Surface gains focus, so that we have it // when we switch to another Tab. Then when we switch back to this tab, we // can easily re-focus that terminal. @@ -57,7 +51,6 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { .label_text = undefined, .box = undefined, .elem = undefined, - .child = undefined, .focus_child = undefined, }; @@ -93,7 +86,7 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { c.gtk_widget_set_size_request(label_text_widget, 100, 1); } - // Create a Box in which we'll later keep either Surface or Paned + // Create a Box in which we'll later keep either Surface or Split const box_widget = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0); c.gtk_widget_set_hexpand(box_widget, 1); c.gtk_widget_set_vexpand(box_widget, 1); @@ -105,7 +98,6 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { }); errdefer surface.destroy(window.app.core_app.alloc); surface.setContainer(.{ .tab_ = self }); - self.child = .{ .surface = surface }; self.elem = .{ .surface = surface }; // Add Surface to the Tab @@ -148,29 +140,16 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { surface.grabFocus(); } -/// Deinits tab by deiniting child if child is Paned. +/// Deinits tab by deiniting child elem. pub fn deinit(self: *Tab) void { - switch (self.child) { - .none => return, - .surface => |s| s.shutdown(), - .paned => |paned| { - paned.deinit(self.window.app.core_app.alloc); - self.window.app.core_app.alloc.destroy(paned); - }, - } -} - -/// Remove the current child from the Tab. Noop if no child set. -pub fn removeChild(self: *Tab) void { - const widget = self.child.widget() orelse return; - c.gtk_box_remove(self.box, widget); - - self.child = .none; + self.elem.shutdown(); } // TODO: move this /// Replace the surface element that this tab is showing. pub fn replaceElem(self: *Tab, elem: Surface.Container.Elem) void { + // _ = c.g_object_ref_sink(self.elem.widget()); + // Remove our previous widget c.gtk_box_remove(self.box, self.elem.widget()); @@ -186,15 +165,6 @@ pub fn closeElem(self: *Tab) void { self.window.closeTab(self); } -/// Sets child to given child and sets parent on child. -pub fn setChild(self: *Tab, child: Child) void { - const widget = child.widget() orelse return; - c.gtk_box_append(self.box, widget); - - child.setParent(.{ .tab = self }); - self.child = child; -} - fn gtkTabCloseClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void { const tab: *Tab = @ptrCast(@alignCast(ud)); const window = tab.window; diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index d12439f4a..7129599d6 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -16,10 +16,8 @@ const input = @import("../../input.zig"); const CoreSurface = @import("../../Surface.zig"); const App = @import("App.zig"); -const Paned = @import("Paned.zig"); const Surface = @import("Surface.zig"); const Tab = @import("Tab.zig"); -const Position = @import("relation.zig").Position; const icon = @import("icon.zig"); const c = @import("c.zig"); @@ -256,75 +254,6 @@ pub fn closeTab(self: *Window, tab: *Tab) void { if (remaining > 0) self.focusCurrentTab(); } -/// Close the surface. This surface must be definitely part of this window. -pub fn closeSurface(self: *Window, surface: *Surface) void { - assert(surface.container.window().? == self); - - // TODO: fixme - // switch (surface.parent) { - // .none => unreachable, - // .tab => |tab| self.closeTab(tab), - // .paned => |paned_tuple| { - // const paned = paned_tuple[0]; - // const position = paned_tuple[1]; - // - // self.closeSurfaceInPaned(surface, paned, position); - // }, - // } -} - -fn closeSurfaceInPaned(self: *Window, surface: *Surface, paned: *Paned, position: Position) void { - const sibling_child, const sibling_widget = switch (position) { - .start => .{ - paned.child2, - c.gtk_paned_get_end_child(paned.paned), - }, - .end => .{ - paned.child1, - c.gtk_paned_get_start_child(paned.paned), - }, - }; - - // Keep explicit reference to sibling's widget (gl_area, or Paned), so it's - // not destroyed when we remove it from GtkPaned. - const sibling_object: *c.GObject = @ptrCast(sibling_widget); - _ = c.g_object_ref(sibling_object); - defer c.g_object_unref(sibling_object); - - // Remove reference on the surface we're closing - surface.setParent(.none); - - // Remove children. - paned.removeChildren(); - // Don't need to call paned.deinit, because we already removed children. - defer self.app.core_app.alloc.destroy(paned); - - switch (paned.parent) { - .none => unreachable, - .tab => |tab| { - // If parent of Paned we belong to is a tab, we can - // replace the child with the other surface - tab.removeChild(); - tab.setChild(sibling_child); - }, - .paned => |parent_paned_tuple| { - const parent_paned = parent_paned_tuple[0]; - const parent_paned_position = parent_paned_tuple[1]; - - parent_paned.replaceChildInPosition(sibling_child, parent_paned_position); - }, - } - - switch (sibling_child) { - .surface => |s| s.grabFocus(), - .paned => |p| { - // Focus on first surface in sibling Paned - p.focusFirstSurfaceInPosition(position); - }, - else => {}, - } -} - /// Returns true if this window has any tabs. pub fn hasTabs(self: *const Window) bool { return c.gtk_notebook_get_n_pages(self.notebook) > 1; diff --git a/src/apprt/gtk/relation.zig b/src/apprt/gtk/relation.zig deleted file mode 100644 index b14b0b86e..000000000 --- a/src/apprt/gtk/relation.zig +++ /dev/null @@ -1,40 +0,0 @@ -const Surface = @import("Surface.zig"); -const Paned = @import("Paned.zig"); -const Tab = @import("Tab.zig"); -const c = @import("c.zig"); - -pub const Position = enum { - start, - end, -}; - -pub const Parent = union(enum) { - none, - tab: *Tab, - paned: struct { - *Paned, - Position, - }, -}; - -pub const Child = union(enum) { - none, - surface: *Surface, - paned: *Paned, - - pub fn setParent(self: Child, parent: Parent) void { - switch (self) { - .none => return, - .surface => |surface| surface.setParent(parent), - .paned => |paned| paned.setParent(parent), - } - } - - pub fn widget(self: Child) ?*c.GtkWidget { - return switch (self) { - .none => null, - .paned => |paned| @ptrCast(@alignCast(paned.paned)), - .surface => |surface| @ptrCast(surface.gl_area), - }; - } -};