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(),
|
.show_gtk_inspector => Action.showGtkInspector(),
|
||||||
|
|
||||||
|
.size_limit => return Action.sizeLimit(target, value),
|
||||||
|
|
||||||
.toggle_maximize => Action.toggleMaximize(target),
|
.toggle_maximize => Action.toggleMaximize(target),
|
||||||
.toggle_fullscreen => Action.toggleFullscreen(target),
|
.toggle_fullscreen => Action.toggleFullscreen(target),
|
||||||
.toggle_tab_overview => return Action.toggleTabOverview(target),
|
.toggle_tab_overview => return Action.toggleTabOverview(target),
|
||||||
|
|
||||||
// Unimplemented but todo on gtk-ng branch
|
// Unimplemented but todo on gtk-ng branch
|
||||||
.size_limit,
|
|
||||||
.prompt_title,
|
.prompt_title,
|
||||||
.toggle_command_palette,
|
.toggle_command_palette,
|
||||||
.inspector,
|
.inspector,
|
||||||
@ -1355,10 +1356,10 @@ const Action = struct {
|
|||||||
.app => return false,
|
.app => return false,
|
||||||
.surface => |core| {
|
.surface => |core| {
|
||||||
const surface = core.rt_surface.surface;
|
const surface = core.rt_surface.surface;
|
||||||
surface.setDefaultSize(
|
surface.setDefaultSize(.{
|
||||||
value.width,
|
.width = value.width,
|
||||||
value.height,
|
.height = value.height,
|
||||||
);
|
});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1666,6 +1667,26 @@ const Action = struct {
|
|||||||
gtk.Window.setInteractiveDebugging(@intFromBool(true));
|
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 {
|
pub fn toggleFullscreen(target: apprt.Target) void {
|
||||||
switch (target) {
|
switch (target) {
|
||||||
.app => {},
|
.app => {},
|
||||||
|
@ -81,7 +81,7 @@ pub const Surface = extern struct {
|
|||||||
const impl = gobject.ext.defineProperty(
|
const impl = gobject.ext.defineProperty(
|
||||||
name,
|
name,
|
||||||
Self,
|
Self,
|
||||||
?*apprt.action.InitialSize,
|
?*Size,
|
||||||
.{
|
.{
|
||||||
.nick = "Default Size",
|
.nick = "Default Size",
|
||||||
.blurb = "The default size of the window for this surface.",
|
.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 @"mouse-hidden" = struct {
|
||||||
pub const name = "mouse-hidden";
|
pub const name = "mouse-hidden";
|
||||||
const impl = gobject.ext.defineProperty(
|
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
|
/// Emitted when the focus wants to be brought to the top and
|
||||||
/// focused.
|
/// focused.
|
||||||
pub const @"present-request" = struct {
|
pub const @"present-request" = struct {
|
||||||
@ -349,7 +375,11 @@ pub const Surface = extern struct {
|
|||||||
cgroup_path: ?[]const u8 = null,
|
cgroup_path: ?[]const u8 = null,
|
||||||
|
|
||||||
/// The default size for a window that embeds this surface.
|
/// 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
|
/// The requested font size. This only applies to initialization
|
||||||
/// and has no effect later.
|
/// and has no effect later.
|
||||||
@ -1201,13 +1231,17 @@ pub const Surface = extern struct {
|
|||||||
priv.mouse_hover_url = null;
|
priv.mouse_hover_url = null;
|
||||||
}
|
}
|
||||||
if (priv.default_size) |v| {
|
if (priv.default_size) |v| {
|
||||||
ext.boxedFree(apprt.action.InitialSize, v);
|
ext.boxedFree(Size, v);
|
||||||
priv.default_size = null;
|
priv.default_size = null;
|
||||||
}
|
}
|
||||||
if (priv.font_size_request) |v| {
|
if (priv.font_size_request) |v| {
|
||||||
glib.ext.destroy(v);
|
glib.ext.destroy(v);
|
||||||
priv.font_size_request = null;
|
priv.font_size_request = null;
|
||||||
}
|
}
|
||||||
|
if (priv.min_size) |v| {
|
||||||
|
ext.boxedFree(Size, v);
|
||||||
|
priv.min_size = null;
|
||||||
|
}
|
||||||
if (priv.pwd) |v| {
|
if (priv.pwd) |v| {
|
||||||
glib.free(@constCast(@ptrCast(v)));
|
glib.free(@constCast(@ptrCast(v)));
|
||||||
priv.pwd = null;
|
priv.pwd = null;
|
||||||
@ -1246,7 +1280,7 @@ pub const Surface = extern struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the default size, if set.
|
/// Return the default size, if set.
|
||||||
pub fn getDefaultSize(self: *Self) ?*apprt.action.InitialSize {
|
pub fn getDefaultSize(self: *Self) ?*Size {
|
||||||
const priv = self.private();
|
const priv = self.private();
|
||||||
return priv.default_size;
|
return priv.default_size;
|
||||||
}
|
}
|
||||||
@ -1254,19 +1288,41 @@ pub const Surface = extern struct {
|
|||||||
/// Set the default size for a window that contains this surface.
|
/// Set the default size for a window that contains this surface.
|
||||||
/// This is up to the embedding widget to respect this. Generally, only
|
/// This is up to the embedding widget to respect this. Generally, only
|
||||||
/// the first surface in a window respects this.
|
/// 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();
|
const priv = self.private();
|
||||||
if (priv.default_size) |v| ext.boxedFree(
|
if (priv.default_size) |v| ext.boxedFree(
|
||||||
apprt.action.InitialSize,
|
Size,
|
||||||
v,
|
v,
|
||||||
);
|
);
|
||||||
priv.default_size = ext.boxedCopy(
|
priv.default_size = ext.boxedCopy(
|
||||||
apprt.action.InitialSize,
|
Size,
|
||||||
&.{ .width = width, .height = height },
|
&size,
|
||||||
);
|
);
|
||||||
self.as(gobject.Object).notifyByPspec(properties.@"default-size".impl.param_spec);
|
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(
|
fn propConfig(
|
||||||
self: *Self,
|
self: *Self,
|
||||||
_: *gobject.ParamSpec,
|
_: *gobject.ParamSpec,
|
||||||
@ -2106,6 +2162,14 @@ pub const Surface = extern struct {
|
|||||||
|
|
||||||
// Store it!
|
// Store it!
|
||||||
priv.core_surface = surface;
|
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 {
|
fn resizeOverlaySchedule(self: *Self) void {
|
||||||
@ -2229,6 +2293,7 @@ pub const Surface = extern struct {
|
|||||||
properties.@"default-size".impl,
|
properties.@"default-size".impl,
|
||||||
properties.@"font-size-request".impl,
|
properties.@"font-size-request".impl,
|
||||||
properties.focused.impl,
|
properties.focused.impl,
|
||||||
|
properties.@"min-size".impl,
|
||||||
properties.@"mouse-shape".impl,
|
properties.@"mouse-shape".impl,
|
||||||
properties.@"mouse-hidden".impl,
|
properties.@"mouse-hidden".impl,
|
||||||
properties.@"mouse-hover-url".impl,
|
properties.@"mouse-hover-url".impl,
|
||||||
@ -2242,6 +2307,7 @@ pub const Surface = extern struct {
|
|||||||
signals.bell.impl.register(.{});
|
signals.bell.impl.register(.{});
|
||||||
signals.@"clipboard-read".impl.register(.{});
|
signals.@"clipboard-read".impl.register(.{});
|
||||||
signals.@"clipboard-write".impl.register(.{});
|
signals.@"clipboard-write".impl.register(.{});
|
||||||
|
signals.init.impl.register(.{});
|
||||||
signals.@"present-request".impl.register(.{});
|
signals.@"present-request".impl.register(.{});
|
||||||
signals.@"toggle-fullscreen".impl.register(.{});
|
signals.@"toggle-fullscreen".impl.register(.{});
|
||||||
signals.@"toggle-maximize".impl.register(.{});
|
signals.@"toggle-maximize".impl.register(.{});
|
||||||
@ -2274,6 +2340,17 @@ pub const Surface = extern struct {
|
|||||||
.{ .name = "GhosttySurfaceCloseScope" },
|
.{ .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.
|
/// 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.
|
/// The configuration that this surface is using.
|
||||||
config: ?*Config = null,
|
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.
|
/// See tabOverviewOpen for why we have this.
|
||||||
tab_overview_focus_timer: ?c_uint = null,
|
tab_overview_focus_timer: ?c_uint = null,
|
||||||
|
|
||||||
@ -913,6 +925,8 @@ pub const Window = extern struct {
|
|||||||
_: c_int,
|
_: c_int,
|
||||||
self: *Self,
|
self: *Self,
|
||||||
) callconv(.c) void {
|
) callconv(.c) void {
|
||||||
|
const priv = self.private();
|
||||||
|
|
||||||
// Get the attached page which must be a Tab object.
|
// Get the attached page which must be a Tab object.
|
||||||
const child = page.getChild();
|
const child = page.getChild();
|
||||||
const tab = gobject.ext.cast(Tab, child) orelse return;
|
const tab = gobject.ext.cast(Tab, child) orelse return;
|
||||||
@ -983,13 +997,20 @@ pub const Window = extern struct {
|
|||||||
self,
|
self,
|
||||||
.{},
|
.{},
|
||||||
);
|
);
|
||||||
_ = gobject.Object.signals.notify.connect(
|
|
||||||
surface,
|
// If we've never had a surface initialize yet, then we register
|
||||||
*Self,
|
// this signal. Its theoretically possible to launch multiple surfaces
|
||||||
surfaceDefaultSize,
|
// before init so we could register this on multiple and that is not
|
||||||
self,
|
// a problem because we'll check the flag again in each handler.
|
||||||
.{ .detail = "default-size" },
|
if (!priv.surface_init) {
|
||||||
);
|
_ = Surface.signals.init.connect(
|
||||||
|
surface,
|
||||||
|
*Self,
|
||||||
|
surfaceInit,
|
||||||
|
self,
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tabViewPageDetached(
|
fn tabViewPageDetached(
|
||||||
@ -1179,21 +1200,29 @@ pub const Window = extern struct {
|
|||||||
// We react to the changes in the propMaximized callback
|
// We react to the changes in the propMaximized callback
|
||||||
}
|
}
|
||||||
|
|
||||||
fn surfaceDefaultSize(
|
fn surfaceInit(
|
||||||
surface: *Surface,
|
surface: *Surface,
|
||||||
_: *gobject.ParamSpec,
|
|
||||||
self: *Self,
|
self: *Self,
|
||||||
) callconv(.c) void {
|
) callconv(.c) void {
|
||||||
const size = surface.getDefaultSize() orelse return;
|
const priv = self.private();
|
||||||
|
|
||||||
// We previously gated this on whether this was called before but
|
// Make sure we init only once
|
||||||
// its useful to always set this to whatever the expected value is
|
if (priv.surface_init) return;
|
||||||
// so we can do a "return to default size" later. This call only
|
priv.surface_init = true;
|
||||||
// affects the window on first load. It won't resize it again later.
|
|
||||||
self.as(gtk.Window).setDefaultSize(
|
// Setup our default and minimum size.
|
||||||
@intCast(size.width),
|
if (surface.getDefaultSize()) |size| {
|
||||||
@intCast(size.height),
|
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(
|
fn actionAbout(
|
||||||
|
Reference in New Issue
Block a user