mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-24 04:36:10 +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 = &.{
|
pub const blueprints: []const Blueprint = &.{
|
||||||
.{ .major = 1, .minor = 2, .name = "config-errors-dialog" },
|
.{ .major = 1, .minor = 2, .name = "config-errors-dialog" },
|
||||||
.{ .major = 1, .minor = 2, .name = "surface" },
|
.{ .major = 1, .minor = 2, .name = "surface" },
|
||||||
.{ .major = 1, .minor = 5, .name = "config-errors-dialog" },
|
|
||||||
.{ .major = 1, .minor = 5, .name = "window" },
|
.{ .major = 1, .minor = 5, .name = "window" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,18 +7,14 @@ const gresource = @import("../build/gresource.zig");
|
|||||||
const adw_version = @import("../adw_version.zig");
|
const adw_version = @import("../adw_version.zig");
|
||||||
const Common = @import("../class.zig").Common;
|
const Common = @import("../class.zig").Common;
|
||||||
const Config = @import("config.zig").Config;
|
const Config = @import("config.zig").Config;
|
||||||
|
const Dialog = @import("dialog.zig").Dialog;
|
||||||
|
|
||||||
const log = std.log.scoped(.gtk_ghostty_config_errors_dialog);
|
const log = std.log.scoped(.gtk_ghostty_config_errors_dialog);
|
||||||
|
|
||||||
pub const ConfigErrorsDialog = extern struct {
|
pub const ConfigErrorsDialog = extern struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
parent_instance: Parent,
|
parent_instance: Parent,
|
||||||
|
pub const Parent = Dialog;
|
||||||
pub const Parent = if (adw_version.supportsDialogs())
|
|
||||||
adw.AlertDialog
|
|
||||||
else
|
|
||||||
adw.MessageDialog;
|
|
||||||
|
|
||||||
pub const getGObjectType = gobject.ext.defineClass(Self, .{
|
pub const getGObjectType = gobject.ext.defineClass(Self, .{
|
||||||
.name = "GhosttyConfigErrorsDialog",
|
.name = "GhosttyConfigErrorsDialog",
|
||||||
.instanceInit = &init,
|
.instanceInit = &init,
|
||||||
@ -76,19 +72,11 @@ pub const ConfigErrorsDialog = extern struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn present(self: *Self, parent: ?*gtk.Widget) void {
|
pub fn present(self: *Self, parent: ?*gtk.Widget) void {
|
||||||
switch (Parent) {
|
self.as(Dialog).present(parent);
|
||||||
adw.AlertDialog => self.as(adw.Dialog).present(parent),
|
|
||||||
adw.MessageDialog => self.as(gtk.Window).present(),
|
|
||||||
else => comptime unreachable,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(self: *Self) void {
|
pub fn close(self: *Self) void {
|
||||||
switch (Parent) {
|
self.as(Dialog).close();
|
||||||
adw.AlertDialog => self.as(adw.Dialog).forceClose(),
|
|
||||||
adw.MessageDialog => self.as(gtk.Window).close(),
|
|
||||||
else => comptime unreachable,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn response(
|
fn response(
|
||||||
@ -147,23 +135,14 @@ pub const ConfigErrorsDialog = extern struct {
|
|||||||
pub const Instance = Self;
|
pub const Instance = Self;
|
||||||
|
|
||||||
fn init(class: *Class) callconv(.C) void {
|
fn init(class: *Class) callconv(.C) void {
|
||||||
|
gobject.ext.ensureType(Dialog);
|
||||||
gtk.Widget.Class.setTemplateFromResource(
|
gtk.Widget.Class.setTemplateFromResource(
|
||||||
class.as(gtk.Widget.Class),
|
class.as(gtk.Widget.Class),
|
||||||
switch (Parent) {
|
comptime gresource.blueprint(.{
|
||||||
adw.AlertDialog => comptime gresource.blueprint(.{
|
.major = 1,
|
||||||
.major = 1,
|
.minor = 2,
|
||||||
.minor = 5,
|
.name = "config-errors-dialog",
|
||||||
.name = "config-errors-dialog",
|
}),
|
||||||
}),
|
|
||||||
|
|
||||||
adw.MessageDialog => comptime gresource.blueprint(.{
|
|
||||||
.major = 1,
|
|
||||||
.minor = 2,
|
|
||||||
.name = "config-errors-dialog",
|
|
||||||
}),
|
|
||||||
|
|
||||||
else => comptime unreachable,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
@ -176,7 +155,7 @@ pub const ConfigErrorsDialog = extern struct {
|
|||||||
|
|
||||||
// Virtual methods
|
// Virtual methods
|
||||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
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 {
|
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;
|
using Gtk 4.0;
|
||||||
|
// This is unused but if we remove it we get a blueprint-compiler error.
|
||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $GhosttyConfigErrorsDialog: Adw.MessageDialog {
|
template $GhosttyConfigErrorsDialog: $GhosttyDialog {
|
||||||
heading: _("Configuration Errors");
|
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.");
|
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