mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-31 22:07:33 +03:00
apprt/gtk-ng: ResizeOverlay class
This commit is contained in:
@ -35,6 +35,7 @@ pub const icon_sizes: []const comptime_int = &.{ 16, 32, 128, 256, 512, 1024 };
|
|||||||
pub const blueprints: []const Blueprint = &.{
|
pub const blueprints: []const Blueprint = &.{
|
||||||
.{ .major = 1, .minor = 2, .name = "close-confirmation-dialog" },
|
.{ .major = 1, .minor = 2, .name = "close-confirmation-dialog" },
|
||||||
.{ .major = 1, .minor = 2, .name = "config-errors-dialog" },
|
.{ .major = 1, .minor = 2, .name = "config-errors-dialog" },
|
||||||
|
.{ .major = 1, .minor = 2, .name = "resize-overlay" },
|
||||||
.{ .major = 1, .minor = 2, .name = "surface" },
|
.{ .major = 1, .minor = 2, .name = "surface" },
|
||||||
.{ .major = 1, .minor = 5, .name = "window" },
|
.{ .major = 1, .minor = 5, .name = "window" },
|
||||||
};
|
};
|
||||||
|
302
src/apprt/gtk-ng/class/resize_overlay.zig
Normal file
302
src/apprt/gtk-ng/class/resize_overlay.zig
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const adw = @import("adw");
|
||||||
|
const glib = @import("glib");
|
||||||
|
const gobject = @import("gobject");
|
||||||
|
const gtk = @import("gtk");
|
||||||
|
|
||||||
|
const gresource = @import("../build/gresource.zig");
|
||||||
|
const Common = @import("../class.zig").Common;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.gtk_ghostty_window);
|
||||||
|
|
||||||
|
/// The overlay that shows the current size while a surface is resizing.
|
||||||
|
/// This can be used generically to show pretty much anything with a
|
||||||
|
/// disappearing overlay, but we have no other use at this point so it
|
||||||
|
/// is named specifically for what it does.
|
||||||
|
///
|
||||||
|
/// General usage:
|
||||||
|
///
|
||||||
|
/// 1. Add it to an overlay
|
||||||
|
/// 2. Set the label with `setLabel`
|
||||||
|
/// 3. Schedule to show it with `schedule`
|
||||||
|
///
|
||||||
|
/// Set any properties to change the behavior.
|
||||||
|
pub const ResizeOverlay = extern struct {
|
||||||
|
const Self = @This();
|
||||||
|
parent_instance: Parent,
|
||||||
|
pub const Parent = adw.Bin;
|
||||||
|
pub const getGObjectType = gobject.ext.defineClass(Self, .{
|
||||||
|
.name = "GhosttyResizeOverlay",
|
||||||
|
.instanceInit = &init,
|
||||||
|
.classInit = &Class.init,
|
||||||
|
.parent_class = &Class.parent,
|
||||||
|
.private = .{ .Type = Private, .offset = &Private.offset },
|
||||||
|
});
|
||||||
|
|
||||||
|
pub const properties = struct {
|
||||||
|
pub const duration = struct {
|
||||||
|
pub const name = "duration";
|
||||||
|
const impl = gobject.ext.defineProperty(
|
||||||
|
name,
|
||||||
|
Self,
|
||||||
|
c_uint,
|
||||||
|
.{
|
||||||
|
.nick = "Duration",
|
||||||
|
.blurb = "The duration this overlay appears in milliseconds.",
|
||||||
|
.default = 750,
|
||||||
|
.minimum = 250,
|
||||||
|
.maximum = std.math.maxInt(c_uint),
|
||||||
|
.accessor = gobject.ext.privateFieldAccessor(
|
||||||
|
Self,
|
||||||
|
Private,
|
||||||
|
&Private.offset,
|
||||||
|
"duration",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const @"first-delay" = struct {
|
||||||
|
pub const name = "first-delay";
|
||||||
|
const impl = gobject.ext.defineProperty(
|
||||||
|
name,
|
||||||
|
Self,
|
||||||
|
c_uint,
|
||||||
|
.{
|
||||||
|
.nick = "First Delay",
|
||||||
|
.blurb = "The delay in milliseconds before any overlay is shown for the first time.",
|
||||||
|
.default = 250,
|
||||||
|
.minimum = 250,
|
||||||
|
.maximum = std.math.maxInt(c_uint),
|
||||||
|
.accessor = gobject.ext.privateFieldAccessor(
|
||||||
|
Self,
|
||||||
|
Private,
|
||||||
|
&Private.offset,
|
||||||
|
"first_delay",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const @"overlay-halign" = struct {
|
||||||
|
pub const name = "overlay-halign";
|
||||||
|
const impl = gobject.ext.defineProperty(
|
||||||
|
name,
|
||||||
|
Self,
|
||||||
|
gtk.Align,
|
||||||
|
.{
|
||||||
|
.nick = "halign",
|
||||||
|
.blurb = "The alignment of the label.",
|
||||||
|
.default = .center,
|
||||||
|
.accessor = gobject.ext.privateFieldAccessor(
|
||||||
|
Self,
|
||||||
|
Private,
|
||||||
|
&Private.offset,
|
||||||
|
"halign",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const @"overlay-valign" = struct {
|
||||||
|
pub const name = "overlay-valign";
|
||||||
|
const impl = gobject.ext.defineProperty(
|
||||||
|
name,
|
||||||
|
Self,
|
||||||
|
gtk.Align,
|
||||||
|
.{
|
||||||
|
.nick = "valign",
|
||||||
|
.blurb = "The alignment of the label.",
|
||||||
|
.default = .center,
|
||||||
|
.accessor = gobject.ext.privateFieldAccessor(
|
||||||
|
Self,
|
||||||
|
Private,
|
||||||
|
&Private.offset,
|
||||||
|
"valign",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const Private = struct {
|
||||||
|
/// The label with the text
|
||||||
|
label: *gtk.Label,
|
||||||
|
|
||||||
|
/// The time that the overlay appears.
|
||||||
|
duration: c_uint,
|
||||||
|
|
||||||
|
/// The first delay before any overlay is shown. Must be specified
|
||||||
|
/// during construction otherwise it has no effect.
|
||||||
|
first_delay: c_uint,
|
||||||
|
|
||||||
|
/// The idle source that we use to update the label.
|
||||||
|
idler: ?c_uint = null,
|
||||||
|
|
||||||
|
/// The timer for dismissing the overlay.
|
||||||
|
timer: ?c_uint = null,
|
||||||
|
|
||||||
|
/// The first delay timer.
|
||||||
|
delay_timer: ?c_uint = null,
|
||||||
|
|
||||||
|
/// The alignment of the label
|
||||||
|
halign: gtk.Align,
|
||||||
|
valign: gtk.Align,
|
||||||
|
|
||||||
|
pub var offset: c_int = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn init(self: *Self, _: *Class) callconv(.C) void {
|
||||||
|
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
||||||
|
|
||||||
|
const priv = self.private();
|
||||||
|
if (priv.first_delay > 0) {
|
||||||
|
priv.delay_timer = glib.timeoutAdd(
|
||||||
|
priv.first_delay,
|
||||||
|
onDelayTimer,
|
||||||
|
self,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the label for the overlay. This will not show the
|
||||||
|
/// overlay if it is currently hidden; you must call schedule.
|
||||||
|
pub fn setLabel(self: *Self, label: [:0]const u8) void {
|
||||||
|
const priv = self.private();
|
||||||
|
priv.label.setText(label.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Schedule the overlay to be shown. To avoid flickering during
|
||||||
|
/// resizes we schedule the overlay to be shown on the next idle tick.
|
||||||
|
pub fn schedule(self: *Self) void {
|
||||||
|
const priv = self.private();
|
||||||
|
|
||||||
|
// If we have a delay timer then we're not showing anything
|
||||||
|
// yet so do nothing.
|
||||||
|
if (priv.delay_timer != null) return;
|
||||||
|
|
||||||
|
// 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 (priv.idler != null) return;
|
||||||
|
priv.idler = glib.idleAdd(onIdle, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn onIdle(ud: ?*anyopaque) callconv(.c) c_int {
|
||||||
|
const self: *Self = @ptrCast(@alignCast(ud orelse return 0));
|
||||||
|
const priv = self.private();
|
||||||
|
|
||||||
|
// No matter what our idler is complete with this callback
|
||||||
|
priv.idler = null;
|
||||||
|
|
||||||
|
// Show ourselves
|
||||||
|
self.as(gtk.Widget).setVisible(1);
|
||||||
|
|
||||||
|
if (priv.timer) |timer| {
|
||||||
|
if (glib.Source.remove(timer) == 0) {
|
||||||
|
log.warn("unable to remove size overlay timer", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
priv.timer = glib.timeoutAdd(
|
||||||
|
priv.duration,
|
||||||
|
onTimer,
|
||||||
|
self,
|
||||||
|
);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn onTimer(ud: ?*anyopaque) callconv(.c) c_int {
|
||||||
|
const self: *Self = @ptrCast(@alignCast(ud orelse return 0));
|
||||||
|
const priv = self.private();
|
||||||
|
priv.timer = null;
|
||||||
|
self.as(gtk.Widget).setVisible(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn onDelayTimer(ud: ?*anyopaque) callconv(.c) c_int {
|
||||||
|
const self: *Self = @ptrCast(@alignCast(ud orelse return 0));
|
||||||
|
const priv = self.private();
|
||||||
|
priv.delay_timer = null;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Virtual methods
|
||||||
|
|
||||||
|
fn dispose(self: *Self) callconv(.C) void {
|
||||||
|
const priv = self.private();
|
||||||
|
if (priv.idler) |v| {
|
||||||
|
if (glib.Source.remove(v) == 0) {
|
||||||
|
log.warn("unable to remove resize overlay idler", .{});
|
||||||
|
}
|
||||||
|
priv.idler = null;
|
||||||
|
}
|
||||||
|
if (priv.timer) |v| {
|
||||||
|
if (glib.Source.remove(v) == 0) {
|
||||||
|
log.warn("unable to remove resize overlay timer", .{});
|
||||||
|
}
|
||||||
|
priv.timer = null;
|
||||||
|
}
|
||||||
|
if (priv.delay_timer) |v| {
|
||||||
|
if (glib.Source.remove(v) == 0) {
|
||||||
|
log.warn("unable to remove resize overlay delay timer", .{});
|
||||||
|
}
|
||||||
|
priv.delay_timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk.Widget.disposeTemplate(
|
||||||
|
self.as(gtk.Widget),
|
||||||
|
getGObjectType(),
|
||||||
|
);
|
||||||
|
|
||||||
|
gobject.Object.virtual_methods.dispose.call(
|
||||||
|
Class.parent,
|
||||||
|
self.as(Parent),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const C = Common(Self, Private);
|
||||||
|
pub const as = C.as;
|
||||||
|
pub const ref = C.ref;
|
||||||
|
pub const unref = C.unref;
|
||||||
|
const private = C.private;
|
||||||
|
|
||||||
|
pub const Class = extern struct {
|
||||||
|
parent_class: Parent.Class,
|
||||||
|
var parent: *Parent.Class = undefined;
|
||||||
|
pub const Instance = Self;
|
||||||
|
|
||||||
|
fn init(class: *Class) callconv(.C) void {
|
||||||
|
gtk.Widget.Class.setTemplateFromResource(
|
||||||
|
class.as(gtk.Widget.Class),
|
||||||
|
comptime gresource.blueprint(.{
|
||||||
|
.major = 1,
|
||||||
|
.minor = 2,
|
||||||
|
.name = "resize-overlay",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Bindings
|
||||||
|
class.bindTemplateChildPrivate("label", .{});
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
gobject.ext.registerProperties(class, &.{
|
||||||
|
properties.duration.impl,
|
||||||
|
properties.@"first-delay".impl,
|
||||||
|
properties.@"overlay-halign".impl,
|
||||||
|
properties.@"overlay-valign".impl,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Virtual methods
|
||||||
|
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const as = C.Class.as;
|
||||||
|
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
||||||
|
};
|
||||||
|
};
|
@ -19,6 +19,7 @@ const ApprtSurface = @import("../Surface.zig");
|
|||||||
const Common = @import("../class.zig").Common;
|
const Common = @import("../class.zig").Common;
|
||||||
const Application = @import("application.zig").Application;
|
const Application = @import("application.zig").Application;
|
||||||
const Config = @import("config.zig").Config;
|
const Config = @import("config.zig").Config;
|
||||||
|
const ResizeOverlay = @import("resize_overlay.zig").ResizeOverlay;
|
||||||
|
|
||||||
const log = std.log.scoped(.gtk_ghostty_surface);
|
const log = std.log.scoped(.gtk_ghostty_surface);
|
||||||
|
|
||||||
@ -202,17 +203,8 @@ pub const Surface = extern struct {
|
|||||||
url_left: *gtk.Label = undefined,
|
url_left: *gtk.Label = undefined,
|
||||||
url_right: *gtk.Label = undefined,
|
url_right: *gtk.Label = undefined,
|
||||||
|
|
||||||
/// The resize label shown when resizing the surface.
|
/// The resize overlay
|
||||||
size_label: *gtk.Label = undefined,
|
resize_overlay: *ResizeOverlay = undefined,
|
||||||
|
|
||||||
/// The idle source that tracks our resize label.
|
|
||||||
size_idler: ?c_uint = null,
|
|
||||||
|
|
||||||
/// If non-null this is a timer for dismissing the resize overlay.
|
|
||||||
size_timer: ?c_uint = null,
|
|
||||||
|
|
||||||
/// Set to true after the first resize event.
|
|
||||||
size_first: bool = true,
|
|
||||||
|
|
||||||
/// The apprt Surface.
|
/// The apprt Surface.
|
||||||
rt_surface: ApprtSurface = undefined,
|
rt_surface: ApprtSurface = undefined,
|
||||||
@ -523,86 +515,6 @@ pub const Surface = extern struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resizeOverlaySchedule(self: *Self) void {
|
|
||||||
const priv = self.private();
|
|
||||||
const config = priv.config orelse return;
|
|
||||||
switch (config.get().@"resize-overlay") {
|
|
||||||
.never => return,
|
|
||||||
.always => {},
|
|
||||||
.@"after-first" => if (priv.size_first) {
|
|
||||||
priv.size_first = false;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// We set this in case we reload our config later to after-first
|
|
||||||
// so we don't miss a tick.
|
|
||||||
priv.size_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 (priv.size_idler != null) return;
|
|
||||||
priv.size_idler = glib.idleAdd(resizeOverlayIdle, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resizeOverlayIdle(ud: ?*anyopaque) callconv(.c) c_int {
|
|
||||||
const self: *Self = @ptrCast(@alignCast(ud orelse return 0));
|
|
||||||
const priv = self.private();
|
|
||||||
|
|
||||||
// No matter what our idler is complete with this callback
|
|
||||||
priv.size_idler = null;
|
|
||||||
|
|
||||||
// We need config from here on out
|
|
||||||
const config = if (priv.config) |c| c.get() else return 0;
|
|
||||||
|
|
||||||
var buf: [32]u8 = undefined;
|
|
||||||
const text = text: {
|
|
||||||
const surface = priv.core_surface orelse return 0;
|
|
||||||
const grid_size = surface.size.grid();
|
|
||||||
break :text std.fmt.bufPrintZ(
|
|
||||||
&buf,
|
|
||||||
"{d} x {d}",
|
|
||||||
.{
|
|
||||||
grid_size.columns,
|
|
||||||
grid_size.rows,
|
|
||||||
},
|
|
||||||
) catch |err| {
|
|
||||||
log.warn("unable to format text: {}", .{err});
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// The resize overlay widget already exists, just update it.
|
|
||||||
priv.size_label.setText(text.ptr);
|
|
||||||
priv.size_label.as(gtk.Widget).setVisible(1);
|
|
||||||
//setPosition(label, &self.config);
|
|
||||||
|
|
||||||
if (priv.size_timer) |timer| {
|
|
||||||
if (glib.Source.remove(timer) == 0) {
|
|
||||||
log.warn("unable to remove size overlay timer", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
priv.size_timer = glib.timeoutAdd(
|
|
||||||
config.@"resize-overlay-duration".asMilliseconds(),
|
|
||||||
resizeOverlayTimerExpired,
|
|
||||||
self,
|
|
||||||
);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resizeOverlayTimerExpired(ud: ?*anyopaque) callconv(.c) c_int {
|
|
||||||
const self: *Self = @ptrCast(@alignCast(ud orelse return 0));
|
|
||||||
const priv = self.private();
|
|
||||||
priv.size_timer = null;
|
|
||||||
priv.size_label.as(gtk.Widget).setVisible(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
// Libghostty Callbacks
|
// Libghostty Callbacks
|
||||||
|
|
||||||
@ -733,9 +645,6 @@ pub const Surface = extern struct {
|
|||||||
.width = 111,
|
.width = 111,
|
||||||
.height = 111,
|
.height = 111,
|
||||||
};
|
};
|
||||||
priv.size_idler = null;
|
|
||||||
priv.size_timer = null;
|
|
||||||
priv.size_first = true;
|
|
||||||
|
|
||||||
// If our configuration is null then we get the configuration
|
// If our configuration is null then we get the configuration
|
||||||
// from the application.
|
// from the application.
|
||||||
@ -932,6 +841,13 @@ pub const Surface = extern struct {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Some property signals
|
// Some property signals
|
||||||
|
_ = gobject.Object.signals.notify.connect(
|
||||||
|
self,
|
||||||
|
?*anyopaque,
|
||||||
|
&propConfig,
|
||||||
|
null,
|
||||||
|
.{ .detail = "config" },
|
||||||
|
);
|
||||||
_ = gobject.Object.signals.notify.connect(
|
_ = gobject.Object.signals.notify.connect(
|
||||||
self,
|
self,
|
||||||
?*anyopaque,
|
?*anyopaque,
|
||||||
@ -957,13 +873,15 @@ pub const Surface = extern struct {
|
|||||||
// Some other initialization steps
|
// Some other initialization steps
|
||||||
self.initUrlOverlay();
|
self.initUrlOverlay();
|
||||||
self.initResizeOverlay();
|
self.initResizeOverlay();
|
||||||
|
|
||||||
|
// Initialize our config
|
||||||
|
self.propConfig(undefined, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initResizeOverlay(self: *Self) void {
|
fn initResizeOverlay(self: *Self) void {
|
||||||
const priv = self.private();
|
const priv = self.private();
|
||||||
const overlay = priv.overlay;
|
const overlay = priv.overlay;
|
||||||
const label = priv.size_label.as(gtk.Widget);
|
overlay.addOverlay(priv.resize_overlay.as(gtk.Widget));
|
||||||
overlay.addOverlay(label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initUrlOverlay(self: *Self) void {
|
fn initUrlOverlay(self: *Self) void {
|
||||||
@ -1008,18 +926,6 @@ pub const Surface = extern struct {
|
|||||||
v.unref();
|
v.unref();
|
||||||
priv.im_context = null;
|
priv.im_context = null;
|
||||||
}
|
}
|
||||||
if (priv.size_idler) |v| {
|
|
||||||
if (glib.Source.remove(v) == 0) {
|
|
||||||
log.warn("unable to remove resize overlay idler", .{});
|
|
||||||
}
|
|
||||||
priv.size_idler = null;
|
|
||||||
}
|
|
||||||
if (priv.size_timer) |v| {
|
|
||||||
if (glib.Source.remove(v) == 0) {
|
|
||||||
log.warn("unable to remove resize overlay timer", .{});
|
|
||||||
}
|
|
||||||
priv.size_timer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
gtk.Widget.disposeTemplate(
|
gtk.Widget.disposeTemplate(
|
||||||
self.as(gtk.Widget),
|
self.as(gtk.Widget),
|
||||||
@ -1075,6 +981,58 @@ pub const Surface = extern struct {
|
|||||||
return self.private().title;
|
return self.private().title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn propConfig(
|
||||||
|
self: *Self,
|
||||||
|
_: *gobject.ParamSpec,
|
||||||
|
_: ?*anyopaque,
|
||||||
|
) callconv(.c) void {
|
||||||
|
const priv = self.private();
|
||||||
|
const config = if (priv.config) |c| c.get() else return;
|
||||||
|
|
||||||
|
// resize-overlay-duration
|
||||||
|
{
|
||||||
|
const ms = config.@"resize-overlay-duration".asMilliseconds();
|
||||||
|
var value = gobject.ext.Value.newFrom(ms);
|
||||||
|
defer value.unset();
|
||||||
|
gobject.Object.setProperty(
|
||||||
|
priv.resize_overlay.as(gobject.Object),
|
||||||
|
"duration",
|
||||||
|
&value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize-overlay-position
|
||||||
|
{
|
||||||
|
const hv: struct {
|
||||||
|
gtk.Align, // halign
|
||||||
|
gtk.Align, // valign
|
||||||
|
} = switch (config.@"resize-overlay-position") {
|
||||||
|
.center => .{ .center, .center },
|
||||||
|
.@"top-left" => .{ .start, .start },
|
||||||
|
.@"top-right" => .{ .end, .start },
|
||||||
|
.@"top-center" => .{ .center, .start },
|
||||||
|
.@"bottom-left" => .{ .start, .end },
|
||||||
|
.@"bottom-right" => .{ .end, .end },
|
||||||
|
.@"bottom-center" => .{ .center, .end },
|
||||||
|
};
|
||||||
|
|
||||||
|
var halign = gobject.ext.Value.newFrom(hv[0]);
|
||||||
|
defer halign.unset();
|
||||||
|
var valign = gobject.ext.Value.newFrom(hv[1]);
|
||||||
|
defer valign.unset();
|
||||||
|
gobject.Object.setProperty(
|
||||||
|
priv.resize_overlay.as(gobject.Object),
|
||||||
|
"overlay-halign",
|
||||||
|
&halign,
|
||||||
|
);
|
||||||
|
gobject.Object.setProperty(
|
||||||
|
priv.resize_overlay.as(gobject.Object),
|
||||||
|
"overlay-valign",
|
||||||
|
&valign,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn propMouseHoverUrl(
|
fn propMouseHoverUrl(
|
||||||
self: *Self,
|
self: *Self,
|
||||||
_: *gobject.ParamSpec,
|
_: *gobject.ParamSpec,
|
||||||
@ -1672,13 +1630,43 @@ pub const Surface = extern struct {
|
|||||||
log.warn("error in size callback err={}", .{err});
|
log.warn("error in size callback err={}", .{err});
|
||||||
};
|
};
|
||||||
|
|
||||||
// If we have resize overlays enabled, setup an idler
|
// Setup our resize overlay if configured
|
||||||
// to show that. We do this in an idle tick because doing it
|
|
||||||
// during the resize results in flickering.
|
|
||||||
self.resizeOverlaySchedule();
|
self.resizeOverlaySchedule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resizeOverlaySchedule(self: *Self) void {
|
||||||
|
const priv = self.private();
|
||||||
|
const surface = priv.core_surface orelse return;
|
||||||
|
|
||||||
|
// Only show the resize overlay if its enabled
|
||||||
|
const config = if (priv.config) |c| c.get() else return;
|
||||||
|
switch (config.@"resize-overlay") {
|
||||||
|
.always, .@"after-first" => {},
|
||||||
|
.never => return,
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have resize overlays enabled, setup an idler
|
||||||
|
// to show that. We do this in an idle tick because doing it
|
||||||
|
// during the resize results in flickering.
|
||||||
|
var buf: [32]u8 = undefined;
|
||||||
|
priv.resize_overlay.setLabel(text: {
|
||||||
|
const grid_size = surface.size.grid();
|
||||||
|
break :text std.fmt.bufPrintZ(
|
||||||
|
&buf,
|
||||||
|
"{d} x {d}",
|
||||||
|
.{
|
||||||
|
grid_size.columns,
|
||||||
|
grid_size.rows,
|
||||||
|
},
|
||||||
|
) catch |err| err: {
|
||||||
|
log.warn("unable to format text: {}", .{err});
|
||||||
|
break :err "";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
priv.resize_overlay.schedule();
|
||||||
|
}
|
||||||
|
|
||||||
const RealizeError = Allocator.Error || error{
|
const RealizeError = Allocator.Error || error{
|
||||||
GLAreaError,
|
GLAreaError,
|
||||||
RendererError,
|
RendererError,
|
||||||
@ -1775,6 +1763,7 @@ pub const Surface = extern struct {
|
|||||||
pub const Instance = Self;
|
pub const Instance = Self;
|
||||||
|
|
||||||
fn init(class: *Class) callconv(.C) void {
|
fn init(class: *Class) callconv(.C) void {
|
||||||
|
gobject.ext.ensureType(ResizeOverlay);
|
||||||
gtk.Widget.Class.setTemplateFromResource(
|
gtk.Widget.Class.setTemplateFromResource(
|
||||||
class.as(gtk.Widget.Class),
|
class.as(gtk.Widget.Class),
|
||||||
comptime gresource.blueprint(.{
|
comptime gresource.blueprint(.{
|
||||||
@ -1789,7 +1778,7 @@ pub const Surface = extern struct {
|
|||||||
class.bindTemplateChildPrivate("gl_area", .{});
|
class.bindTemplateChildPrivate("gl_area", .{});
|
||||||
class.bindTemplateChildPrivate("url_left", .{});
|
class.bindTemplateChildPrivate("url_left", .{});
|
||||||
class.bindTemplateChildPrivate("url_right", .{});
|
class.bindTemplateChildPrivate("url_right", .{});
|
||||||
class.bindTemplateChildPrivate("size_label", .{});
|
class.bindTemplateChildPrivate("resize_overlay", .{});
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
gobject.ext.registerProperties(class, &.{
|
gobject.ext.registerProperties(class, &.{
|
||||||
|
@ -23,7 +23,7 @@ label.url-overlay.right {
|
|||||||
border-radius: 6px 0px 0px 0px;
|
border-radius: 6px 0px 0px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
label.size-overlay {
|
.size-overlay label {
|
||||||
padding: 4px 8px 4px 8px;
|
padding: 4px 8px 4px 8px;
|
||||||
border-radius: 6px 6px 6px 6px;
|
border-radius: 6px 6px 6px 6px;
|
||||||
outline-style: solid;
|
outline-style: solid;
|
||||||
|
22
src/apprt/gtk-ng/ui/1.2/resize-overlay.blp
Normal file
22
src/apprt/gtk-ng/ui/1.2/resize-overlay.blp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using Gtk 4.0;
|
||||||
|
using Adw 1;
|
||||||
|
// We can't inherit directly from Label because its an opaque
|
||||||
|
// type in zig-gobject.
|
||||||
|
template $GhosttyResizeOverlay: Adw.Bin {
|
||||||
|
visible: false;
|
||||||
|
duration: 750;
|
||||||
|
first-delay: 250;
|
||||||
|
overlay-halign: center;
|
||||||
|
overlay-valign: center;
|
||||||
|
// See surface.blp for why we need to wrap this.
|
||||||
|
Adw.Bin {
|
||||||
|
Label label {
|
||||||
|
focusable: false;
|
||||||
|
focus-on-click: false;
|
||||||
|
justify: center;
|
||||||
|
selectable: false;
|
||||||
|
halign: bind template.overlay-halign;
|
||||||
|
valign: bind template.overlay-valign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,17 +43,8 @@ Label url_right {
|
|||||||
label: bind template.mouse-hover-url;
|
label: bind template.mouse-hover-url;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The label that shows the resize information
|
$GhosttyResizeOverlay resize_overlay {
|
||||||
Label size_label {
|
|
||||||
styles [
|
styles [
|
||||||
"size-overlay",
|
"size-overlay",
|
||||||
]
|
]
|
||||||
|
|
||||||
visible: false;
|
|
||||||
focusable: false;
|
|
||||||
focus-on-click: false;
|
|
||||||
justify: center;
|
|
||||||
selectable: false;
|
|
||||||
halign: center;
|
|
||||||
valign: center;
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user