From 0e043bc0e479d17b60c4401ca82742ee2269e4f0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 25 Sep 2024 11:25:20 -0700 Subject: [PATCH] apprt: transition all hasDecls in App.zig to use the new action dispatch --- src/App.zig | 33 +++++++++++++++++---------------- src/apprt/action.zig | 26 +++++++++++++++++++++++++- src/apprt/glfw.zig | 12 +++++++----- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/App.zig b/src/App.zig index 65153127a..4462c7c83 100644 --- a/src/App.zig +++ b/src/App.zig @@ -138,7 +138,13 @@ pub fn addSurface(self: *App, rt_surface: *apprt.Surface) !void { // Since we have non-zero surfaces, we can cancel the quit timer. // It is up to the apprt if there is a quit timer at all and if it // should be canceled. - if (@hasDecl(apprt.App, "cancelQuitTimer")) rt_surface.app.cancelQuitTimer(); + rt_surface.app.performAction( + .{ .surface = &rt_surface.core_surface }, + .quit_timer, + .stop, + ) catch |err| { + log.warn("error stopping quit timer err={}", .{err}); + }; } /// Delete the surface from the known surface list. This will NOT call the @@ -166,8 +172,13 @@ pub fn deleteSurface(self: *App, rt_surface: *apprt.Surface) void { // If we have no surfaces, we can start the quit timer. It is up to the // apprt to determine if this is necessary. - if (@hasDecl(apprt.App, "startQuitTimer") and - self.surfaces.items.len == 0) rt_surface.app.startQuitTimer(); + if (self.surfaces.items.len == 0) rt_surface.app.performAction( + .{ .surface = &rt_surface.core_surface }, + .quit_timer, + .start, + ) catch |err| { + log.warn("error starting quit timer err={}", .{err}); + }; } /// The last focused surface. This is only valid while on the main thread @@ -194,7 +205,7 @@ fn drainMailbox(self: *App, rt_app: *apprt.App) !void { log.debug("mailbox message={s}", .{@tagName(message)}); switch (message) { .reload_config => try self.reloadConfig(rt_app), - .open_config => try self.openConfig(rt_app), + .open_config => try self.performAction(rt_app, .open_config), .new_window => |msg| try self.newWindow(rt_app, msg), .close => |surface| try self.closeSurface(surface), .quit => try self.setQuit(), @@ -205,12 +216,6 @@ fn drainMailbox(self: *App, rt_app: *apprt.App) !void { } } -pub fn openConfig(self: *App, rt_app: *apprt.App) !void { - _ = self; - log.debug("opening configuration", .{}); - try rt_app.openConfig(); -} - pub fn reloadConfig(self: *App, rt_app: *apprt.App) !void { log.debug("reloading configuration", .{}); if (try rt_app.reloadConfig()) |new| { @@ -316,13 +321,9 @@ pub fn performAction( .ignore => {}, .quit => try self.setQuit(), .new_window => try self.newWindow(rt_app, .{ .parent = null }), - .open_config => try self.openConfig(rt_app), + .open_config => try rt_app.performAction(.app, .open_config, {}), .reload_config => try self.reloadConfig(rt_app), - .close_all_windows => { - if (@hasDecl(apprt.App, "closeAllWindows")) { - rt_app.closeAllWindows(); - } else log.warn("runtime doesn't implement closeAllWindows", .{}); - }, + .close_all_windows => try rt_app.performAction(.app, .close_all_windows, {}), } } diff --git a/src/apprt/action.zig b/src/apprt/action.zig index 38b318109..d0f551646 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -2,10 +2,34 @@ const std = @import("std"); const assert = std.debug.assert; const CoreSurface = @import("../Surface.zig"); -/// The possible actions an apprt has to react to. +/// The possible actions an apprt has to react to. Actions are one-way +/// messages that are sent to the app runtime to trigger some behavior. +/// +/// Actions are very often key binding actions but can also be triggered +/// by lifecycle events. For example, the `quit_timer` action is not bindable. +/// +/// Importantly, actions are generally OPTIONAL to implement by an apprt. +/// Required functionality is called directly on the runtime structure so +/// there is a compiler error if an action is not implemented. pub const Action = union(enum) { + /// Open a new window. The target determines whether properties such + /// as font size should be inherited. new_window, + /// Close all open windows. + close_all_windows, + + /// Open the Ghostty configuration. This is platform-specific about + /// what it means; it can mean opening a dedicated UI or just opening + /// a file in a text editor. + open_config, + + /// Called when there are no more surfaces and the app should quit + /// after the configured delay. This can be cancelled by sending + /// another quit_timer action with "stop". Multiple "starts" shouldn't + /// happen and can be ignored or cause a restart it isn't that important. + quit_timer: enum { start, stop }, + /// The enum of keys in the tagged union. pub const Key = @typeInfo(Action).Union.tag_type.?; diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index e5405eb32..8bf07c8a4 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -141,12 +141,14 @@ pub const App = struct { .app => null, .surface => |v| v, }), - } - } - /// Open the configuration in the system editor. - pub fn openConfig(self: *App) !void { - try configpkg.edit.open(self.app.alloc); + .open_config => try configpkg.edit.open(self.app.alloc), + + // Unimplemented + .close_all_windows, + .quit_timer, + => log.warn("unimplemented action={}", .{action}), + } } /// Reload the configuration. This should return the new configuration.