mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-22 19:56:08 +03:00
Merge pull request #1937 from rockorager/gtk-split-opacity
gtk: implement unfocused-split opacity and fill
This commit is contained in:
@ -70,6 +70,9 @@ 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,
|
||||||
|
|
||||||
|
/// CSS Provider for any styles based on ghostty configuration values
|
||||||
|
css_provider: *c.GtkCssProvider,
|
||||||
|
|
||||||
pub fn init(core_app: *CoreApp, opts: Options) !App {
|
pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||||
_ = opts;
|
_ = opts;
|
||||||
|
|
||||||
@ -268,6 +271,9 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const css_provider = c.gtk_css_provider_new();
|
||||||
|
try loadRuntimeCss(&config, css_provider);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.core_app = core_app,
|
.core_app = core_app,
|
||||||
.app = app,
|
.app = app,
|
||||||
@ -280,6 +286,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
|||||||
// This means that another instance of the GTK app is running and
|
// This means that another instance of the GTK app is running and
|
||||||
// our "activate" call above will open a window.
|
// our "activate" call above will open a window.
|
||||||
.running = c.g_application_get_is_remote(gapp) == 0,
|
.running = c.g_application_get_is_remote(gapp) == 0,
|
||||||
|
.css_provider = css_provider,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,6 +334,7 @@ pub fn reloadConfig(self: *App) !?*const Config {
|
|||||||
fn syncConfigChanges(self: *App) !void {
|
fn syncConfigChanges(self: *App) !void {
|
||||||
try self.updateConfigErrors();
|
try self.updateConfigErrors();
|
||||||
try self.syncActionAccelerators();
|
try self.syncActionAccelerators();
|
||||||
|
try loadRuntimeCss(&self.config, self.css_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This should be called whenever the configuration changes to update
|
/// This should be called whenever the configuration changes to update
|
||||||
@ -379,6 +387,33 @@ fn syncActionAccelerator(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn loadRuntimeCss(config: *const Config, provider: *c.GtkCssProvider) !void {
|
||||||
|
const fill: Config.Color = config.@"unfocused-split-fill" orelse config.background;
|
||||||
|
const fmt =
|
||||||
|
\\widget.unfocused-split {{
|
||||||
|
\\ opacity: {d:.2};
|
||||||
|
\\ background-color: rgb({d},{d},{d});
|
||||||
|
\\}}
|
||||||
|
;
|
||||||
|
// The length required is always less than the length of the pre-formatted string:
|
||||||
|
// -> '{d:.2}' gets replaced with max 4 bytes (0.00)
|
||||||
|
// -> each {d} could be replaced with max 3 bytes
|
||||||
|
var css_buf: [fmt.len]u8 = undefined;
|
||||||
|
|
||||||
|
const css = try std.fmt.bufPrintZ(
|
||||||
|
&css_buf,
|
||||||
|
fmt,
|
||||||
|
.{
|
||||||
|
config.@"unfocused-split-opacity",
|
||||||
|
fill.r,
|
||||||
|
fill.g,
|
||||||
|
fill.b,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// Clears any previously loaded CSS from this provider
|
||||||
|
c.gtk_css_provider_load_from_data(provider, css, @intCast(css.len));
|
||||||
|
}
|
||||||
|
|
||||||
/// Called by CoreApp to wake up the event loop.
|
/// Called by CoreApp to wake up the event loop.
|
||||||
pub fn wakeup(self: App) void {
|
pub fn wakeup(self: App) void {
|
||||||
_ = self;
|
_ = self;
|
||||||
|
@ -77,6 +77,7 @@ pub fn init(
|
|||||||
.parent = &sibling.core_surface,
|
.parent = &sibling.core_surface,
|
||||||
});
|
});
|
||||||
errdefer surface.destroy(alloc);
|
errdefer surface.destroy(alloc);
|
||||||
|
sibling.dimSurface();
|
||||||
|
|
||||||
// Create the actual GTKPaned, attach the proper children.
|
// Create the actual GTKPaned, attach the proper children.
|
||||||
const orientation: c_uint = switch (direction) {
|
const orientation: c_uint = switch (direction) {
|
||||||
|
@ -325,6 +325,9 @@ gl_area: *c.GtkGLArea,
|
|||||||
/// If non-null this is the widget on the overlay that shows the URL.
|
/// If non-null this is the widget on the overlay that shows the URL.
|
||||||
url_widget: ?URLWidget = null,
|
url_widget: ?URLWidget = null,
|
||||||
|
|
||||||
|
/// If non-null this is the widget on the overlay which dims the surface when it is unfocused
|
||||||
|
unfocused_widget: ?*c.GtkWidget = null,
|
||||||
|
|
||||||
/// Any active cursor we may have
|
/// Any active cursor we may have
|
||||||
cursor: ?*c.GdkCursor = null,
|
cursor: ?*c.GdkCursor = null,
|
||||||
|
|
||||||
@ -590,6 +593,10 @@ pub fn deinit(self: *Surface) void {
|
|||||||
// Free all our GTK stuff
|
// Free all our GTK stuff
|
||||||
c.g_object_unref(self.im_context);
|
c.g_object_unref(self.im_context);
|
||||||
if (self.cursor) |cursor| c.g_object_unref(cursor);
|
if (self.cursor) |cursor| c.g_object_unref(cursor);
|
||||||
|
if (self.unfocused_widget) |widget| {
|
||||||
|
c.gtk_overlay_remove_overlay(self.overlay, widget);
|
||||||
|
self.unfocused_widget = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unref removes the long-held reference to the gl_area and kicks off the
|
// unref removes the long-held reference to the gl_area and kicks off the
|
||||||
@ -1809,6 +1816,12 @@ 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);
|
||||||
|
|
||||||
|
// Remove the unfocused widget overlay, if we have one
|
||||||
|
if (self.unfocused_widget) |widget| {
|
||||||
|
c.gtk_overlay_remove_overlay(self.overlay, widget);
|
||||||
|
self.unfocused_widget = null;
|
||||||
|
}
|
||||||
|
|
||||||
// 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,12 +1836,29 @@ 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 dim the surface if we are a split
|
||||||
|
switch (self.container) {
|
||||||
|
.split_br,
|
||||||
|
.split_tl,
|
||||||
|
=> self.dimSurface(),
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds the unfocused_widget to the overlay. If the unfocused_widget has already been added, this
|
||||||
|
/// is a no-op
|
||||||
|
pub fn dimSurface(self: *Surface) void {
|
||||||
|
if (self.unfocused_widget != null) return;
|
||||||
|
self.unfocused_widget = c.gtk_drawing_area_new();
|
||||||
|
c.gtk_widget_add_css_class(self.unfocused_widget.?, "unfocused-split");
|
||||||
|
c.gtk_overlay_add_overlay(self.overlay, self.unfocused_widget.?);
|
||||||
|
}
|
||||||
|
|
||||||
fn gtkCloseConfirmation(
|
fn gtkCloseConfirmation(
|
||||||
alert: *c.GtkMessageDialog,
|
alert: *c.GtkMessageDialog,
|
||||||
response: c.gint,
|
response: c.gint,
|
||||||
|
@ -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,11 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internally, GTK ensures that only one instance of this provider exists in the provider list
|
||||||
|
// for the display.
|
||||||
|
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
|
// 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