From 3257203b6c0b1e9d415ee6f6c3e9a19fc6b2ae0b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 16 Jul 2025 09:29:33 -0700 Subject: [PATCH] apprt/gtk-ng: start basic window --- src/apprt/gtk-ng/build/gresource.zig | 35 +++++++++++++-- src/apprt/gtk-ng/class/application.zig | 6 +++ src/apprt/gtk-ng/class/window.zig | 59 ++++++++++++++++++++++++++ src/apprt/gtk-ng/ui/1.5/window.blp | 8 ++-- 4 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 src/apprt/gtk-ng/class/window.zig diff --git a/src/apprt/gtk-ng/build/gresource.zig b/src/apprt/gtk-ng/build/gresource.zig index 6f9245de5..db5c2cf6e 100644 --- a/src/apprt/gtk-ng/build/gresource.zig +++ b/src/apprt/gtk-ng/build/gresource.zig @@ -29,12 +29,14 @@ pub const icon_sizes: []const comptime_int = &.{ 16, 32, 128, 256, 512, 1024 }; /// setup in the build system. /// /// These will be asserted to exist at runtime. -pub const blueprints: []const struct { +pub const blueprints: []const Blueprint = &.{ + .{ .major = 1, .minor = 5, .name = "window" }, +}; + +pub const Blueprint = struct { major: u16, minor: u16, name: []const u8, -} = &.{ - .{ .major = 1, .minor = 5, .name = "window" }, }; /// The list of filepaths that we depend on. Used for the build @@ -60,6 +62,33 @@ pub const file_inputs = deps: { break :deps deps; }; +/// Returns the matching blueprint resource path for the given blueprint +/// definition. This will fail at compile time if the blueprint is not +/// found. +/// +/// Must be called at comptime. +pub fn blueprint(comptime bp: Blueprint) [:0]const u8 { + // The comptime block around this whole thing forces an error if + // the caller attempts to call this function at runtime. + comptime { + for (blueprints) |candidate| { + if (candidate.major == bp.major and + candidate.minor == bp.minor and + std.mem.eql(u8, candidate.name, bp.name)) + { + return std.fmt.comptimePrint("{s}/ui/{d}.{d}/{s}.ui", .{ + prefix, + candidate.major, + candidate.minor, + candidate.name, + }); + } + } + + @compileError("invalid blueprint"); + } +} + pub fn main() !void { var debug_allocator: std.heap.DebugAllocator(.{}) = .init; defer _ = debug_allocator.deinit(); diff --git a/src/apprt/gtk-ng/class/application.zig b/src/apprt/gtk-ng/class/application.zig index 2c807a8f9..a2fd05a67 100644 --- a/src/apprt/gtk-ng/class/application.zig +++ b/src/apprt/gtk-ng/class/application.zig @@ -5,6 +5,7 @@ const adw = @import("adw"); const gio = @import("gio"); const glib = @import("glib"); const gobject = @import("gobject"); +const gtk = @import("gtk"); const build_config = @import("../../../build_config.zig"); const apprt = @import("../../../apprt.zig"); @@ -13,6 +14,8 @@ const CoreApp = @import("../../../App.zig"); const configpkg = @import("../../../config.zig"); const Config = configpkg.Config; +const GhosttyWindow = @import("window.zig").GhosttyWindow; + const log = std.log.scoped(.gtk_ghostty_application); /// The primary entrypoint for the Ghostty GTK application. @@ -380,6 +383,9 @@ pub const GhosttyApplication = extern struct { Class.parent, self.as(Parent), ); + + const win = GhosttyWindow.new(self); + gtk.Window.present(win.as(gtk.Window)); } fn finalize(self: *GhosttyApplication) callconv(.C) void { diff --git a/src/apprt/gtk-ng/class/window.zig b/src/apprt/gtk-ng/class/window.zig new file mode 100644 index 000000000..df8b99cee --- /dev/null +++ b/src/apprt/gtk-ng/class/window.zig @@ -0,0 +1,59 @@ +const std = @import("std"); +const adw = @import("adw"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const gresource = @import("../build/gresource.zig"); +const GhosttyApplication = @import("application.zig").GhosttyApplication; + +const log = std.log.scoped(.gtk_ghostty_window); + +pub const GhosttyWindow = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.ApplicationWindow; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + const Private = struct { + _todo: u8 = 0, + var offset: c_int = 0; + }; + + pub fn new(app: *GhosttyApplication) *Self { + return gobject.ext.newInstance(Self, .{ .application = app }); + } + + fn init(win: *GhosttyWindow, _: *Class) callconv(.C) void { + gtk.Widget.initTemplate(win.as(gtk.Widget)); + } + + pub fn as(win: *Self, comptime T: type) *T { + return gobject.ext.as(T, win); + } + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.C) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "window", + }), + ); + } + + pub fn as(class: *Class, comptime T: type) *T { + return gobject.ext.as(T, class); + } + }; +}; diff --git a/src/apprt/gtk-ng/ui/1.5/window.blp b/src/apprt/gtk-ng/ui/1.5/window.blp index 22ba886ff..d6321537e 100644 --- a/src/apprt/gtk-ng/ui/1.5/window.blp +++ b/src/apprt/gtk-ng/ui/1.5/window.blp @@ -1,8 +1,8 @@ using Gtk 4.0; using Adw 1; -Adw.Window { - Label { - label: "Hello"; - } +template $GhosttyWindow: Adw.ApplicationWindow { + content: Label { + label: "Hello, Ghostty!"; + }; }