mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
Performable tab and split navigation (#5644)
Partial fixes #5552 (for GTK). This PR adds the core infrastructure for keybind actions that are implemented as runtime app actions to be performable. This is done by having `rt_app.performAction` return a boolean. By default all runtime app actions return `true` (the action was performed) unless they are modified to return `true`/`false` as appropriate. The GTK apprt is modified so that `goto_split`, `previous_tab`, `next_tab`, `last_tab`, and `goto_tab` are performable. macOS support will need to be added in a subsequent commit. This doesn't completely solve the issue for the OP because if the `goto_split` isn't performable there is no fallback to`previous_tab` or `next_tab`. I don't think that the approach taken in #5579 is the right one as it conflates split and tab navigation unconditionally which I don't think is what everyone would want. Either a separate action that explicitly combines the actions or a solution to #3175 will be the ultimate solution I believe.
This commit is contained in:
@ -644,7 +644,7 @@ typedef void (*ghostty_runtime_write_clipboard_cb)(void*,
|
|||||||
ghostty_clipboard_e,
|
ghostty_clipboard_e,
|
||||||
bool);
|
bool);
|
||||||
typedef void (*ghostty_runtime_close_surface_cb)(void*, bool);
|
typedef void (*ghostty_runtime_close_surface_cb)(void*, bool);
|
||||||
typedef void (*ghostty_runtime_action_cb)(ghostty_app_t,
|
typedef bool (*ghostty_runtime_action_cb)(ghostty_app_t,
|
||||||
ghostty_target_s,
|
ghostty_target_s,
|
||||||
ghostty_action_s);
|
ghostty_action_s);
|
||||||
|
|
||||||
|
@ -423,7 +423,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
// MARK: Actions (macOS)
|
// MARK: Actions (macOS)
|
||||||
|
|
||||||
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) {
|
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) -> Bool {
|
||||||
// Make sure it a target we understand so all our action handlers can assert
|
// Make sure it a target we understand so all our action handlers can assert
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
case GHOSTTY_TARGET_APP, GHOSTTY_TARGET_SURFACE:
|
case GHOSTTY_TARGET_APP, GHOSTTY_TARGET_SURFACE:
|
||||||
@ -431,7 +431,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
Ghostty.logger.warning("unknown action target=\(target.tag.rawValue)")
|
Ghostty.logger.warning("unknown action target=\(target.tag.rawValue)")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action dispatch
|
// Action dispatch
|
||||||
@ -541,10 +541,15 @@ extension Ghostty {
|
|||||||
fallthrough
|
fallthrough
|
||||||
case GHOSTTY_ACTION_QUIT_TIMER:
|
case GHOSTTY_ACTION_QUIT_TIMER:
|
||||||
Ghostty.logger.info("known but unimplemented action action=\(action.tag.rawValue)")
|
Ghostty.logger.info("known but unimplemented action action=\(action.tag.rawValue)")
|
||||||
|
return false
|
||||||
default:
|
default:
|
||||||
Ghostty.logger.warning("unknown action action=\(action.tag.rawValue)")
|
Ghostty.logger.warning("unknown action action=\(action.tag.rawValue)")
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we reached here then we assume performed since all unknown actions
|
||||||
|
// are captured in the switch and return false.
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func quit(_ app: ghostty_app_t) {
|
private static func quit(_ app: ghostty_app_t) {
|
||||||
|
24
src/App.zig
24
src/App.zig
@ -161,7 +161,7 @@ pub fn updateConfig(self: *App, rt_app: *apprt.App, config: *const Config) !void
|
|||||||
const applied: *const configpkg.Config = if (applied_) |*c| c else config;
|
const applied: *const configpkg.Config = if (applied_) |*c| c else config;
|
||||||
|
|
||||||
// Notify the apprt that the app has changed configuration.
|
// Notify the apprt that the app has changed configuration.
|
||||||
try rt_app.performAction(
|
_ = try rt_app.performAction(
|
||||||
.app,
|
.app,
|
||||||
.config_change,
|
.config_change,
|
||||||
.{ .config = applied },
|
.{ .config = applied },
|
||||||
@ -180,7 +180,7 @@ pub fn addSurface(
|
|||||||
// Since we have non-zero surfaces, we can cancel the quit timer.
|
// 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
|
// It is up to the apprt if there is a quit timer at all and if it
|
||||||
// should be canceled.
|
// should be canceled.
|
||||||
rt_surface.app.performAction(
|
_ = rt_surface.app.performAction(
|
||||||
.app,
|
.app,
|
||||||
.quit_timer,
|
.quit_timer,
|
||||||
.stop,
|
.stop,
|
||||||
@ -214,7 +214,7 @@ 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
|
// If we have no surfaces, we can start the quit timer. It is up to the
|
||||||
// apprt to determine if this is necessary.
|
// apprt to determine if this is necessary.
|
||||||
if (self.surfaces.items.len == 0) rt_surface.app.performAction(
|
if (self.surfaces.items.len == 0) _ = rt_surface.app.performAction(
|
||||||
.app,
|
.app,
|
||||||
.quit_timer,
|
.quit_timer,
|
||||||
.start,
|
.start,
|
||||||
@ -294,7 +294,7 @@ pub fn newWindow(self: *App, rt_app: *apprt.App, msg: Message.NewWindow) !void {
|
|||||||
break :target .app;
|
break :target .app;
|
||||||
};
|
};
|
||||||
|
|
||||||
try rt_app.performAction(
|
_ = try rt_app.performAction(
|
||||||
target,
|
target,
|
||||||
.new_window,
|
.new_window,
|
||||||
{},
|
{},
|
||||||
@ -419,7 +419,7 @@ pub fn colorSchemeEvent(
|
|||||||
|
|
||||||
// Request our configuration be reloaded because the new scheme may
|
// Request our configuration be reloaded because the new scheme may
|
||||||
// impact the colors of the app.
|
// impact the colors of the app.
|
||||||
try rt_app.performAction(
|
_ = try rt_app.performAction(
|
||||||
.app,
|
.app,
|
||||||
.reload_config,
|
.reload_config,
|
||||||
.{ .soft = true },
|
.{ .soft = true },
|
||||||
@ -437,13 +437,13 @@ pub fn performAction(
|
|||||||
switch (action) {
|
switch (action) {
|
||||||
.unbind => unreachable,
|
.unbind => unreachable,
|
||||||
.ignore => {},
|
.ignore => {},
|
||||||
.quit => try rt_app.performAction(.app, .quit, {}),
|
.quit => _ = try rt_app.performAction(.app, .quit, {}),
|
||||||
.new_window => try self.newWindow(rt_app, .{ .parent = null }),
|
.new_window => _ = try self.newWindow(rt_app, .{ .parent = null }),
|
||||||
.open_config => try rt_app.performAction(.app, .open_config, {}),
|
.open_config => _ = try rt_app.performAction(.app, .open_config, {}),
|
||||||
.reload_config => try rt_app.performAction(.app, .reload_config, .{}),
|
.reload_config => _ = try rt_app.performAction(.app, .reload_config, .{}),
|
||||||
.close_all_windows => try rt_app.performAction(.app, .close_all_windows, {}),
|
.close_all_windows => _ = try rt_app.performAction(.app, .close_all_windows, {}),
|
||||||
.toggle_quick_terminal => try rt_app.performAction(.app, .toggle_quick_terminal, {}),
|
.toggle_quick_terminal => _ = try rt_app.performAction(.app, .toggle_quick_terminal, {}),
|
||||||
.toggle_visibility => try rt_app.performAction(.app, .toggle_visibility, {}),
|
.toggle_visibility => _ = try rt_app.performAction(.app, .toggle_visibility, {}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,7 +569,7 @@ pub fn init(
|
|||||||
errdefer self.io.deinit();
|
errdefer self.io.deinit();
|
||||||
|
|
||||||
// Report initial cell size on surface creation
|
// Report initial cell size on surface creation
|
||||||
try rt_app.performAction(
|
_ = try rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.cell_size,
|
.cell_size,
|
||||||
.{ .width = size.cell.width, .height = size.cell.height },
|
.{ .width = size.cell.width, .height = size.cell.height },
|
||||||
@ -581,7 +581,7 @@ pub fn init(
|
|||||||
const min_window_width_cells: u32 = 10;
|
const min_window_width_cells: u32 = 10;
|
||||||
const min_window_height_cells: u32 = 4;
|
const min_window_height_cells: u32 = 4;
|
||||||
|
|
||||||
try rt_app.performAction(
|
_ = try rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.size_limit,
|
.size_limit,
|
||||||
.{
|
.{
|
||||||
@ -645,7 +645,7 @@ pub fn init(
|
|||||||
size.padding.top +
|
size.padding.top +
|
||||||
size.padding.bottom;
|
size.padding.bottom;
|
||||||
|
|
||||||
rt_app.performAction(
|
_ = rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.initial_size,
|
.initial_size,
|
||||||
.{ .width = final_width, .height = final_height },
|
.{ .width = final_width, .height = final_height },
|
||||||
@ -657,7 +657,7 @@ pub fn init(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (config.title) |title| {
|
if (config.title) |title| {
|
||||||
try rt_app.performAction(
|
_ = try rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.set_title,
|
.set_title,
|
||||||
.{ .title = title },
|
.{ .title = title },
|
||||||
@ -678,7 +678,7 @@ pub fn init(
|
|||||||
break :xdg;
|
break :xdg;
|
||||||
};
|
};
|
||||||
defer alloc.free(title);
|
defer alloc.free(title);
|
||||||
try rt_app.performAction(
|
_ = try rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.set_title,
|
.set_title,
|
||||||
.{ .title = title },
|
.{ .title = title },
|
||||||
@ -831,7 +831,7 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
|
|||||||
// We know that our title should end in 0.
|
// We know that our title should end in 0.
|
||||||
const slice = std.mem.sliceTo(@as([*:0]const u8, @ptrCast(v)), 0);
|
const slice = std.mem.sliceTo(@as([*:0]const u8, @ptrCast(v)), 0);
|
||||||
log.debug("changing title \"{s}\"", .{slice});
|
log.debug("changing title \"{s}\"", .{slice});
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.set_title,
|
.set_title,
|
||||||
.{ .title = slice },
|
.{ .title = slice },
|
||||||
@ -867,7 +867,7 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
|
|||||||
.color_change => |change| {
|
.color_change => |change| {
|
||||||
// Notify our apprt, but don't send a mode 2031 DSR report
|
// Notify our apprt, but don't send a mode 2031 DSR report
|
||||||
// because VT sequences were used to change the color.
|
// because VT sequences were used to change the color.
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.color_change,
|
.color_change,
|
||||||
.{
|
.{
|
||||||
@ -886,7 +886,7 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
|
|||||||
|
|
||||||
.set_mouse_shape => |shape| {
|
.set_mouse_shape => |shape| {
|
||||||
log.debug("changing mouse shape: {}", .{shape});
|
log.debug("changing mouse shape: {}", .{shape});
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_shape,
|
.mouse_shape,
|
||||||
shape,
|
shape,
|
||||||
@ -918,7 +918,7 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
|
|||||||
const str = try self.alloc.dupeZ(u8, w.slice());
|
const str = try self.alloc.dupeZ(u8, w.slice());
|
||||||
defer self.alloc.free(str);
|
defer self.alloc.free(str);
|
||||||
|
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.pwd,
|
.pwd,
|
||||||
.{ .pwd = str },
|
.{ .pwd = str },
|
||||||
@ -969,7 +969,7 @@ fn passwordInput(self: *Surface, v: bool) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Notify our apprt so it can do whatever it wants.
|
// Notify our apprt so it can do whatever it wants.
|
||||||
self.rt_app.performAction(
|
_ = self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.secure_input,
|
.secure_input,
|
||||||
if (v) .on else .off,
|
if (v) .on else .off,
|
||||||
@ -1058,7 +1058,7 @@ fn mouseRefreshLinks(
|
|||||||
self.renderer_state.mouse.point = pos_vp;
|
self.renderer_state.mouse.point = pos_vp;
|
||||||
self.mouse.over_link = true;
|
self.mouse.over_link = true;
|
||||||
self.renderer_state.terminal.screen.dirty.hyperlink_hover = true;
|
self.renderer_state.terminal.screen.dirty.hyperlink_hover = true;
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_shape,
|
.mouse_shape,
|
||||||
.pointer,
|
.pointer,
|
||||||
@ -1071,7 +1071,7 @@ fn mouseRefreshLinks(
|
|||||||
.trim = false,
|
.trim = false,
|
||||||
});
|
});
|
||||||
defer self.alloc.free(str);
|
defer self.alloc.free(str);
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_over_link,
|
.mouse_over_link,
|
||||||
.{ .url = str },
|
.{ .url = str },
|
||||||
@ -1085,7 +1085,7 @@ fn mouseRefreshLinks(
|
|||||||
log.warn("failed to get URI for OSC8 hyperlink", .{});
|
log.warn("failed to get URI for OSC8 hyperlink", .{});
|
||||||
break :link;
|
break :link;
|
||||||
};
|
};
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_over_link,
|
.mouse_over_link,
|
||||||
.{ .url = uri },
|
.{ .url = uri },
|
||||||
@ -1095,12 +1095,12 @@ fn mouseRefreshLinks(
|
|||||||
|
|
||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
} else if (over_link) {
|
} else if (over_link) {
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_shape,
|
.mouse_shape,
|
||||||
self.io.terminal.mouse_shape,
|
self.io.terminal.mouse_shape,
|
||||||
);
|
);
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_over_link,
|
.mouse_over_link,
|
||||||
.{ .url = "" },
|
.{ .url = "" },
|
||||||
@ -1112,7 +1112,7 @@ fn mouseRefreshLinks(
|
|||||||
/// Called when our renderer health state changes.
|
/// Called when our renderer health state changes.
|
||||||
fn updateRendererHealth(self: *Surface, health: renderer.Health) void {
|
fn updateRendererHealth(self: *Surface, health: renderer.Health) void {
|
||||||
log.warn("renderer health status change status={}", .{health});
|
log.warn("renderer health status change status={}", .{health});
|
||||||
self.rt_app.performAction(
|
_ = self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.renderer_health,
|
.renderer_health,
|
||||||
health,
|
health,
|
||||||
@ -1124,7 +1124,7 @@ fn updateRendererHealth(self: *Surface, health: renderer.Health) void {
|
|||||||
/// This should be called anytime `config_conditional_state` changes
|
/// This should be called anytime `config_conditional_state` changes
|
||||||
/// so that the apprt can reload the configuration.
|
/// so that the apprt can reload the configuration.
|
||||||
fn notifyConfigConditionalState(self: *Surface) void {
|
fn notifyConfigConditionalState(self: *Surface) void {
|
||||||
self.rt_app.performAction(
|
_ = self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.reload_config,
|
.reload_config,
|
||||||
.{ .soft = true },
|
.{ .soft = true },
|
||||||
@ -1204,14 +1204,14 @@ pub fn updateConfig(
|
|||||||
|
|
||||||
// If we have a title set then we update our window to have the
|
// If we have a title set then we update our window to have the
|
||||||
// newly configured title.
|
// newly configured title.
|
||||||
if (config.title) |title| try self.rt_app.performAction(
|
if (config.title) |title| _ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.set_title,
|
.set_title,
|
||||||
.{ .title = title },
|
.{ .title = title },
|
||||||
);
|
);
|
||||||
|
|
||||||
// Notify the window
|
// Notify the window
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.config_change,
|
.config_change,
|
||||||
.{ .config = config },
|
.{ .config = config },
|
||||||
@ -1478,7 +1478,7 @@ fn setCellSize(self: *Surface, size: renderer.CellSize) !void {
|
|||||||
self.io.queueMessage(.{ .resize = self.size }, .unlocked);
|
self.io.queueMessage(.{ .resize = self.size }, .unlocked);
|
||||||
|
|
||||||
// Notify the window
|
// Notify the window
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.cell_size,
|
.cell_size,
|
||||||
.{ .width = size.width, .height = size.height },
|
.{ .width = size.width, .height = size.height },
|
||||||
@ -1774,12 +1774,12 @@ pub fn keyCallback(
|
|||||||
};
|
};
|
||||||
} else if (self.io.terminal.flags.mouse_event != .none and !self.mouse.mods.shift) {
|
} else if (self.io.terminal.flags.mouse_event != .none and !self.mouse.mods.shift) {
|
||||||
// If we have mouse reports on and we don't have shift pressed, we reset state
|
// If we have mouse reports on and we don't have shift pressed, we reset state
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_shape,
|
.mouse_shape,
|
||||||
self.io.terminal.mouse_shape,
|
self.io.terminal.mouse_shape,
|
||||||
);
|
);
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_over_link,
|
.mouse_over_link,
|
||||||
.{ .url = "" },
|
.{ .url = "" },
|
||||||
@ -1797,7 +1797,7 @@ pub fn keyCallback(
|
|||||||
.mods = self.mouse.mods,
|
.mods = self.mouse.mods,
|
||||||
.over_link = self.mouse.over_link,
|
.over_link = self.mouse.over_link,
|
||||||
.hidden = self.mouse.hidden,
|
.hidden = self.mouse.hidden,
|
||||||
}).keyToMouseShape()) |shape| try self.rt_app.performAction(
|
}).keyToMouseShape()) |shape| _ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_shape,
|
.mouse_shape,
|
||||||
shape,
|
shape,
|
||||||
@ -1922,7 +1922,7 @@ fn maybeHandleBinding(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start or continue our key sequence
|
// Start or continue our key sequence
|
||||||
self.rt_app.performAction(
|
_ = self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.key_sequence,
|
.key_sequence,
|
||||||
.{ .trigger = entry.key_ptr.* },
|
.{ .trigger = entry.key_ptr.* },
|
||||||
@ -2031,7 +2031,7 @@ fn endKeySequence(
|
|||||||
mem: KeySequenceMemory,
|
mem: KeySequenceMemory,
|
||||||
) void {
|
) void {
|
||||||
// Notify apprt key sequence ended
|
// Notify apprt key sequence ended
|
||||||
self.rt_app.performAction(
|
_ = self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.key_sequence,
|
.key_sequence,
|
||||||
.end,
|
.end,
|
||||||
@ -3367,12 +3367,12 @@ pub fn cursorPosCallback(
|
|||||||
self.mouse.link_point = null;
|
self.mouse.link_point = null;
|
||||||
if (self.mouse.over_link) {
|
if (self.mouse.over_link) {
|
||||||
self.mouse.over_link = false;
|
self.mouse.over_link = false;
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_shape,
|
.mouse_shape,
|
||||||
self.io.terminal.mouse_shape,
|
self.io.terminal.mouse_shape,
|
||||||
);
|
);
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_over_link,
|
.mouse_over_link,
|
||||||
.{ .url = "" },
|
.{ .url = "" },
|
||||||
@ -3798,7 +3798,7 @@ fn scrollToBottom(self: *Surface) !void {
|
|||||||
fn hideMouse(self: *Surface) void {
|
fn hideMouse(self: *Surface) void {
|
||||||
if (self.mouse.hidden) return;
|
if (self.mouse.hidden) return;
|
||||||
self.mouse.hidden = true;
|
self.mouse.hidden = true;
|
||||||
self.rt_app.performAction(
|
_ = self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_visibility,
|
.mouse_visibility,
|
||||||
.hidden,
|
.hidden,
|
||||||
@ -3810,7 +3810,7 @@ fn hideMouse(self: *Surface) void {
|
|||||||
fn showMouse(self: *Surface) void {
|
fn showMouse(self: *Surface) void {
|
||||||
if (!self.mouse.hidden) return;
|
if (!self.mouse.hidden) return;
|
||||||
self.mouse.hidden = false;
|
self.mouse.hidden = false;
|
||||||
self.rt_app.performAction(
|
_ = self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_visibility,
|
.mouse_visibility,
|
||||||
.visible,
|
.visible,
|
||||||
@ -4101,13 +4101,13 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
v,
|
v,
|
||||||
),
|
),
|
||||||
|
|
||||||
.new_tab => try self.rt_app.performAction(
|
.new_tab => return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.new_tab,
|
.new_tab,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
|
|
||||||
.close_tab => try self.rt_app.performAction(
|
.close_tab => return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.close_tab,
|
.close_tab,
|
||||||
{},
|
{},
|
||||||
@ -4117,7 +4117,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
.next_tab,
|
.next_tab,
|
||||||
.last_tab,
|
.last_tab,
|
||||||
.goto_tab,
|
.goto_tab,
|
||||||
=> |v, tag| try self.rt_app.performAction(
|
=> |v, tag| return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.goto_tab,
|
.goto_tab,
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
@ -4129,13 +4129,13 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
.move_tab => |position| try self.rt_app.performAction(
|
.move_tab => |position| return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.move_tab,
|
.move_tab,
|
||||||
.{ .amount = position },
|
.{ .amount = position },
|
||||||
),
|
),
|
||||||
|
|
||||||
.new_split => |direction| try self.rt_app.performAction(
|
.new_split => |direction| return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.new_split,
|
.new_split,
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
@ -4150,7 +4150,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
.goto_split => |direction| try self.rt_app.performAction(
|
.goto_split => |direction| return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.goto_split,
|
.goto_split,
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
@ -4161,7 +4161,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
.resize_split => |value| try self.rt_app.performAction(
|
.resize_split => |value| return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.resize_split,
|
.resize_split,
|
||||||
.{
|
.{
|
||||||
@ -4175,25 +4175,25 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
.equalize_splits => try self.rt_app.performAction(
|
.equalize_splits => return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.equalize_splits,
|
.equalize_splits,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
|
|
||||||
.toggle_split_zoom => try self.rt_app.performAction(
|
.toggle_split_zoom => return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.toggle_split_zoom,
|
.toggle_split_zoom,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
|
|
||||||
.toggle_maximize => try self.rt_app.performAction(
|
.toggle_maximize => return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.toggle_maximize,
|
.toggle_maximize,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
|
|
||||||
.toggle_fullscreen => try self.rt_app.performAction(
|
.toggle_fullscreen => return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.toggle_fullscreen,
|
.toggle_fullscreen,
|
||||||
switch (self.config.macos_non_native_fullscreen) {
|
switch (self.config.macos_non_native_fullscreen) {
|
||||||
@ -4203,19 +4203,19 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
.toggle_window_decorations => try self.rt_app.performAction(
|
.toggle_window_decorations => return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.toggle_window_decorations,
|
.toggle_window_decorations,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
|
|
||||||
.toggle_tab_overview => try self.rt_app.performAction(
|
.toggle_tab_overview => return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.toggle_tab_overview,
|
.toggle_tab_overview,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
|
|
||||||
.toggle_secure_input => try self.rt_app.performAction(
|
.toggle_secure_input => return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.secure_input,
|
.secure_input,
|
||||||
.toggle,
|
.toggle,
|
||||||
@ -4229,7 +4229,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.inspector => |mode| try self.rt_app.performAction(
|
.inspector => |mode| return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.inspector,
|
.inspector,
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
@ -4676,7 +4676,7 @@ fn showDesktopNotification(self: *Surface, title: [:0]const u8, body: [:0]const
|
|||||||
|
|
||||||
self.app.last_notification_time = now;
|
self.app.last_notification_time = now;
|
||||||
self.app.last_notification_digest = new_digest;
|
self.app.last_notification_digest = new_digest;
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.desktop_notification,
|
.desktop_notification,
|
||||||
.{
|
.{
|
||||||
@ -4696,7 +4696,7 @@ fn crashThreadState(self: *Surface) crash.sentry.ThreadState {
|
|||||||
/// Tell the surface to present itself to the user. This may involve raising the
|
/// Tell the surface to present itself to the user. This may involve raising the
|
||||||
/// window and switching tabs.
|
/// window and switching tabs.
|
||||||
fn presentSurface(self: *Surface) !void {
|
fn presentSurface(self: *Surface) !void {
|
||||||
try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.present_terminal,
|
.present_terminal,
|
||||||
{},
|
{},
|
||||||
|
@ -46,7 +46,7 @@ pub const App = struct {
|
|||||||
wakeup: *const fn (AppUD) callconv(.C) void,
|
wakeup: *const fn (AppUD) callconv(.C) void,
|
||||||
|
|
||||||
/// Callback called to handle an action.
|
/// Callback called to handle an action.
|
||||||
action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.C) void,
|
action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.C) bool,
|
||||||
|
|
||||||
/// Read the clipboard value. The return value must be preserved
|
/// Read the clipboard value. The return value must be preserved
|
||||||
/// by the host until the next call. If there is no valid clipboard
|
/// by the host until the next call. If there is no valid clipboard
|
||||||
@ -478,13 +478,14 @@ pub const App = struct {
|
|||||||
surface.queueInspectorRender();
|
surface.queueInspectorRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a given action.
|
/// Perform a given action. Returns `true` if the action was able to be
|
||||||
|
/// performed, `false` otherwise.
|
||||||
pub fn performAction(
|
pub fn performAction(
|
||||||
self: *App,
|
self: *App,
|
||||||
target: apprt.Target,
|
target: apprt.Target,
|
||||||
comptime action: apprt.Action.Key,
|
comptime action: apprt.Action.Key,
|
||||||
value: apprt.Action.Value(action),
|
value: apprt.Action.Value(action),
|
||||||
) !void {
|
) !bool {
|
||||||
// Special case certain actions before they are sent to the
|
// Special case certain actions before they are sent to the
|
||||||
// embedded apprt.
|
// embedded apprt.
|
||||||
self.performPreAction(target, action, value);
|
self.performPreAction(target, action, value);
|
||||||
@ -494,7 +495,7 @@ pub const App = struct {
|
|||||||
action,
|
action,
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
self.opts.action(
|
return self.opts.action(
|
||||||
self,
|
self,
|
||||||
target.cval(),
|
target.cval(),
|
||||||
@unionInit(apprt.Action, @tagName(action), value).cval(),
|
@unionInit(apprt.Action, @tagName(action), value).cval(),
|
||||||
@ -1006,7 +1007,7 @@ pub const Surface = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn queueInspectorRender(self: *Surface) void {
|
fn queueInspectorRender(self: *Surface) void {
|
||||||
self.app.performAction(
|
_ = self.app.performAction(
|
||||||
.{ .surface = &self.core_surface },
|
.{ .surface = &self.core_surface },
|
||||||
.render_inspector,
|
.render_inspector,
|
||||||
{},
|
{},
|
||||||
@ -1457,7 +1458,7 @@ pub const CAPI = struct {
|
|||||||
|
|
||||||
/// Open the configuration.
|
/// Open the configuration.
|
||||||
export fn ghostty_app_open_config(v: *App) void {
|
export fn ghostty_app_open_config(v: *App) void {
|
||||||
v.performAction(.app, .open_config, {}) catch |err| {
|
_ = v.performAction(.app, .open_config, {}) catch |err| {
|
||||||
log.err("error reloading config err={}", .{err});
|
log.err("error reloading config err={}", .{err});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -1800,7 +1801,7 @@ pub const CAPI = struct {
|
|||||||
|
|
||||||
/// Request that the surface split in the given direction.
|
/// Request that the surface split in the given direction.
|
||||||
export fn ghostty_surface_split(ptr: *Surface, direction: apprt.action.SplitDirection) void {
|
export fn ghostty_surface_split(ptr: *Surface, direction: apprt.action.SplitDirection) void {
|
||||||
ptr.app.performAction(
|
_ = ptr.app.performAction(
|
||||||
.{ .surface = &ptr.core_surface },
|
.{ .surface = &ptr.core_surface },
|
||||||
.new_split,
|
.new_split,
|
||||||
direction,
|
direction,
|
||||||
@ -1815,7 +1816,7 @@ pub const CAPI = struct {
|
|||||||
ptr: *Surface,
|
ptr: *Surface,
|
||||||
direction: apprt.action.GotoSplit,
|
direction: apprt.action.GotoSplit,
|
||||||
) void {
|
) void {
|
||||||
ptr.app.performAction(
|
_ = ptr.app.performAction(
|
||||||
.{ .surface = &ptr.core_surface },
|
.{ .surface = &ptr.core_surface },
|
||||||
.goto_split,
|
.goto_split,
|
||||||
direction,
|
direction,
|
||||||
@ -1834,7 +1835,7 @@ pub const CAPI = struct {
|
|||||||
direction: apprt.action.ResizeSplit.Direction,
|
direction: apprt.action.ResizeSplit.Direction,
|
||||||
amount: u16,
|
amount: u16,
|
||||||
) void {
|
) void {
|
||||||
ptr.app.performAction(
|
_ = ptr.app.performAction(
|
||||||
.{ .surface = &ptr.core_surface },
|
.{ .surface = &ptr.core_surface },
|
||||||
.resize_split,
|
.resize_split,
|
||||||
.{ .direction = direction, .amount = amount },
|
.{ .direction = direction, .amount = amount },
|
||||||
@ -1846,7 +1847,7 @@ pub const CAPI = struct {
|
|||||||
|
|
||||||
/// Equalize the size of all splits in the current window.
|
/// Equalize the size of all splits in the current window.
|
||||||
export fn ghostty_surface_split_equalize(ptr: *Surface) void {
|
export fn ghostty_surface_split_equalize(ptr: *Surface) void {
|
||||||
ptr.app.performAction(
|
_ = ptr.app.performAction(
|
||||||
.{ .surface = &ptr.core_surface },
|
.{ .surface = &ptr.core_surface },
|
||||||
.equalize_splits,
|
.equalize_splits,
|
||||||
{},
|
{},
|
||||||
|
@ -147,13 +147,14 @@ pub const App = struct {
|
|||||||
glfw.postEmptyEvent();
|
glfw.postEmptyEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a given action.
|
/// Perform a given action. Returns `true` if the action was able to be
|
||||||
|
/// performed, `false` otherwise.
|
||||||
pub fn performAction(
|
pub fn performAction(
|
||||||
self: *App,
|
self: *App,
|
||||||
target: apprt.Target,
|
target: apprt.Target,
|
||||||
comptime action: apprt.Action.Key,
|
comptime action: apprt.Action.Key,
|
||||||
value: apprt.Action.Value(action),
|
value: apprt.Action.Value(action),
|
||||||
) !void {
|
) !bool {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
.quit => self.quit = true,
|
.quit => self.quit = true,
|
||||||
|
|
||||||
@ -238,8 +239,13 @@ pub const App = struct {
|
|||||||
.pwd,
|
.pwd,
|
||||||
.config_change,
|
.config_change,
|
||||||
.toggle_maximize,
|
.toggle_maximize,
|
||||||
=> log.info("unimplemented action={}", .{action}),
|
=> {
|
||||||
|
log.info("unimplemented action={}", .{action});
|
||||||
|
return false;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reload the configuration. This should return the new configuration.
|
/// Reload the configuration. This should return the new configuration.
|
||||||
|
@ -507,13 +507,14 @@ pub fn terminate(self: *App) void {
|
|||||||
self.config.deinit();
|
self.config.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a given action.
|
/// Perform a given action. Returns `true` if the action was able to be
|
||||||
|
/// performed, `false` otherwise.
|
||||||
pub fn performAction(
|
pub fn performAction(
|
||||||
self: *App,
|
self: *App,
|
||||||
target: apprt.Target,
|
target: apprt.Target,
|
||||||
comptime action: apprt.Action.Key,
|
comptime action: apprt.Action.Key,
|
||||||
value: apprt.Action.Value(action),
|
value: apprt.Action.Value(action),
|
||||||
) !void {
|
) !bool {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
.quit => self.quit(),
|
.quit => self.quit(),
|
||||||
.new_window => _ = try self.newWindow(switch (target) {
|
.new_window => _ = try self.newWindow(switch (target) {
|
||||||
@ -525,12 +526,12 @@ pub fn performAction(
|
|||||||
|
|
||||||
.new_tab => try self.newTab(target),
|
.new_tab => try self.newTab(target),
|
||||||
.close_tab => try self.closeTab(target),
|
.close_tab => try self.closeTab(target),
|
||||||
.goto_tab => self.gotoTab(target, value),
|
.goto_tab => return self.gotoTab(target, value),
|
||||||
.move_tab => self.moveTab(target, value),
|
.move_tab => self.moveTab(target, value),
|
||||||
.new_split => try self.newSplit(target, value),
|
.new_split => try self.newSplit(target, value),
|
||||||
.resize_split => self.resizeSplit(target, value),
|
.resize_split => self.resizeSplit(target, value),
|
||||||
.equalize_splits => self.equalizeSplits(target),
|
.equalize_splits => self.equalizeSplits(target),
|
||||||
.goto_split => self.gotoSplit(target, value),
|
.goto_split => return self.gotoSplit(target, value),
|
||||||
.open_config => try configpkg.edit.open(self.core_app.alloc),
|
.open_config => try configpkg.edit.open(self.core_app.alloc),
|
||||||
.config_change => self.configChange(target, value.config),
|
.config_change => self.configChange(target, value.config),
|
||||||
.reload_config => try self.reloadConfig(target, value),
|
.reload_config => try self.reloadConfig(target, value),
|
||||||
@ -559,8 +560,15 @@ pub fn performAction(
|
|||||||
.render_inspector,
|
.render_inspector,
|
||||||
.renderer_health,
|
.renderer_health,
|
||||||
.color_change,
|
.color_change,
|
||||||
=> log.warn("unimplemented action={}", .{action}),
|
=> {
|
||||||
|
log.warn("unimplemented action={}", .{action});
|
||||||
|
return false;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We can assume it was handled because all unknown/unimplemented actions
|
||||||
|
// are caught above.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn newTab(_: *App, target: apprt.Target) !void {
|
fn newTab(_: *App, target: apprt.Target) !void {
|
||||||
@ -597,24 +605,24 @@ fn closeTab(_: *App, target: apprt.Target) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gotoTab(_: *App, target: apprt.Target, tab: apprt.action.GotoTab) void {
|
fn gotoTab(_: *App, target: apprt.Target, tab: apprt.action.GotoTab) bool {
|
||||||
switch (target) {
|
switch (target) {
|
||||||
.app => {},
|
.app => return false,
|
||||||
.surface => |v| {
|
.surface => |v| {
|
||||||
const window = v.rt_surface.container.window() orelse {
|
const window = v.rt_surface.container.window() orelse {
|
||||||
log.info(
|
log.info(
|
||||||
"gotoTab invalid for container={s}",
|
"gotoTab invalid for container={s}",
|
||||||
.{@tagName(v.rt_surface.container)},
|
.{@tagName(v.rt_surface.container)},
|
||||||
);
|
);
|
||||||
return;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (tab) {
|
return switch (tab) {
|
||||||
.previous => window.gotoPreviousTab(v.rt_surface),
|
.previous => window.gotoPreviousTab(v.rt_surface),
|
||||||
.next => window.gotoNextTab(v.rt_surface),
|
.next => window.gotoNextTab(v.rt_surface),
|
||||||
.last => window.gotoLastTab(),
|
.last => window.gotoLastTab(),
|
||||||
else => window.gotoTab(@intCast(@intFromEnum(tab))),
|
else => window.gotoTab(@intCast(@intFromEnum(tab))),
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -668,18 +676,22 @@ fn gotoSplit(
|
|||||||
_: *const App,
|
_: *const App,
|
||||||
target: apprt.Target,
|
target: apprt.Target,
|
||||||
direction: apprt.action.GotoSplit,
|
direction: apprt.action.GotoSplit,
|
||||||
) void {
|
) bool {
|
||||||
switch (target) {
|
switch (target) {
|
||||||
.app => {},
|
.app => return false,
|
||||||
.surface => |v| {
|
.surface => |v| {
|
||||||
const s = v.rt_surface.container.split() orelse return;
|
const s = v.rt_surface.container.split() orelse return false;
|
||||||
const map = s.directionMap(switch (v.rt_surface.container) {
|
const map = s.directionMap(switch (v.rt_surface.container) {
|
||||||
.split_tl => .top_left,
|
.split_tl => .top_left,
|
||||||
.split_br => .bottom_right,
|
.split_br => .bottom_right,
|
||||||
.none, .tab_ => unreachable,
|
.none, .tab_ => unreachable,
|
||||||
});
|
});
|
||||||
const surface_ = map.get(direction) orelse return;
|
const surface_ = map.get(direction) orelse return false;
|
||||||
if (surface_) |surface| surface.grabFocus();
|
if (surface_) |surface| {
|
||||||
|
surface.grabFocus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2124,7 +2124,7 @@ pub fn present(self: *Surface) void {
|
|||||||
if (self.container.window()) |window| {
|
if (self.container.window()) |window| {
|
||||||
if (self.container.tab()) |tab| {
|
if (self.container.tab()) |tab| {
|
||||||
if (window.notebook.getTabPosition(tab)) |position|
|
if (window.notebook.getTabPosition(tab)) |position|
|
||||||
window.notebook.gotoNthTab(position);
|
_ = window.notebook.gotoNthTab(position);
|
||||||
}
|
}
|
||||||
c.gtk_window_present(window.window);
|
c.gtk_window_present(window.window);
|
||||||
}
|
}
|
||||||
|
@ -506,23 +506,25 @@ pub fn closeTab(self: *Window, tab: *Tab) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Go to the previous tab for a surface.
|
/// Go to the previous tab for a surface.
|
||||||
pub fn gotoPreviousTab(self: *Window, surface: *Surface) void {
|
pub fn gotoPreviousTab(self: *Window, surface: *Surface) bool {
|
||||||
const tab = surface.container.tab() orelse {
|
const tab = surface.container.tab() orelse {
|
||||||
log.info("surface is not attached to a tab bar, cannot navigate", .{});
|
log.info("surface is not attached to a tab bar, cannot navigate", .{});
|
||||||
return;
|
return false;
|
||||||
};
|
};
|
||||||
self.notebook.gotoPreviousTab(tab);
|
if (!self.notebook.gotoPreviousTab(tab)) return false;
|
||||||
self.focusCurrentTab();
|
self.focusCurrentTab();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Go to the next tab for a surface.
|
/// Go to the next tab for a surface.
|
||||||
pub fn gotoNextTab(self: *Window, surface: *Surface) void {
|
pub fn gotoNextTab(self: *Window, surface: *Surface) bool {
|
||||||
const tab = surface.container.tab() orelse {
|
const tab = surface.container.tab() orelse {
|
||||||
log.info("surface is not attached to a tab bar, cannot navigate", .{});
|
log.info("surface is not attached to a tab bar, cannot navigate", .{});
|
||||||
return;
|
return false;
|
||||||
};
|
};
|
||||||
self.notebook.gotoNextTab(tab);
|
if (!self.notebook.gotoNextTab(tab)) return false;
|
||||||
self.focusCurrentTab();
|
self.focusCurrentTab();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move the current tab for a surface.
|
/// Move the current tab for a surface.
|
||||||
@ -535,19 +537,20 @@ pub fn moveTab(self: *Window, surface: *Surface, position: c_int) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Go to the last tab for a surface.
|
/// Go to the last tab for a surface.
|
||||||
pub fn gotoLastTab(self: *Window) void {
|
pub fn gotoLastTab(self: *Window) bool {
|
||||||
const max = self.notebook.nPages();
|
const max = self.notebook.nPages();
|
||||||
self.gotoTab(@intCast(max));
|
return self.gotoTab(@intCast(max));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Go to the specific tab index.
|
/// Go to the specific tab index.
|
||||||
pub fn gotoTab(self: *Window, n: usize) void {
|
pub fn gotoTab(self: *Window, n: usize) bool {
|
||||||
if (n == 0) return;
|
if (n == 0) return false;
|
||||||
const max = self.notebook.nPages();
|
const max = self.notebook.nPages();
|
||||||
if (max == 0) return;
|
if (max == 0) return false;
|
||||||
const page_idx = std.math.cast(c_int, n - 1) orelse return;
|
const page_idx = std.math.cast(c_int, n - 1) orelse return false;
|
||||||
self.notebook.gotoNthTab(@min(page_idx, max - 1));
|
if (!self.notebook.gotoNthTab(@min(page_idx, max - 1))) return false;
|
||||||
self.focusCurrentTab();
|
self.focusCurrentTab();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle tab overview (if present)
|
/// Toggle tab overview (if present)
|
||||||
|
@ -59,11 +59,14 @@ pub const Notebook = union(enum) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gotoNthTab(self: *Notebook, position: c_int) void {
|
pub fn gotoNthTab(self: *Notebook, position: c_int) bool {
|
||||||
|
const current_page_ = self.currentPage();
|
||||||
|
if (current_page_) |current_page| if (current_page == position) return false;
|
||||||
switch (self.*) {
|
switch (self.*) {
|
||||||
.adw => |*adw| adw.gotoNthTab(position),
|
.adw => |*adw| adw.gotoNthTab(position),
|
||||||
.gtk => |*gtk| gtk.gotoNthTab(position),
|
.gtk => |*gtk| gtk.gotoNthTab(position),
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getTabPosition(self: *Notebook, tab: *Tab) ?c_int {
|
pub fn getTabPosition(self: *Notebook, tab: *Tab) ?c_int {
|
||||||
@ -73,8 +76,8 @@ pub const Notebook = union(enum) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gotoPreviousTab(self: *Notebook, tab: *Tab) void {
|
pub fn gotoPreviousTab(self: *Notebook, tab: *Tab) bool {
|
||||||
const page_idx = self.getTabPosition(tab) orelse return;
|
const page_idx = self.getTabPosition(tab) orelse return false;
|
||||||
|
|
||||||
// The next index is the previous or we wrap around.
|
// The next index is the previous or we wrap around.
|
||||||
const next_idx = if (page_idx > 0) page_idx - 1 else next_idx: {
|
const next_idx = if (page_idx > 0) page_idx - 1 else next_idx: {
|
||||||
@ -83,19 +86,21 @@ pub const Notebook = union(enum) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Do nothing if we have one tab
|
// Do nothing if we have one tab
|
||||||
if (next_idx == page_idx) return;
|
if (next_idx == page_idx) return false;
|
||||||
|
|
||||||
self.gotoNthTab(next_idx);
|
return self.gotoNthTab(next_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gotoNextTab(self: *Notebook, tab: *Tab) void {
|
pub fn gotoNextTab(self: *Notebook, tab: *Tab) bool {
|
||||||
const page_idx = self.getTabPosition(tab) orelse return;
|
const page_idx = self.getTabPosition(tab) orelse return false;
|
||||||
|
|
||||||
const max = self.nPages() -| 1;
|
const max = self.nPages() -| 1;
|
||||||
const next_idx = if (page_idx < max) page_idx + 1 else 0;
|
const next_idx = if (page_idx < max) page_idx + 1 else 0;
|
||||||
if (next_idx == page_idx) return;
|
|
||||||
|
|
||||||
self.gotoNthTab(next_idx);
|
// Do nothing if we have one tab
|
||||||
|
if (next_idx == page_idx) return false;
|
||||||
|
|
||||||
|
return self.gotoNthTab(next_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn moveTab(self: *Notebook, tab: *Tab, position: c_int) void {
|
pub fn moveTab(self: *Notebook, tab: *Tab, position: c_int) void {
|
||||||
|
Reference in New Issue
Block a user