mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 00:36:07 +03:00
gtk: implement unfocused-split opacity and fill
For a long time, us GTK users have been subject to lesser UX by not knowing which split was focused. Improve the GTK UX by implementing both unfocused-split-opacity and unfocused-split-fill. This is implemented by setting the background-color of the notebook stack, and conditionally applying a new css class "unfocused-split" to the unfocused split.
This commit is contained in:
@ -70,6 +70,10 @@ x11_xkb: ?x11.Xkb = null,
|
|||||||
/// and initialization was successful.
|
/// and initialization was successful.
|
||||||
transient_cgroup_base: ?[]const u8 = null,
|
transient_cgroup_base: ?[]const u8 = null,
|
||||||
|
|
||||||
|
/// True if we have initialized runtime CSS. We only need to do this once for the application, but
|
||||||
|
/// we can't perform the intialization until we have created a window
|
||||||
|
runtime_css_intialized: bool = false,
|
||||||
|
|
||||||
pub fn init(core_app: *CoreApp, opts: Options) !App {
|
pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||||
_ = opts;
|
_ = opts;
|
||||||
|
|
||||||
|
@ -1809,6 +1809,9 @@ fn gtkFocusEnter(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) vo
|
|||||||
// Notify our IM context
|
// Notify our IM context
|
||||||
c.gtk_im_context_focus_in(self.im_context);
|
c.gtk_im_context_focus_in(self.im_context);
|
||||||
|
|
||||||
|
// Unconditionally remove the unfocused split css class
|
||||||
|
c.gtk_widget_remove_css_class(@ptrCast(@alignCast(self.gl_area)), "unfocused-split");
|
||||||
|
|
||||||
// Notify our surface
|
// Notify our surface
|
||||||
self.core_surface.focusCallback(true) catch |err| {
|
self.core_surface.focusCallback(true) catch |err| {
|
||||||
log.err("error in focus callback err={}", .{err});
|
log.err("error in focus callback err={}", .{err});
|
||||||
@ -1823,6 +1826,14 @@ fn gtkFocusLeave(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) vo
|
|||||||
// Notify our IM context
|
// Notify our IM context
|
||||||
c.gtk_im_context_focus_out(self.im_context);
|
c.gtk_im_context_focus_out(self.im_context);
|
||||||
|
|
||||||
|
// We only add the unfocused-split class if we are actually a split
|
||||||
|
switch (self.container) {
|
||||||
|
.split_br,
|
||||||
|
.split_tl,
|
||||||
|
=> c.gtk_widget_add_css_class(@ptrCast(@alignCast(self.gl_area)), "unfocused-split"),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
self.core_surface.focusCallback(false) catch |err| {
|
self.core_surface.focusCallback(false) catch |err| {
|
||||||
log.err("error in focus callback err={}", .{err});
|
log.err("error in focus callback err={}", .{err});
|
||||||
return;
|
return;
|
||||||
|
@ -16,6 +16,7 @@ const input = @import("../../input.zig");
|
|||||||
const CoreSurface = @import("../../Surface.zig");
|
const CoreSurface = @import("../../Surface.zig");
|
||||||
|
|
||||||
const App = @import("App.zig");
|
const App = @import("App.zig");
|
||||||
|
const Color = configpkg.Config.Color;
|
||||||
const Surface = @import("Surface.zig");
|
const Surface = @import("Surface.zig");
|
||||||
const Tab = @import("Tab.zig");
|
const Tab = @import("Tab.zig");
|
||||||
const c = @import("c.zig");
|
const c = @import("c.zig");
|
||||||
@ -71,6 +72,35 @@ pub fn init(self: *Window, app: *App) !void {
|
|||||||
c.gtk_widget_set_opacity(@ptrCast(window), app.config.@"background-opacity");
|
c.gtk_widget_set_opacity(@ptrCast(window), app.config.@"background-opacity");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!app.runtime_css_intialized) {
|
||||||
|
// Intialize runtime CSS. This CSS requires ghostty configuration values so we don't set it
|
||||||
|
// in style.css. We also have to have a window in order to add a style_context to a display,
|
||||||
|
// so we intialize after creation of our first window
|
||||||
|
app.runtime_css_intialized = true;
|
||||||
|
|
||||||
|
const display = c.gdk_display_get_default();
|
||||||
|
const provider = c.gtk_css_provider_new();
|
||||||
|
defer c.g_object_unref(provider);
|
||||||
|
const fill: Color = app.config.@"unfocused-split-fill" orelse app.config.background;
|
||||||
|
var css_buf: [128]u8 = undefined;
|
||||||
|
|
||||||
|
// We will add the unfocused-split class in our focus callbacks. We unconditionally add the
|
||||||
|
// background-color to the notebook stack because it only comes into play if we have an
|
||||||
|
// unfocused split
|
||||||
|
const css = try std.fmt.bufPrintZ(
|
||||||
|
&css_buf,
|
||||||
|
"widget.unfocused-split {{ opacity: {d:.2}; }}\nstack {{ background-color: rgb({d},{d},{d});}}",
|
||||||
|
.{
|
||||||
|
app.config.@"unfocused-split-opacity",
|
||||||
|
fill.r,
|
||||||
|
fill.g,
|
||||||
|
fill.b,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
c.gtk_css_provider_load_from_string(provider, css);
|
||||||
|
c.gtk_style_context_add_provider_for_display(display, @ptrCast(provider), c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||||
|
}
|
||||||
|
|
||||||
// Use the new GTK4 header bar. We only create a header bar if we have
|
// Use the new GTK4 header bar. We only create a header bar if we have
|
||||||
// window decorations.
|
// window decorations.
|
||||||
if (app.config.@"window-decoration") {
|
if (app.config.@"window-decoration") {
|
||||||
|
@ -409,8 +409,6 @@ palette: Palette = .{},
|
|||||||
/// is 0.15. This value still looks weird but you can at least see what's going
|
/// is 0.15. This value still looks weird but you can at least see what's going
|
||||||
/// on. A value outside of the range 0.15 to 1 will be clamped to the nearest
|
/// on. A value outside of the range 0.15 to 1 will be clamped to the nearest
|
||||||
/// valid value.
|
/// valid value.
|
||||||
///
|
|
||||||
/// This is only supported on macOS.
|
|
||||||
@"unfocused-split-opacity": f64 = 0.7,
|
@"unfocused-split-opacity": f64 = 0.7,
|
||||||
|
|
||||||
/// The color to dim the unfocused split. Unfocused splits are dimmed by
|
/// The color to dim the unfocused split. Unfocused splits are dimmed by
|
||||||
@ -418,8 +416,6 @@ palette: Palette = .{},
|
|||||||
/// that rectangle and can be used to carefully control the dimming effect.
|
/// that rectangle and can be used to carefully control the dimming effect.
|
||||||
///
|
///
|
||||||
/// This will default to the background color.
|
/// This will default to the background color.
|
||||||
///
|
|
||||||
/// This is only supported on macOS.
|
|
||||||
@"unfocused-split-fill": ?Color = null,
|
@"unfocused-split-fill": ?Color = null,
|
||||||
|
|
||||||
/// The command to run, usually a shell. If this is not an absolute path, it'll
|
/// The command to run, usually a shell. If this is not an absolute path, it'll
|
||||||
|
Reference in New Issue
Block a user