gtk: fix replacing of splits, remove dead code

This commit is contained in:
Thorsten Ball
2023-11-23 19:45:19 +01:00
committed by Mitchell Hashimoto
parent ecbe910714
commit 8cf9d97ac3
6 changed files with 61 additions and 493 deletions

View File

@ -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,
};
}

View File

@ -11,8 +11,6 @@ const CoreSurface = @import("../../Surface.zig");
const Surface = @import("Surface.zig"); const Surface = @import("Surface.zig");
const Tab = @import("Tab.zig"); const Tab = @import("Tab.zig");
const Position = @import("relation.zig").Position;
const Child = @import("relation.zig").Child;
const c = @import("c.zig"); const c = @import("c.zig");
const log = std.log.scoped(.gtk); const log = std.log.scoped(.gtk);
@ -92,113 +90,38 @@ pub fn init(
surface.grabFocus(); surface.grabFocus();
} }
/// Focus on first Surface that can be found in given position. If there's a /// Remove the top left child.
/// Split in the position, it will focus on the first surface in that position. pub fn removeTopLeft(self: *Split) void {
pub fn focusFirstSurfaceInPosition(self: *Split, position: Position) void { self.removeChild(self.top_left, self.bottom_right);
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. /// 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 // Remove our children since we are going to no longer be
// a split anyways. This prevents widgets with multiple parents. // a split anyways. This prevents widgets with multiple parents.
self.removeChildren(); self.removeChildren();
// Our container must become whatever our bottom right is // Our container must become whatever our top left is
self.container.replace(self.bottom_right); 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 // 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_start_child(@ptrCast(self.paned), null);
c.gtk_paned_set_end_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,
};
}

View File

@ -17,7 +17,6 @@ const Split = @import("Split.zig");
const Tab = @import("Tab.zig"); const Tab = @import("Tab.zig");
const Window = @import("Window.zig"); const Window = @import("Window.zig");
const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig"); const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
const Parent = @import("relation.zig").Parent;
const inspector = @import("inspector.zig"); const inspector = @import("inspector.zig");
const gtk_key = @import("key.zig"); const gtk_key = @import("key.zig");
const c = @import("c.zig"); const c = @import("c.zig");
@ -79,6 +78,22 @@ pub const Container = union(enum) {
.split => |s| &s.container, .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. /// 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 /// from a surface to a split or a split back to a surface or
/// a split to a nested split and so on. /// a split to a nested split and so on.
pub fn replace(self: Container, elem: Elem) void { 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 // Move the element into the container
switch (self) { switch (self) {
.none => {}, .none => {},
.tab_ => |t| t.replaceElem(elem), .tab_ => |t| {
t.replaceElem(elem);
},
inline .split_tl, .split_br => |ptr| { inline .split_tl, .split_br => |ptr| {
const s = self.split().?; const s = self.split().?;
s.replace(ptr, elem); s.replace(ptr, elem);
@ -137,13 +156,23 @@ pub const Container = union(enum) {
/// children to effectively notify they're containing that /// children to effectively notify they're containing that
/// all children at this level are exiting. /// all children at this level are exiting.
pub fn remove(self: Container) void { pub fn remove(self: Container) void {
log.warn("Container.remove", .{});
switch (self) { switch (self) {
.none => {}, .none => {},
.tab_ => |t| t.closeElem(), .tab_ => |t| t.closeElem(),
.split_tl => self.split().?.removeTopLeft(), .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 /// 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( pub fn setMouseShape(
self: *Surface, self: *Surface,
shape: terminal.MouseShape, shape: terminal.MouseShape,

View File

@ -10,9 +10,6 @@ const font = @import("../../font/main.zig");
const input = @import("../../input.zig"); const input = @import("../../input.zig");
const CoreSurface = @import("../../Surface.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 Surface = @import("Surface.zig");
const Window = @import("Window.zig"); const Window = @import("Window.zig");
const c = @import("c.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. /// The element of this tab so that we can handle splits and so on.
elem: Surface.Container.Elem, 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 // 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 // when we switch to another Tab. Then when we switch back to this tab, we
// can easily re-focus that terminal. // can easily re-focus that terminal.
@ -57,7 +51,6 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
.label_text = undefined, .label_text = undefined,
.box = undefined, .box = undefined,
.elem = undefined, .elem = undefined,
.child = undefined,
.focus_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); 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); const box_widget = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0);
c.gtk_widget_set_hexpand(box_widget, 1); c.gtk_widget_set_hexpand(box_widget, 1);
c.gtk_widget_set_vexpand(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); errdefer surface.destroy(window.app.core_app.alloc);
surface.setContainer(.{ .tab_ = self }); surface.setContainer(.{ .tab_ = self });
self.child = .{ .surface = surface };
self.elem = .{ .surface = surface }; self.elem = .{ .surface = surface };
// Add Surface to the Tab // Add Surface to the Tab
@ -148,29 +140,16 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
surface.grabFocus(); surface.grabFocus();
} }
/// Deinits tab by deiniting child if child is Paned. /// Deinits tab by deiniting child elem.
pub fn deinit(self: *Tab) void { pub fn deinit(self: *Tab) void {
switch (self.child) { self.elem.shutdown();
.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;
} }
// TODO: move this // TODO: move this
/// Replace the surface element that this tab is showing. /// Replace the surface element that this tab is showing.
pub fn replaceElem(self: *Tab, elem: Surface.Container.Elem) void { pub fn replaceElem(self: *Tab, elem: Surface.Container.Elem) void {
// _ = c.g_object_ref_sink(self.elem.widget());
// Remove our previous widget // Remove our previous widget
c.gtk_box_remove(self.box, self.elem.widget()); c.gtk_box_remove(self.box, self.elem.widget());
@ -186,15 +165,6 @@ pub fn closeElem(self: *Tab) void {
self.window.closeTab(self); 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 { fn gtkTabCloseClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void {
const tab: *Tab = @ptrCast(@alignCast(ud)); const tab: *Tab = @ptrCast(@alignCast(ud));
const window = tab.window; const window = tab.window;

View File

@ -16,10 +16,8 @@ const input = @import("../../input.zig");
const CoreSurface = @import("../../Surface.zig"); const CoreSurface = @import("../../Surface.zig");
const App = @import("App.zig"); const App = @import("App.zig");
const Paned = @import("Paned.zig");
const Surface = @import("Surface.zig"); const Surface = @import("Surface.zig");
const Tab = @import("Tab.zig"); const Tab = @import("Tab.zig");
const Position = @import("relation.zig").Position;
const icon = @import("icon.zig"); const icon = @import("icon.zig");
const c = @import("c.zig"); const c = @import("c.zig");
@ -256,75 +254,6 @@ pub fn closeTab(self: *Window, tab: *Tab) void {
if (remaining > 0) self.focusCurrentTab(); 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. /// Returns true if this window has any tabs.
pub fn hasTabs(self: *const Window) bool { pub fn hasTabs(self: *const Window) bool {
return c.gtk_notebook_get_n_pages(self.notebook) > 1; return c.gtk_notebook_get_n_pages(self.notebook) > 1;

View File

@ -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),
};
}
};