Files
ghostty/src/apprt/gtk/ResizeOverlay.zig
Qwerasd 2384bd69cc style: use decl literals
This commit changes a LOT of areas of the code to use decl literals
instead of redundantly referring to the type.

These changes were mostly driven by some regex searches and then manual
adjustment on a case-by-case basis.

I almost certainly missed quite a few places where decl literals could
be used, but this is a good first step in converting things, and other
instances can be addressed when they're discovered.

I tested GLFW+Metal and building the framework on macOS and tested a GTK
build on Linux, so I'm 99% sure I didn't introduce any syntax errors or
other problems with this. (fingers crossed)
2025-05-26 21:50:14 -06:00

207 lines
6.5 KiB
Zig

const ResizeOverlay = @This();
const std = @import("std");
const glib = @import("glib");
const gtk = @import("gtk");
const configpkg = @import("../../config.zig");
const Surface = @import("Surface.zig");
const log = std.log.scoped(.gtk);
/// local copy of configuration data
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
/// surface when it is resized.
label: ?*gtk.Label = null,
/// If non-null this is a timer for dismissing the resize overlay.
timer: ?c_uint = null,
/// If non-null this is a timer for dismissing the resize overlay.
idler: ?c_uint = null,
/// If true, the next resize event will be the first one.
first: bool = true,
/// 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
/// is done later.
pub fn init(self: *ResizeOverlay, surface: *Surface, config: *const configpkg.Config) void {
self.* = .{
.surface = surface,
.config = .init(config),
};
}
pub fn updateConfig(self: *ResizeOverlay, config: *const configpkg.Config) void {
self.config = .init(config);
}
/// De-initialize the ResizeOverlay. This removes any pending idlers/timers that
/// may not have fired yet.
pub fn deinit(self: *ResizeOverlay) void {
if (self.idler) |idler| {
if (glib.Source.remove(idler) == 0) {
log.warn("unable to remove resize overlay idler", .{});
}
self.idler = null;
}
if (self.timer) |timer| {
if (glib.Source.remove(timer) == 0) {
log.warn("unable to remove resize overlay timer", .{});
}
self.timer = null;
}
}
/// If we're configured to do so, update the text in the resize overlay widget
/// and make it visible. Schedule a timer to hide the widget after the delay
/// expires.
///
/// If we're not configured to show the overlay, do nothing.
pub fn maybeShow(self: *ResizeOverlay) void {
switch (self.config.resize_overlay) {
.never => return,
.always => {},
.@"after-first" => if (self.first) {
self.first = false;
return;
},
}
self.first = false;
// When updating a widget, wait until GTK is "idle", i.e. not in the middle
// of doing any other updates. Since we are called in the middle of resizing
// GTK is doing a lot of work rearranging all of the widgets. Not doing this
// results in a lot of warnings from GTK and _horrible_ flickering of the
// resize overlay.
if (self.idler != null) return;
self.idler = glib.idleAdd(gtkUpdate, self);
}
/// Actually update the overlay widget. This should only be called from a GTK
/// idle handler.
fn gtkUpdate(ud: ?*anyopaque) callconv(.c) c_int {
const self: *ResizeOverlay = @ptrCast(@alignCast(ud orelse return 0));
// No matter what our idler is complete with this callback
self.idler = null;
const grid_size = self.surface.core_surface.size.grid();
var buf: [32]u8 = undefined;
const text = std.fmt.bufPrintZ(
&buf,
"{d} x {d}",
.{
grid_size.columns,
grid_size.rows,
},
) catch |err| {
log.err("unable to format text: {}", .{err});
return 0;
};
if (self.label) |label| {
// The resize overlay widget already exists, just update it.
label.setText(text.ptr);
setPosition(label, &self.config);
show(label);
} else {
// Create the resize overlay widget.
const label = gtk.Label.new(text.ptr);
label.setJustify(gtk.Justification.center);
label.setSelectable(0);
setPosition(label, &self.config);
const widget = label.as(gtk.Widget);
widget.addCssClass("view");
widget.addCssClass("size-overlay");
widget.setFocusable(0);
widget.setCanTarget(0);
const overlay: *gtk.Overlay = @ptrCast(@alignCast(self.surface.overlay));
overlay.addOverlay(widget);
self.label = label;
}
if (self.timer) |timer| {
if (glib.Source.remove(timer) == 0) {
log.warn("unable to remove size overlay timer", .{});
}
}
self.timer = glib.timeoutAdd(
self.surface.app.config.@"resize-overlay-duration".asMilliseconds(),
gtkTimerExpired,
self,
);
return 0;
}
// This should only be called from a GTK idle handler or timer.
fn show(label: *gtk.Label) void {
const widget = label.as(gtk.Widget);
widget.removeCssClass("hidden");
}
// This should only be called from a GTK idle handler or timer.
fn hide(label: *gtk.Label) void {
const widget = label.as(gtk.Widget);
widget.addCssClass("hidden");
}
/// 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.
/// This should only be called from a GTK idle handler.
fn setPosition(label: *gtk.Label, config: *DerivedConfig) void {
const widget = label.as(gtk.Widget);
widget.setHalign(
switch (config.resize_overlay_position) {
.center, .@"top-center", .@"bottom-center" => gtk.Align.center,
.@"top-left", .@"bottom-left" => gtk.Align.start,
.@"top-right", .@"bottom-right" => gtk.Align.end,
},
);
widget.setValign(
switch (config.resize_overlay_position) {
.center => gtk.Align.center,
.@"top-left", .@"top-center", .@"top-right" => gtk.Align.start,
.@"bottom-left", .@"bottom-center", .@"bottom-right" => gtk.Align.end,
},
);
}
/// If this fires, it means that the delay period has expired and the resize
/// overlay widget should be hidden.
fn gtkTimerExpired(ud: ?*anyopaque) callconv(.c) c_int {
const self: *ResizeOverlay = @ptrCast(@alignCast(ud orelse return 0));
self.timer = null;
if (self.label) |label| hide(label);
return 0;
}