mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
apprt/gtk-ng: size_limit apprt action
This ports the same behavior from GTK, mostly. This also fixes a bug where the limits would be enforced on reload. Instead, we should only enforce them on the first surface ever.
This commit is contained in:
@ -545,12 +545,13 @@ pub const Application = extern struct {
|
||||
|
||||
.show_gtk_inspector => Action.showGtkInspector(),
|
||||
|
||||
.size_limit => return Action.sizeLimit(target, value),
|
||||
|
||||
.toggle_maximize => Action.toggleMaximize(target),
|
||||
.toggle_fullscreen => Action.toggleFullscreen(target),
|
||||
.toggle_tab_overview => return Action.toggleTabOverview(target),
|
||||
|
||||
// Unimplemented but todo on gtk-ng branch
|
||||
.size_limit,
|
||||
.prompt_title,
|
||||
.toggle_command_palette,
|
||||
.inspector,
|
||||
@ -1355,10 +1356,10 @@ const Action = struct {
|
||||
.app => return false,
|
||||
.surface => |core| {
|
||||
const surface = core.rt_surface.surface;
|
||||
surface.setDefaultSize(
|
||||
value.width,
|
||||
value.height,
|
||||
);
|
||||
surface.setDefaultSize(.{
|
||||
.width = value.width,
|
||||
.height = value.height,
|
||||
});
|
||||
return true;
|
||||
},
|
||||
}
|
||||
@ -1666,6 +1667,26 @@ const Action = struct {
|
||||
gtk.Window.setInteractiveDebugging(@intFromBool(true));
|
||||
}
|
||||
|
||||
pub fn sizeLimit(
|
||||
target: apprt.Target,
|
||||
value: apprt.action.SizeLimit,
|
||||
) bool {
|
||||
switch (target) {
|
||||
.app => return false,
|
||||
.surface => |core| {
|
||||
// Note: we ignore the max size currently because we have
|
||||
// no mechanism to enforce it.
|
||||
const surface = core.rt_surface.surface;
|
||||
surface.setMinSize(.{
|
||||
.width = value.min_width,
|
||||
.height = value.min_height,
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggleFullscreen(target: apprt.Target) void {
|
||||
switch (target) {
|
||||
.app => {},
|
||||
|
@ -81,7 +81,7 @@ pub const Surface = extern struct {
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
?*apprt.action.InitialSize,
|
||||
?*Size,
|
||||
.{
|
||||
.nick = "Default Size",
|
||||
.blurb = "The default size of the window for this surface.",
|
||||
@ -124,6 +124,20 @@ pub const Surface = extern struct {
|
||||
);
|
||||
};
|
||||
|
||||
pub const @"min-size" = struct {
|
||||
pub const name = "min-size";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
?*Size,
|
||||
.{
|
||||
.nick = "Minimum Size",
|
||||
.blurb = "The minimum size of the surface.",
|
||||
.accessor = C.privateBoxedFieldAccessor("min_size"),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const @"mouse-hidden" = struct {
|
||||
pub const name = "mouse-hidden";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
@ -300,6 +314,18 @@ pub const Surface = extern struct {
|
||||
);
|
||||
};
|
||||
|
||||
/// Emitted after the surface is initialized.
|
||||
pub const init = struct {
|
||||
pub const name = "init";
|
||||
pub const connect = impl.connect;
|
||||
const impl = gobject.ext.defineSignal(
|
||||
name,
|
||||
Self,
|
||||
&.{},
|
||||
void,
|
||||
);
|
||||
};
|
||||
|
||||
/// Emitted when the focus wants to be brought to the top and
|
||||
/// focused.
|
||||
pub const @"present-request" = struct {
|
||||
@ -349,7 +375,11 @@ pub const Surface = extern struct {
|
||||
cgroup_path: ?[]const u8 = null,
|
||||
|
||||
/// The default size for a window that embeds this surface.
|
||||
default_size: ?*apprt.action.InitialSize = null,
|
||||
default_size: ?*Size = null,
|
||||
|
||||
/// The minimum size for this surface. Embedders enforce this,
|
||||
/// not the surface itself.
|
||||
min_size: ?*Size = null,
|
||||
|
||||
/// The requested font size. This only applies to initialization
|
||||
/// and has no effect later.
|
||||
@ -1201,13 +1231,17 @@ pub const Surface = extern struct {
|
||||
priv.mouse_hover_url = null;
|
||||
}
|
||||
if (priv.default_size) |v| {
|
||||
ext.boxedFree(apprt.action.InitialSize, v);
|
||||
ext.boxedFree(Size, v);
|
||||
priv.default_size = null;
|
||||
}
|
||||
if (priv.font_size_request) |v| {
|
||||
glib.ext.destroy(v);
|
||||
priv.font_size_request = null;
|
||||
}
|
||||
if (priv.min_size) |v| {
|
||||
ext.boxedFree(Size, v);
|
||||
priv.min_size = null;
|
||||
}
|
||||
if (priv.pwd) |v| {
|
||||
glib.free(@constCast(@ptrCast(v)));
|
||||
priv.pwd = null;
|
||||
@ -1246,7 +1280,7 @@ pub const Surface = extern struct {
|
||||
}
|
||||
|
||||
/// Return the default size, if set.
|
||||
pub fn getDefaultSize(self: *Self) ?*apprt.action.InitialSize {
|
||||
pub fn getDefaultSize(self: *Self) ?*Size {
|
||||
const priv = self.private();
|
||||
return priv.default_size;
|
||||
}
|
||||
@ -1254,19 +1288,41 @@ pub const Surface = extern struct {
|
||||
/// Set the default size for a window that contains this surface.
|
||||
/// This is up to the embedding widget to respect this. Generally, only
|
||||
/// the first surface in a window respects this.
|
||||
pub fn setDefaultSize(self: *Self, width: u32, height: u32) void {
|
||||
pub fn setDefaultSize(self: *Self, size: Size) void {
|
||||
const priv = self.private();
|
||||
if (priv.default_size) |v| ext.boxedFree(
|
||||
apprt.action.InitialSize,
|
||||
Size,
|
||||
v,
|
||||
);
|
||||
priv.default_size = ext.boxedCopy(
|
||||
apprt.action.InitialSize,
|
||||
&.{ .width = width, .height = height },
|
||||
Size,
|
||||
&size,
|
||||
);
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"default-size".impl.param_spec);
|
||||
}
|
||||
|
||||
/// Return the min size, if set.
|
||||
pub fn getMinSize(self: *Self) ?*Size {
|
||||
const priv = self.private();
|
||||
return priv.min_size;
|
||||
}
|
||||
|
||||
/// Set the min size for a window that contains this surface.
|
||||
/// This is up to the embedding widget to respect this. Generally, only
|
||||
/// the first surface in a window respects this.
|
||||
pub fn setMinSize(self: *Self, size: Size) void {
|
||||
const priv = self.private();
|
||||
if (priv.min_size) |v| ext.boxedFree(
|
||||
Size,
|
||||
v,
|
||||
);
|
||||
priv.min_size = ext.boxedCopy(
|
||||
Size,
|
||||
&size,
|
||||
);
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"min-size".impl.param_spec);
|
||||
}
|
||||
|
||||
fn propConfig(
|
||||
self: *Self,
|
||||
_: *gobject.ParamSpec,
|
||||
@ -2106,6 +2162,14 @@ pub const Surface = extern struct {
|
||||
|
||||
// Store it!
|
||||
priv.core_surface = surface;
|
||||
|
||||
// Emit the signal that we initialized the surface.
|
||||
Surface.signals.init.impl.emit(
|
||||
self,
|
||||
null,
|
||||
.{},
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
fn resizeOverlaySchedule(self: *Self) void {
|
||||
@ -2229,6 +2293,7 @@ pub const Surface = extern struct {
|
||||
properties.@"default-size".impl,
|
||||
properties.@"font-size-request".impl,
|
||||
properties.focused.impl,
|
||||
properties.@"min-size".impl,
|
||||
properties.@"mouse-shape".impl,
|
||||
properties.@"mouse-hidden".impl,
|
||||
properties.@"mouse-hover-url".impl,
|
||||
@ -2242,6 +2307,7 @@ pub const Surface = extern struct {
|
||||
signals.bell.impl.register(.{});
|
||||
signals.@"clipboard-read".impl.register(.{});
|
||||
signals.@"clipboard-write".impl.register(.{});
|
||||
signals.init.impl.register(.{});
|
||||
signals.@"present-request".impl.register(.{});
|
||||
signals.@"toggle-fullscreen".impl.register(.{});
|
||||
signals.@"toggle-maximize".impl.register(.{});
|
||||
@ -2274,6 +2340,17 @@ pub const Surface = extern struct {
|
||||
.{ .name = "GhosttySurfaceCloseScope" },
|
||||
);
|
||||
};
|
||||
|
||||
/// Simple dimensions struct for the surface used by various properties.
|
||||
pub const Size = extern struct {
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
||||
pub const getGObjectType = gobject.ext.defineBoxed(
|
||||
Size,
|
||||
.{ .name = "GhosttySurfaceSize" },
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
/// The state of the key event while we're doing IM composition.
|
||||
|
@ -211,6 +211,18 @@ pub const Window = extern struct {
|
||||
/// The configuration that this surface is using.
|
||||
config: ?*Config = null,
|
||||
|
||||
/// Kind of hacky to have this but this lets us know if we've
|
||||
/// initialized any single surface yet. We need this because we
|
||||
/// gate default size on this so that we don't resize the window
|
||||
/// after surfaces already exist.
|
||||
///
|
||||
/// I think long term we can probably get rid of this by implementing
|
||||
/// a property or method that gets us all the surfaces in all the
|
||||
/// tabs and checking if we have zero or one that isn't initialized.
|
||||
///
|
||||
/// For now, this logic is more similar to our legacy GTK side.
|
||||
surface_init: bool = false,
|
||||
|
||||
/// See tabOverviewOpen for why we have this.
|
||||
tab_overview_focus_timer: ?c_uint = null,
|
||||
|
||||
@ -913,6 +925,8 @@ pub const Window = extern struct {
|
||||
_: c_int,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
const priv = self.private();
|
||||
|
||||
// Get the attached page which must be a Tab object.
|
||||
const child = page.getChild();
|
||||
const tab = gobject.ext.cast(Tab, child) orelse return;
|
||||
@ -983,13 +997,20 @@ pub const Window = extern struct {
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
surface,
|
||||
*Self,
|
||||
surfaceDefaultSize,
|
||||
self,
|
||||
.{ .detail = "default-size" },
|
||||
);
|
||||
|
||||
// If we've never had a surface initialize yet, then we register
|
||||
// this signal. Its theoretically possible to launch multiple surfaces
|
||||
// before init so we could register this on multiple and that is not
|
||||
// a problem because we'll check the flag again in each handler.
|
||||
if (!priv.surface_init) {
|
||||
_ = Surface.signals.init.connect(
|
||||
surface,
|
||||
*Self,
|
||||
surfaceInit,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn tabViewPageDetached(
|
||||
@ -1179,21 +1200,29 @@ pub const Window = extern struct {
|
||||
// We react to the changes in the propMaximized callback
|
||||
}
|
||||
|
||||
fn surfaceDefaultSize(
|
||||
fn surfaceInit(
|
||||
surface: *Surface,
|
||||
_: *gobject.ParamSpec,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
const size = surface.getDefaultSize() orelse return;
|
||||
const priv = self.private();
|
||||
|
||||
// We previously gated this on whether this was called before but
|
||||
// its useful to always set this to whatever the expected value is
|
||||
// so we can do a "return to default size" later. This call only
|
||||
// affects the window on first load. It won't resize it again later.
|
||||
self.as(gtk.Window).setDefaultSize(
|
||||
@intCast(size.width),
|
||||
@intCast(size.height),
|
||||
);
|
||||
// Make sure we init only once
|
||||
if (priv.surface_init) return;
|
||||
priv.surface_init = true;
|
||||
|
||||
// Setup our default and minimum size.
|
||||
if (surface.getDefaultSize()) |size| {
|
||||
self.as(gtk.Window).setDefaultSize(
|
||||
@intCast(size.width),
|
||||
@intCast(size.height),
|
||||
);
|
||||
}
|
||||
if (surface.getMinSize()) |size| {
|
||||
self.as(gtk.Widget).setSizeRequest(
|
||||
@intCast(size.width),
|
||||
@intCast(size.height),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn actionAbout(
|
||||
|
Reference in New Issue
Block a user