mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-24 04:36:10 +03:00
apprt/gtk-ng: implement Surface.close
(#7994)
A small, simple change. This implements the `Surface.close` apprt required function. After this PR, a process exiting within the terminal will close the window properly (unless `wait-after-command` is set of course!). This doesn't yet show close confirmation. I'm working on some dialog refactors on the side to see if we can simplify our Adw 1.2 vs. 1.5 dialogs and didn't want to include it here. Close now works by the `GhosttySurface` class emitting the `close-request` signal (similar to a `gtk.Window`) and the parent container is responsible for closing it. This will let us reuse the surface within different contexts: tabs, splits, etc. This also remove the unused `shouldClose`, `setShouldClose` apprt APIs which are a holdover from the glfw days!
This commit is contained in:
12
src/App.zig
12
src/App.zig
@ -132,18 +132,6 @@ pub fn destroy(self: *App) void {
|
||||
/// events. This should be called by the application runtime on every loop
|
||||
/// tick.
|
||||
pub fn tick(self: *App, rt_app: *apprt.App) !void {
|
||||
// If any surfaces are closing, destroy them
|
||||
var i: usize = 0;
|
||||
while (i < self.surfaces.items.len) {
|
||||
const surface = self.surfaces.items[i];
|
||||
if (surface.shouldClose()) {
|
||||
surface.close(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Drain our mailbox
|
||||
try self.drainMailbox(rt_app);
|
||||
}
|
||||
|
@ -705,15 +705,6 @@ pub const Surface = struct {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setShouldClose(self: *Surface) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn shouldClose(self: *const Surface) bool {
|
||||
_ = self;
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn getCursorPos(self: *const Surface) !apprt.CursorPos {
|
||||
return self.cursor_pos;
|
||||
}
|
||||
|
@ -26,13 +26,7 @@ pub fn rtApp(self: *Self) *ApprtApp {
|
||||
}
|
||||
|
||||
pub fn close(self: *Self, process_active: bool) void {
|
||||
_ = self;
|
||||
_ = process_active;
|
||||
}
|
||||
|
||||
pub fn shouldClose(self: *Self) bool {
|
||||
_ = self;
|
||||
return false;
|
||||
self.surface.close(process_active);
|
||||
}
|
||||
|
||||
pub fn getTitle(self: *Self) ?[:0]const u8 {
|
||||
|
@ -54,6 +54,29 @@ pub const Surface = extern struct {
|
||||
};
|
||||
};
|
||||
|
||||
pub const signals = struct {
|
||||
/// Emitted whenever the surface would like to be closed for any
|
||||
/// reason.
|
||||
///
|
||||
/// The surface view does NOT handle its own close confirmation.
|
||||
/// If there is a process alive then the boolean parameter will
|
||||
/// specify it and the parent widget should handle this request.
|
||||
///
|
||||
/// This signal lets the containing widget decide how closure works.
|
||||
/// This lets this Surface widget be used as a split, tab, etc.
|
||||
/// without it having to be aware of its own semantics.
|
||||
pub const @"close-request" = struct {
|
||||
pub const name = "close-request";
|
||||
pub const connect = impl.connect;
|
||||
const impl = gobject.ext.defineSignal(
|
||||
name,
|
||||
Self,
|
||||
&.{bool},
|
||||
void,
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const Private = struct {
|
||||
/// The configuration that this surface is using.
|
||||
config: ?*Config = null,
|
||||
@ -374,6 +397,15 @@ pub const Surface = extern struct {
|
||||
//---------------------------------------------------------------
|
||||
// Libghostty Callbacks
|
||||
|
||||
pub fn close(self: *Self, process_active: bool) void {
|
||||
signals.@"close-request".impl.emit(
|
||||
self,
|
||||
null,
|
||||
.{process_active},
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getContentScale(self: *Self) apprt.ContentScale {
|
||||
const priv = self.private();
|
||||
const gl_area = priv.gl_area;
|
||||
@ -1341,6 +1373,9 @@ pub const Surface = extern struct {
|
||||
properties.config.impl,
|
||||
});
|
||||
|
||||
// Signals
|
||||
signals.@"close-request".impl.register(.{});
|
||||
|
||||
// Virtual methods
|
||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
|
||||
|
@ -1,4 +1,5 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const adw = @import("adw");
|
||||
const gobject = @import("gobject");
|
||||
const gtk = @import("gtk");
|
||||
@ -23,7 +24,9 @@ pub const Window = extern struct {
|
||||
});
|
||||
|
||||
const Private = struct {
|
||||
_todo: u8,
|
||||
/// The surface in the view.
|
||||
surface: *Surface = undefined,
|
||||
|
||||
pub var offset: c_int = 0;
|
||||
};
|
||||
|
||||
@ -33,8 +36,20 @@ pub const Window = extern struct {
|
||||
|
||||
fn init(self: *Self, _: *Class) callconv(.C) void {
|
||||
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
||||
|
||||
const surface = self.private().surface;
|
||||
_ = Surface.signals.@"close-request".connect(
|
||||
surface,
|
||||
*Self,
|
||||
surfaceCloseRequest,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Virtual methods
|
||||
|
||||
fn dispose(self: *Self) callconv(.C) void {
|
||||
gtk.Widget.disposeTemplate(
|
||||
self.as(gtk.Widget),
|
||||
@ -47,6 +62,21 @@ pub const Window = extern struct {
|
||||
);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Signal handlers
|
||||
|
||||
fn surfaceCloseRequest(
|
||||
surface: *Surface,
|
||||
process_active: bool,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
// Todo
|
||||
_ = process_active;
|
||||
|
||||
assert(surface == self.private().surface);
|
||||
self.as(gtk.Window).close();
|
||||
}
|
||||
|
||||
const C = Common(Self, Private);
|
||||
pub const as = C.as;
|
||||
pub const ref = C.ref;
|
||||
@ -60,7 +90,6 @@ pub const Window = extern struct {
|
||||
|
||||
fn init(class: *Class) callconv(.C) void {
|
||||
gobject.ext.ensureType(Surface);
|
||||
|
||||
gtk.Widget.Class.setTemplateFromResource(
|
||||
class.as(gtk.Widget.Class),
|
||||
comptime gresource.blueprint(.{
|
||||
@ -70,11 +99,14 @@ pub const Window = extern struct {
|
||||
}),
|
||||
);
|
||||
|
||||
// Bindings
|
||||
class.bindTemplateChildPrivate("surface", .{});
|
||||
|
||||
// Virtual methods
|
||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||
}
|
||||
|
||||
pub fn as(class: *Class, comptime T: type) *T {
|
||||
return gobject.ext.as(T, class);
|
||||
}
|
||||
pub const as = C.Class.as;
|
||||
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
||||
};
|
||||
};
|
||||
|
@ -2,5 +2,5 @@ using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $GhosttyWindow: Adw.ApplicationWindow {
|
||||
content: $GhosttySurface {};
|
||||
content: $GhosttySurface surface {};
|
||||
}
|
||||
|
@ -877,15 +877,6 @@ pub fn controlInspector(
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setShouldClose(self: *Surface) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn shouldClose(self: *const Surface) bool {
|
||||
_ = self;
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn getContentScale(self: *const Surface) !apprt.ContentScale {
|
||||
const gtk_scale: f32 = scale: {
|
||||
const widget = self.gl_area.as(gtk.Widget);
|
||||
|
Reference in New Issue
Block a user