mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-01-07 05:43:20 +00:00
gtk-ng: don't use signals to toggle command palette (#8182)
This commit is contained in:
@@ -55,8 +55,8 @@
|
||||
.gobject = .{
|
||||
// https://github.com/jcollie/ghostty-gobject based on zig_gobject
|
||||
// Temporary until we generate them at build time automatically.
|
||||
.url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-07-34-1/ghostty-gobject-0.14.1-2025-08-07-34-1.tar.zst",
|
||||
.hash = "gobject-0.3.0-Skun7F_XnABQYabYdzLoVbO3bCcJIwxE3NCPs1_fG2ma",
|
||||
.url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst",
|
||||
.hash = "gobject-0.3.0-Skun7AngnABC2BPiaoobs6YSSzSgMuEIcjb2rYrRyaAM",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
|
||||
6
build.zig.zon.json
generated
6
build.zig.zon.json
generated
@@ -24,10 +24,10 @@
|
||||
"url": "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz",
|
||||
"hash": "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U="
|
||||
},
|
||||
"gobject-0.3.0-Skun7F_XnABQYabYdzLoVbO3bCcJIwxE3NCPs1_fG2ma": {
|
||||
"gobject-0.3.0-Skun7AngnABC2BPiaoobs6YSSzSgMuEIcjb2rYrRyaAM": {
|
||||
"name": "gobject",
|
||||
"url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-07-34-1/ghostty-gobject-0.14.1-2025-08-07-34-1.tar.zst",
|
||||
"hash": "sha256-43IIiHR5J7PfgG9JXSlGgC6WztC10fXyIhGZfY9xceQ="
|
||||
"url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst",
|
||||
"hash": "sha256-B0ziLzKud+kdKu5T1BTE9GMh8EPM/KhhhoNJlys5QPI="
|
||||
},
|
||||
"N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr": {
|
||||
"name": "gtk4_layer_shell",
|
||||
|
||||
6
build.zig.zon.nix
generated
6
build.zig.zon.nix
generated
@@ -122,11 +122,11 @@ in
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "gobject-0.3.0-Skun7F_XnABQYabYdzLoVbO3bCcJIwxE3NCPs1_fG2ma";
|
||||
name = "gobject-0.3.0-Skun7AngnABC2BPiaoobs6YSSzSgMuEIcjb2rYrRyaAM";
|
||||
path = fetchZigArtifact {
|
||||
name = "gobject";
|
||||
url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-07-34-1/ghostty-gobject-0.14.1-2025-08-07-34-1.tar.zst";
|
||||
hash = "sha256-43IIiHR5J7PfgG9JXSlGgC6WztC10fXyIhGZfY9xceQ=";
|
||||
url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst";
|
||||
hash = "sha256-B0ziLzKud+kdKu5T1BTE9GMh8EPM/KhhhoNJlys5QPI=";
|
||||
};
|
||||
}
|
||||
{
|
||||
|
||||
2
build.zig.zon.txt
generated
2
build.zig.zon.txt
generated
@@ -27,7 +27,7 @@ https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d6
|
||||
https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz
|
||||
https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz
|
||||
https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz
|
||||
https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-07-34-1/ghostty-gobject-0.14.1-2025-08-07-34-1.tar.zst
|
||||
https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/3cbeca99efa10beba24b0efe86331736f09f9ed1.tar.gz
|
||||
https://github.com/mitchellh/libxev/archive/7f803181b158a10fec8619f793e3b4df515566cb.tar.gz
|
||||
https://github.com/mitchellh/zig-objc/archive/c9e917a4e15a983b672ca779c7985d738a2d517c.tar.gz
|
||||
|
||||
@@ -31,9 +31,9 @@
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-07-34-1/ghostty-gobject-0.14.1-2025-08-07-34-1.tar.zst",
|
||||
"dest": "vendor/p/gobject-0.3.0-Skun7F_XnABQYabYdzLoVbO3bCcJIwxE3NCPs1_fG2ma",
|
||||
"sha256": "e3720888747927b3df806f495d2946802e96ced0b5d1f5f22211997d8f7171e4"
|
||||
"url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst",
|
||||
"dest": "vendor/p/gobject-0.3.0-Skun7AngnABC2BPiaoobs6YSSzSgMuEIcjb2rYrRyaAM",
|
||||
"sha256": "074ce22f32ae77e91d2aee53d414c4f46321f043ccfca861868349972b3940f2"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
|
||||
@@ -133,7 +133,7 @@ pub const Application = extern struct {
|
||||
/// If non-null, we're currently showing a config errors dialog.
|
||||
/// This is a WeakRef because the dialog can close on its own
|
||||
/// outside of our own lifecycle and that's okay.
|
||||
config_errors_dialog: WeakRef(ConfigErrorsDialog) = .{},
|
||||
config_errors_dialog: WeakRef(ConfigErrorsDialog) = .empty,
|
||||
|
||||
/// glib source for our signal handler.
|
||||
signal_source: ?c_uint = null,
|
||||
|
||||
@@ -174,13 +174,20 @@ pub const CommandPalette = extern struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn searchStopped(_: *gtk.SearchEntry, self: *CommandPalette) callconv(.c) void {
|
||||
// ESC was pressed - close the palette
|
||||
fn close(self: *CommandPalette) void {
|
||||
const priv = self.private();
|
||||
_ = priv.dialog.close();
|
||||
}
|
||||
|
||||
fn dialogClosed(_: *adw.Dialog, self: *CommandPalette) callconv(.c) void {
|
||||
self.unref();
|
||||
}
|
||||
|
||||
fn searchStopped(_: *gtk.SearchEntry, self: *CommandPalette) callconv(.c) void {
|
||||
// ESC was pressed - close the palette
|
||||
self.close();
|
||||
}
|
||||
|
||||
fn searchActivated(_: *gtk.SearchEntry, self: *CommandPalette) callconv(.c) void {
|
||||
// If Enter is pressed, activate the selected entry
|
||||
const priv = self.private();
|
||||
@@ -198,11 +205,9 @@ pub const CommandPalette = extern struct {
|
||||
pub fn toggle(self: *CommandPalette, window: *Window) void {
|
||||
const priv = self.private();
|
||||
|
||||
// If the dialog has been shown, close it and unref ourselves so all of
|
||||
// our memory is reclaimed.
|
||||
// If the dialog has been shown, close it.
|
||||
if (priv.dialog.as(gtk.Widget).getRealized() != 0) {
|
||||
_ = priv.dialog.close();
|
||||
self.unref();
|
||||
self.close();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -218,22 +223,17 @@ pub const CommandPalette = extern struct {
|
||||
fn activated(self: *CommandPalette, pos: c_uint) void {
|
||||
const priv = self.private();
|
||||
|
||||
// Use priv.model and not priv.source here to use the list of *visible* results
|
||||
const object_ = priv.model.as(gio.ListModel).getObject(pos);
|
||||
defer if (object_) |object| object.unref();
|
||||
|
||||
// Close before running the action in order to avoid being replaced by
|
||||
// another dialog (such as the change title dialog). If that occurs then
|
||||
// the command palette dialog won't be counted as having closed properly
|
||||
// and cannot receive focus when reopened.
|
||||
_ = priv.dialog.close();
|
||||
|
||||
// We are always done with the command palette when this finishes, even
|
||||
// if there were errors.
|
||||
defer self.unref();
|
||||
|
||||
// Use priv.model and not priv.source here to use the list of *visible* results
|
||||
const object = priv.model.as(gio.ListModel).getObject(pos) orelse return;
|
||||
defer object.unref();
|
||||
|
||||
const cmd = gobject.ext.cast(Command, object) orelse return;
|
||||
self.close();
|
||||
|
||||
const cmd = gobject.ext.cast(Command, object_ orelse return) orelse return;
|
||||
const action = cmd.getAction() orelse return;
|
||||
|
||||
// Signal that an an action has been selected. Signals are synchronous
|
||||
@@ -277,6 +277,7 @@ pub const CommandPalette = extern struct {
|
||||
class.bindTemplateChildPrivate("source", .{});
|
||||
|
||||
// Template Callbacks
|
||||
class.bindTemplateCallback("closed", &dialogClosed);
|
||||
class.bindTemplateCallback("notify_config", &propConfig);
|
||||
class.bindTemplateCallback("search_stopped", &searchStopped);
|
||||
class.bindTemplateCallback("search_activated", &searchActivated);
|
||||
|
||||
@@ -119,7 +119,7 @@ pub const SplitTree = extern struct {
|
||||
|
||||
/// Last focused surface in the tree. We need this to handle various
|
||||
/// tree change states.
|
||||
last_focused: WeakRef(Surface) = .{},
|
||||
last_focused: WeakRef(Surface) = .empty,
|
||||
|
||||
/// The source that we use to rebuild the tree. This is also
|
||||
/// used to debounce updates.
|
||||
|
||||
@@ -361,19 +361,6 @@ pub const Surface = extern struct {
|
||||
void,
|
||||
);
|
||||
};
|
||||
|
||||
/// Emitted when this surface requests that the command palette be
|
||||
/// toggled.
|
||||
pub const @"toggle-command-palette" = struct {
|
||||
pub const name = "toggle-command-palette";
|
||||
pub const connect = impl.connect;
|
||||
const impl = gobject.ext.defineSignal(
|
||||
name,
|
||||
Self,
|
||||
&.{},
|
||||
void,
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const Private = struct {
|
||||
@@ -564,13 +551,8 @@ pub const Surface = extern struct {
|
||||
}
|
||||
|
||||
pub fn toggleCommandPalette(self: *Self) bool {
|
||||
signals.@"toggle-command-palette".impl.emit(
|
||||
self,
|
||||
null,
|
||||
.{},
|
||||
null,
|
||||
);
|
||||
return true;
|
||||
// TODO: pass the surface with the action
|
||||
return self.as(gtk.Widget).activateAction("win.toggle-command-palette", null) != 0;
|
||||
}
|
||||
|
||||
/// Set the current progress report state.
|
||||
@@ -2423,7 +2405,6 @@ pub const Surface = extern struct {
|
||||
signals.@"present-request".impl.register(.{});
|
||||
signals.@"toggle-fullscreen".impl.register(.{});
|
||||
signals.@"toggle-maximize".impl.register(.{});
|
||||
signals.@"toggle-command-palette".impl.register(.{});
|
||||
|
||||
// Virtual methods
|
||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||
|
||||
@@ -28,6 +28,7 @@ const Surface = @import("surface.zig").Surface;
|
||||
const Tab = @import("tab.zig").Tab;
|
||||
const DebugWarning = @import("debug_warning.zig").DebugWarning;
|
||||
const CommandPalette = @import("command_palette.zig").CommandPalette;
|
||||
const WeakRef = @import("../weak_ref.zig").WeakRef;
|
||||
|
||||
const log = std.log.scoped(.gtk_ghostty_window);
|
||||
|
||||
@@ -249,7 +250,7 @@ pub const Window = extern struct {
|
||||
tab_overview_focus_timer: ?c_uint = null,
|
||||
|
||||
/// A weak reference to a command palette.
|
||||
command_palette: gobject.WeakRef = std.mem.zeroes(gobject.WeakRef),
|
||||
command_palette: WeakRef(CommandPalette) = .empty,
|
||||
|
||||
// Template bindings
|
||||
tab_overview: *adw.TabOverview,
|
||||
@@ -343,6 +344,7 @@ pub const Window = extern struct {
|
||||
.{ "paste", actionPaste, null },
|
||||
.{ "reset", actionReset, null },
|
||||
.{ "clear", actionClear, null },
|
||||
// TODO: accept the surface that toggled the command palette
|
||||
.{ "toggle-command-palette", actionToggleCommandPalette, null },
|
||||
};
|
||||
|
||||
@@ -714,13 +716,6 @@ pub const Window = extern struct {
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = Surface.signals.@"toggle-command-palette".connect(
|
||||
surface,
|
||||
*Self,
|
||||
surfaceToggleCommandPalette,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// If we've never had a surface initialize yet, then we register
|
||||
// this signal. Its theoretically possible to launch multiple surfaces
|
||||
@@ -1070,10 +1065,14 @@ pub const Window = extern struct {
|
||||
|
||||
fn dispose(self: *Self) callconv(.c) void {
|
||||
const priv = self.private();
|
||||
|
||||
priv.command_palette.set(null);
|
||||
|
||||
if (priv.config) |v| {
|
||||
v.unref();
|
||||
priv.config = null;
|
||||
}
|
||||
|
||||
priv.tab_bindings.setSource(null);
|
||||
|
||||
gtk.Widget.disposeTemplate(
|
||||
@@ -1529,15 +1528,6 @@ pub const Window = extern struct {
|
||||
// We react to the changes in the propMaximized callback
|
||||
}
|
||||
|
||||
/// React to a signal from a surface requesting that the command palette
|
||||
/// be toggled.
|
||||
fn surfaceToggleCommandPalette(
|
||||
_: *Surface,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
self.toggleCommandPalette();
|
||||
}
|
||||
|
||||
fn surfaceInit(
|
||||
surface: *Surface,
|
||||
self: *Self,
|
||||
@@ -1719,12 +1709,15 @@ pub const Window = extern struct {
|
||||
}
|
||||
|
||||
/// Toggle the command palette.
|
||||
///
|
||||
/// TODO: accept the surface that toggled the command palette as a parameter
|
||||
fn toggleCommandPalette(self: *Window) void {
|
||||
const priv = self.private();
|
||||
|
||||
// Get a reference to a command palette. First check the weak reference
|
||||
// that we save to see if we already have stored. If we don't then
|
||||
// that we save to see if we already have one stored. If we don't then
|
||||
// create a new one.
|
||||
const command_palette = gobject.ext.cast(CommandPalette, priv.command_palette.get()) orelse command_palette: {
|
||||
const command_palette = priv.command_palette.get() orelse command_palette: {
|
||||
// Create a fresh command palette.
|
||||
const command_palette = CommandPalette.new();
|
||||
|
||||
@@ -1747,14 +1740,14 @@ pub const Window = extern struct {
|
||||
.{},
|
||||
);
|
||||
|
||||
// Save a weak reference to the command palette. We use a weak reference to avoid
|
||||
// reference counting cycles that might cause problems later.
|
||||
priv.command_palette.set(command_palette);
|
||||
|
||||
break :command_palette command_palette;
|
||||
};
|
||||
defer command_palette.unref();
|
||||
|
||||
// Save a weak reference to the command palette. We use a weak reference to avoid
|
||||
// reference counting cycles that might cause problems later.
|
||||
priv.command_palette.set(command_palette.as(gobject.Object));
|
||||
|
||||
// Tell the command palette to toggle itself. If the dialog gets
|
||||
// presented (instead of hidden) it will be modal over our window.
|
||||
command_palette.toggle(self);
|
||||
@@ -1772,6 +1765,8 @@ pub const Window = extern struct {
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.c) void {
|
||||
// TODO: accept the surface that toggled the command palette as a
|
||||
// parameter
|
||||
self.toggleCommandPalette();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using Adw 1;
|
||||
|
||||
Adw.Dialog dialog {
|
||||
content-width: 700;
|
||||
closed => $closed();
|
||||
|
||||
Adw.ToolbarView {
|
||||
top-bar-style: flat;
|
||||
|
||||
@@ -10,6 +10,8 @@ pub fn WeakRef(comptime T: type) type {
|
||||
|
||||
ref: gobject.WeakRef = std.mem.zeroes(gobject.WeakRef),
|
||||
|
||||
pub const empty: Self = .{};
|
||||
|
||||
/// Set the weak reference to the given object. This will not
|
||||
/// increase the reference count of the object.
|
||||
pub fn set(self: *Self, v_: ?*T) void {
|
||||
@@ -23,14 +25,9 @@ pub fn WeakRef(comptime T: type) type {
|
||||
/// Get a strong reference to the object, or null if the object
|
||||
/// has been finalized. This increases the reference count by one.
|
||||
pub fn get(self: *Self) ?*T {
|
||||
// The GIR of g_weak_ref_get has a bug where the optional
|
||||
// is not encoded. Or, it may be a bug in zig-gobject.
|
||||
const obj_: ?*gobject.Object = @ptrCast(self.ref.get());
|
||||
const obj = obj_ orelse return null;
|
||||
|
||||
// We can't use `as` because `as` guarantees conversion and
|
||||
// that can't be statically guaranteed.
|
||||
return gobject.ext.cast(T, obj);
|
||||
return gobject.ext.cast(T, self.ref.get() orelse return null);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -38,7 +35,7 @@ pub fn WeakRef(comptime T: type) type {
|
||||
test WeakRef {
|
||||
const testing = std.testing;
|
||||
|
||||
var ref: WeakRef(gtk.TextBuffer) = .{};
|
||||
var ref: WeakRef(gtk.TextBuffer) = .empty;
|
||||
const obj: *gtk.TextBuffer = .new(null);
|
||||
ref.set(obj);
|
||||
ref.get().?.unref(); // The "?" asserts non-null
|
||||
|
||||
Reference in New Issue
Block a user