Merge c418cadf7f into a4cb73db84
commit
748808179d
|
|
@ -48,9 +48,9 @@ pub const blueprints: []const Blueprint = &.{
|
|||
.{ .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" },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ const Config = @import("config.zig").Config;
|
|||
const Surface = @import("surface.zig").Surface;
|
||||
const SplitTree = @import("split_tree.zig").SplitTree;
|
||||
const Window = @import("window.zig").Window;
|
||||
const Tab = @import("tab.zig").Tab;
|
||||
const CloseConfirmationDialog = @import("close_confirmation_dialog.zig").CloseConfirmationDialog;
|
||||
const ConfigErrorsDialog = @import("config_errors_dialog.zig").ConfigErrorsDialog;
|
||||
const GlobalShortcuts = @import("global_shortcuts.zig").GlobalShortcuts;
|
||||
|
|
@ -2329,8 +2330,21 @@ const Action = struct {
|
|||
},
|
||||
},
|
||||
.tab => {
|
||||
// GTK does not yet support tab title prompting
|
||||
return false;
|
||||
switch (target) {
|
||||
.app => return false,
|
||||
.surface => |v| {
|
||||
const surface = v.rt_surface.surface;
|
||||
const tab = ext.getAncestor(
|
||||
Tab,
|
||||
surface.as(gtk.Widget),
|
||||
) orelse {
|
||||
log.warn("surface is not in a tab, ignoring prompt_tab_title", .{});
|
||||
return false;
|
||||
};
|
||||
tab.promptTabTitle();
|
||||
return true;
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -14,6 +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 TitleDialog = @import("title_dialog.zig")
|
||||
.TitleDialog;
|
||||
|
||||
const log = std.log.scoped(.gtk_ghostty_window);
|
||||
|
||||
|
|
@ -125,6 +127,18 @@ pub const Tab = extern struct {
|
|||
},
|
||||
);
|
||||
};
|
||||
pub const @"title-override" = struct {
|
||||
pub const name = "title-override";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
?[:0]const u8,
|
||||
.{
|
||||
.default = null,
|
||||
.accessor = C.privateStringFieldAccessor("title_override"),
|
||||
},
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
pub const signals = struct {
|
||||
|
|
@ -148,6 +162,9 @@ pub const Tab = extern struct {
|
|||
/// The title of this tab. This is usually bound to the active surface.
|
||||
title: ?[:0]const u8 = null,
|
||||
|
||||
/// The manually overridden title from `promptTabTitle`.
|
||||
title_override: ?[:0]const u8 = null,
|
||||
|
||||
/// The tooltip of this tab. This is usually bound to the active surface.
|
||||
tooltip: ?[:0]const u8 = null,
|
||||
|
||||
|
|
@ -198,6 +215,7 @@ pub const Tab = extern struct {
|
|||
const actions = [_]ext.actions.Action(Self){
|
||||
.init("close", actionClose, s_param_type),
|
||||
.init("ring-bell", actionRingBell, null),
|
||||
.init("prompt-tab-title", actionPromptTabTitle, null),
|
||||
};
|
||||
|
||||
_ = ext.actions.addAsGroup(Self, self, "tab", &actions);
|
||||
|
|
@ -206,6 +224,37 @@ pub const Tab = extern struct {
|
|||
//---------------------------------------------------------------
|
||||
// Properties
|
||||
|
||||
/// Overridden title. This will be generally be shown over the title
|
||||
/// unless this is unset (null).
|
||||
pub fn setTitleOverride(self: *Self, title: ?[:0]const u8) void {
|
||||
const priv = self.private();
|
||||
if (priv.title_override) |v| glib.free(@ptrCast(@constCast(v)));
|
||||
priv.title_override = null;
|
||||
if (title) |v| priv.title_override = glib.ext.dupeZ(u8, v);
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"title-override".impl.param_spec);
|
||||
}
|
||||
fn titleDialogSet(
|
||||
_: *TitleDialog,
|
||||
title_ptr: [*:0]const u8,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
const title = std.mem.span(title_ptr);
|
||||
self.setTitleOverride(if (title.len == 0) null else title);
|
||||
}
|
||||
pub fn promptTabTitle(self: *Self) void {
|
||||
const priv = self.private();
|
||||
const dialog = TitleDialog.new(.tab, priv.title_override orelse priv.title);
|
||||
_ = TitleDialog.signals.set.connect(
|
||||
dialog,
|
||||
*Self,
|
||||
titleDialogSet,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
dialog.present(self.as(gtk.Widget));
|
||||
}
|
||||
|
||||
/// Get the currently active surface. See the "active-surface" property.
|
||||
/// This does not ref the value.
|
||||
pub fn getActiveSurface(self: *Self) ?*Surface {
|
||||
|
|
@ -351,6 +400,14 @@ pub const Tab = extern struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn actionPromptTabTitle(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
self.promptTabTitle();
|
||||
}
|
||||
|
||||
fn actionRingBell(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
|
|
@ -372,7 +429,8 @@ pub const Tab = extern struct {
|
|||
_: *Self,
|
||||
config_: ?*Config,
|
||||
terminal_: ?[*:0]const u8,
|
||||
override_: ?[*:0]const u8,
|
||||
surface_override_: ?[*:0]const u8,
|
||||
tab_override: ?[*:0]const u8,
|
||||
zoomed_: c_int,
|
||||
bell_ringing_: c_int,
|
||||
_: *gobject.ParamSpec,
|
||||
|
|
@ -380,7 +438,8 @@ pub const Tab = extern struct {
|
|||
const zoomed = zoomed_ != 0;
|
||||
const bell_ringing = bell_ringing_ != 0;
|
||||
|
||||
// Our plain title is the overridden title if it exists, otherwise
|
||||
// Our plain title is the manually tab overriden title if it exists,
|
||||
// otherwise the overridden title if it exists, otherwise
|
||||
// the terminal title if it exists, otherwise a default string.
|
||||
const plain = plain: {
|
||||
const default = "Ghostty";
|
||||
|
|
@ -389,7 +448,8 @@ pub const Tab = extern struct {
|
|||
break :title config.get().title orelse null;
|
||||
};
|
||||
|
||||
const plain = override_ orelse
|
||||
const plain = tab_override orelse
|
||||
surface_override_ orelse
|
||||
terminal_ orelse
|
||||
config_title orelse
|
||||
break :plain default;
|
||||
|
|
@ -453,6 +513,7 @@ pub const Tab = extern struct {
|
|||
properties.@"split-tree".impl,
|
||||
properties.@"surface-tree".impl,
|
||||
properties.title.impl,
|
||||
properties.@"title-override".impl,
|
||||
properties.tooltip.impl,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
);
|
||||
};
|
||||
|
|
@ -335,6 +335,7 @@ pub const Window = extern struct {
|
|||
.init("close-tab", actionCloseTab, s_variant_type),
|
||||
.init("new-tab", actionNewTab, null),
|
||||
.init("new-window", actionNewWindow, null),
|
||||
.init("prompt-tab-title", actionPromptTabTitle, null),
|
||||
.init("ring-bell", actionRingBell, null),
|
||||
.init("split-right", actionSplitRight, null),
|
||||
.init("split-left", actionSplitLeft, null),
|
||||
|
|
@ -1763,6 +1764,14 @@ pub const Window = extern struct {
|
|||
self.performBindingAction(.new_tab);
|
||||
}
|
||||
|
||||
fn actionPromptTabTitle(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.prompt_tab_title);
|
||||
}
|
||||
|
||||
fn actionSplitRight(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
|
|
|
|||
|
|
@ -277,6 +277,11 @@ menu context_menu_model {
|
|||
submenu {
|
||||
label: _("Tab");
|
||||
|
||||
item {
|
||||
label: _("Change Tab Title...");
|
||||
action: "tab.prompt-tab-title";
|
||||
}
|
||||
|
||||
item {
|
||||
label: _("New Tab");
|
||||
action: "win.new-tab";
|
||||
|
|
|
|||
|
|
@ -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, 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 {
|
||||
|
|
|
|||
|
|
@ -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 [
|
||||
|
|
@ -218,6 +218,11 @@ menu main_menu {
|
|||
}
|
||||
|
||||
section {
|
||||
item {
|
||||
label: _("Change Tab Title…");
|
||||
action: "win.prompt-tab-title";
|
||||
}
|
||||
|
||||
item {
|
||||
label: _("New Tab");
|
||||
action: "win.new-tab";
|
||||
|
|
|
|||
Loading…
Reference in New Issue