Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 92 additions & 19 deletions src/apprt/gtk-ng/class/window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const gtk = @import("gtk");
const i18n = @import("../../../os/main.zig").i18n;
const apprt = @import("../../../apprt.zig");
const configpkg = @import("../../../config.zig");
const TitlebarStyle = configpkg.Config.GtkTitlebarStyle;
const input = @import("../../../input.zig");
const CoreSurface = @import("../../../Surface.zig");
const ext = @import("../ext.zig");
Expand Down Expand Up @@ -96,6 +97,25 @@ pub const Window = extern struct {
);
};

pub const @"titlebar-style" = struct {
pub const name = "titlebar-style";
const impl = gobject.ext.defineProperty(
name,
Self,
TitlebarStyle,
.{
.default = .native,
.accessor = gobject.ext.typedAccessor(
Self,
TitlebarStyle,
.{
.getter = Self.getTitlebarStyle,
},
),
},
);
};

pub const @"headerbar-visible" = struct {
pub const name = "headerbar-visible";
const impl = gobject.ext.defineProperty(
Expand Down Expand Up @@ -548,6 +568,7 @@ pub const Window = extern struct {
"tabs-visible",
"tabs-wide",
"toolbar-style",
"titlebar-style",
}) |key| {
self.as(gobject.Object).notifyByPspec(
@field(properties, key).impl.param_spec,
Expand Down Expand Up @@ -814,6 +835,14 @@ pub const Window = extern struct {
return false;
}

fn isFullscreen(self: *Window) bool {
return self.as(gtk.Window).isFullscreen() != 0;
}

fn isMaximized(self: *Window) bool {
return self.as(gtk.Window).isMaximized() != 0;
}

fn getHeaderbarVisible(self: *Self) bool {
const priv = self.private();

Expand All @@ -825,46 +854,72 @@ pub const Window = extern struct {
if (priv.quick_terminal) return false;

// If we're fullscreen we never show the header bar.
if (self.as(gtk.Window).isFullscreen() != 0) return false;
if (self.isFullscreen()) return false;

// The remainder needs a config
const config_obj = self.private().config orelse return true;
const config = config_obj.get();

// *Conditionally* disable the header bar when maximized,
// and gtk-titlebar-hide-when-maximized is set
if (self.as(gtk.Window).isMaximized() != 0 and
config.@"gtk-titlebar-hide-when-maximized")
{
// *Conditionally* disable the header bar when maximized, and
// gtk-titlebar-hide-when-maximized is set
if (self.isMaximized() and config.@"gtk-titlebar-hide-when-maximized") {
return false;
}

return config.@"gtk-titlebar";
return switch (config.@"gtk-titlebar-style") {
// If the titlebar style is tabs never show the titlebar.
.tabs => false,

// If the titlebar style is native show the titlebar if configured
// to do so.
.native => config.@"gtk-titlebar",
};
}

fn getTabsAutohide(self: *Self) bool {
const priv = self.private();
const config = if (priv.config) |v| v.get() else return true;
return switch (config.@"window-show-tab-bar") {
// Auto we always autohide... obviously.
.auto => true,

// Always we never autohide because we always show the tab bar.
.always => false,
return switch (config.@"gtk-titlebar-style") {
// If the titlebar style is tabs we cannot autohide.
.tabs => false,

.native => switch (config.@"window-show-tab-bar") {
// Auto we always autohide... obviously.
.auto => true,

// Never we autohide because it doesn't actually matter,
// since getTabsVisible will return false.
.never => true,
// Always we never autohide because we always show the tab bar.
.always => false,

// Never we autohide because it doesn't actually matter,
// since getTabsVisible will return false.
.never => true,
},
};
}

fn getTabsVisible(self: *Self) bool {
const priv = self.private();
const config = if (priv.config) |v| v.get() else return true;
return switch (config.@"window-show-tab-bar") {
.always, .auto => true,
.never => false,
};

switch (config.@"gtk-titlebar-style") {
.tabs => {
// *Conditionally* disable the tab bar when maximized, the titlebar
// style is tabs, and gtk-titlebar-hide-when-maximized is set.
if (self.isMaximized() and config.@"gtk-titlebar-hide-when-maximized") {
return false;
}

// If the titlebar style is tabs the tab bar must always be visible.
return true;
},
.native => {
return switch (config.@"window-show-tab-bar") {
.always, .auto => true,
.never => false,
};
},
}
}

fn getTabsWide(self: *Self) bool {
Expand All @@ -883,6 +938,12 @@ pub const Window = extern struct {
};
}

fn getTitlebarStyle(self: *Self) TitlebarStyle {
const priv = self.private();
const config = if (priv.config) |v| v.get() else return .native;
return config.@"gtk-titlebar-style";
}

fn propConfig(
_: *adw.ApplicationWindow,
_: *gobject.ParamSpec,
Expand Down Expand Up @@ -992,6 +1053,16 @@ pub const Window = extern struct {
};
}

fn closureTitlebarStyleIsTab(
_: *Self,
value: TitlebarStyle,
) callconv(.c) bool {
return switch (value) {
.native => false,
.tabs => true,
};
}

//---------------------------------------------------------------
// Virtual methods

Expand Down Expand Up @@ -1703,6 +1774,7 @@ pub const Window = extern struct {
properties.@"tabs-visible".impl,
properties.@"tabs-wide".impl,
properties.@"toolbar-style".impl,
properties.@"titlebar-style".impl,
});

// Bindings
Expand Down Expand Up @@ -1730,6 +1802,7 @@ pub const Window = extern struct {
class.bindTemplateCallback("notify_menu_active", &propMenuActive);
class.bindTemplateCallback("notify_quick_terminal", &propQuickTerminal);
class.bindTemplateCallback("notify_scale_factor", &propScaleFactor);
class.bindTemplateCallback("titlebar_style_is_tabs", &closureTitlebarStyleIsTab);

// Virtual methods
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
Expand Down
58 changes: 58 additions & 0 deletions src/apprt/gtk-ng/ui/1.5/window.blp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,64 @@ template $GhosttyWindow: Adw.ApplicationWindow {
expand-tabs: bind template.tabs-wide;
view: tab_view;
visible: bind template.tabs-visible;

[start]
Gtk.Box {
orientation: horizontal;
visible: bind $titlebar_style_is_tabs(template.titlebar-style) as <bool>;

Gtk.WindowControls {
side: start;
}

Adw.SplitButton {
styles [
"flat",
]

clicked => $new_tab();
icon-name: "tab-new-symbolic";
tooltip-text: _("New Tab");
dropdown-tooltip: _("New Split");
menu-model: split_menu;
can-focus: false;
focus-on-click: false;
}
}

[end]
Gtk.Box {
orientation: horizontal;
visible: bind $titlebar_style_is_tabs(template.titlebar-style) as <bool>;

Gtk.ToggleButton {
styles [
"flat",
]

icon-name: "view-grid-symbolic";
tooltip-text: _("View Open Tabs");
active: bind tab_overview.open bidirectional;
can-focus: false;
focus-on-click: false;
}

Gtk.MenuButton {
styles [
"flat",
]

notify::active => $notify_menu_active();
icon-name: "open-menu-symbolic";
menu-model: main_menu;
tooltip-text: _("Main Menu");
can-focus: false;
}

Gtk.WindowControls {
side: end;
}
}
}

Box {
Expand Down
30 changes: 30 additions & 0 deletions src/config/Config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2892,6 +2892,21 @@ else
/// more subtle border.
@"gtk-toolbar-style": GtkToolbarStyle = .raised,

/// The style of the GTK titlbar. Available values are `native` and `tabs`.
///
/// The `native` titlebar style is a traditional titlebar with a title, a few
/// buttons and window controls. A separate tab bar will show up below the
/// titlebar if you have multiple tabs open in the window.
///
/// The `tabs` titlebar merges the tab bar and the traditional titlebar.
/// This frees up vertical space on your screen if you use multiple tabs. One
/// limitation of the `tabs` titlebar is that you cannot drag the titlebar
/// by the titles any longer (as they are tab titles now). Other areas of the
/// `tabs` title bar can be used to drag the window around.
///
/// The default style is `native`.
@"gtk-titlebar-style": GtkTitlebarStyle = .native,

/// If `true` (default), then the Ghostty GTK tabs will be "wide." Wide tabs
/// are the new typical Gnome style where tabs fill their available space.
/// If you set this to `false` then tabs will only take up space they need,
Expand Down Expand Up @@ -6947,6 +6962,21 @@ pub const GtkToolbarStyle = enum {
@"raised-border",
};

/// See gtk-titlebar-style
pub const GtkTitlebarStyle = enum(c_int) {
native,
tabs,

pub const getGObjectType = switch (build_config.app_runtime) {
.gtk, .@"gtk-ng" => @import("gobject").ext.defineEnum(
GtkTitlebarStyle,
.{ .name = "GhosttyGtkTitlebarStyle" },
),

.none => void,
};
};

/// See app-notifications
pub const AppNotifications = packed struct {
@"clipboard-copy": bool = true,
Expand Down