apprt/gtk-ng: hook up all the bindings for the config errors dialog

This commit is contained in:
Mitchell Hashimoto
2025-07-16 20:49:53 -07:00
parent c3ba6e252e
commit 531d4a480e
5 changed files with 144 additions and 16 deletions

View File

@ -21,6 +21,7 @@ const adw_version = @import("../adw_version.zig");
const gtk_version = @import("../gtk_version.zig");
const GhosttyConfig = @import("config.zig").GhosttyConfig;
const GhosttyWindow = @import("window.zig").GhosttyWindow;
const ConfigErrorsDialog = @import("config_errors_dialog.zig").ConfigErrorsDialog;
const log = std.log.scoped(.gtk_ghostty_application);
@ -320,6 +321,11 @@ pub const GhosttyApplication = extern struct {
fn startup(self: *GhosttyApplication) callconv(.C) void {
log.debug("startup", .{});
gio.Application.virtual_methods.startup.call(
Class.parent,
self.as(Parent),
);
// Setup our event loop
self.startupXev();
@ -331,10 +337,15 @@ pub const GhosttyApplication = extern struct {
log.warn("TODO", .{});
};
gio.Application.virtual_methods.startup.call(
Class.parent,
self.as(Parent),
);
// If we have any config diagnostics from loading, then we
// show the diagnostics dialog. We show this one as a general
// modal (not to any specific window) because we don't even
// know if the window will load.
const priv = self.private();
if (priv.config.hasDiagnostics()) {
const dialog: *ConfigErrorsDialog = .new(priv.config);
dialog.present(null);
}
}
/// Configure libxev to use a specific backend.
@ -474,8 +485,8 @@ pub const GhosttyApplication = extern struct {
self.as(Parent),
);
const win = GhosttyWindow.new(self);
gtk.Window.present(win.as(gtk.Window));
// const win = GhosttyWindow.new(self);
// gtk.Window.present(win.as(gtk.Window));
}
fn finalize(self: *GhosttyApplication) callconv(.C) void {

View File

@ -29,6 +29,36 @@ pub const GhosttyConfig = extern struct {
.private = .{ .Type = Private, .offset = &Private.offset },
});
pub const properties = struct {
pub const @"diagnostics-buffer" = gobject.ext.defineProperty(
"diagnostics-buffer",
Self,
?*gtk.TextBuffer,
.{
.nick = "Dignostics Buffer",
.blurb = "A TextBuffer that contains the diagnostics.",
.default = null,
.accessor = .{
.getter = Self.diagnosticsBuffer,
},
},
);
pub const @"has-diagnostics" = gobject.ext.defineProperty(
"has-diagnostics",
Self,
bool,
.{
.nick = "has-diagnostics",
.blurb = "Whether the configuration has diagnostics.",
.default = false,
.accessor = .{
.getter = Self.hasDiagnostics,
},
},
);
};
const Private = struct {
config: Config,
@ -55,6 +85,48 @@ pub const GhosttyConfig = extern struct {
return &self.private().config;
}
/// Get the mutable configuration. This is usually NOT recommended
/// because any changes to the config won't be propagated to anyone
/// with a reference to this object. If you know what you're doing, then
/// you can use this.
pub fn getMut(self: *Self) *Config {
return &self.private().config;
}
/// Returns whether this configuration has any diagnostics.
pub fn hasDiagnostics(self: *Self) bool {
const config = self.get();
return !config._diagnostics.empty();
}
/// Reads the diagnostics of this configuration as a TextBuffer,
/// or returns null if there are no diagnostics.
pub fn diagnosticsBuffer(self: *Self) ?*gtk.TextBuffer {
const config = self.get();
if (config._diagnostics.empty()) return null;
const text_buf: *gtk.TextBuffer = .new(null);
errdefer text_buf.unref();
var buf: [4095:0]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
for (config._diagnostics.items()) |diag| {
fbs.reset();
diag.write(fbs.writer()) catch |err| {
log.warn(
"error writing diagnostic to buffer err={}",
.{err},
);
continue;
};
text_buf.insertAtCursor(&buf, @intCast(fbs.pos));
text_buf.insertAtCursor("\n", 1);
}
return text_buf;
}
fn finalize(self: *Self) callconv(.C) void {
self.private().config.deinit();
@ -91,6 +163,10 @@ pub const GhosttyConfig = extern struct {
fn init(class: *Class) callconv(.C) void {
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
gobject.ext.registerProperties(class, &.{
properties.@"diagnostics-buffer",
properties.@"has-diagnostics",
});
}
};
};

View File

@ -5,11 +5,11 @@ const gtk = @import("gtk");
const gresource = @import("../build/gresource.zig");
const adw_version = @import("../adw_version.zig");
const GhosttyConfig = @import("config.zig").GhosttyConfig;
const Config = @import("config.zig").GhosttyConfig;
const log = std.log.scoped(.gtk_ghostty_window);
pub const GhosttyConfigErrors = extern struct {
pub const ConfigErrorsDialog = extern struct {
const Self = @This();
parent_instance: Parent,
@ -19,18 +19,38 @@ pub const GhosttyConfigErrors = extern struct {
adw.MessageDialog;
pub const getGObjectType = gobject.ext.defineClass(Self, .{
.name = "GhosttyConfigErrorsDialog",
.instanceInit = &init,
.classInit = &Class.init,
.parent_class = &Class.parent,
.private = .{ .Type = Private, .offset = &Private.offset },
});
pub const properties = struct {
pub const config = gobject.ext.defineProperty(
"config",
Self,
?*Config,
.{
.nick = "config",
.blurb = "The configuration that this dialog is showing errors for.",
.default = null,
.accessor = gobject.ext.privateFieldAccessor(
Self,
Private,
&Private.offset,
"config",
),
},
);
};
const Private = struct {
_todo: u8 = 0,
config: ?*Config,
var offset: c_int = 0;
};
pub fn new(config: *GhosttyConfig) *Self {
pub fn new(config: *Config) *Self {
return gobject.ext.newInstance(Self, .{
.config = config,
});
@ -40,6 +60,13 @@ pub const GhosttyConfigErrors = extern struct {
gtk.Widget.initTemplate(self.as(gtk.Widget));
}
pub fn present(self: *Self, parent: ?*gtk.Widget) void {
switch (Parent) {
adw.AlertDialog => self.as(adw.Dialog).present(parent),
else => unreachable,
}
}
pub fn as(win: *Self, comptime T: type) *T {
return gobject.ext.as(T, win);
}
@ -58,9 +85,14 @@ pub const GhosttyConfigErrors = extern struct {
.minor = 5,
.name = "config-errors-dialog",
}),
else => unreachable,
},
);
gobject.ext.registerProperties(class, &.{
properties.config,
});
}
pub fn as(class: *Class, comptime T: type) *T {

View File

@ -20,7 +20,7 @@ pub const GhosttyWindow = extern struct {
});
const Private = struct {
_todo: u8 = 0,
_todo: u8,
var offset: c_int = 0;
};
@ -28,12 +28,20 @@ pub const GhosttyWindow = extern struct {
return gobject.ext.newInstance(Self, .{ .application = app });
}
fn init(win: *GhosttyWindow, _: *Class) callconv(.C) void {
gtk.Widget.initTemplate(win.as(gtk.Widget));
fn init(self: *GhosttyWindow, _: *Class) callconv(.C) void {
gtk.Widget.initTemplate(self.as(gtk.Widget));
}
pub fn as(win: *Self, comptime T: type) *T {
return gobject.ext.as(T, win);
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,
);
}
pub const Class = extern struct {

View File

@ -1,7 +1,7 @@
using Gtk 4.0;
using Adw 1;
Adw.AlertDialog config_errors_dialog {
template $GhosttyConfigErrorsDialog: Adw.AlertDialog {
heading: _("Configuration Errors");
body: _("One or more configuration errors were found. Please review the errors below, and either reload your configuration or ignore these errors.");
@ -21,6 +21,7 @@ Adw.AlertDialog config_errors_dialog {
bottom-margin: 8;
left-margin: 8;
right-margin: 8;
buffer: bind (template.config as <$GhosttyConfig>).diagnostics-buffer;
}
};
}