From aba1b85503b07d1a898c7739dc0ca72c7d914b6b Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Sat, 30 Sep 2023 00:43:17 -0400 Subject: [PATCH] Add `Parent` and `Child` types for tracking splits; Add methods for adding start and end children in `Paned` widget; --- src/apprt/gtk/Paned.zig | 93 +++++++++++++++++++++++++++++++++++---- src/apprt/gtk/Surface.zig | 8 ++++ src/apprt/gtk/Tab.zig | 15 +++++-- src/apprt/gtk/parent.zig | 16 +++++++ 4 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 src/apprt/gtk/parent.zig diff --git a/src/apprt/gtk/Paned.zig b/src/apprt/gtk/Paned.zig index c6f6a600d..88cd21158 100644 --- a/src/apprt/gtk/Paned.zig +++ b/src/apprt/gtk/Paned.zig @@ -2,33 +2,40 @@ const Paned = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; +const assert = std.debug.assert; const font = @import("../../font/main.zig"); const CoreSurface = @import("../../Surface.zig"); const Window = @import("Window.zig"); const Surface = @import("Surface.zig"); const Tab = @import("Tab.zig"); +const Position = @import("parent.zig").Position; +const Parent = @import("parent.zig").Parent; const c = @import("c.zig"); const Child = union(enum) { surface: *Surface, paned: *Paned, empty: void, + + const Self = @This(); + + fn is_empty(self: Self) bool { + switch (self) { + Child.empty => return true, + else => return false, + } + } }; -const Parent = union(enum) { - tab: *Tab, - paned: *Paned, -}; - -// We'll need to keep a reference to the Window this belongs to for various reasons -window: *c.GtkWindow, +/// We'll need to keep a reference to the Window this belongs to for various reasons +Window: *c.GtkWindow, // We keep track of the tab label's text so that if a child widget of this pane // gets focus (and is a Surface) we can reset the tab label appropriately label_text: *c.GtkWidget, -// Our actual GtkPaned widget +/// Our actual GtkPaned widget paned: *c.GtkPaned, // We have two children, each of which can be either a Surface, another pane, @@ -96,3 +103,73 @@ pub fn newSurface(self: *Paned, parent_: ?*CoreSurface) !*Surface { }); return surface; } + +fn addChild1Surface(self: *Paned, surface: *Surface) void { + assert(self.child1.is_empty()); + self.child1 = Child{ .surface = surface }; + surface.parent = Surface.Parent{ .paned = .{ self, Position.start } }; + c.gtk_paned_pack1(@ptrCast(self.paned), @ptrCast(surface.gl_area), 1, 0); + if (self.child2.is_empty()) { + c.gtk_paned_set_position(self.paned, 100); + } else { + c.gtk_paned_set_position(self.paned, 50); + } +} + +fn addChild2Surface(self: *Paned, surface: *Surface) void { + assert(self.child1.is_empty()); + self.child2 = Child{ .surface = surface }; + surface.parent = Surface.Parent{ .paned = .{ self, Position.end } }; + c.gtk_paned_pack2(@ptrCast(self.paned), @ptrCast(surface.gl_area), 1, 0); + if (self.child2.is_empty()) { + c.gtk_paned_set_position(self.paned, 0); + } else { + c.gtk_paned_set_position(self.paned, 50); + } +} + +fn addChild1Paned(self: *Paned, child: *Paned) void { + assert(self.child1.is_empty()); + self.child1 = Child{ .paned = child }; + child.parent = Parent{ .paned = .{ self, Position.start } }; + c.gtk_paned_pack1(@ptrCast(self.paned), @ptrCast(child.paned), 1, 0); + if (self.child2.is_empty()) { + c.gtk_paned_set_position(self.paned, 100); + } else { + c.gtk_paned_set_position(self.paned, 50); + } +} + +fn addChild2Paned(self: *Paned, child: *Paned) void { + assert(self.child1.is_empty()); + self.child2 = Child{ .paned = child }; + child.parent = Parent{ .paned = .{ self, Position.end } }; + c.gtk_paned_pack2(@ptrCast(self.paned), @ptrCast(child.paned), 1, 0); + if (self.child2.is_empty()) { + c.gtk_paned_set_position(self.paned, 0); + } else { + c.gtk_paned_set_position(self.paned, 50); + } +} + +fn removeChild1(self: *Paned) void { + assert(!self.child1.is_empty()); + // todo +} + +fn removeChild2(self: *Paned) void { + assert(!self.child1.is_empty()); + // todo +} + +pub fn splitStartPosition(self: *Paned, orientation: c.GtkOrientation) !void { + _ = orientation; + _ = self; + // todo +} + +pub fn splitEndPosition(self: *Paned, orientation: c.GtkOrientation) !void { + _ = orientation; + _ = self; + // todo +} diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 66ccb2047..b5572607b 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -12,8 +12,12 @@ const terminal = @import("../../terminal/main.zig"); const CoreSurface = @import("../../Surface.zig"); const App = @import("App.zig"); +const Paned = @import("Paned.zig"); +const Tab = @import("Tab.zig"); const Window = @import("Window.zig"); const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig"); +const Position = @import("parent.zig").Position; +const Parent = @import("parent.zig").Parent; const inspector = @import("inspector.zig"); const gtk_key = @import("key.zig"); const c = @import("c.zig"); @@ -66,6 +70,9 @@ app: *App, /// The window we're part of window: *Window, +/// Our parent widget +parent: Parent, + /// Our GTK area gl_area: *c.GtkGLArea, @@ -154,6 +161,7 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { self.* = .{ .app = app, .window = opts.window, + .parent = Parent.none, .gl_area = opts.gl_area, .title = if (opts.title_label) |label| .{ .label = label, diff --git a/src/apprt/gtk/Tab.zig b/src/apprt/gtk/Tab.zig index d0cce3650..7cfad8e63 100644 --- a/src/apprt/gtk/Tab.zig +++ b/src/apprt/gtk/Tab.zig @@ -9,6 +9,10 @@ const Surface = @import("Surface.zig"); const Window = @import("Window.zig"); const c = @import("c.zig"); +const log = std.log.scoped(.gtk); + +const GHOSTTY_TAB = "ghostty_tab"; + const Child = union(enum) { surface: *Surface, paned: *Paned, @@ -30,7 +34,7 @@ focus_child: *Surface, pub fn create(alloc: Allocator, window: *Window, parent_: ?*CoreSurface) !*Tab { var tab = try alloc.create(Tab); errdefer alloc.destroy(tab); - try tab.init(window, _parent); + try tab.init(window, parent_); } pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { @@ -80,7 +84,7 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { 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); - self.box = ptrCast(box_widget); + self.box = @ptrCast(box_widget); // Initialize the GtkGLArea and attach it to our surface. // The surface starts in the "unrealized" state because we have to @@ -114,7 +118,7 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { } // Set the userdata of the close button so it points to this page. - c.g_object_set_data(@ptrCast(box), GHOSTTY_TAB, self); + c.g_object_set_data(@ptrCast(box_widget), GHOSTTY_TAB, self); // Switch to the new tab c.gtk_notebook_set_current_page(self.notebook, page_idx); @@ -123,3 +127,8 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { // creating a window we want to always focus on the widget. _ = c.gtk_widget_grab_focus(box_widget); } + +fn gtkTabCloseClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void { + _ = ud; + // todo +} diff --git a/src/apprt/gtk/parent.zig b/src/apprt/gtk/parent.zig new file mode 100644 index 000000000..7c4539fa0 --- /dev/null +++ b/src/apprt/gtk/parent.zig @@ -0,0 +1,16 @@ +const Paned = @import("Paned.zig"); +const Tab = @import("Tab.zig"); + +pub const Position = enum { + start, + end, +}; + +pub const Parent = union(enum) { + none: void, + tab: *Tab, + paned: struct { + *Paned, + Position, + }, +};