gtk-ng: show on-screen keyboard on LMB release (#8224)

pull/8227/head
Leah Amelia Chen 2025-08-14 05:00:17 +08:00 committed by GitHub
commit 92d6395a8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 71 additions and 12 deletions

View File

@ -757,6 +757,7 @@ typedef enum {
GHOSTTY_ACTION_OPEN_URL,
GHOSTTY_ACTION_SHOW_CHILD_EXITED,
GHOSTTY_ACTION_PROGRESS_REPORT,
GHOSTTY_ACTION_SHOW_ON_SCREEN_KEYBOARD,
} ghostty_action_tag_e;
typedef union {

View File

@ -4804,6 +4804,12 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
{},
),
.show_on_screen_keyboard => return try self.rt_app.performAction(
.{ .surface = self },
.show_on_screen_keyboard,
{},
),
.select_all => {
const sel = self.io.terminal.screen.selectAll();
if (sel) |s| {

View File

@ -291,6 +291,9 @@ pub const Action = union(Key) {
/// Show a native GUI notification about the progress of some TUI operation.
progress_report: terminal.osc.Command.ProgressReport,
/// Show the on-screen keyboard.
show_on_screen_keyboard,
/// Sync with: ghostty_action_tag_e
pub const Key = enum(c_int) {
quit,
@ -345,6 +348,7 @@ pub const Action = union(Key) {
open_url,
show_child_exited,
progress_report,
show_on_screen_keyboard,
};
/// Sync with: ghostty_action_u

View File

@ -618,6 +618,7 @@ pub const Application = extern struct {
.toggle_window_decorations => return Action.toggleWindowDecorations(target),
.toggle_command_palette => return Action.toggleCommandPalette(target),
.toggle_split_zoom => return Action.toggleSplitZoom(target),
.show_on_screen_keyboard => return Action.showOnScreenKeyboard(target),
// Unimplemented but todo on gtk-ng branch
.inspector,
@ -2146,6 +2147,21 @@ const Action = struct {
}
}
pub fn showOnScreenKeyboard(target: apprt.Target) bool {
switch (target) {
.app => {
log.warn("show_on_screen_keyboard to app is unexpected", .{});
return false;
},
// NOTE: Even though `activateOsk` takes a gdk.Event, it's currently
// unused by all implementations of `activateOsk` as of GTK 4.18.
// The commit that introduced the method (ce6aa73c) clarifies that
// the event *may* be used by other IM backends, but for Linux desktop
// environments this doesn't matter.
.surface => |v| return v.rt_surface.surface.showOnScreenKeyboard(null),
}
}
fn getQuickTerminalWindow() ?*Window {
// Find a quick terminal window.
const list = gtk.Window.listToplevels();

View File

@ -573,6 +573,11 @@ pub const Surface = extern struct {
return self.as(gtk.Widget).activateAction("win.toggle-command-palette", null) != 0;
}
pub fn showOnScreenKeyboard(self: *Self, event: ?*gdk.Event) bool {
const priv = self.private();
return priv.im_context.as(gtk.IMContext).activateOsk(event) != 0;
}
/// Set the current progress report state.
pub fn setProgressReport(
self: *Self,
@ -1946,18 +1951,29 @@ pub const Surface = extern struct {
const event = gesture.as(gtk.EventController).getCurrentEvent() orelse return;
const priv = self.private();
if (priv.core_surface) |surface| {
const gtk_mods = event.getModifierState();
const button = translateMouseButton(gesture.as(gtk.GestureSingle).getCurrentButton());
const mods = gtk_key.translateMods(gtk_mods);
_ = surface.mouseButtonCallback(
.release,
button,
mods,
) catch |err| {
log.warn("error in key callback err={}", .{err});
return;
};
const surface = priv.core_surface orelse return;
const gtk_mods = event.getModifierState();
const button = translateMouseButton(gesture.as(gtk.GestureSingle).getCurrentButton());
const mods = gtk_key.translateMods(gtk_mods);
const consumed = surface.mouseButtonCallback(
.release,
button,
mods,
) catch |err| {
log.warn("error in key callback err={}", .{err});
return;
};
// Trigger the on-screen keyboard if we have no selection,
// and that the mouse event hasn't been intercepted by the callback.
//
// It's better to do this here rather than within the core callback
// since we have direct access to the underlying gdk.Event here.
if (!consumed and button == .left and !surface.hasSelection()) {
if (!self.showOnScreenKeyboard(event)) {
log.warn("failed to activate the on-screen keyboard", .{});
}
}
}

View File

@ -539,6 +539,7 @@ pub fn performAction(
.check_for_updates,
.undo,
.redo,
.show_on_screen_keyboard,
=> {
log.warn("unimplemented action={}", .{action});
return false;

View File

@ -524,6 +524,14 @@ pub const Action = union(enum) {
/// Has no effect on macOS.
show_gtk_inspector,
/// Show the on-screen keyboard if one is present.
///
/// Only implemented on Linux (GTK). On GNOME, the "Screen Keyboard"
/// accessibility feature must be turned on, which can be found under
/// Settings > Accessibility > Typing. Other platforms are as of now
/// untested.
show_on_screen_keyboard,
/// Open the configuration file in the default OS editor.
///
/// If your default OS editor isn't configured then this will fail.
@ -1051,6 +1059,7 @@ pub const Action = union(enum) {
.toggle_window_float_on_top,
.toggle_secure_input,
.toggle_command_palette,
.show_on_screen_keyboard,
.reset_window_size,
.crash,
=> .surface,

View File

@ -369,6 +369,12 @@ fn actionCommands(action: Action.Key) []const Command {
.description = "Show the GTK inspector.",
}},
.show_on_screen_keyboard => comptime &.{.{
.action = .show_on_screen_keyboard,
.title = "Show On-Screen Keyboard",
.description = "Show the on-screen keyboard if present.",
}},
.open_config => comptime &.{.{
.action = .open_config,
.title = "Open Config",