From 2d7baaa7d703065454d79d0be9294e16cde6843c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 15 Jul 2024 21:23:52 -0700 Subject: [PATCH] apprt/gtk: toggle_window_decorations keybinding Fixes #1943 --- src/Surface.zig | 6 +++++ src/apprt/gtk/Surface.zig | 12 +++++++++ src/apprt/gtk/Window.zig | 56 ++++++++++++++++++++++----------------- src/config/Config.zig | 40 +++++++++++++++++++--------- src/input/Binding.zig | 3 +++ 5 files changed, 81 insertions(+), 36 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 7ee05d642..9c4431ddc 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -3504,6 +3504,12 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool } else log.warn("runtime doesn't implement toggleFullscreen", .{}); }, + .toggle_window_decorations => { + if (@hasDecl(apprt.Surface, "toggleWindowDecorations")) { + self.rt_surface.toggleWindowDecorations(); + } else log.warn("runtime doesn't implement toggleWindowDecorations", .{}); + }, + .select_all => { const sel = self.io.terminal.screen.selectAll(); if (sel) |s| { diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 05fce6905..ef8bdc81e 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -721,6 +721,18 @@ pub fn toggleFullscreen(self: *Surface, mac_non_native: configpkg.NonNativeFulls window.toggleFullscreen(mac_non_native); } +pub fn toggleWindowDecorations(self: *Surface) void { + const window = self.container.window() orelse { + log.info( + "toggleWindowDecorations invalid for container={s}", + .{@tagName(self.container)}, + ); + return; + }; + + window.toggleWindowDecorations(); +} + pub fn getTitleLabel(self: *Surface) ?*c.GtkWidget { switch (self.title) { .none => return null, diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 7a3418bb4..4dbe2a979 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -77,31 +77,30 @@ pub fn init(self: *Window, app: *App) !void { const display = c.gdk_display_get_default(); c.gtk_style_context_add_provider_for_display(display, @ptrCast(app.css_provider), c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - // Use the new GTK4 header bar. We only create a header bar if we have - // window decorations. - if (app.config.@"window-decoration") { - // gtk-titlebar can also be used to disable the header bar (but keep - // the window manager's decorations). - if (app.config.@"gtk-titlebar") { - const header = c.gtk_header_bar_new(); - c.gtk_window_set_titlebar(gtk_window, header); - { - const btn = c.gtk_menu_button_new(); - c.gtk_widget_set_tooltip_text(btn, "Main Menu"); - c.gtk_menu_button_set_icon_name(@ptrCast(btn), "open-menu-symbolic"); - c.gtk_menu_button_set_menu_model(@ptrCast(btn), @ptrCast(@alignCast(app.menu))); - c.gtk_header_bar_pack_end(@ptrCast(header), btn); - } - { - const btn = c.gtk_button_new_from_icon_name("tab-new-symbolic"); - c.gtk_widget_set_tooltip_text(btn, "New Tab"); - c.gtk_header_bar_pack_end(@ptrCast(header), btn); - _ = c.g_signal_connect_data(btn, "clicked", c.G_CALLBACK(>kTabNewClick), self, null, c.G_CONNECT_DEFAULT); - } + // gtk-titlebar can be used to disable the header bar (but keep + // the window manager's decorations). We create this no matter if we + // are decorated or not because we can have a keybind to toggle the + // decorations. + if (app.config.@"gtk-titlebar") { + const header = c.gtk_header_bar_new(); + c.gtk_window_set_titlebar(gtk_window, header); + { + const btn = c.gtk_menu_button_new(); + c.gtk_widget_set_tooltip_text(btn, "Main Menu"); + c.gtk_menu_button_set_icon_name(@ptrCast(btn), "open-menu-symbolic"); + c.gtk_menu_button_set_menu_model(@ptrCast(btn), @ptrCast(@alignCast(app.menu))); + c.gtk_header_bar_pack_end(@ptrCast(header), btn); } - } else { - // Hide window decoration if configured. This has to happen before - // `gtk_widget_show`. + { + const btn = c.gtk_button_new_from_icon_name("tab-new-symbolic"); + c.gtk_widget_set_tooltip_text(btn, "New Tab"); + c.gtk_header_bar_pack_end(@ptrCast(header), btn); + _ = c.g_signal_connect_data(btn, "clicked", c.G_CALLBACK(>kTabNewClick), self, null, c.G_CONNECT_DEFAULT); + } + } + + // If we are disabling decorations then disable them right away. + if (!app.config.@"window-decoration") { c.gtk_window_set_decorated(gtk_window, 0); } @@ -296,6 +295,15 @@ pub fn toggleFullscreen(self: *Window, _: configpkg.NonNativeFullscreen) void { } } +/// Toggle the window decorations for this window. +pub fn toggleWindowDecorations(self: *Window) void { + if (c.gtk_window_get_decorated(self.window) == 0) { + c.gtk_window_set_decorated(self.window, 1); + } else { + c.gtk_window_set_decorated(self.window, 0); + } +} + /// Grabs focus on the currently selected tab. fn focusCurrentTab(self: *Window) void { const page_idx = c.gtk_notebook_get_current_page(self.notebook); diff --git a/src/config/Config.zig b/src/config/Config.zig index e4de1d22e..6f780bb33 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -571,6 +571,11 @@ class: ?[:0]const u8 = null, /// /// * only a single key input is allowed, `ctrl+a+b` is invalid. /// +/// * the key input can be prefixed with `physical:` to specify a +/// physical key mapping rather than a logical one. A physical key +/// mapping responds to the hardware keycode and not the keycode +/// translated by any system keyboard layouts. Example: "ctrl+physical:a" +/// /// Valid modifiers are `shift`, `ctrl` (alias: `control`), `alt` (alias: `opt`, /// `option`), and `super` (alias: `cmd`, `command`). You may use the modifier /// or the alias. When debugging keybinds, the non-aliased modifier will always @@ -608,6 +613,12 @@ class: ?[:0]const u8 = null, /// * `keybind=clear` will clear all set keybindings. Warning: this /// removes ALL keybindings up to this point, including the default /// keybindings. +/// +/// A keybind by default causes the input to be consumed. This means that the +/// associated encoding (if any) will not be sent to the running program +/// in the terminal. If you wish to send the encoded value to the program, +/// specify the "unconsumed:" prefix before the entire keybind. For example: +/// "unconsumed:ctrl+a=reload_config" keybind: Keybinds = .{}, /// Window padding. This applies padding between the terminal cells and the @@ -669,7 +680,12 @@ keybind: Keybinds = .{}, /// * `true` /// * `false` - windows won't have native decorations, i.e. titlebar and /// borders. On MacOS this also disables tabs and tab overview. - +/// +/// The "toggle_window_decoration" keybind action can be used to create +/// a keybinding to toggle this setting at runtime. +/// +/// Changing this configuration in your configuration and reloading will +/// only affect new windows. Existing windows will not be affected. @"window-decoration": bool = true, /// The font that will be used for the application's window and tab titles. @@ -781,17 +797,6 @@ keybind: Keybinds = .{}, // Default is false. @"focus-follows-mouse": bool = false, -/// When enabled, the full GTK titlebar is displayed instead of your window -/// manager's simple titlebar. The behavior of this option will vary with your -/// window manager. -/// -/// This option does nothing when `window-decoration` is false or when running -/// under macOS. -/// -/// Changing this value at runtime and reloading the configuration will only -/// affect new windows. -@"gtk-titlebar": bool = true, - /// Whether to allow programs running in the terminal to read/write to the /// system clipboard (OSC 52, for googling). The default is to allow clipboard /// reading after prompting the user and allow writing unconditionally. @@ -1102,6 +1107,17 @@ keybind: Keybinds = .{}, /// so you can test single instance without conflicting with release builds. @"gtk-single-instance": GtkSingleInstance = .desktop, +/// When enabled, the full GTK titlebar is displayed instead of your window +/// manager's simple titlebar. The behavior of this option will vary with your +/// window manager. +/// +/// This option does nothing when `window-decoration` is false or when running +/// under macOS. +/// +/// Changing this value at runtime and reloading the configuration will only +/// affect new windows. +@"gtk-titlebar": bool = true, + /// Determines the side of the screen that the GTK tab bar will stick to. /// Top, bottom, left, and right are supported. The default is top. @"gtk-tabs-location": GtkTabsLocation = .top, diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 5640843d9..27c620f15 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -266,6 +266,9 @@ pub const Action = union(enum) { /// Toggle fullscreen mode of window. toggle_fullscreen: void, + /// Toggle window decorations on and off. This only works on Linux. + toggle_window_decorations: void, + /// Quit ghostty. quit: void,