mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
core: add app focused state, make App.keyEvent focus aware
This commit is contained in:
62
src/App.zig
62
src/App.zig
@ -30,6 +30,21 @@ alloc: Allocator,
|
|||||||
/// The list of surfaces that are currently active.
|
/// The list of surfaces that are currently active.
|
||||||
surfaces: SurfaceList,
|
surfaces: SurfaceList,
|
||||||
|
|
||||||
|
/// This is true if the app that Ghostty is in is focused. This may
|
||||||
|
/// mean that no surfaces (terminals) are focused but the app is still
|
||||||
|
/// focused, i.e. may an about window. On macOS, this concept is known
|
||||||
|
/// as the "active" app while focused windows are known as the
|
||||||
|
/// "main" window.
|
||||||
|
///
|
||||||
|
/// This is used to determine if keyboard shortcuts that are non-global
|
||||||
|
/// should be processed. If the app is not focused, then we don't want
|
||||||
|
/// to process keyboard shortcuts that are not global.
|
||||||
|
///
|
||||||
|
/// This defaults to true since we assume that the app is focused when
|
||||||
|
/// Ghostty is initialized but a well behaved apprt should call
|
||||||
|
/// focusEvent to set this to the correct value right away.
|
||||||
|
focused: bool = true,
|
||||||
|
|
||||||
/// The last focused surface. This surface may not be valid;
|
/// The last focused surface. This surface may not be valid;
|
||||||
/// you must always call hasSurface to validate it.
|
/// you must always call hasSurface to validate it.
|
||||||
focused_surface: ?*Surface = null,
|
focused_surface: ?*Surface = null,
|
||||||
@ -54,6 +69,9 @@ last_notification_digest: u64 = 0,
|
|||||||
/// Initialize the main app instance. This creates the main window, sets
|
/// Initialize the main app instance. This creates the main window, sets
|
||||||
/// up the renderer state, compiles the shaders, etc. This is the primary
|
/// up the renderer state, compiles the shaders, etc. This is the primary
|
||||||
/// "startup" logic.
|
/// "startup" logic.
|
||||||
|
///
|
||||||
|
/// After calling this function, well behaved apprts should then call
|
||||||
|
/// `focusEvent` to set the initial focus state of the app.
|
||||||
pub fn create(
|
pub fn create(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
) !*App {
|
) !*App {
|
||||||
@ -265,9 +283,21 @@ pub fn setQuit(self: *App) !void {
|
|||||||
self.quit = true;
|
self.quit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle an app-level focus event. This should be called whenever
|
||||||
|
/// the focus state of the entire app containing Ghostty changes.
|
||||||
|
/// This is separate from surface focus events. See the `focused`
|
||||||
|
/// field for more information.
|
||||||
|
pub fn focusEvent(self: *App, focused: bool) void {
|
||||||
|
self.focused = focused;
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle a key event at the app-scope. If this key event is used,
|
/// Handle a key event at the app-scope. If this key event is used,
|
||||||
/// this will return true and the caller shouldn't continue processing
|
/// this will return true and the caller shouldn't continue processing
|
||||||
/// the event. If the event is not used, this will return false.
|
/// the event. If the event is not used, this will return false.
|
||||||
|
///
|
||||||
|
/// If the app currently has focus then all key events are processed.
|
||||||
|
/// If the app does not have focus then only global key events are
|
||||||
|
/// processed.
|
||||||
pub fn keyEvent(
|
pub fn keyEvent(
|
||||||
self: *App,
|
self: *App,
|
||||||
rt_app: *apprt.App,
|
rt_app: *apprt.App,
|
||||||
@ -294,13 +324,33 @@ pub fn keyEvent(
|
|||||||
.leaf => |leaf| leaf,
|
.leaf => |leaf| leaf,
|
||||||
};
|
};
|
||||||
|
|
||||||
// We only care about global keybinds
|
// If we aren't focused, then we only process global keybinds.
|
||||||
if (!leaf.flags.global) return false;
|
if (!self.focused and !leaf.flags.global) return false;
|
||||||
|
|
||||||
// Perform the action
|
// Global keybinds are done using performAll so that they
|
||||||
self.performAllAction(rt_app, leaf.action) catch |err| {
|
// can target all surfaces too.
|
||||||
log.warn("error performing global keybind action action={s} err={}", .{
|
if (leaf.flags.global) {
|
||||||
@tagName(leaf.action),
|
self.performAllAction(rt_app, leaf.action) catch |err| {
|
||||||
|
log.warn("error performing global keybind action action={s} err={}", .{
|
||||||
|
@tagName(leaf.action),
|
||||||
|
err,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be focused to process non-global keybinds
|
||||||
|
assert(self.focused);
|
||||||
|
assert(!leaf.flags.global);
|
||||||
|
|
||||||
|
// If we are focused, then we process keybinds only if they are
|
||||||
|
// app-scoped. Otherwise, we do nothing. Surface-scoped should
|
||||||
|
// be processed by Surface.keyEvent.
|
||||||
|
const app_action = leaf.action.scoped(.app) orelse return false;
|
||||||
|
self.performAction(rt_app, app_action) catch |err| {
|
||||||
|
log.warn("error performing app keybind action action={s} err={}", .{
|
||||||
|
@tagName(app_action),
|
||||||
err,
|
err,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user