Added a hopeful quit blocking dialog.

This commit is contained in:
Greyson Parrelli committed 2026-08-25 16:24:00 -04:00
1 parent 3721ec38ba
commit 198258c181
4 files changed
+292 -2

No files matched your search

+51
View File
@@ -128,6 +128,15 @@ arena: std.heap.ArenaAllocator,
theme: Theme = .system,
/// Whether closing the window asks first.
///
/// On by default, which is the opposite of what a terminal usually does — and
/// deliberately so. A window here is not one shell but a whole arrangement of
/// them, and under a tiling compositor the binding that closes it sits a
/// modifier away from the ones that move focus. There is nothing else between
/// a mistyped chord and every shell in every tab exiting.
confirm_quit: bool = true,
/// Changes to the palette. Empty means every colour is at its default, which is
/// the state anyone who never opens the colour editor stays in — and it is the
/// reason the defaults can be retuned in a later version and still reach them.
@@ -246,6 +255,10 @@ fn parse(self: *Settings, text: []const u8) ParseError!void {
}
}
if (root.get("confirm_quit")) |value| {
if (value == .bool) self.confirm_quit = value.bool;
}
if (root.get("colors")) |value| self.parseColors(value);
if (root.get("tint")) |value| self.parseTint(value);
@@ -595,6 +608,8 @@ fn serialize(self: *Settings) SaveError![]u8 {
try json.write(format_version);
try json.objectField("theme");
try json.write(@tagName(self.theme));
try json.objectField("confirm_quit");
try json.write(self.confirm_quit);
// Only the colours that have been changed, and only the schemes that have
// any. The object itself is written even when empty, for the same reason
@@ -731,6 +746,42 @@ fn forTesting() Settings {
return .{ .arena = .init(std.testing.allocator) };
}
test "confirming a quit is on unless the file turns it off" {
var settings = forTesting();
defer settings.arena.deinit();
// Nothing said about it: on. That is what anyone who never opens the
// settings page gets, and it is the half of this that has to be right.
try std.testing.expect(settings.confirm_quit);
try settings.parse(
\\{"theme": "dark", "confirm_quit": false}
);
try std.testing.expect(!settings.confirm_quit);
// And it goes back out. Saving rewrites the whole file, so a flag that
// didn't come out of `serialize` would be handed back by the next theme
// change — with a confirmation dialog someone had switched off.
const text = try settings.serialize();
defer std.testing.allocator.free(text);
try std.testing.expect(std.mem.indexOf(u8, text, "\"confirm_quit\": false") != null);
}
test "a confirm_quit we can't read leaves the confirmation up" {
var settings = forTesting();
defer settings.arena.deinit();
try settings.parse(
\\{"confirm_quit": "no", "theme": "dark"}
);
// Failing safe is the only direction worth failing in here, and the rest of
// the file still lands.
try std.testing.expect(settings.confirm_quit);
try std.testing.expectEqual(Theme.dark, settings.theme);
}
test "palette overrides are read per scheme" {
var settings = forTesting();
defer settings.arena.deinit();