mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-22 01:18:36 +03:00
gtk: update ResizeOverlay for zig-gobject (#5941)
Also switch to a "DerivedConfig" model so that we aren't referring to a global copy of the config.
This commit is contained in:
@ -1,24 +1,45 @@
|
|||||||
const ResizeOverlay = @This();
|
const ResizeOverlay = @This();
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const c = @import("c.zig").c;
|
|
||||||
|
const glib = @import("glib");
|
||||||
|
const gtk = @import("gtk");
|
||||||
|
|
||||||
const configpkg = @import("../../config.zig");
|
const configpkg = @import("../../config.zig");
|
||||||
const Surface = @import("Surface.zig");
|
const Surface = @import("Surface.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.gtk);
|
const log = std.log.scoped(.gtk);
|
||||||
|
|
||||||
/// Back reference to the surface we belong to
|
/// local copy of configuration data
|
||||||
surface: ?*Surface = null,
|
const DerivedConfig = struct {
|
||||||
|
resize_overlay: configpkg.Config.ResizeOverlay,
|
||||||
|
resize_overlay_position: configpkg.Config.ResizeOverlayPosition,
|
||||||
|
resize_overlay_duration: configpkg.Config.Duration,
|
||||||
|
|
||||||
|
pub fn init(config: *const configpkg.Config) DerivedConfig {
|
||||||
|
return .{
|
||||||
|
.resize_overlay = config.@"resize-overlay",
|
||||||
|
.resize_overlay_position = config.@"resize-overlay-position",
|
||||||
|
.resize_overlay_duration = config.@"resize-overlay-duration",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// the surface that we are attached to
|
||||||
|
surface: *Surface,
|
||||||
|
|
||||||
|
/// a copy of the configuration that we need to operate
|
||||||
|
config: DerivedConfig,
|
||||||
|
|
||||||
/// If non-null this is the widget on the overlay that shows the size of the
|
/// If non-null this is the widget on the overlay that shows the size of the
|
||||||
/// surface when it is resized.
|
/// surface when it is resized.
|
||||||
widget: ?*c.GtkWidget = null,
|
label: ?*gtk.Label = null,
|
||||||
|
|
||||||
/// If non-null this is a timer for dismissing the resize overlay.
|
/// If non-null this is a timer for dismissing the resize overlay.
|
||||||
timer: ?c.guint = null,
|
timer: ?c_uint = null,
|
||||||
|
|
||||||
/// If non-null this is a timer for dismissing the resize overlay.
|
/// If non-null this is a timer for dismissing the resize overlay.
|
||||||
idler: ?c.guint = null,
|
idler: ?c_uint = null,
|
||||||
|
|
||||||
/// If true, the next resize event will be the first one.
|
/// If true, the next resize event will be the first one.
|
||||||
first: bool = true,
|
first: bool = true,
|
||||||
@ -26,24 +47,29 @@ first: bool = true,
|
|||||||
/// Initialize the ResizeOverlay. This doesn't do anything more than save a
|
/// Initialize the ResizeOverlay. This doesn't do anything more than save a
|
||||||
/// pointer to the surface that we are a part of as all of the widget creation
|
/// pointer to the surface that we are a part of as all of the widget creation
|
||||||
/// is done later.
|
/// is done later.
|
||||||
pub fn init(surface: *Surface) ResizeOverlay {
|
pub fn init(self: *ResizeOverlay, surface: *Surface, config: *const configpkg.Config) void {
|
||||||
return .{
|
self.* = .{
|
||||||
.surface = surface,
|
.surface = surface,
|
||||||
|
.config = DerivedConfig.init(config),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn updateConfig(self: *ResizeOverlay, config: *const configpkg.Config) void {
|
||||||
|
self.config = DerivedConfig.init(config);
|
||||||
|
}
|
||||||
|
|
||||||
/// De-initialize the ResizeOverlay. This removes any pending idlers/timers that
|
/// De-initialize the ResizeOverlay. This removes any pending idlers/timers that
|
||||||
/// may not have fired yet.
|
/// may not have fired yet.
|
||||||
pub fn deinit(self: *ResizeOverlay) void {
|
pub fn deinit(self: *ResizeOverlay) void {
|
||||||
if (self.idler) |idler| {
|
if (self.idler) |idler| {
|
||||||
if (c.g_source_remove(idler) == c.FALSE) {
|
if (glib.Source.remove(idler) == 0) {
|
||||||
log.warn("unable to remove resize overlay idler", .{});
|
log.warn("unable to remove resize overlay idler", .{});
|
||||||
}
|
}
|
||||||
self.idler = null;
|
self.idler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.timer) |timer| {
|
if (self.timer) |timer| {
|
||||||
if (c.g_source_remove(timer) == c.FALSE) {
|
if (glib.Source.remove(timer) == 0) {
|
||||||
log.warn("unable to remove resize overlay timer", .{});
|
log.warn("unable to remove resize overlay timer", .{});
|
||||||
}
|
}
|
||||||
self.timer = null;
|
self.timer = null;
|
||||||
@ -56,12 +82,7 @@ pub fn deinit(self: *ResizeOverlay) void {
|
|||||||
///
|
///
|
||||||
/// If we're not configured to show the overlay, do nothing.
|
/// If we're not configured to show the overlay, do nothing.
|
||||||
pub fn maybeShow(self: *ResizeOverlay) void {
|
pub fn maybeShow(self: *ResizeOverlay) void {
|
||||||
const surface = self.surface orelse {
|
switch (self.config.resize_overlay) {
|
||||||
log.err("resize overlay configured without a surface", .{});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (surface.app.config.@"resize-overlay") {
|
|
||||||
.never => return,
|
.never => return,
|
||||||
.always => {},
|
.always => {},
|
||||||
.@"after-first" => if (self.first) {
|
.@"after-first" => if (self.first) {
|
||||||
@ -78,23 +99,18 @@ pub fn maybeShow(self: *ResizeOverlay) void {
|
|||||||
// results in a lot of warnings from GTK and _horrible_ flickering of the
|
// results in a lot of warnings from GTK and _horrible_ flickering of the
|
||||||
// resize overlay.
|
// resize overlay.
|
||||||
if (self.idler != null) return;
|
if (self.idler != null) return;
|
||||||
self.idler = c.g_idle_add(gtkUpdate, @ptrCast(self));
|
self.idler = glib.idleAdd(gtkUpdate, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Actually update the overlay widget. This should only be called from a GTK
|
/// Actually update the overlay widget. This should only be called from a GTK
|
||||||
/// idle handler.
|
/// idle handler.
|
||||||
fn gtkUpdate(ud: ?*anyopaque) callconv(.C) c.gboolean {
|
fn gtkUpdate(ud: ?*anyopaque) callconv(.C) c_int {
|
||||||
const self: *ResizeOverlay = @ptrCast(@alignCast(ud));
|
const self: *ResizeOverlay = @ptrCast(@alignCast(ud orelse return 0));
|
||||||
|
|
||||||
// No matter what our idler is complete with this callback
|
// No matter what our idler is complete with this callback
|
||||||
self.idler = null;
|
self.idler = null;
|
||||||
|
|
||||||
const surface = self.surface orelse {
|
const grid_size = self.surface.core_surface.size.grid();
|
||||||
log.err("resize overlay configured without a surface", .{});
|
|
||||||
return c.FALSE;
|
|
||||||
};
|
|
||||||
|
|
||||||
const grid_size = surface.core_surface.size.grid();
|
|
||||||
var buf: [32]u8 = undefined;
|
var buf: [32]u8 = undefined;
|
||||||
const text = std.fmt.bufPrintZ(
|
const text = std.fmt.bufPrintZ(
|
||||||
&buf,
|
&buf,
|
||||||
@ -105,88 +121,86 @@ fn gtkUpdate(ud: ?*anyopaque) callconv(.C) c.gboolean {
|
|||||||
},
|
},
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
log.err("unable to format text: {}", .{err});
|
log.err("unable to format text: {}", .{err});
|
||||||
return c.FALSE;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (self.widget) |widget| {
|
if (self.label) |label| {
|
||||||
// The resize overlay widget already exists, just update it.
|
// The resize overlay widget already exists, just update it.
|
||||||
c.gtk_label_set_text(@ptrCast(widget), text.ptr);
|
label.setText(text.ptr);
|
||||||
setPosition(widget, &surface.app.config);
|
setPosition(label, &self.config);
|
||||||
show(widget);
|
show(label);
|
||||||
} else {
|
} else {
|
||||||
// Create the resize overlay widget.
|
// Create the resize overlay widget.
|
||||||
const widget = c.gtk_label_new(text.ptr);
|
const label = gtk.Label.new(text.ptr);
|
||||||
|
label.setJustify(gtk.Justification.center);
|
||||||
|
label.setSelectable(0);
|
||||||
|
setPosition(label, &self.config);
|
||||||
|
|
||||||
c.gtk_widget_add_css_class(widget, "view");
|
const widget = label.as(gtk.Widget);
|
||||||
c.gtk_widget_add_css_class(widget, "size-overlay");
|
widget.addCssClass("view");
|
||||||
c.gtk_widget_set_focusable(widget, c.FALSE);
|
widget.addCssClass("size-overlay");
|
||||||
c.gtk_widget_set_can_target(widget, c.FALSE);
|
widget.setFocusable(0);
|
||||||
c.gtk_label_set_justify(@ptrCast(widget), c.GTK_JUSTIFY_CENTER);
|
widget.setCanTarget(0);
|
||||||
c.gtk_label_set_selectable(@ptrCast(widget), c.FALSE);
|
|
||||||
setPosition(widget, &surface.app.config);
|
|
||||||
|
|
||||||
c.gtk_overlay_add_overlay(surface.overlay, widget);
|
const overlay: *gtk.Overlay = @ptrCast(@alignCast(self.surface.overlay));
|
||||||
|
overlay.addOverlay(widget);
|
||||||
|
|
||||||
self.widget = widget;
|
self.label = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.timer) |timer| {
|
if (self.timer) |timer| {
|
||||||
if (c.g_source_remove(timer) == c.FALSE) {
|
if (glib.Source.remove(timer) == 0) {
|
||||||
log.warn("unable to remove size overlay timer", .{});
|
log.warn("unable to remove size overlay timer", .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.timer = c.g_timeout_add(
|
|
||||||
surface.app.config.@"resize-overlay-duration".asMilliseconds(),
|
self.timer = glib.timeoutAdd(
|
||||||
|
self.surface.app.config.@"resize-overlay-duration".asMilliseconds(),
|
||||||
gtkTimerExpired,
|
gtkTimerExpired,
|
||||||
@ptrCast(self),
|
self,
|
||||||
);
|
);
|
||||||
|
|
||||||
return c.FALSE;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should only be called from a GTK idle handler or timer.
|
// This should only be called from a GTK idle handler or timer.
|
||||||
fn show(widget: *c.GtkWidget) void {
|
fn show(label: *gtk.Label) void {
|
||||||
// The CSS class is used only by libadwaita.
|
const widget = label.as(gtk.Widget);
|
||||||
c.gtk_widget_remove_css_class(@ptrCast(widget), "hidden");
|
widget.removeCssClass("hidden");
|
||||||
// Set the visibility for non-libadwaita usage.
|
|
||||||
c.gtk_widget_set_visible(@ptrCast(widget), 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should only be called from a GTK idle handler or timer.
|
// This should only be called from a GTK idle handler or timer.
|
||||||
fn hide(widget: *c.GtkWidget) void {
|
fn hide(label: *gtk.Label) void {
|
||||||
// The CSS class is used only by libadwaita.
|
const widget = label.as(gtk.Widget);
|
||||||
c.gtk_widget_add_css_class(widget, "hidden");
|
widget.addCssClass("hidden");
|
||||||
// Set the visibility for non-libadwaita usage.
|
|
||||||
c.gtk_widget_set_visible(widget, c.FALSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the position of the resize overlay widget. It might seem excessive to
|
/// Update the position of the resize overlay widget. It might seem excessive to
|
||||||
/// do this often, but it should make hot config reloading of the position work.
|
/// do this often, but it should make hot config reloading of the position work.
|
||||||
/// This should only be called from a GTK idle handler.
|
/// This should only be called from a GTK idle handler.
|
||||||
fn setPosition(widget: *c.GtkWidget, config: *configpkg.Config) void {
|
fn setPosition(label: *gtk.Label, config: *DerivedConfig) void {
|
||||||
c.gtk_widget_set_halign(
|
const widget = label.as(gtk.Widget);
|
||||||
@ptrCast(widget),
|
widget.setHalign(
|
||||||
switch (config.@"resize-overlay-position") {
|
switch (config.resize_overlay_position) {
|
||||||
.center, .@"top-center", .@"bottom-center" => c.GTK_ALIGN_CENTER,
|
.center, .@"top-center", .@"bottom-center" => gtk.Align.center,
|
||||||
.@"top-left", .@"bottom-left" => c.GTK_ALIGN_START,
|
.@"top-left", .@"bottom-left" => gtk.Align.start,
|
||||||
.@"top-right", .@"bottom-right" => c.GTK_ALIGN_END,
|
.@"top-right", .@"bottom-right" => gtk.Align.end,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
c.gtk_widget_set_valign(
|
widget.setValign(
|
||||||
@ptrCast(widget),
|
switch (config.resize_overlay_position) {
|
||||||
switch (config.@"resize-overlay-position") {
|
.center => gtk.Align.center,
|
||||||
.center => c.GTK_ALIGN_CENTER,
|
.@"top-left", .@"top-center", .@"top-right" => gtk.Align.start,
|
||||||
.@"top-left", .@"top-center", .@"top-right" => c.GTK_ALIGN_START,
|
.@"bottom-left", .@"bottom-center", .@"bottom-right" => gtk.Align.end,
|
||||||
.@"bottom-left", .@"bottom-center", .@"bottom-right" => c.GTK_ALIGN_END,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If this fires, it means that the delay period has expired and the resize
|
/// If this fires, it means that the delay period has expired and the resize
|
||||||
/// overlay widget should be hidden.
|
/// overlay widget should be hidden.
|
||||||
fn gtkTimerExpired(ud: ?*anyopaque) callconv(.C) c.gboolean {
|
fn gtkTimerExpired(ud: ?*anyopaque) callconv(.C) c_int {
|
||||||
const self: *ResizeOverlay = @ptrCast(@alignCast(ud));
|
const self: *ResizeOverlay = @ptrCast(@alignCast(ud orelse return 0));
|
||||||
self.timer = null;
|
self.timer = null;
|
||||||
if (self.widget) |widget| hide(widget);
|
if (self.label) |label| hide(label);
|
||||||
return c.FALSE;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -273,8 +273,8 @@ pub const URLWidget = struct {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Show it
|
// Show it
|
||||||
c.gtk_overlay_add_overlay(@ptrCast(surface.overlay), left);
|
c.gtk_overlay_add_overlay(surface.overlay, left);
|
||||||
c.gtk_overlay_add_overlay(@ptrCast(surface.overlay), right);
|
c.gtk_overlay_add_overlay(surface.overlay, right);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.left = left,
|
.left = left,
|
||||||
@ -283,8 +283,8 @@ pub const URLWidget = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *URLWidget, overlay: *c.GtkOverlay) void {
|
pub fn deinit(self: *URLWidget, overlay: *c.GtkOverlay) void {
|
||||||
c.gtk_overlay_remove_overlay(@ptrCast(overlay), @ptrCast(self.left));
|
c.gtk_overlay_remove_overlay(overlay, @ptrCast(self.left));
|
||||||
c.gtk_overlay_remove_overlay(@ptrCast(overlay), @ptrCast(self.right));
|
c.gtk_overlay_remove_overlay(overlay, @ptrCast(self.right));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setText(self: *const URLWidget, str: [:0]const u8) void {
|
pub fn setText(self: *const URLWidget, str: [:0]const u8) void {
|
||||||
@ -336,7 +336,7 @@ gl_area: *c.GtkGLArea,
|
|||||||
url_widget: ?URLWidget = null,
|
url_widget: ?URLWidget = null,
|
||||||
|
|
||||||
/// The overlay that shows resizing information.
|
/// The overlay that shows resizing information.
|
||||||
resize_overlay: ResizeOverlay = .{},
|
resize_overlay: ResizeOverlay = undefined,
|
||||||
|
|
||||||
/// Whether or not the current surface is zoomed in (see `toggle_split_zoom`).
|
/// Whether or not the current surface is zoomed in (see `toggle_split_zoom`).
|
||||||
zoomed_in: bool = false,
|
zoomed_in: bool = false,
|
||||||
@ -583,7 +583,7 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void {
|
|||||||
.container = .{ .none = {} },
|
.container = .{ .none = {} },
|
||||||
.overlay = @ptrCast(overlay),
|
.overlay = @ptrCast(overlay),
|
||||||
.gl_area = @ptrCast(gl_area),
|
.gl_area = @ptrCast(gl_area),
|
||||||
.resize_overlay = ResizeOverlay.init(self),
|
.resize_overlay = undefined,
|
||||||
.title_text = null,
|
.title_text = null,
|
||||||
.core_surface = undefined,
|
.core_surface = undefined,
|
||||||
.font_size = font_size,
|
.font_size = font_size,
|
||||||
@ -600,6 +600,9 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void {
|
|||||||
self.context_menu.init(self);
|
self.context_menu.init(self);
|
||||||
self.context_menu.setParent(@ptrCast(@alignCast(overlay)));
|
self.context_menu.setParent(@ptrCast(@alignCast(overlay)));
|
||||||
|
|
||||||
|
// initialize the resize overlay
|
||||||
|
self.resize_overlay.init(self, &app.config);
|
||||||
|
|
||||||
// Set our default mouse shape
|
// Set our default mouse shape
|
||||||
try self.setMouseShape(.text);
|
try self.setMouseShape(.text);
|
||||||
|
|
||||||
@ -706,8 +709,7 @@ pub fn deinit(self: *Surface) void {
|
|||||||
|
|
||||||
/// Update our local copy of any configuration that we use.
|
/// Update our local copy of any configuration that we use.
|
||||||
pub fn updateConfig(self: *Surface, config: *const configpkg.Config) !void {
|
pub fn updateConfig(self: *Surface, config: *const configpkg.Config) !void {
|
||||||
_ = self;
|
self.resize_overlay.updateConfig(config);
|
||||||
_ = config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
Reference in New Issue
Block a user