apprt/gtk-ng: extract common methods into a mixin

Every GObject class we're ever going to make has the same handful of
methods. This adds *just enough* noise to be annoying. This commit
extracts the common ones so far into a central mixin. Since Zig is
removing `usingnamespace` and has no other mixin mechanism, we must
forward the decls, but this is still cleaner imo than what we did
before.

I suspect longer term we can probably abstract more of the `zig-gobject`
boilerplate into a higher level abstraction but I'm not confident doing
that yet across the 4 classes we have so far.
This commit is contained in:
Mitchell Hashimoto
2025-07-18 07:22:16 -07:00
parent 7d99042070
commit 833f7f1142
5 changed files with 70 additions and 68 deletions

View File

@ -24,3 +24,45 @@ pub fn unrefLater(obj: anytype) void {
}
}).callback, obj.as(gobject.Object));
}
/// Common methods for all GObject classes we create.
pub fn Common(
comptime Self: type,
comptime Private: ?type,
) type {
return struct {
/// Upcast our type to a parent type or interface. This will fail at
/// compile time if the cast isn't 100% safe. For unsafe casts,
/// use `gobject.ext.cast` instead. We don't have a helper for that
/// because its uncommon and unsafe behavior should be noisier.
pub fn as(self: *Self, comptime T: type) *T {
return gobject.ext.as(T, self);
}
/// Increase the reference count of the object.
pub fn ref(self: *Self) *Self {
return @ptrCast(@alignCast(gobject.Object.ref(self.as(gobject.Object))));
}
/// Decrease the reference count of the object.
pub fn unref(self: *Self) void {
gobject.Object.unref(self.as(gobject.Object));
}
/// Access the private data of the object. This should be forwarded
/// via a non-pub const usually.
pub const private = if (Private) |P| (struct {
fn private(self: *Self) *P {
return gobject.ext.impl_helpers.getPrivate(
self,
P,
P.offset,
);
}
}).private else {};
};
}
test {
@import("std").testing.refAllDecls(@This());
}

View File

@ -20,6 +20,7 @@ const CoreConfig = configpkg.Config;
const adw_version = @import("../adw_version.zig");
const gtk_version = @import("../gtk_version.zig");
const ApprtApp = @import("../App.zig");
const Common = @import("../class.zig").Common;
const WeakRef = @import("../weak_ref.zig").WeakRef;
const Config = @import("config.zig").Config;
const Window = @import("window.zig").Window;
@ -84,7 +85,7 @@ pub const Application = extern struct {
/// outside of our own lifecycle and that's okay.
config_errors_dialog: WeakRef(ConfigErrorsDialog) = .{},
var offset: c_int = 0;
pub var offset: c_int = 0;
};
/// Creates a new Application instance.
@ -699,21 +700,11 @@ pub const Application = extern struct {
return self.private().core_app.alloc;
}
pub fn as(app: *Self, comptime T: type) *T {
return gobject.ext.as(T, app);
}
pub fn unref(self: *Self) void {
gobject.Object.unref(self.as(gobject.Object));
}
fn private(self: *Self) *Private {
return gobject.ext.impl_helpers.getPrivate(
self,
Private,
Private.offset,
);
}
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,

View File

@ -9,6 +9,7 @@ const configpkg = @import("../../../config.zig");
const CoreConfig = configpkg.Config;
const unrefLater = @import("../class.zig").unrefLater;
const Common = @import("../class.zig").Common;
const log = std.log.scoped(.gtk_ghostty_config);
@ -66,7 +67,7 @@ pub const Config = extern struct {
const Private = struct {
config: CoreConfig,
var offset: c_int = 0;
pub var offset: c_int = 0;
};
/// Create a new GhosttyConfig from a loaded configuration.
@ -141,25 +142,11 @@ pub const Config = extern struct {
);
}
pub fn as(self: *Self, comptime T: type) *T {
return gobject.ext.as(T, self);
}
pub fn ref(self: *Self) *Self {
return @ptrCast(@alignCast(gobject.Object.ref(self.as(gobject.Object))));
}
pub fn unref(self: *Self) void {
gobject.Object.unref(self.as(gobject.Object));
}
fn private(self: *Self) *Private {
return gobject.ext.impl_helpers.getPrivate(
self,
Private,
Private.offset,
);
}
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,

View File

@ -5,6 +5,7 @@ const gtk = @import("gtk");
const gresource = @import("../build/gresource.zig");
const adw_version = @import("../adw_version.zig");
const Common = @import("../class.zig").Common;
const Config = @import("config.zig").Config;
const log = std.log.scoped(.gtk_ghostty_config_errors_dialog);
@ -58,7 +59,7 @@ pub const ConfigErrorsDialog = extern struct {
const Private = struct {
config: ?*Config,
var offset: c_int = 0;
pub var offset: c_int = 0;
};
pub fn new(config: *Config) *Self {
@ -129,25 +130,11 @@ pub const ConfigErrorsDialog = extern struct {
priv.config = config;
}
pub fn as(win: *Self, comptime T: type) *T {
return gobject.ext.as(T, win);
}
pub fn ref(self: *Self) *Self {
return @ptrCast(@alignCast(gobject.Object.ref(self.as(gobject.Object))));
}
pub fn unref(self: *Self) void {
gobject.Object.unref(self.as(gobject.Object));
}
fn private(self: *Self) *Private {
return gobject.ext.impl_helpers.getPrivate(
self,
Private,
Private.offset,
);
}
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,

View File

@ -4,6 +4,7 @@ const gobject = @import("gobject");
const gtk = @import("gtk");
const gresource = @import("../build/gresource.zig");
const Common = @import("../class.zig").Common;
const Application = @import("application.zig").Application;
const log = std.log.scoped(.gtk_ghostty_window);
@ -22,7 +23,7 @@ pub const Window = extern struct {
const Private = struct {
_todo: u8,
var offset: c_int = 0;
pub var offset: c_int = 0;
};
pub fn new(app: *Application) *Self {
@ -45,17 +46,11 @@ pub const Window = extern struct {
);
}
pub fn as(self: *Self, comptime T: type) *T {
return gobject.ext.as(T, self);
}
fn private(self: *Self) *Private {
return gobject.ext.impl_helpers.getPrivate(
self,
Private,
Private.offset,
);
}
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,