mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
gtk/adw: add context menu to tab titles to rename tab
This commit is contained in:
124
src/apprt/gtk/RenameTabAdw.zig
Normal file
124
src/apprt/gtk/RenameTabAdw.zig
Normal file
@ -0,0 +1,124 @@
|
||||
const RenameTabAdw = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const c = @import("c.zig").c;
|
||||
const Builder = @import("Builder.zig");
|
||||
const Tab = @import("Tab.zig");
|
||||
const NotebookAdw = @import("notebook_adw.zig").NotebookAdw;
|
||||
|
||||
const log = std.log.scoped(.gtk_rename_tab);
|
||||
|
||||
tab: ?*Tab = null,
|
||||
dialog: ?*c.AdwDialog = null,
|
||||
title: ?*c.AdwEntryRow = null,
|
||||
|
||||
pub fn init(self: *RenameTabAdw) void {
|
||||
self.* = .{};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *RenameTabAdw) void {
|
||||
if (self.dialog) |dialog| {
|
||||
_ = c.adw_dialog_close(dialog);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show(self: *RenameTabAdw, tab: *Tab) void {
|
||||
if (self.tab) |old_tab| {
|
||||
if (old_tab == tab) {
|
||||
if (self.dialog) |dialog| {
|
||||
c.gtk_window_present(@ptrCast(@alignCast(dialog)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self.dialog) |dialog| {
|
||||
_ = c.adw_dialog_close(dialog);
|
||||
}
|
||||
|
||||
self.* = .{};
|
||||
|
||||
assert(tab.window.notebook == .adw);
|
||||
|
||||
const builder = Builder.init("window-adw-rename-tab");
|
||||
defer builder.deinit();
|
||||
|
||||
const dialog = builder.getObject(c.AdwDialog, "rename-tab");
|
||||
|
||||
const title = builder.getObject(c.AdwEntryRow, "title");
|
||||
|
||||
if (tab.manual_title) |manual_label| {
|
||||
var value: c.GValue = std.mem.zeroes(c.GValue);
|
||||
defer c.g_value_unset(&value);
|
||||
_ = c.g_value_init(&value, c.G_TYPE_STRING);
|
||||
c.g_value_set_string(&value, manual_label.ptr);
|
||||
c.g_object_set_property(@ptrCast(@alignCast(title)), "text", &value);
|
||||
}
|
||||
|
||||
self.* = .{
|
||||
.tab = tab,
|
||||
.dialog = dialog,
|
||||
.title = title,
|
||||
};
|
||||
|
||||
_ = c.g_signal_connect_data(dialog, "closed", c.G_CALLBACK(&adwDialogClosed), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(title, "apply", c.G_CALLBACK(&adwEntryRowApply), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(title, "entry-activated", c.G_CALLBACK(&adwEntryRowApply), self, null, c.G_CONNECT_DEFAULT);
|
||||
|
||||
c.adw_dialog_present(dialog, @ptrCast(@alignCast(tab.box)));
|
||||
}
|
||||
|
||||
fn adwDialogClosed(dialog: *c.AdwDialog, ud: ?*anyopaque) callconv(.C) void {
|
||||
const self: *RenameTabAdw = @ptrCast(@alignCast(ud orelse return));
|
||||
|
||||
assert(dialog == self.dialog);
|
||||
|
||||
if (self.tab) |tab| {
|
||||
tab.window.focusCurrentTab();
|
||||
}
|
||||
|
||||
self.* = .{};
|
||||
}
|
||||
|
||||
fn adwEntryRowApply(
|
||||
_: *c.GObject,
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
const self: *RenameTabAdw = @ptrCast(@alignCast(ud orelse return));
|
||||
const tab = self.tab orelse return;
|
||||
const title = self.title orelse return;
|
||||
|
||||
var value: c.GValue = std.mem.zeroes(c.GValue);
|
||||
defer c.g_value_unset(&value);
|
||||
_ = c.g_value_init(&value, c.G_TYPE_STRING);
|
||||
c.g_object_get_property(@ptrCast(@alignCast(title)), "text", &value);
|
||||
|
||||
const text = c.g_value_get_string(&value);
|
||||
|
||||
tab.setManualTitle(std.mem.span(text));
|
||||
|
||||
_ = c.adw_dialog_close(self.dialog);
|
||||
}
|
||||
|
||||
fn adwEntryRowEntryActivated(
|
||||
_: *c.GObject,
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
const self: *RenameTabAdw = @ptrCast(@alignCast(ud orelse return));
|
||||
const tab = self.tab orelse return;
|
||||
const title = self.title orelse return;
|
||||
|
||||
var value: c.GValue = std.mem.zeroes(c.GValue);
|
||||
defer c.g_value_unset(&value);
|
||||
_ = c.g_value_init(&value, c.G_TYPE_STRING);
|
||||
|
||||
c.g_object_get_property(@ptrCast(@alignCast(title)), "text", &value);
|
||||
|
||||
const text = c.g_value_get_string(&value);
|
||||
|
||||
tab.setManualTitle(std.mem.span(text));
|
||||
|
||||
_ = c.adw_dialog_close(self.dialog);
|
||||
}
|
@ -908,7 +908,7 @@ fn updateTitleLabels(self: *Surface) void {
|
||||
|
||||
// If we have a tab and are the focused child, then we have to update the tab
|
||||
if (self.container.tab()) |tab| {
|
||||
if (tab.focus_child == self) tab.setLabelText(title);
|
||||
if (tab.focus_child == self) tab.setTitleText(title);
|
||||
}
|
||||
|
||||
// If we have a window and are focused, then we have to update the window title.
|
||||
|
@ -37,6 +37,12 @@ elem: Surface.Container.Elem,
|
||||
// can easily re-focus that terminal.
|
||||
focus_child: ?*Surface,
|
||||
|
||||
/// Manually set title, will override titles set by ESC sequences.
|
||||
manual_title: ?[:0]const u8 = null,
|
||||
|
||||
/// The last title set by ESC sequences.
|
||||
auto_title: ?[:0]const u8 = null,
|
||||
|
||||
pub fn create(alloc: Allocator, window: *Window, parent_: ?*CoreSurface) !*Tab {
|
||||
var tab = try alloc.create(Tab);
|
||||
errdefer alloc.destroy(tab);
|
||||
@ -88,6 +94,8 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
|
||||
|
||||
/// Deinits tab by deiniting child elem.
|
||||
pub fn deinit(self: *Tab, alloc: Allocator) void {
|
||||
if (self.manual_title) |manual_title| alloc.free(manual_title);
|
||||
if (self.auto_title) |auto_title| alloc.free(auto_title);
|
||||
self.elem.deinit(alloc);
|
||||
}
|
||||
|
||||
@ -108,8 +116,49 @@ pub fn replaceElem(self: *Tab, elem: Surface.Container.Elem) void {
|
||||
self.elem = elem;
|
||||
}
|
||||
|
||||
pub fn setLabelText(self: *Tab, title: [:0]const u8) void {
|
||||
self.window.notebook.setTabLabel(self, title);
|
||||
pub fn setManualTitle(self: *Tab, title: []const u8) void {
|
||||
const alloc = self.window.app.core_app.alloc;
|
||||
|
||||
self.unsetManualTitle();
|
||||
|
||||
const stripped = std.mem.trim(u8, title, &std.ascii.whitespace);
|
||||
if (stripped.len == 0) {
|
||||
if (self.auto_title) |auto_title| {
|
||||
self.window.notebook.setTabTitle(self, auto_title);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const manual_title = alloc.dupeZ(u8, title) catch |err| {
|
||||
log.warn("unable to copy manual title: {}", .{err});
|
||||
return;
|
||||
};
|
||||
self.manual_title = manual_title;
|
||||
|
||||
self.window.notebook.setTabTitle(self, manual_title);
|
||||
}
|
||||
|
||||
pub fn unsetManualTitle(self: *Tab) void {
|
||||
const alloc = self.window.app.core_app.alloc;
|
||||
|
||||
if (self.manual_title) |manual_title| {
|
||||
alloc.free(manual_title);
|
||||
self.manual_title = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setTitleText(self: *Tab, title: [:0]const u8) void {
|
||||
const alloc = self.window.app.core_app.alloc;
|
||||
if (self.manual_title) |manual_title| {
|
||||
self.window.notebook.setTabTitle(self, manual_title);
|
||||
return;
|
||||
}
|
||||
if (self.auto_title) |auto_title| {
|
||||
alloc.free(auto_title);
|
||||
self.auto_title = null;
|
||||
}
|
||||
self.auto_title = alloc.dupeZ(u8, title) catch null;
|
||||
self.window.notebook.setTabTitle(self, title);
|
||||
}
|
||||
|
||||
pub fn setTooltipText(self: *Tab, tooltip: [:0]const u8) void {
|
||||
|
@ -510,6 +510,7 @@ fn initActions(self: *Window) void {
|
||||
.{ "paste", >kActionPaste },
|
||||
.{ "reset", >kActionReset },
|
||||
.{ "clear", >kActionClear },
|
||||
.{ "rename-tab", >kActionRenameTab },
|
||||
};
|
||||
|
||||
inline for (actions) |entry| {
|
||||
@ -528,6 +529,7 @@ fn initActions(self: *Window) void {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Window) void {
|
||||
self.notebook.deinit();
|
||||
self.winproto.deinit(self.app.core_app.alloc);
|
||||
|
||||
if (self.adw_tab_overview_focus_timer) |timer| {
|
||||
@ -1138,6 +1140,15 @@ fn gtkActionClear(
|
||||
};
|
||||
}
|
||||
|
||||
fn gtkActionRenameTab(
|
||||
_: *c.GSimpleAction,
|
||||
_: *c.GVariant,
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
const self: *Window = @ptrCast(@alignCast(ud orelse return));
|
||||
self.notebook.showRenameTab();
|
||||
}
|
||||
|
||||
/// Returns the surface to use for an action.
|
||||
pub fn actionSurface(self: *Window) ?*CoreSurface {
|
||||
const tab = self.notebook.currentTab() orelse return null;
|
||||
|
@ -1,7 +1,11 @@
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
|
||||
pub const c = @cImport({
|
||||
@cInclude("gtk/gtk.h");
|
||||
if (build_options.adwaita) {
|
||||
@cInclude("libadwaita-1/adwaita.h");
|
||||
}
|
||||
});
|
||||
|
||||
pub fn main() !void {
|
||||
@ -17,6 +21,12 @@ pub fn main() !void {
|
||||
};
|
||||
defer alloc.free(filename);
|
||||
|
||||
if (comptime build_options.adwaita) {
|
||||
c.adw_init();
|
||||
} else {
|
||||
if (std.mem.indexOf(u8, filename, "adw")) |_| return;
|
||||
}
|
||||
|
||||
const builder = c.gtk_builder_new_from_file(filename.ptr);
|
||||
defer c.g_object_unref(builder);
|
||||
}
|
||||
|
@ -54,9 +54,11 @@ const icons = [_]struct {
|
||||
};
|
||||
|
||||
pub const ui_files = [_][]const u8{
|
||||
"menu-adw-notebook-tab",
|
||||
"menu-surface-context_menu",
|
||||
"menu-window-menubar",
|
||||
"menu-window-titlebar_menu",
|
||||
"window-adw-rename-tab",
|
||||
};
|
||||
|
||||
pub const gresource_xml = comptimeGenerateGResourceXML();
|
||||
|
@ -12,8 +12,6 @@ const log = std.log.scoped(.gtk);
|
||||
|
||||
const AdwTabView = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwTabView else anyopaque;
|
||||
|
||||
/// An abstraction over the GTK notebook and Adwaita tab view to manage
|
||||
/// all the terminal tabs in a window.
|
||||
/// An abstraction over the GTK notebook and Adwaita tab view to manage
|
||||
/// all the terminal tabs in a window.
|
||||
pub const Notebook = union(enum) {
|
||||
@ -28,6 +26,12 @@ pub const Notebook = union(enum) {
|
||||
return NotebookGtk.init(self);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Notebook) void {
|
||||
switch (self.*) {
|
||||
inline else => |*n| n.deinit(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn asWidget(self: *Notebook) *c.GtkWidget {
|
||||
return switch (self.*) {
|
||||
.adw => |*adw| adw.asWidget(),
|
||||
@ -121,10 +125,10 @@ pub const Notebook = union(enum) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setTabLabel(self: *Notebook, tab: *Tab, title: [:0]const u8) void {
|
||||
pub fn setTabTitle(self: *Notebook, tab: *Tab, title: [:0]const u8) void {
|
||||
switch (self.*) {
|
||||
.adw => |*adw| adw.setTabLabel(tab, title),
|
||||
.gtk => |*gtk| gtk.setTabLabel(tab, title),
|
||||
.adw => |*adw| adw.setTabTitle(tab, title),
|
||||
.gtk => |*gtk| gtk.setTabTitle(tab, title),
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,6 +162,13 @@ pub const Notebook = union(enum) {
|
||||
.gtk => |*gtk| gtk.closeTab(tab),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn showRenameTab(self: *Notebook) void {
|
||||
switch (self.*) {
|
||||
.adw => |*adw| adw.showRenameTab(),
|
||||
.gtk => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn createWindow(currentWindow: *Window) !*Window {
|
||||
|
@ -7,6 +7,8 @@ const Tab = @import("Tab.zig");
|
||||
const Notebook = @import("notebook.zig").Notebook;
|
||||
const createWindow = @import("notebook.zig").createWindow;
|
||||
const adwaita = @import("adwaita.zig");
|
||||
const Builder = @import("Builder.zig");
|
||||
const RenameTabAdw = if (adwaita.versionAtLeast(0, 0, 0)) @import("RenameTabAdw.zig") else struct {};
|
||||
|
||||
const log = std.log.scoped(.gtk);
|
||||
|
||||
@ -14,6 +16,9 @@ const AdwTabView = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwTabView else anyopa
|
||||
const AdwTabPage = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwTabPage else anyopaque;
|
||||
|
||||
pub const NotebookAdw = struct {
|
||||
/// The window that we belong to
|
||||
window: *Window,
|
||||
|
||||
/// the tab view
|
||||
tab_view: *AdwTabView,
|
||||
|
||||
@ -25,7 +30,14 @@ pub const NotebookAdw = struct {
|
||||
/// confirming or not.
|
||||
forcing_close: bool = false,
|
||||
|
||||
/// The last tab that was selected with the "setup-menu" signal.
|
||||
last_setup_menu_tab: ?*Tab = null,
|
||||
|
||||
/// rename tab window
|
||||
rename_tab: RenameTabAdw = .{},
|
||||
|
||||
pub fn init(notebook: *Notebook) void {
|
||||
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||
const window: *Window = @fieldParentPtr("notebook", notebook);
|
||||
const app = window.app;
|
||||
assert(adwaita.enabled(&app.config));
|
||||
@ -33,33 +45,46 @@ pub const NotebookAdw = struct {
|
||||
const tab_view: *c.AdwTabView = c.adw_tab_view_new().?;
|
||||
c.gtk_widget_add_css_class(@ptrCast(@alignCast(tab_view)), "notebook");
|
||||
|
||||
if (comptime adwaita.versionAtLeast(1, 2, 0) and adwaita.versionAtLeast(1, 2, 0)) {
|
||||
if (adwaita.versionAtLeast(1, 2, 0)) {
|
||||
// Adwaita enables all of the shortcuts by default.
|
||||
// We want to manage keybindings ourselves.
|
||||
c.adw_tab_view_remove_shortcuts(tab_view, c.ADW_TAB_VIEW_SHORTCUT_ALL_SHORTCUTS);
|
||||
}
|
||||
|
||||
const builder = Builder.init("menu-adw-notebook-tab");
|
||||
defer builder.deinit();
|
||||
|
||||
c.adw_tab_view_set_menu_model(tab_view, builder.getObject(c.GMenuModel, "menu"));
|
||||
|
||||
notebook.* = .{
|
||||
.adw = .{
|
||||
.window = window,
|
||||
.tab_view = tab_view,
|
||||
},
|
||||
};
|
||||
|
||||
_ = c.g_signal_connect_data(tab_view, "page-attached", c.G_CALLBACK(&adwPageAttached), window, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(tab_view, "close-page", c.G_CALLBACK(&adwClosePage), window, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(tab_view, "create-window", c.G_CALLBACK(&adwTabViewCreateWindow), window, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(tab_view, "notify::selected-page", c.G_CALLBACK(&adwSelectPage), window, null, c.G_CONNECT_DEFAULT);
|
||||
const self = ¬ebook.adw;
|
||||
|
||||
_ = c.g_signal_connect_data(tab_view, "page-attached", c.G_CALLBACK(&adwPageAttached), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(tab_view, "close-page", c.G_CALLBACK(&adwClosePage), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(tab_view, "create-window", c.G_CALLBACK(&adwTabViewCreateWindow), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(tab_view, "notify::selected-page", c.G_CALLBACK(&adwSelectPage), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(tab_view, "setup-menu", c.G_CALLBACK(&adwTabViewSetupMenu), self, null, c.G_CONNECT_DEFAULT);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *NotebookAdw) void {
|
||||
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||
self.rename_tab.deinit();
|
||||
}
|
||||
|
||||
pub fn asWidget(self: *NotebookAdw) *c.GtkWidget {
|
||||
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||
return @ptrCast(@alignCast(self.tab_view));
|
||||
}
|
||||
|
||||
pub fn nPages(self: *NotebookAdw) c_int {
|
||||
if (comptime adwaita.versionAtLeast(0, 0, 0))
|
||||
return c.adw_tab_view_get_n_pages(self.tab_view)
|
||||
else
|
||||
unreachable;
|
||||
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||
return c.adw_tab_view_get_n_pages(self.tab_view);
|
||||
}
|
||||
|
||||
/// Returns the index of the currently selected page.
|
||||
@ -98,7 +123,7 @@ pub const NotebookAdw = struct {
|
||||
_ = c.adw_tab_view_reorder_page(self.tab_view, page, position);
|
||||
}
|
||||
|
||||
pub fn setTabLabel(self: *NotebookAdw, tab: *Tab, title: [:0]const u8) void {
|
||||
pub fn setTabTitle(self: *NotebookAdw, tab: *Tab, title: [:0]const u8) void {
|
||||
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||
const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box));
|
||||
c.adw_tab_page_set_title(page, title.ptr);
|
||||
@ -155,16 +180,23 @@ pub const NotebookAdw = struct {
|
||||
c.gtk_window_destroy(window);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn showRenameTab(self: *NotebookAdw) void {
|
||||
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||
const tab = self.last_setup_menu_tab orelse return;
|
||||
assert(tab.window == self.window);
|
||||
self.rename_tab.show(tab);
|
||||
}
|
||||
};
|
||||
|
||||
fn adwPageAttached(_: *AdwTabView, page: *c.AdwTabPage, _: c_int, ud: ?*anyopaque) callconv(.C) void {
|
||||
const window: *Window = @ptrCast(@alignCast(ud.?));
|
||||
const self: *NotebookAdw = @ptrCast(@alignCast(ud orelse return));
|
||||
|
||||
const child = c.adw_tab_page_get_child(page);
|
||||
const tab: *Tab = @ptrCast(@alignCast(c.g_object_get_data(@ptrCast(child), Tab.GHOSTTY_TAB) orelse return));
|
||||
tab.window = window;
|
||||
tab.window = self.window;
|
||||
|
||||
window.focusCurrentTab();
|
||||
self.window.focusCurrentTab();
|
||||
}
|
||||
|
||||
fn adwClosePage(
|
||||
@ -172,20 +204,20 @@ fn adwClosePage(
|
||||
page: *c.AdwTabPage,
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) c.gboolean {
|
||||
const self: *NotebookAdw = @ptrCast(@alignCast(ud orelse return 0));
|
||||
|
||||
const child = c.adw_tab_page_get_child(page);
|
||||
const tab: *Tab = @ptrCast(@alignCast(c.g_object_get_data(
|
||||
@ptrCast(child),
|
||||
Tab.GHOSTTY_TAB,
|
||||
) orelse return 0));
|
||||
|
||||
const window: *Window = @ptrCast(@alignCast(ud.?));
|
||||
const notebook = window.notebook.adw;
|
||||
c.adw_tab_view_close_page_finish(
|
||||
notebook.tab_view,
|
||||
self.tab_view,
|
||||
page,
|
||||
@intFromBool(notebook.forcing_close),
|
||||
@intFromBool(self.forcing_close),
|
||||
);
|
||||
if (!notebook.forcing_close) tab.closeWithConfirmation();
|
||||
if (!self.forcing_close) tab.closeWithConfirmation();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -193,8 +225,8 @@ fn adwTabViewCreateWindow(
|
||||
_: *AdwTabView,
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) ?*AdwTabView {
|
||||
const currentWindow: *Window = @ptrCast(@alignCast(ud.?));
|
||||
const window = createWindow(currentWindow) catch |err| {
|
||||
const self: *NotebookAdw = @ptrCast(@alignCast(ud orelse return null));
|
||||
const window = createWindow(self.window) catch |err| {
|
||||
log.warn("error creating new window error={}", .{err});
|
||||
return null;
|
||||
};
|
||||
@ -202,8 +234,22 @@ fn adwTabViewCreateWindow(
|
||||
}
|
||||
|
||||
fn adwSelectPage(_: *c.GObject, _: *c.GParamSpec, ud: ?*anyopaque) void {
|
||||
const window: *Window = @ptrCast(@alignCast(ud.?));
|
||||
const page = c.adw_tab_view_get_selected_page(window.notebook.adw.tab_view) orelse return;
|
||||
const self: *NotebookAdw = @ptrCast(@alignCast(ud orelse return));
|
||||
|
||||
const page = c.adw_tab_view_get_selected_page(self.tab_view) orelse return;
|
||||
const title = c.adw_tab_page_get_title(page);
|
||||
window.setTitle(std.mem.span(title));
|
||||
self.window.setTitle(std.mem.span(title));
|
||||
}
|
||||
|
||||
fn adwTabViewSetupMenu(tab_view: *AdwTabView, page: *AdwTabPage, ud: ?*anyopaque) callconv(.C) void {
|
||||
const self: *NotebookAdw = @ptrCast(@alignCast(ud orelse return));
|
||||
|
||||
assert(self.tab_view == tab_view);
|
||||
|
||||
const child = c.adw_tab_page_get_child(page);
|
||||
const tab: *Tab = @ptrCast(@alignCast(
|
||||
c.g_object_get_data(@ptrCast(child), Tab.GHOSTTY_TAB) orelse return,
|
||||
));
|
||||
|
||||
self.last_setup_menu_tab = tab;
|
||||
}
|
||||
|
@ -59,6 +59,10 @@ pub const NotebookGtk = struct {
|
||||
_ = c.g_signal_connect_data(gtk_notebook, "create-window", c.G_CALLBACK(>kNotebookCreateWindow), window, null, c.G_CONNECT_DEFAULT);
|
||||
}
|
||||
|
||||
pub fn deinit(self: NotebookGtk) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
/// return the underlying widget as a generic GtkWidget
|
||||
pub fn asWidget(self: *NotebookGtk) *c.GtkWidget {
|
||||
return @ptrCast(@alignCast(self.notebook));
|
||||
@ -101,7 +105,7 @@ pub const NotebookGtk = struct {
|
||||
c.gtk_notebook_reorder_child(self.notebook, @ptrCast(tab.box), position);
|
||||
}
|
||||
|
||||
pub fn setTabLabel(_: *NotebookGtk, tab: *Tab, title: [:0]const u8) void {
|
||||
pub fn setTabTitle(_: *NotebookGtk, tab: *Tab, title: [:0]const u8) void {
|
||||
c.gtk_label_set_text(tab.label_text, title.ptr);
|
||||
}
|
||||
|
||||
|
13
src/apprt/gtk/ui/menu-adw-notebook-tab.ui
Normal file
13
src/apprt/gtk/ui/menu-adw-notebook-tab.ui
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<interface domain="com.mitchellh.ghostty">
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<menu id="menu">
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Rename</attribute>
|
||||
<attribute name="action">win.rename-tab</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
</interface>
|
||||
|
33
src/apprt/gtk/ui/window-adw-rename-tab.ui
Normal file
33
src/apprt/gtk/ui/window-adw-rename-tab.ui
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<interface domain="com.mitchellh.ghostty">
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<requires lib="Adw" version="1.0"/>
|
||||
<object class="AdwDialog" id="rename-tab">
|
||||
<property name="content-width">350</property>
|
||||
<property name="title">Set Tab Title</property>
|
||||
<child>
|
||||
<object class="AdwToolbarView">
|
||||
<child type="top">
|
||||
<object class="AdwHeaderBar"/>
|
||||
</child>
|
||||
<property name="content">
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="margin-bottom">18</property>
|
||||
<property name="margin-end">18</property>
|
||||
<property name="margin-start">18</property>
|
||||
<property name="margin-top">18</property>
|
||||
<child>
|
||||
<object class="AdwEntryRow" id="title">
|
||||
<property name="title" translatable="yes">Title</property>
|
||||
<property name="show-apply-button">true</property>
|
||||
<property name="enable-emoji-completion">true</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
||||
|
@ -475,7 +475,9 @@ pub fn add(
|
||||
.root_source_file = b.path("src/apprt/gtk/builder_check.zig"),
|
||||
.target = b.host,
|
||||
});
|
||||
builder_check.root_module.addOptions("build_options", self.options);
|
||||
builder_check.linkSystemLibrary2("gtk4", dynamic_link_opts);
|
||||
if (self.config.adwaita) builder_check.linkSystemLibrary2("libadwaita-1", dynamic_link_opts);
|
||||
builder_check.linkLibC();
|
||||
|
||||
for (gresource.dependencies) |pathname| {
|
||||
|
Reference in New Issue
Block a user