diff --git a/src/apprt/gtk/build/gresource.zig b/src/apprt/gtk/build/gresource.zig index 282643cfc..97b5d7ec4 100644 --- a/src/apprt/gtk/build/gresource.zig +++ b/src/apprt/gtk/build/gresource.zig @@ -42,16 +42,15 @@ pub const blueprints: []const Blueprint = &.{ .{ .major = 1, .minor = 5, .name = "imgui-widget" }, .{ .major = 1, .minor = 5, .name = "inspector-widget" }, .{ .major = 1, .minor = 5, .name = "inspector-window" }, - .{ .major = 1, .minor = 5, .name = "prompt-tab-title-dialog" }, .{ .major = 1, .minor = 2, .name = "resize-overlay" }, .{ .major = 1, .minor = 2, .name = "search-overlay" }, .{ .major = 1, .minor = 5, .name = "split-tree" }, .{ .major = 1, .minor = 5, .name = "split-tree-split" }, .{ .major = 1, .minor = 2, .name = "surface" }, .{ .major = 1, .minor = 5, .name = "surface-scrolled-window" }, - .{ .major = 1, .minor = 5, .name = "surface-title-dialog" }, .{ .major = 1, .minor = 3, .name = "surface-child-exited" }, .{ .major = 1, .minor = 5, .name = "tab" }, + .{ .major = 1, .minor = 5, .name = "title-dialog" }, .{ .major = 1, .minor = 5, .name = "window" }, .{ .major = 1, .minor = 5, .name = "command-palette" }, }; diff --git a/src/apprt/gtk/class/prompt_tab_title_dialog.zig b/src/apprt/gtk/class/prompt_tab_title_dialog.zig deleted file mode 100644 index 00163ac79..000000000 --- a/src/apprt/gtk/class/prompt_tab_title_dialog.zig +++ /dev/null @@ -1,186 +0,0 @@ -const std = @import("std"); -const adw = @import("adw"); -const gio = @import("gio"); -const glib = @import("glib"); -const gobject = @import("gobject"); -const gtk = @import("gtk"); - -const gresource = @import("../build/gresource.zig"); -const ext = @import("../ext.zig"); -const Common = @import("../class.zig").Common; - -const log = std.log.scoped(.gtk_ghostty_prompt_tab_title_dialog); - -pub const PromptTabTitleDialog = extern struct { - const Self = @This(); - parent_instance: Parent, - pub const Parent = adw.AlertDialog; - pub const getGObjectType = gobject.ext - .defineClass(Self, .{ - .name = "GhosttyPromptTabTitleDialog", - .instanceInit = &init, - .classInit = &Class.init, - .parent_class = &Class.parent, - .private = .{ .Type = Private, .offset = &Private.offset }, - }); - pub const properties = struct { - pub const @"initial-value" = struct { - pub const name = "initial-value"; - pub const get = impl.get; - pub const set = impl.set; - const impl = gobject.ext.defineProperty( - name, - Self, - ?[:0]const u8, - .{ - .default = null, - .accessor = C.privateStringFieldAccessor("initial_value"), - }, - ); - }; - }; - pub const signals = struct { - /// Set the title to the given value. - pub const set = struct { - pub const name = "set"; - pub const connect = impl.connect; - const impl = gobject.ext.defineSignal( - name, - Self, - &.{[*:0]const u8}, - void, - ); - }; - }; - - const Private = struct { - /// The initial value of the entry field. - initial_value: ?[:0]const u8 = null, - - // Template bindings - entry: *gtk.Entry, - - pub var offset: c_int = 0; - }; - fn init(self: *Self, _: *Class) callconv(.c) void { - gtk.Widget.initTemplate(self.as(gtk.Widget)); - } - pub fn present(self: *Self, parent_: *gtk.Widget) void { - // If we have a window we can attach to, we prefer that. - const parent: *gtk.Widget = if (ext.getAncestor( - adw.ApplicationWindow, - parent_, - )) |window| - window.as(gtk.Widget) - else if (ext.getAncestor( - adw.Window, - parent_, - )) |window| - window.as(gtk.Widget) - else - parent_; - - // Set our initial value - const priv = self.private(); - if (priv.initial_value) |v| { - priv.entry.getBuffer().setText(v, -1); - } - - // Show it. We could also just use virtual methods to bind to - // response but this is pretty simple. - self.as(adw.AlertDialog).choose( - parent, - null, - alertDialogReady, - self, - ); - } - - fn alertDialogReady( - _: ?*gobject.Object, - result: *gio.AsyncResult, - ud: ?*anyopaque, - ) callconv(.c) void { - const self: *Self = @ptrCast(@alignCast(ud)); - const response = self.as(adw.AlertDialog).chooseFinish(result); - - // If we didn't hit "okay" then we do nothing. - if (std.mem.orderZ(u8, "ok", response) != .eq) return; - - // Emit our signal with the new title. - const title = std.mem.span(self.private().entry.getBuffer().getText()); - signals.set.impl.emit( - self, - null, - .{title.ptr}, - null, - ); - } - - fn dispose(self: *Self) callconv(.c) void { - gtk.Widget.disposeTemplate( - self.as(gtk.Widget), - getGObjectType(), - ); - - gobject.Object.virtual_methods.dispose.call( - Class.parent, - self.as(Parent), - ); - } - - fn finalize(self: *Self) callconv(.c) void { - const priv = self.private(); - if (priv.initial_value) |v| { - glib.free(@ptrCast(@constCast(v))); - priv.initial_value = null; - } - - gobject.Object.virtual_methods.finalize.call( - Class.parent, - self.as(Parent), - ); - } - - const C = Common(Self, Private); - pub const as = C.as; - pub const ref = C.ref; - pub const unref = C.unref; - const private = C.private; - - pub const Class = extern struct { - parent_class: Parent.Class, - var parent: *Parent.Class = undefined; - pub const Instance = Self; - - fn init(class: *Class) callconv(.c) void { - gtk.Widget.Class.setTemplateFromResource( - class.as(gtk.Widget.Class), - comptime gresource.blueprint(.{ - .major = 1, - .minor = 5, - .name = "prompt-tab-title-dialog", - }), - ); - - // Signals - signals.set.impl.register(.{}); - - // Bindings - class.bindTemplateChildPrivate("entry", .{}); - - // Properties - gobject.ext.registerProperties(class, &.{ - properties.@"initial-value".impl, - }); - - // Virtual methods - gobject.Object.virtual_methods.dispose.implement(class, &dispose); - gobject.Object.virtual_methods.finalize.implement(class, &finalize); - } - - pub const as = C.Class.as; - pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; - pub const bindTemplateCallback = C.Class.bindTemplateCallback; - }; -}; diff --git a/src/apprt/gtk/class/surface.zig b/src/apprt/gtk/class/surface.zig index 548ae1a6a..638abf693 100644 --- a/src/apprt/gtk/class/surface.zig +++ b/src/apprt/gtk/class/surface.zig @@ -28,7 +28,8 @@ const ResizeOverlay = @import("resize_overlay.zig").ResizeOverlay; const SearchOverlay = @import("search_overlay.zig").SearchOverlay; const ChildExited = @import("surface_child_exited.zig").SurfaceChildExited; const ClipboardConfirmationDialog = @import("clipboard_confirmation_dialog.zig").ClipboardConfirmationDialog; -const TitleDialog = @import("surface_title_dialog.zig").SurfaceTitleDialog; +const TitleDialog = @import("title_dialog.zig") + .TitleDialog; const Window = @import("window.zig").Window; const InspectorWindow = @import("inspector_window.zig").InspectorWindow; const i18n = @import("../../../os/i18n.zig"); @@ -1241,12 +1242,7 @@ pub const Surface = extern struct { /// Prompt for a manual title change for the surface. pub fn promptTitle(self: *Self) void { const priv = self.private(); - const dialog = gobject.ext.newInstance( - TitleDialog, - .{ - .@"initial-value" = priv.title_override orelse priv.title, - }, - ); + const dialog = TitleDialog.new(.surface, priv.title_override orelse priv.title); _ = TitleDialog.signals.set.connect( dialog, *Self, diff --git a/src/apprt/gtk/class/tab.zig b/src/apprt/gtk/class/tab.zig index 8426e89c3..affd7d243 100644 --- a/src/apprt/gtk/class/tab.zig +++ b/src/apprt/gtk/class/tab.zig @@ -14,8 +14,8 @@ const Config = @import("config.zig").Config; const Application = @import("application.zig").Application; const SplitTree = @import("split_tree.zig").SplitTree; const Surface = @import("surface.zig").Surface; -const TabDialog = @import("prompt_tab_title_dialog.zig") - .PromptTabTitleDialog; +const TitleDialog = @import("title_dialog.zig") + .TitleDialog; const log = std.log.scoped(.gtk_ghostty_window); @@ -233,8 +233,8 @@ pub const Tab = extern struct { if (title) |v| priv.title_override = glib.ext.dupeZ(u8, v); self.as(gobject.Object).notifyByPspec(properties.@"title-override".impl.param_spec); } - fn tabDialogSet( - _: *TabDialog, + fn titleDialogSet( + _: *TitleDialog, title_ptr: [*:0]const u8, self: *Self, ) callconv(.c) void { @@ -243,16 +243,11 @@ pub const Tab = extern struct { } pub fn promptTabTitle(self: *Self) void { const priv = self.private(); - const dialog = gobject.ext.newInstance( - TabDialog, - .{ - .@"initial-value" = priv.title_override orelse priv.title, - }, - ); - _ = TabDialog.signals.set.connect( + const dialog = TitleDialog.new(.tab, priv.title_override orelse priv.title); + _ = TitleDialog.signals.set.connect( dialog, *Self, - tabDialogSet, + titleDialogSet, self, .{}, ); diff --git a/src/apprt/gtk/class/surface_title_dialog.zig b/src/apprt/gtk/class/title_dialog.zig similarity index 77% rename from src/apprt/gtk/class/surface_title_dialog.zig rename to src/apprt/gtk/class/title_dialog.zig index aa1d1a153..ac95ae4b6 100644 --- a/src/apprt/gtk/class/surface_title_dialog.zig +++ b/src/apprt/gtk/class/title_dialog.zig @@ -6,17 +6,19 @@ const gobject = @import("gobject"); const gtk = @import("gtk"); const gresource = @import("../build/gresource.zig"); +const i18n = @import("../../../os/main.zig").i18n; const ext = @import("../ext.zig"); const Common = @import("../class.zig").Common; +const Dialog = @import("dialog.zig").Dialog; -const log = std.log.scoped(.gtk_ghostty_surface_title_dialog); +const log = std.log.scoped(.gtk_ghostty_title_dialog); -pub const SurfaceTitleDialog = extern struct { +pub const TitleDialog = extern struct { const Self = @This(); parent_instance: Parent, pub const Parent = adw.AlertDialog; pub const getGObjectType = gobject.ext.defineClass(Self, .{ - .name = "GhosttySurfaceTitleDialog", + .name = "GhosttyTitleDialog", .instanceInit = &init, .classInit = &Class.init, .parent_class = &Class.parent, @@ -24,6 +26,24 @@ pub const SurfaceTitleDialog = extern struct { }); pub const properties = struct { + pub const target = struct { + pub const name = "target"; + const impl = gobject.ext.defineProperty( + name, + Self, + Target, + .{ + .default = .surface, + .accessor = gobject.ext + .privateFieldAccessor( + Self, + Private, + &Private.offset, + "target", + ), + }, + ); + }; pub const @"initial-value" = struct { pub const name = "initial-value"; pub const get = impl.get; @@ -59,6 +79,7 @@ pub const SurfaceTitleDialog = extern struct { initial_value: ?[:0]const u8 = null, // Template bindings + target: Target, entry: *gtk.Entry, pub var offset: c_int = 0; @@ -68,6 +89,10 @@ pub const SurfaceTitleDialog = extern struct { gtk.Widget.initTemplate(self.as(gtk.Widget)); } + pub fn new(target: Target, initial_value: ?[:0]const u8) *Self { + return gobject.ext.newInstance(Self, .{ .target = target, .@"initial-value" = initial_value }); + } + pub fn present(self: *Self, parent_: *gtk.Widget) void { // If we have a window we can attach to, we prefer that. const parent: *gtk.Widget = if (ext.getAncestor( @@ -89,6 +114,9 @@ pub const SurfaceTitleDialog = extern struct { priv.entry.getBuffer().setText(v, -1); } + // Set the title for the dialog + self.as(Dialog.Parent).setHeading(priv.target.title()); + // Show it. We could also just use virtual methods to bind to // response but this is pretty simple. self.as(adw.AlertDialog).choose( @@ -162,7 +190,7 @@ pub const SurfaceTitleDialog = extern struct { comptime gresource.blueprint(.{ .major = 1, .minor = 5, - .name = "surface-title-dialog", + .name = "title-dialog", }), ); @@ -175,6 +203,7 @@ pub const SurfaceTitleDialog = extern struct { // Properties gobject.ext.registerProperties(class, &.{ properties.@"initial-value".impl, + properties.target.impl, }); // Virtual methods @@ -187,3 +216,19 @@ pub const SurfaceTitleDialog = extern struct { pub const bindTemplateCallback = C.Class.bindTemplateCallback; }; }; + +pub const Target = enum(c_int) { + surface, + tab, + pub fn title(self: Target) [*:0]const u8 { + return switch (self) { + .surface => i18n._("Change Terminal Title"), + .tab => i18n._("Change Tab Title"), + }; + } + + pub const getGObjectType = gobject.ext.defineEnum( + Target, + .{ .name = "GhosttyTitleDialogTarget" }, + ); +}; diff --git a/src/apprt/gtk/ui/1.5/prompt-tab-title-dialog.blp b/src/apprt/gtk/ui/1.5/prompt-tab-title-dialog.blp deleted file mode 100644 index 695b5521e..000000000 --- a/src/apprt/gtk/ui/1.5/prompt-tab-title-dialog.blp +++ /dev/null @@ -1,19 +0,0 @@ -using Gtk 4.0; -using Adw 1; - -template $GhosttyPromptTabTitleDialog: Adw.AlertDialog { - heading: _("Change Tab Title"); - body: _("Leave blank to restore the default."); - - responses [ - cancel: _("Cancel"), - ok: _("OK") suggested, - ] - - default-response: "ok"; - focus-widget: entry; - - extra-child: Entry entry { - activates-default: true; - }; -} diff --git a/src/apprt/gtk/ui/1.5/tab.blp b/src/apprt/gtk/ui/1.5/tab.blp index 2df290da3..55f2e7ef4 100644 --- a/src/apprt/gtk/ui/1.5/tab.blp +++ b/src/apprt/gtk/ui/1.5/tab.blp @@ -8,7 +8,7 @@ template $GhosttyTab: Box { orientation: vertical; hexpand: true; vexpand: true; - title: bind $computed_title(template.config, split_tree.active-surface as <$GhosttySurface>.title, split_tree.active-surface as <$GhosttySurface>.title-override, template.title-override,split_tree.is-zoomed, split_tree.active-surface as <$GhosttySurface>.bell-ringing) as ; + title: bind $computed_title(template.config, split_tree.active-surface as <$GhosttySurface>.title, split_tree.active-surface as <$GhosttySurface>.title-override, template.title-override, split_tree.is-zoomed, split_tree.active-surface as <$GhosttySurface>.bell-ringing) as ; tooltip: bind split_tree.active-surface as <$GhosttySurface>.pwd; $GhosttySplitTree split_tree { diff --git a/src/apprt/gtk/ui/1.5/surface-title-dialog.blp b/src/apprt/gtk/ui/1.5/title-dialog.blp similarity index 74% rename from src/apprt/gtk/ui/1.5/surface-title-dialog.blp rename to src/apprt/gtk/ui/1.5/title-dialog.blp index 90d9f9c0b..737a92b51 100644 --- a/src/apprt/gtk/ui/1.5/surface-title-dialog.blp +++ b/src/apprt/gtk/ui/1.5/title-dialog.blp @@ -1,8 +1,7 @@ using Gtk 4.0; using Adw 1; -template $GhosttySurfaceTitleDialog: Adw.AlertDialog { - heading: _("Change Terminal Title"); +template $GhosttyTitleDialog: Adw.AlertDialog { body: _("Leave blank to restore the default title."); responses [