mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-23 12:16:11 +03:00
apprt/gtk-ng: abstract our alert vs msg dialog into a superclass (#7995)
This introduces a new `GhosttyDialog` class that either inherits from `adw.MessageDialog` or `adw.AlertDialog`, depending on the version of libadwaita we compile against. This is the same logic we used previously. This lets us have a single libadw 1.2 blueprint file for all dialogs and we just do the right thing at compile time!
This commit is contained in:
@ -32,7 +32,6 @@ pub const icon_sizes: []const comptime_int = &.{ 16, 32, 128, 256, 512, 1024 };
|
||||
pub const blueprints: []const Blueprint = &.{
|
||||
.{ .major = 1, .minor = 2, .name = "config-errors-dialog" },
|
||||
.{ .major = 1, .minor = 2, .name = "surface" },
|
||||
.{ .major = 1, .minor = 5, .name = "config-errors-dialog" },
|
||||
.{ .major = 1, .minor = 5, .name = "window" },
|
||||
};
|
||||
|
||||
|
@ -7,18 +7,14 @@ 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 Dialog = @import("dialog.zig").Dialog;
|
||||
|
||||
const log = std.log.scoped(.gtk_ghostty_config_errors_dialog);
|
||||
|
||||
pub const ConfigErrorsDialog = extern struct {
|
||||
const Self = @This();
|
||||
parent_instance: Parent,
|
||||
|
||||
pub const Parent = if (adw_version.supportsDialogs())
|
||||
adw.AlertDialog
|
||||
else
|
||||
adw.MessageDialog;
|
||||
|
||||
pub const Parent = Dialog;
|
||||
pub const getGObjectType = gobject.ext.defineClass(Self, .{
|
||||
.name = "GhosttyConfigErrorsDialog",
|
||||
.instanceInit = &init,
|
||||
@ -76,19 +72,11 @@ pub const ConfigErrorsDialog = extern struct {
|
||||
}
|
||||
|
||||
pub fn present(self: *Self, parent: ?*gtk.Widget) void {
|
||||
switch (Parent) {
|
||||
adw.AlertDialog => self.as(adw.Dialog).present(parent),
|
||||
adw.MessageDialog => self.as(gtk.Window).present(),
|
||||
else => comptime unreachable,
|
||||
}
|
||||
self.as(Dialog).present(parent);
|
||||
}
|
||||
|
||||
pub fn close(self: *Self) void {
|
||||
switch (Parent) {
|
||||
adw.AlertDialog => self.as(adw.Dialog).forceClose(),
|
||||
adw.MessageDialog => self.as(gtk.Window).close(),
|
||||
else => comptime unreachable,
|
||||
}
|
||||
self.as(Dialog).close();
|
||||
}
|
||||
|
||||
fn response(
|
||||
@ -147,23 +135,14 @@ pub const ConfigErrorsDialog = extern struct {
|
||||
pub const Instance = Self;
|
||||
|
||||
fn init(class: *Class) callconv(.C) void {
|
||||
gobject.ext.ensureType(Dialog);
|
||||
gtk.Widget.Class.setTemplateFromResource(
|
||||
class.as(gtk.Widget.Class),
|
||||
switch (Parent) {
|
||||
adw.AlertDialog => comptime gresource.blueprint(.{
|
||||
.major = 1,
|
||||
.minor = 5,
|
||||
.name = "config-errors-dialog",
|
||||
}),
|
||||
|
||||
adw.MessageDialog => comptime gresource.blueprint(.{
|
||||
.major = 1,
|
||||
.minor = 2,
|
||||
.name = "config-errors-dialog",
|
||||
}),
|
||||
|
||||
else => comptime unreachable,
|
||||
},
|
||||
comptime gresource.blueprint(.{
|
||||
.major = 1,
|
||||
.minor = 2,
|
||||
.name = "config-errors-dialog",
|
||||
}),
|
||||
);
|
||||
|
||||
// Properties
|
||||
@ -176,7 +155,7 @@ pub const ConfigErrorsDialog = extern struct {
|
||||
|
||||
// Virtual methods
|
||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||
Parent.virtual_methods.response.implement(class, &response);
|
||||
Dialog.virtual_methods.response.implement(class, &response);
|
||||
}
|
||||
|
||||
pub fn as(class: *Class, comptime T: type) *T {
|
||||
|
93
src/apprt/gtk-ng/class/dialog.zig
Normal file
93
src/apprt/gtk-ng/class/dialog.zig
Normal file
@ -0,0 +1,93 @@
|
||||
const std = @import("std");
|
||||
const adw = @import("adw");
|
||||
const gobject = @import("gobject");
|
||||
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_dialog);
|
||||
|
||||
/// Dialog is a simple abstraction over the `adw.AlertDialog` and
|
||||
/// `adw.MessageDialog` widgets, chosen at comptime based on the linked
|
||||
/// Adwaita version.
|
||||
///
|
||||
/// Once we drop support for Adwaita <= 1.2, this can be fully removed
|
||||
/// and we can use `adw.AlertDialog` directly.
|
||||
pub const Dialog = extern struct {
|
||||
const Self = @This();
|
||||
parent_instance: Parent,
|
||||
|
||||
pub const Parent = if (adw_version.supportsDialogs())
|
||||
adw.AlertDialog
|
||||
else
|
||||
adw.MessageDialog;
|
||||
|
||||
pub const getGObjectType = gobject.ext.defineClass(Self, .{
|
||||
.name = "GhosttyDialog",
|
||||
.classInit = &Class.init,
|
||||
.parent_class = &Class.parent,
|
||||
});
|
||||
|
||||
pub const virtual_methods = struct {
|
||||
/// Forwarded from parent so subclasses can reference this
|
||||
/// directly. This will make it easier to remove Dialog in the future.
|
||||
pub const response = Parent.virtual_methods.response;
|
||||
};
|
||||
|
||||
pub fn present(self: *Self, parent: ?*gtk.Widget) void {
|
||||
switch (Parent) {
|
||||
adw.AlertDialog => self.as(adw.Dialog).present(parent),
|
||||
|
||||
adw.MessageDialog => {
|
||||
// Reset the previous parent window
|
||||
self.as(gtk.Window).setTransientFor(null);
|
||||
|
||||
// We need to get the window for the parent in order
|
||||
// to set the transient-for property on the MessageDialog.
|
||||
if (parent) |widget| parent: {
|
||||
const root = gtk.Widget.getRoot(widget) orelse break :parent;
|
||||
const window = gobject.ext.cast(
|
||||
gtk.Window,
|
||||
root,
|
||||
) orelse break :parent;
|
||||
self.as(gtk.Window).setTransientFor(window);
|
||||
}
|
||||
|
||||
self.as(gtk.Window).present();
|
||||
},
|
||||
|
||||
else => comptime unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(self: *Self) void {
|
||||
switch (Parent) {
|
||||
adw.AlertDialog => self.as(adw.Dialog).forceClose(),
|
||||
adw.MessageDialog => self.as(gtk.Window).close(),
|
||||
else => comptime unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
const C = Common(Self, null);
|
||||
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,
|
||||
var parent: *Parent.Class = undefined;
|
||||
pub const Instance = Self;
|
||||
|
||||
fn init(class: *Class) callconv(.C) void {
|
||||
_ = class;
|
||||
}
|
||||
|
||||
pub fn as(class: *Class, comptime T: type) *T {
|
||||
return gobject.ext.as(T, class);
|
||||
}
|
||||
};
|
||||
};
|
@ -1,7 +1,8 @@
|
||||
using Gtk 4.0;
|
||||
// This is unused but if we remove it we get a blueprint-compiler error.
|
||||
using Adw 1;
|
||||
|
||||
template $GhosttyConfigErrorsDialog: Adw.MessageDialog {
|
||||
template $GhosttyConfigErrorsDialog: $GhosttyDialog {
|
||||
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.");
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
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.");
|
||||
|
||||
responses [
|
||||
ignore: _("Ignore"),
|
||||
reload: _("Reload Configuration") suggested,
|
||||
]
|
||||
|
||||
extra-child: ScrolledWindow {
|
||||
min-content-width: 500;
|
||||
min-content-height: 100;
|
||||
|
||||
TextView {
|
||||
editable: false;
|
||||
cursor-visible: false;
|
||||
top-margin: 8;
|
||||
bottom-margin: 8;
|
||||
left-margin: 8;
|
||||
right-margin: 8;
|
||||
buffer: bind (template.config as <$GhosttyConfig>).diagnostics-buffer;
|
||||
}
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user