Add hook system.

This commit is contained in:
Greyson Parrelli
2026-08-11 16:24:53 -04:00
parent 73a0187db5
commit dd60e52005
9 changed files with 788 additions and 3 deletions
+36
View File
@@ -20,6 +20,7 @@ const View = @This();
pub const Side = Layout.Side;
pub const Kind = Pane.Kind;
pub const Status = Pane.Status;
/// Where a dragged pane would land.
pub const Target = struct {
@@ -72,11 +73,13 @@ closing: bool = false,
on_empty: *const fn (ctx: ?*anyopaque) void,
on_title: *const fn (ctx: ?*anyopaque) void,
on_status: *const fn (ctx: ?*anyopaque) void,
ctx: ?*anyopaque = null,
pub const Callbacks = struct {
on_empty: *const fn (ctx: ?*anyopaque) void,
on_title: *const fn (ctx: ?*anyopaque) void,
on_status: *const fn (ctx: ?*anyopaque) void,
ctx: ?*anyopaque,
};
@@ -90,6 +93,7 @@ pub fn create(alloc: std.mem.Allocator, cbs: Callbacks) !*View {
.layout = .{ .alloc = alloc },
.on_empty = cbs.on_empty,
.on_title = cbs.on_title,
.on_status = cbs.on_status,
.ctx = cbs.ctx,
};
@@ -215,6 +219,10 @@ pub fn closePane(self: *View, pane: *Pane) void {
self.setFocused(self.panes.items[next]);
self.panes.items[next].grabFocus();
self.on_title(self.ctx);
// Closing a busy pane changes what the tab as a whole is reporting, so
// the row has to be recomputed even though no pane changed its own state.
self.on_status(self.ctx);
}
pub fn setFocused(self: *View, pane: *Pane) void {
@@ -229,6 +237,34 @@ pub fn paneTitleChanged(self: *View, pane: *Pane) void {
if (self.focused == pane or self.panes.items.len == 1) self.on_title(self.ctx);
}
pub fn paneStatusChanged(self: *View, pane: *Pane) void {
_ = pane;
if (self.closing) return;
self.on_status(self.ctx);
}
/// The view's status: the most urgent thing any of its panes is reporting.
///
/// A tab is one row however many panes it holds, so the row has to say
/// something about all of them at once. Urgency wins rather than, say, the
/// focused pane's status, because the whole point of the indicator is to be
/// read from another tab — a pane blocked on a permission prompt matters
/// even if it isn't the one you left focused.
pub fn status(self: *View) Status {
var worst: Status = .idle;
for (self.panes.items) |pane| {
worst = switch (pane.status) {
// Nothing outranks a pane waiting on the user, so this can
// return the moment one is found.
.needs_input => return .needs_input,
.failed => .failed,
.busy => if (worst == .failed) worst else .busy,
.idle => worst,
};
}
return worst;
}
// -------------------------------------------------------------------------
// Layouts