apprt/gkt: a lot of things are broken

This commit is contained in:
Mitchell Hashimoto
2023-11-02 10:48:57 -07:00
parent 79a9d417d1
commit 4c1300ab69
4 changed files with 130 additions and 78 deletions

View File

@ -12,7 +12,6 @@ const CoreSurface = @import("../../Surface.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");
@ -25,27 +24,8 @@ paned: *c.GtkPaned,
container: Surface.Container,
/// The elements of this split panel.
top_left: Elem,
bottom_right: Elem,
/// Elem is the possible element of the split.
pub const Elem = union(enum) {
/// A surface is a leaf element of the split -- a terminal surface.
surface: *Surface,
/// A split is a nested split within a split. This lets you for example
/// have a horizontal split with a vertical split on the left side
/// (amongst all other possible combinations).
split: *Split,
/// Returns the GTK widget to add to the paned for the given element
pub fn widget(self: Child) *c.GtkWidget {
return switch (self) {
.surface => |surface| @ptrCast(surface.gl_area),
.split => |split| @ptrCast(@alignCast(split.paned)),
};
}
};
top_left: Surface.Container.Elem,
bottom_right: Surface.Container.Elem,
/// Create a new split panel with the given sibling surface in the given
/// direction. The direction is where the new surface will be initialized.
@ -73,7 +53,6 @@ pub fn init(
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);
@ -93,25 +72,21 @@ pub fn init(
sibling.container = .{ .split_tl = &self.top_left };
surface.container = .{ .split_br = &self.bottom_right };
// If the sibling is already in a split, then we need to
// nest them properly. This gets the pointer to the split element
// that the original split was in, then updates it to point to this
// split. This split then contains the surface as an element.
if (container.splitElem()) |parent_elem| {
parent_elem.* = .{ .split = self };
}
self.* = .{
.paned = @ptrCast(paned),
.container = container,
.top_left = .{ .surface = sibling },
.bottom_right = .{ .surface = surface },
};
}
/// Set the parent of Split.
pub fn setParent(self: *Split, parent: Parent) void {
self.parent = parent;
// Replace the previous containers element with our split.
// This allows a non-split to become a split, a split to
// become a nested split, etc.
container.replace(.{ .split = self });
// Update our children so that our GL area is properly
// added to the paned.
self.updateChildren();
}
/// Focus on first Surface that can be found in given position. If there's a
@ -211,6 +186,23 @@ fn removeChildInPosition(self: *Split, position: Position) void {
}
}
// TODO: ehhhhhh
pub fn replace(
self: *Split,
ptr: *Surface.Container.Elem,
new: Surface.Container.Elem,
) void {
// We can write our element directly. There's nothing special.
assert(&self.top_left == ptr or &self.bottom_right == ptr);
ptr.* = new;
// Update our paned children. This will reset the divider
// position but we want to keep it in place so save and restore it.
const pos = c.gtk_paned_get_position(self.paned);
self.updateChildren();
c.gtk_paned_set_position(self.paned, pos);
}
/// Update the paned children to represent the current state.
/// This should be called anytime the top/left or bottom/right
/// element is changed.

View File

