Refactor SurfaceTitleDialog to TitleDialog

pull/9904/head
David Matos 2025-12-17 19:36:55 +01:00
parent a03d721599
commit c6891da01d
8 changed files with 62 additions and 233 deletions

View File

@ -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" },
};

View File

@ -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;
};
};

View File

@ -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,

View File

@ -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,
.{},
);

View File

@ -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" },
);
};

View File

@ -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;
};
}

View File

@ -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 <string>;
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 <string>;
tooltip: bind split_tree.active-surface as <$GhosttySurface>.pwd;
$GhosttySplitTree split_tree {

View File

@ -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 [