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 we have a tab and are the focused child, then we have to update the tab
|
||||||
if (self.container.tab()) |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.
|
// 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.
|
// can easily re-focus that terminal.
|
||||||
focus_child: ?*Surface,
|
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 {
|
pub fn create(alloc: Allocator, window: *Window, parent_: ?*CoreSurface) !*Tab {
|
||||||
var tab = try alloc.create(Tab);
|
var tab = try alloc.create(Tab);
|
||||||
errdefer alloc.destroy(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.
|
/// Deinits tab by deiniting child elem.
|
||||||
pub fn deinit(self: *Tab, alloc: Allocator) void {
|
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);
|
self.elem.deinit(alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,8 +116,49 @@ pub fn replaceElem(self: *Tab, elem: Surface.Container.Elem) void {
|
|||||||
self.elem = elem;
|
self.elem = elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setLabelText(self: *Tab, title: [:0]const u8) void {
|
pub fn setManualTitle(self: *Tab, title: []const u8) void {
|
||||||
self.window.notebook.setTabLabel(self, title);
|
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 {
|
pub fn setTooltipText(self: *Tab, tooltip: [:0]const u8) void {
|
||||||
|
@ -510,6 +510,7 @@ fn initActions(self: *Window) void {
|
|||||||
.{ "paste", >kActionPaste },
|
.{ "paste", >kActionPaste },
|
||||||
.{ "reset", >kActionReset },
|
.{ "reset", >kActionReset },
|
||||||
.{ "clear", >kActionClear },
|
.{ "clear", >kActionClear },
|
||||||
|
.{ "rename-tab", >kActionRenameTab },
|
||||||
};
|
};
|
||||||
|
|
||||||
inline for (actions) |entry| {
|
inline for (actions) |entry| {
|
||||||
@ -528,6 +529,7 @@ fn initActions(self: *Window) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Window) void {
|
pub fn deinit(self: *Window) void {
|
||||||
|
self.notebook.deinit();
|
||||||
self.winproto.deinit(self.app.core_app.alloc);
|
self.winproto.deinit(self.app.core_app.alloc);
|
||||||
|
|
||||||
if (self.adw_tab_overview_focus_timer) |timer| {
|
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.
|
/// Returns the surface to use for an action.
|
||||||
pub fn actionSurface(self: *Window) ?*CoreSurface {
|
pub fn actionSurface(self: *Window) ?*CoreSurface {
|
||||||
const tab = self.notebook.currentTab() orelse return null;
|
const tab = self.notebook.currentTab() orelse return null;
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
|
||||||
pub const c = @cImport({
|
pub const c = @cImport({
|
||||||
@cInclude("gtk/gtk.h");
|
@cInclude("gtk/gtk.h");
|
||||||
|
if (build_options.adwaita) {
|
||||||
|
@cInclude("libadwaita-1/adwaita.h");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
@ -17,6 +21,12 @@ pub fn main() !void {
|
|||||||
};
|
};
|
||||||
defer alloc.free(filename);
|
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);
|
const builder = c.gtk_builder_new_from_file(filename.ptr);
|
||||||
defer c.g_object_unref(builder);
|
defer c.g_object_unref(builder);
|
||||||
}
|
}
|
||||||
|
@ -54,9 +54,11 @@ const icons = [_]struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const ui_files = [_][]const u8{
|
pub const ui_files = [_][]const u8{
|
||||||
|
"menu-adw-notebook-tab",
|
||||||
"menu-surface-context_menu",
|
"menu-surface-context_menu",
|
||||||
"menu-window-menubar",
|
"menu-window-menubar",
|
||||||
"menu-window-titlebar_menu",
|
"menu-window-titlebar_menu",
|
||||||
|
"window-adw-rename-tab",
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const gresource_xml = comptimeGenerateGResourceXML();
|
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;
|
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
|
/// An abstraction over the GTK notebook and Adwaita tab view to manage
|
||||||
/// all the terminal tabs in a window.
|
/// all the terminal tabs in a window.
|
||||||
pub const Notebook = union(enum) {
|
pub const Notebook = union(enum) {
|
||||||
@ -28,6 +26,12 @@ pub const Notebook = union(enum) {
|
|||||||
return NotebookGtk.init(self);
|
return NotebookGtk.init(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Notebook) void {
|
||||||
|
switch (self.*) {
|
||||||
|
inline else => |*n| n.deinit(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asWidget(self: *Notebook) *c.GtkWidget {
|
pub fn asWidget(self: *Notebook) *c.GtkWidget {
|
||||||
return switch (self.*) {
|
return switch (self.*) {
|
||||||
.adw => |*adw| adw.asWidget(),
|
.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.*) {
|
switch (self.*) {
|
||||||
.adw => |*adw| adw.setTabLabel(tab, title),
|
.adw => |*adw| adw.setTabTitle(tab, title),
|
||||||
.gtk => |*gtk| gtk.setTabLabel(tab, title),
|
.gtk => |*gtk| gtk.setTabTitle(tab, title),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +162,13 @@ pub const Notebook = union(enum) {
|
|||||||
.gtk => |*gtk| gtk.closeTab(tab),
|
.gtk => |*gtk| gtk.closeTab(tab),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn showRenameTab(self: *Notebook) void {
|
||||||
|
switch (self.*) {
|
||||||
|
.adw => |*adw| adw.showRenameTab(),
|
||||||
|
.gtk => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn createWindow(currentWindow: *Window) !*Window {
|
pub fn createWindow(currentWindow: *Window) !*Window {
|
||||||
|
@ -7,6 +7,8 @@ const Tab = @import("Tab.zig");
|
|||||||
const Notebook = @import("notebook.zig").Notebook;
|
const Notebook = @import("notebook.zig").Notebook;
|
||||||
const createWindow = @import("notebook.zig").createWindow;
|
const createWindow = @import("notebook.zig").createWindow;
|
||||||
const adwaita = @import("adwaita.zig");
|
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);
|
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;
|
const AdwTabPage = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwTabPage else anyopaque;
|
||||||
|
|
||||||
pub const NotebookAdw = struct {
|
pub const NotebookAdw = struct {
|
||||||
|
/// The window that we belong to
|
||||||
|
window: *Window,
|
||||||
|
|
||||||
/// the tab view
|
/// the tab view
|
||||||
tab_view: *AdwTabView,
|
tab_view: *AdwTabView,
|
||||||
|
|
||||||
@ -25,7 +30,14 @@ pub const NotebookAdw = struct {
|
|||||||
/// confirming or not.
|
/// confirming or not.
|
||||||
forcing_close: bool = false,
|
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 {
|
pub fn init(notebook: *Notebook) void {
|
||||||
|
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||||
const window: *Window = @fieldParentPtr("notebook", notebook);
|
const window: *Window = @fieldParentPtr("notebook", notebook);
|
||||||
const app = window.app;
|
const app = window.app;
|
||||||
assert(adwaita.enabled(&app.config));
|
assert(adwaita.enabled(&app.config));
|
||||||
@ -33,33 +45,46 @@ pub const NotebookAdw = struct {
|
|||||||
const tab_view: *c.AdwTabView = c.adw_tab_view_new().?;
|
const tab_view: *c.AdwTabView = c.adw_tab_view_new().?;
|
||||||
c.gtk_widget_add_css_class(@ptrCast(@alignCast(tab_view)), "notebook");
|
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.
|
// Adwaita enables all of the shortcuts by default.
|
||||||
// We want to manage keybindings ourselves.
|
// We want to manage keybindings ourselves.
|
||||||
c.adw_tab_view_remove_shortcuts(tab_view, c.ADW_TAB_VIEW_SHORTCUT_ALL_SHORTCUTS);
|
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.* = .{
|
notebook.* = .{
|
||||||
.adw = .{
|
.adw = .{
|
||||||
|
.window = window,
|
||||||
.tab_view = tab_view,
|
.tab_view = tab_view,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = c.g_signal_connect_data(tab_view, "page-attached", c.G_CALLBACK(&adwPageAttached), window, null, c.G_CONNECT_DEFAULT);
|
const self = ¬ebook.adw;
|
||||||
_ = 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, "page-attached", c.G_CALLBACK(&adwPageAttached), self, 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);
|
_ = 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 {
|
pub fn asWidget(self: *NotebookAdw) *c.GtkWidget {
|
||||||
|
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||||
return @ptrCast(@alignCast(self.tab_view));
|
return @ptrCast(@alignCast(self.tab_view));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nPages(self: *NotebookAdw) c_int {
|
pub fn nPages(self: *NotebookAdw) c_int {
|
||||||
if (comptime adwaita.versionAtLeast(0, 0, 0))
|
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||||
return c.adw_tab_view_get_n_pages(self.tab_view)
|
return c.adw_tab_view_get_n_pages(self.tab_view);
|
||||||
else
|
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the index of the currently selected page.
|
/// 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);
|
_ = 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;
|
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||||
const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box));
|
const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box));
|
||||||
c.adw_tab_page_set_title(page, title.ptr);
|
c.adw_tab_page_set_title(page, title.ptr);
|
||||||
@ -155,16 +180,23 @@ pub const NotebookAdw = struct {
|
|||||||
c.gtk_window_destroy(window);
|
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 {
|
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 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));
|
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(
|
fn adwClosePage(
|
||||||
@ -172,20 +204,20 @@ fn adwClosePage(
|
|||||||
page: *c.AdwTabPage,
|
page: *c.AdwTabPage,
|
||||||
ud: ?*anyopaque,
|
ud: ?*anyopaque,
|
||||||
) callconv(.C) c.gboolean {
|
) callconv(.C) c.gboolean {
|
||||||
|
const self: *NotebookAdw = @ptrCast(@alignCast(ud orelse return 0));
|
||||||
|
|
||||||
const child = c.adw_tab_page_get_child(page);
|
const child = c.adw_tab_page_get_child(page);
|
||||||
const tab: *Tab = @ptrCast(@alignCast(c.g_object_get_data(
|
const tab: *Tab = @ptrCast(@alignCast(c.g_object_get_data(
|
||||||
@ptrCast(child),
|
@ptrCast(child),
|
||||||
Tab.GHOSTTY_TAB,
|
Tab.GHOSTTY_TAB,
|
||||||
) orelse return 0));
|
) orelse return 0));
|
||||||
|
|
||||||
const window: *Window = @ptrCast(@alignCast(ud.?));
|
|
||||||
const notebook = window.notebook.adw;
|
|
||||||
c.adw_tab_view_close_page_finish(
|
c.adw_tab_view_close_page_finish(
|
||||||
notebook.tab_view,
|
self.tab_view,
|
||||||
page,
|
page,
|
||||||
@intFromBool(notebook.forcing_close),
|
@intFromBool(self.forcing_close),
|
||||||
);
|
);
|
||||||
if (!notebook.forcing_close) tab.closeWithConfirmation();
|
if (!self.forcing_close) tab.closeWithConfirmation();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,8 +225,8 @@ fn adwTabViewCreateWindow(
|
|||||||
_: *AdwTabView,
|
_: *AdwTabView,
|
||||||
ud: ?*anyopaque,
|
ud: ?*anyopaque,
|
||||||
) callconv(.C) ?*AdwTabView {
|
) callconv(.C) ?*AdwTabView {
|
||||||
const currentWindow: *Window = @ptrCast(@alignCast(ud.?));
|
const self: *NotebookAdw = @ptrCast(@alignCast(ud orelse return null));
|
||||||
const window = createWindow(currentWindow) catch |err| {
|
const window = createWindow(self.window) catch |err| {
|
||||||
log.warn("error creating new window error={}", .{err});
|
log.warn("error creating new window error={}", .{err});
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
@ -202,8 +234,22 @@ fn adwTabViewCreateWindow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn adwSelectPage(_: *c.GObject, _: *c.GParamSpec, ud: ?*anyopaque) void {
|
fn adwSelectPage(_: *c.GObject, _: *c.GParamSpec, ud: ?*anyopaque) void {
|
||||||
const window: *Window = @ptrCast(@alignCast(ud.?));
|
const self: *NotebookAdw = @ptrCast(@alignCast(ud orelse return));
|
||||||
const page = c.adw_tab_view_get_selected_page(window.notebook.adw.tab_view) 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);
|
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);
|
_ = 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
|
/// return the underlying widget as a generic GtkWidget
|
||||||
pub fn asWidget(self: *NotebookGtk) *c.GtkWidget {
|
pub fn asWidget(self: *NotebookGtk) *c.GtkWidget {
|
||||||
return @ptrCast(@alignCast(self.notebook));
|
return @ptrCast(@alignCast(self.notebook));
|
||||||
@ -101,7 +105,7 @@ pub const NotebookGtk = struct {
|
|||||||
c.gtk_notebook_reorder_child(self.notebook, @ptrCast(tab.box), position);
|
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);
|
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"),
|
.root_source_file = b.path("src/apprt/gtk/builder_check.zig"),
|
||||||
.target = b.host,
|
.target = b.host,
|
||||||
});
|
});
|
||||||
|
builder_check.root_module.addOptions("build_options", self.options);
|
||||||
builder_check.linkSystemLibrary2("gtk4", dynamic_link_opts);
|
builder_check.linkSystemLibrary2("gtk4", dynamic_link_opts);
|
||||||
|
if (self.config.adwaita) builder_check.linkSystemLibrary2("libadwaita-1", dynamic_link_opts);
|
||||||
builder_check.linkLibC();
|
builder_check.linkLibC();
|
||||||
|
|
||||||
for (gresource.dependencies) |pathname| {
|
for (gresource.dependencies) |pathname| {
|
||||||
|
Reference in New Issue
Block a user