mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-22 19:56:08 +03:00
apprt/gkt: a lot of things are broken
This commit is contained in:
@ -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.
|
||||
|
@ -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| {
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user