mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-19 18:26:13 +03:00
apprt/gtk-ng: hook up all the refs to show the dialog
This commit is contained in:
@ -10,6 +10,8 @@ pub const Application = @import("gtk-ng/class/application.zig").Application;
|
|||||||
pub const Window = @import("gtk-ng/class/window.zig").Window;
|
pub const Window = @import("gtk-ng/class/window.zig").Window;
|
||||||
pub const Config = @import("gtk-ng/class/config.zig").Config;
|
pub const Config = @import("gtk-ng/class/config.zig").Config;
|
||||||
|
|
||||||
|
pub const WeakRef = @import("gtk-ng/weak_ref.zig").WeakRef;
|
||||||
|
|
||||||
test {
|
test {
|
||||||
@import("std").testing.refAllDecls(@This());
|
@import("std").testing.refAllDecls(@This());
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ const CoreConfig = configpkg.Config;
|
|||||||
|
|
||||||
const adw_version = @import("../adw_version.zig");
|
const adw_version = @import("../adw_version.zig");
|
||||||
const gtk_version = @import("../gtk_version.zig");
|
const gtk_version = @import("../gtk_version.zig");
|
||||||
|
const WeakRef = @import("../weak_ref.zig").WeakRef;
|
||||||
const Config = @import("config.zig").Config;
|
const Config = @import("config.zig").Config;
|
||||||
const Window = @import("window.zig").Window;
|
const Window = @import("window.zig").Window;
|
||||||
const ConfigErrorsDialog = @import("config_errors_dialog.zig").ConfigErrorsDialog;
|
const ConfigErrorsDialog = @import("config_errors_dialog.zig").ConfigErrorsDialog;
|
||||||
@ -72,6 +73,9 @@ pub const Application = extern struct {
|
|||||||
/// only be set by the main loop thread.
|
/// only be set by the main loop thread.
|
||||||
running: bool = false,
|
running: bool = false,
|
||||||
|
|
||||||
|
/// If non-null, we're currently showing a config errors dialog.
|
||||||
|
config_errors_dialog: WeakRef(ConfigErrorsDialog) = .{},
|
||||||
|
|
||||||
var offset: c_int = 0;
|
var offset: c_int = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -107,14 +111,10 @@ pub const Application = extern struct {
|
|||||||
// us to startup.
|
// us to startup.
|
||||||
var default: CoreConfig = try .default(alloc);
|
var default: CoreConfig = try .default(alloc);
|
||||||
errdefer default.deinit();
|
errdefer default.deinit();
|
||||||
const config_arena = default._arena.?.allocator();
|
try default.addDiagnosticFmt(
|
||||||
try default._diagnostics.append(config_arena, .{
|
|
||||||
.message = try std.fmt.allocPrintZ(
|
|
||||||
config_arena,
|
|
||||||
"error loading user configuration: {}",
|
"error loading user configuration: {}",
|
||||||
.{err},
|
.{err},
|
||||||
),
|
);
|
||||||
});
|
|
||||||
|
|
||||||
break :err default;
|
break :err default;
|
||||||
};
|
};
|
||||||
@ -190,8 +190,10 @@ pub const Application = extern struct {
|
|||||||
// callback that GObject calls, but we can't pass this data through
|
// callback that GObject calls, but we can't pass this data through
|
||||||
// to there (and we don't need it there directly) so this is here.
|
// to there (and we don't need it there directly) so this is here.
|
||||||
const priv = self.private();
|
const priv = self.private();
|
||||||
priv.core_app = core_app;
|
priv.* = .{
|
||||||
priv.config = config_obj;
|
.core_app = core_app,
|
||||||
|
.config = config_obj,
|
||||||
|
};
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -303,22 +305,6 @@ pub const Application = extern struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn startup(self: *Self) callconv(.C) void {
|
fn startup(self: *Self) callconv(.C) void {
|
||||||
log.debug("startup", .{});
|
log.debug("startup", .{});
|
||||||
|
|
||||||
@ -334,19 +320,26 @@ pub const Application = extern struct {
|
|||||||
self.startupStyleManager();
|
self.startupStyleManager();
|
||||||
|
|
||||||
// Setup our cgroup for the application.
|
// Setup our cgroup for the application.
|
||||||
self.startupCgroup() catch {
|
self.startupCgroup() catch |err| {
|
||||||
log.warn("TODO", .{});
|
log.warn("cgroup initialization failed err={}", .{err});
|
||||||
|
|
||||||
|
// Add it to our config diagnostics so it shows up in a GUI dialog.
|
||||||
|
// Admittedly this has two issues: (1) we shuldn't be using the
|
||||||
|
// config errors dialog for this long term and (2) using a mut
|
||||||
|
// ref to the config wouldn't propagate changes to UI properly,
|
||||||
|
// but we're in startup mode so its okay.
|
||||||
|
const config = self.private().config.getMut();
|
||||||
|
config.addDiagnosticFmt(
|
||||||
|
"cgroup initialization failed: {}",
|
||||||
|
.{err},
|
||||||
|
) catch {};
|
||||||
};
|
};
|
||||||
|
|
||||||
// If we have any config diagnostics from loading, then we
|
// If we have any config diagnostics from loading, then we
|
||||||
// show the diagnostics dialog. We show this one as a general
|
// show the diagnostics dialog. We show this one as a general
|
||||||
// modal (not to any specific window) because we don't even
|
// modal (not to any specific window) because we don't even
|
||||||
// know if the window will load.
|
// know if the window will load.
|
||||||
const priv = self.private();
|
self.showConfigErrorsDialog();
|
||||||
if (priv.config.hasDiagnostics()) {
|
|
||||||
const dialog: *ConfigErrorsDialog = .new(priv.config);
|
|
||||||
dialog.present(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure libxev to use a specific backend.
|
/// Configure libxev to use a specific backend.
|
||||||
@ -490,6 +483,19 @@ pub const Application = extern struct {
|
|||||||
// gtk.Window.present(win.as(gtk.Window));
|
// gtk.Window.present(win.as(gtk.Window));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dispose(self: *Self) callconv(.C) void {
|
||||||
|
const priv = self.private();
|
||||||
|
if (priv.config_errors_dialog.get()) |diag| {
|
||||||
|
diag.close();
|
||||||
|
diag.unref(); // strong ref from get()
|
||||||
|
}
|
||||||
|
|
||||||
|
gobject.Object.virtual_methods.dispose.call(
|
||||||
|
Class.parent,
|
||||||
|
self.as(Parent),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn finalize(self: *Self) callconv(.C) void {
|
fn finalize(self: *Self) callconv(.C) void {
|
||||||
self.deinit();
|
self.deinit();
|
||||||
gobject.Object.virtual_methods.finalize.call(
|
gobject.Object.virtual_methods.finalize.call(
|
||||||
@ -513,10 +519,62 @@ pub const Application = extern struct {
|
|||||||
log.debug("style manager changed scheme={}", .{color_scheme});
|
log.debug("style manager changed scheme={}", .{color_scheme});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Show the config errors dialog if the config on our application
|
||||||
|
/// has diagnostics.
|
||||||
|
fn showConfigErrorsDialog(self: *Self) void {
|
||||||
|
const priv = self.private();
|
||||||
|
|
||||||
|
// If we already have a dialog, just update the config.
|
||||||
|
if (priv.config_errors_dialog.get()) |diag| {
|
||||||
|
defer diag.unref(); // get gets a strong ref
|
||||||
|
|
||||||
|
var value = gobject.ext.Value.newFrom(priv.config);
|
||||||
|
defer value.unset();
|
||||||
|
gobject.Object.setProperty(
|
||||||
|
diag.as(gobject.Object),
|
||||||
|
"config",
|
||||||
|
&value,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!priv.config.hasDiagnostics()) {
|
||||||
|
diag.close();
|
||||||
|
} else {
|
||||||
|
diag.present(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No diagnostics, do nothing.
|
||||||
|
if (!priv.config.hasDiagnostics()) return;
|
||||||
|
|
||||||
|
// No dialog yet, initialize a new one. There's no need to unref
|
||||||
|
// here because the widget that it becomes a part of takes ownership.
|
||||||
|
const dialog: *ConfigErrorsDialog = .new(priv.config);
|
||||||
|
dialog.present(null);
|
||||||
|
priv.config_errors_dialog.set(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
fn allocator(self: *Self) std.mem.Allocator {
|
fn allocator(self: *Self) std.mem.Allocator {
|
||||||
return self.private().core_app.alloc;
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub const Class = extern struct {
|
pub const Class = extern struct {
|
||||||
parent_class: Parent.Class,
|
parent_class: Parent.Class,
|
||||||
var parent: *Parent.Class = undefined;
|
var parent: *Parent.Class = undefined;
|
||||||
@ -542,6 +600,7 @@ pub const Application = extern struct {
|
|||||||
// Virtual methods
|
// Virtual methods
|
||||||
gio.Application.virtual_methods.activate.implement(class, &activate);
|
gio.Application.virtual_methods.activate.implement(class, &activate);
|
||||||
gio.Application.virtual_methods.startup.implement(class, &startup);
|
gio.Application.virtual_methods.startup.implement(class, &startup);
|
||||||
|
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||||
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
|
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,7 @@ const gresource = @import("../build/gresource.zig");
|
|||||||
const adw_version = @import("../adw_version.zig");
|
const adw_version = @import("../adw_version.zig");
|
||||||
const Config = @import("config.zig").Config;
|
const Config = @import("config.zig").Config;
|
||||||
|
|
||||||
const log = std.log.scoped(.gtk_ghostty_window);
|
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();
|
||||||
@ -78,6 +78,13 @@ pub const ConfigErrorsDialog = extern struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn close(self: *Self) void {
|
||||||
|
switch (Parent) {
|
||||||
|
adw.AlertDialog => self.as(adw.Dialog).forceClose(),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn response(
|
fn response(
|
||||||
self: *Self,
|
self: *Self,
|
||||||
response_id: [*:0]const u8,
|
response_id: [*:0]const u8,
|
||||||
@ -92,6 +99,7 @@ pub const ConfigErrorsDialog = extern struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dispose(self: *Self) callconv(.C) void {
|
fn dispose(self: *Self) callconv(.C) void {
|
||||||
|
log.warn("DISPOSE", .{});
|
||||||
gtk.Widget.disposeTemplate(self.as(gtk.Widget), getGObjectType());
|
gtk.Widget.disposeTemplate(self.as(gtk.Widget), getGObjectType());
|
||||||
|
|
||||||
const priv = self.private();
|
const priv = self.private();
|
||||||
@ -121,6 +129,14 @@ pub const ConfigErrorsDialog = extern struct {
|
|||||||
return gobject.ext.as(T, win);
|
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 {
|
fn private(self: *Self) *Private {
|
||||||
return gobject.ext.impl_helpers.getPrivate(
|
return gobject.ext.impl_helpers.getPrivate(
|
||||||
self,
|
self,
|
||||||
|
43
src/apprt/gtk-ng/weak_ref.zig
Normal file
43
src/apprt/gtk-ng/weak_ref.zig
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const gtk = @import("gtk");
|
||||||
|
const gobject = @import("gobject");
|
||||||
|
|
||||||
|
/// A lightweight wrapper around gobject.WeakRef to make it type-safe
|
||||||
|
/// to hold a single type of value.
|
||||||
|
pub fn WeakRef(comptime T: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
ref: gobject.WeakRef = std.mem.zeroes(gobject.WeakRef),
|
||||||
|
|
||||||
|
/// Set the weak reference to the given object. This will not
|
||||||
|
/// increase the reference count of the object.
|
||||||
|
pub fn set(self: *Self, v: *T) void {
|
||||||
|
self.ref.set(v.as(gobject.Object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a strong reference to the object, or null if the object
|
||||||
|
/// has been finalized. This increases the reference count by one.
|
||||||
|
pub fn get(self: *Self) ?*T {
|
||||||
|
// The GIR of g_weak_ref_get has a bug where the optional
|
||||||
|
// is not encoded. Or, it may be a bug in zig-gobject.
|
||||||
|
const obj_: ?*gobject.Object = @ptrCast(self.ref.get());
|
||||||
|
const obj = obj_ orelse return null;
|
||||||
|
|
||||||
|
// We can't use `as` because `as` guarantees conversion and
|
||||||
|
// that can't be statically guaranteed.
|
||||||
|
return gobject.ext.cast(T, obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test WeakRef {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
var ref: WeakRef(gtk.TextBuffer) = .{};
|
||||||
|
const obj: *gtk.TextBuffer = .new(null);
|
||||||
|
ref.set(obj);
|
||||||
|
ref.get().?.unref(); // The "?" asserts non-null
|
||||||
|
obj.unref();
|
||||||
|
try testing.expect(ref.get() == null);
|
||||||
|
}
|
@ -4068,6 +4068,23 @@ fn compatBoldIsBright(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a diagnostic message to the config with the given string.
|
||||||
|
/// This is always added with a location of "none".
|
||||||
|
pub fn addDiagnosticFmt(
|
||||||
|
self: *Config,
|
||||||
|
comptime fmt: []const u8,
|
||||||
|
args: anytype,
|
||||||
|
) Allocator.Error!void {
|
||||||
|
const alloc = self._arena.?.allocator();
|
||||||
|
try self._diagnostics.append(alloc, .{
|
||||||
|
.message = try std.fmt.allocPrintZ(
|
||||||
|
alloc,
|
||||||
|
fmt,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a shallow copy of this config. This will share all the memory
|
/// Create a shallow copy of this config. This will share all the memory
|
||||||
/// allocated with the previous config but will have a new arena for
|
/// allocated with the previous config but will have a new arena for
|
||||||
/// any changes or new allocations. The config should have `deinit`
|
/// any changes or new allocations. The config should have `deinit`
|
||||||
|
Reference in New Issue
Block a user