@ -32,9 +32,6 @@ pub const Options = struct {
/// The parent surface to inherit settings such as font size, working
/// directory, etc. from.
parent2: ?*CoreSurface = null,
/// The parent this surface is created under.
parent: Parent,
};
/// The container that this surface is directly attached to.
@ -49,8 +46,33 @@ pub const Container = union(enum) {
/// A split within a split hierarchy. The key determines the
/// position of the split within the parent split.
split_tl: *Split.Elem,
split_br: *Split.Elem,
split_tl: *Elem,
split_br: *Elem,
/// Elem is the possible element of any container. A container can
/// hold both a surface and a split. Any valid container should
/// have an Elem value so that it can be properly used with
/// splits.
pub const Elem = union(enum) {
/// A surface is a leaf element of the split -- a terminal
/// surface.
surface: *Surface,
/// A split is a nested split within a split. This lets you
/// for example have a horizontal split with a vertical split
/// on the left side (amongst all other possible
/// combinations).
split: *Split,
/// Returns the GTK widget to add to the paned for the given
/// element
pub fn widget(self: Elem) *c.GtkWidget {
return switch (self) {
.surface => |s| @ptrCast(s.gl_area),
.split => |s| @ptrCast(@alignCast(s.paned)),
};
}
};
/// Returns the window that this surface is attached to.
pub fn window(self: Container) ?*Window {
@ -85,14 +107,19 @@ pub const Container = union(enum) {
};
}
/// Returns the element of the split that this container
/// is attached to.
pub fn splitElem(self: Container) ?*Split.Elem {
return switch (self) {
.none, .tab_ => null,
.split_tl => |ptr| ptr,
.split_br => |ptr| ptr,
};
/// Replace the container's element with this element. This is
/// used by children to modify their parents to for example change
/// 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 {
switch (self) {
.none => {},
.tab_ => |t| t.replaceElem(elem),
inline .split_tl, .split_br => |ptr| {
const s = self.split().?;
s.replace(ptr, elem);
},
}
}
};
@ -111,9 +138,6 @@ container: Container = .{ .none = {} },
/// The app we're part of
app: *App,
/// The parent we belong to
parent: Parent,
/// Our GTK area
gl_area: *c.GtkGLArea,
@ -158,9 +182,20 @@ pub fn create(alloc: Allocator, app: *App, opts: Options) !*Surface {
pub fn init(self: *Surface, app: *App, opts: Options) !void {
const widget: *c.GtkWidget = c.gtk_gl_area_new();
const gl_area: *c.GtkGLArea = @ptrCast(widget);
// We grab the floating reference to GL area. This lets the
// GL area be moved around i.e. between a split, a tab, etc.
// without having to be really careful about ordering to
// prevent a destroy.
// TODO: unref in deinit
_ = c.g_object_ref_sink(@ptrCast(gl_area));
errdefer c.g_object_unref(@ptrCast(gl_area));
// We want the gl area to expand to fill the parent container.
c.gtk_widget_set_hexpand(widget, 1);
c.gtk_widget_set_vexpand(widget, 1);
// Various other GL properties
c.gtk_widget_set_cursor_from_name(@ptrCast(gl_area), "text");
c.gtk_gl_area_set_required_version(gl_area, 3, 3);
c.gtk_gl_area_set_has_stencil_buffer(gl_area, 0);
@ -226,7 +261,6 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void {
self.* = .{
.app = app,
.container = .{ .none = {} },
.parent = opts.parent,
.gl_area = gl_area,
.title_text_buf = undefined,
.title_text_buf_len = 0,
@ -450,19 +484,21 @@ pub fn getTitleLabel(self: *Surface) ?*c.GtkWidget {
pub fn newSplit(self: *Surface, direction: input.SplitDirection) !void {
log.debug("splitting direction={}", .{direction});
const alloc = self.app.core_app.alloc;
_ = try Split.create(alloc, self, direction);
switch (self.parent) {
.none => return,
.paned => |parent_paned_tuple| {
const paned = parent_paned_tuple[0];
const position = parent_paned_tuple[1];
try paned.splitSurfaceInPosition(position, direction);
},
.tab => |tab| {
try tab.splitSurface(direction);
},
}
// switch (self.parent) {
// .none => return,
// .paned => |parent_paned_tuple| {
// const paned = parent_paned_tuple[0];
// const position = parent_paned_tuple[1];
//
// try paned.splitSurfaceInPosition(position, direction);
// },
// .tab => |tab| {
// try tab.splitSurface(direction);
// },
// }
}
pub fn newTab(self: *Surface) !void {
@ -852,6 +888,7 @@ fn gtkRealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void {
fn gtkUnrealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void {
_ = area;
log.debug("gl surface unrealized", .{});
const self = userdataSelf(ud.?);
self.core_surface.renderer.displayUnrealized();
@ -973,7 +1010,8 @@ fn gtkMouseDown(
// If we have siblings, we also update the title, since it means
// another sibling might have updated the title.
if (self.parent != Parent.tab) self.updateTitleLabels();
// TODO: fixme
//if (self.parent != Parent.tab) self.updateTitleLabels();
}
self.core_surface.mouseButtonCallback(.press, button, mods) catch |err| {

View File

@ -22,12 +22,21 @@ const log = std.log.scoped(.gtk);
pub const GHOSTTY_TAB = "ghostty_tab";
window: *Window,
/// The tab label.
label_text: *c.GtkLabel,
// We'll put our children into this box instead of packing them directly, so
// that we can send the box into `c.g_signal_connect_data` for the close button
/// We'll put our children into this box instead of packing them
/// directly, so that we can send the box into `c.g_signal_connect_data`
/// for the close button
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.
@ -47,6 +56,7 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
.window = window,
.label_text = undefined,
.box = undefined,
.elem = undefined,
.child = undefined,
.focus_child = undefined,
};
@ -92,11 +102,11 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
// Create the initial surface since all tabs start as a single non-split
var surface = try Surface.create(window.app.core_app.alloc, window.app, .{
.parent2 = parent_,
.parent = .{ .tab = self },
});
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
const gl_area_widget = @as(*c.GtkWidget, @ptrCast(surface.gl_area));
@ -178,6 +188,17 @@ pub fn removeChild(self: *Tab) void {
self.child = .none;
}
// TODO: move this
/// Replace the surface element that this tab is showing.
pub fn replaceElem(self: *Tab, elem: Surface.Container.Elem) void {
// Remove our previous widget
c.gtk_box_remove(self.box, self.elem.widget());
// Add our new one
c.gtk_box_append(self.box, elem.widget());
self.elem = elem;
}
/// Sets child to given child and sets parent on child.
pub fn setChild(self: *Tab, child: Child) void {
const widget = child.widget() orelse return;

View File

@ -260,16 +260,17 @@ pub fn closeTab(self: *Window, tab: *Tab) void {
pub fn closeSurface(self: *Window, surface: *Surface) void {
assert(surface.container.window().? == self);
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);
},
}
// 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 {