mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +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
|
/// events. This should be called by the application runtime on every loop
|
||||||
/// tick.
|
/// tick.
|
||||||
pub fn tick(self: *App, rt_app: *apprt.App) !void {
|
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
|
// Drain our mailbox
|
||||||
try self.drainMailbox(rt_app);
|
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 {
|
pub fn getCursorPos(self: *const Surface) !apprt.CursorPos {
|
||||||
return self.cursor_pos;
|
return self.cursor_pos;
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,7 @@ pub fn rtApp(self: *Self) *ApprtApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(self: *Self, process_active: bool) void {
|
pub fn close(self: *Self, process_active: bool) void {
|
||||||
_ = self;
|
self.surface.close(process_active);
|
||||||
_ = process_active;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shouldClose(self: *Self) bool {
|
|
||||||
_ = self;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getTitle(self: *Self) ?[:0]const u8 {
|
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 {
|
const Private = struct {
|
||||||
/// The configuration that this surface is using.
|
/// The configuration that this surface is using.
|
||||||
config: ?*Config = null,
|
config: ?*Config = null,
|
||||||
@ -374,6 +397,15 @@ pub const Surface = extern struct {
|
|||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
// Libghostty Callbacks
|
// 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 {
|
pub fn getContentScale(self: *Self) apprt.ContentScale {
|
||||||
const priv = self.private();
|
const priv = self.private();
|
||||||
const gl_area = priv.gl_area;
|
const gl_area = priv.gl_area;
|
||||||
@ -1341,6 +1373,9 @@ pub const Surface = extern struct {
|
|||||||
properties.config.impl,
|
properties.config.impl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Signals
|
||||||
|
signals.@"close-request".impl.register(.{});
|
||||||
|
|
||||||
// Virtual methods
|
// Virtual methods
|
||||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||||
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
|
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
const adw = @import("adw");
|
const adw = @import("adw");
|
||||||
const gobject = @import("gobject");
|
const gobject = @import("gobject");
|
||||||
const gtk = @import("gtk");
|
const gtk = @import("gtk");
|
||||||
@ -23,7 +24,9 @@ pub const Window = extern struct {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const Private = struct {
|
const Private = struct {
|
||||||
_todo: u8,
|
/// The surface in the view.
|
||||||
|
surface: *Surface = undefined,
|
||||||
|
|
||||||
pub var offset: c_int = 0;
|
pub var offset: c_int = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -33,8 +36,20 @@ pub const Window = extern struct {
|
|||||||
|
|
||||||
fn init(self: *Self, _: *Class) callconv(.C) void {
|
fn init(self: *Self, _: *Class) callconv(.C) void {
|
||||||
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
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 {
|
fn dispose(self: *Self) callconv(.C) void {
|
||||||
gtk.Widget.disposeTemplate(
|
gtk.Widget.disposeTemplate(
|
||||||
self.as(gtk.Widget),
|
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);
|
const C = Common(Self, Private);
|
||||||
pub const as = C.as;
|
pub const as = C.as;
|
||||||
pub const ref = C.ref;
|
pub const ref = C.ref;
|
||||||
@ -60,7 +90,6 @@ pub const Window = extern struct {
|
|||||||
|
|
||||||
fn init(class: *Class) callconv(.C) void {
|
fn init(class: *Class) callconv(.C) void {
|
||||||
gobject.ext.ensureType(Surface);
|
gobject.ext.ensureType(Surface);
|
||||||
|
|
||||||
gtk.Widget.Class.setTemplateFromResource(
|
gtk.Widget.Class.setTemplateFromResource(
|
||||||
class.as(gtk.Widget.Class),
|
class.as(gtk.Widget.Class),
|
||||||
comptime gresource.blueprint(.{
|
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);
|
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as(class: *Class, comptime T: type) *T {
|
pub const as = C.Class.as;
|
||||||
return gobject.ext.as(T, class);
|
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -2,5 +2,5 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $GhosttyWindow: Adw.ApplicationWindow {
|
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 {
|
pub fn getContentScale(self: *const Surface) !apprt.ContentScale {
|
||||||
const gtk_scale: f32 = scale: {
|
const gtk_scale: f32 = scale: {
|
||||||
const widget = self.gl_area.as(gtk.Widget);
|
const widget = self.gl_area.as(gtk.Widget);
|
||||||
|
Reference in New Issue
Block a user