Add notifications.

This commit is contained in:
Greyson Parrelli committed 2026-08-26 23:24:14 -04:00
1 parent afad6a2636
commit 7414c89fe6
33 files changed
+1026 -381

No files matched your search

+77 -3
View File
@@ -109,6 +109,9 @@ colors: *PaletteEditor,
/// one place and read in another; nothing re-checks it.
confirm_quit: *gtk.Switch,
/// The notifications toggle, held for the same reason.
notifications: *gtk.Switch,
/// Container the startup rows live in, so rows can be added and removed after
/// the page is already on screen.
startup_box: *gtk.Box,
@@ -145,6 +148,7 @@ pub fn present(alloc: std.mem.Allocator, parent: *gtk.Window, opts: Options) !vo
.theme_buttons = undefined,
.colors = undefined,
.confirm_quit = gtk.Switch.new(),
.notifications = gtk.Switch.new(),
.startup_box = gtk.Box.new(.vertical, 6),
.error_label = gtk.Label.new(null),
};
@@ -165,6 +169,7 @@ pub fn present(alloc: std.mem.Allocator, parent: *gtk.Window, opts: Options) !vo
content.as(gtk.Widget).addCssClass("playpen-dialog-content");
content.append(self.buildAppearance());
content.append(self.buildNotifications());
content.append(self.buildQuitting());
content.append(self.buildStartup());
@@ -219,7 +224,7 @@ pub fn close() void {
// -------------------------------------------------------------------------
// Appearance
/// The Appearance section: the colour scheme, and the palette it resolves to.
/// The Appearance section: the color scheme, and the palette it resolves to.
fn buildAppearance(self: *SettingsDialog) *gtk.Widget {
const group = gtk.Box.new(.vertical, 10);
group.as(gtk.Widget).addCssClass("playpen-settings-group");
@@ -266,7 +271,7 @@ fn onEditorReport(ctx: ?*anyopaque, message: ?[:0]const u8) void {
/// The scheme picker: one toggle per choice, drawn as a single linked control.
///
/// They are not put in a GTK radio group. Grouped toggles can't be unchecked by
/// clicking the active one, which is the behaviour we want, but the group also
/// clicking the active one, which is the behavior we want, but the group also
/// emits `toggled` twice per change — once off, once on — and the off half
/// would apply whichever scheme happened to be next in the list. Handling the
/// exclusivity here instead makes each click exactly one decision.
@@ -326,13 +331,82 @@ fn onThemeToggled(button: *gtk.ToggleButton, self: *SettingsDialog) callconv(.c)
appearance.setTheme(values[index]);
// The palette editor edits whichever scheme is on screen, and that is what
// just changed: its swatches now stand for the other palette's colours.
// just changed: its swatches now stand for the other palette's colors.
self.colors.refresh();
}
// -------------------------------------------------------------------------
// Quitting
/// The Notifications section: whether a finish reaches the desktop.
///
/// The whole-app half of the mute. The per-tab half lives in each row's menu,
/// because it is a thing you decide about one tab while looking at it, and
/// walking to a settings window to say "not this one, for the next hour" would
/// mean nobody ever did.
fn buildNotifications(self: *SettingsDialog) *gtk.Widget {
const group = gtk.Box.new(.vertical, 10);
group.as(gtk.Widget).addCssClass("playpen-settings-group");
const title = gtk.Label.new("Notifications");
title.setXalign(0);
title.as(gtk.Widget).addCssClass("playpen-settings-title");
group.append(title.as(gtk.Widget));
const row = gtk.Box.new(.horizontal, 12);
const labels = gtk.Box.new(.vertical, 2);
labels.as(gtk.Widget).setHexpand(1);
labels.as(gtk.Widget).setValign(.center);
const name = gtk.Label.new("Tell me when a pane finishes");
name.setXalign(0);
name.as(gtk.Widget).addCssClass("playpen-dialog-label");
labels.append(name.as(gtk.Widget));
const hint_label = gtk.Label.new(
"A desktop notification when something running in a tab goes quiet, " ++
"for the tabs you aren't looking at. Off, the sidebar dots are the " ++
"only word you get. One tab at a time can be muted from its row menu.",
);
hint_label.setXalign(0);
hint_label.setWrap(1);
hint_label.as(gtk.Widget).addCssClass("playpen-dialog-sublabel");
labels.append(hint_label.as(gtk.Widget));
row.append(labels.as(gtk.Widget));
self.notifications.setActive(@intFromBool(Settings.get().notifications));
self.notifications.as(gtk.Widget).setValign(.center);
_ = gobject.Object.signals.notify.connect(
self.notifications,
*SettingsDialog,
&onNotificationsChanged,
self,
.{ .detail = "active" },
);
row.append(self.notifications.as(gtk.Widget));
group.append(row.as(gtk.Widget));
return group.as(gtk.Widget);
}
/// Straight to the file, for the same reason `onConfirmQuitChanged` is.
fn onNotificationsChanged(
toggle: *gtk.Switch,
_: *gobject.ParamSpec,
self: *SettingsDialog,
) callconv(.c) void {
Settings.get().notifications = toggle.getActive() != 0;
Settings.get().save() catch {
self.showError("Could not write the settings file.");
return;
};
self.showError(null);
}
/// The Quitting section: whether closing the window asks first.
///
/// Above the startup list rather than below it. That list grows with the number