Add notifications.
This commit is contained in:
+54
-54
@@ -1,30 +1,30 @@
|
||||
//! The colour editor: one swatch per name in the palette, for the scheme that
|
||||
//! The color editor: one swatch per name in the palette, for the scheme that
|
||||
//! is on screen.
|
||||
//!
|
||||
//! It edits **the palette you can see**, and that is the whole design. Every
|
||||
//! colour here paints something behind this window, so a change is its own
|
||||
//! preview — pick a sidebar colour and the sidebar is that colour before the
|
||||
//! color here paints something behind this window, so a change is its own
|
||||
//! preview — pick a sidebar color and the sidebar is that color before the
|
||||
//! dialog has closed. Editing the *other* scheme's palette from here would mean
|
||||
//! a page full of swatches whose effect you have to take on faith, so the
|
||||
//! answer to "I want to change the light one" is the theme picker above: switch
|
||||
//! to light, and this section switches with it.
|
||||
//!
|
||||
//! Overrides are stored per colour rather than as a whole palette, which is what
|
||||
//! Overrides are stored per color rather than as a whole palette, which is what
|
||||
//! lets a row say whether it has been changed and put itself back, and what lets
|
||||
//! the defaults be retuned in a later version for everyone who never touched
|
||||
//! them. The terminal's colours go one step further: left alone they *follow*
|
||||
//! the surface and text colours above them (see `palette.Key.inherits`), so
|
||||
//! recolouring the app recolours the terminal drawn on it without asking anyone
|
||||
//! to set the same colour twice — which is also why every row is re-read after
|
||||
//! them. The terminal's colors go one step further: left alone they *follow*
|
||||
//! the surface and text colors above them (see `palette.Key.inherits`), so
|
||||
//! recoloring the app recolors the terminal drawn on it without asking anyone
|
||||
//! to set the same color twice — which is also why every row is re-read after
|
||||
//! any change, not just the row that changed.
|
||||
//!
|
||||
//! The swatches start collapsed, and the two controls above them do not. That
|
||||
//! split is the point of the base colour: thirty-nine swatches is a fair way to
|
||||
//! split is the point of the base color: thirty-nine swatches is a fair way to
|
||||
//! *correct* a theme and a miserable way to *choose* one, so the path that is
|
||||
//! always on offer is "pick a colour, get a palette" (`tint.zig`), and the
|
||||
//! always on offer is "pick a color, get a palette" (`tint.zig`), and the
|
||||
//! wall of swatches is behind a disclosure for the times that isn't enough. The
|
||||
//! two compose — a swatch that has been set by hand outranks the generated
|
||||
//! colour underneath it — which is why picking a new base does not clear the
|
||||
//! color underneath it — which is why picking a new base does not clear the
|
||||
//! corrections someone made to the last one.
|
||||
|
||||
const std = @import("std");
|
||||
@@ -49,7 +49,7 @@ pub const Options = struct {
|
||||
ctx: ?*anyopaque = null,
|
||||
};
|
||||
|
||||
/// One colour: its swatch, and — for the ones listed as rows rather than packed
|
||||
/// One color: its swatch, and — for the ones listed as rows rather than packed
|
||||
/// into the ANSI grid — the hex it currently resolves to and the button that
|
||||
/// puts it back.
|
||||
const Row = struct {
|
||||
@@ -60,7 +60,7 @@ const Row = struct {
|
||||
revert: ?*gtk.Button = null,
|
||||
};
|
||||
|
||||
/// One section heading, kept only so its reset button can be greyed out when
|
||||
/// One section heading, kept only so its reset button can be grayed out when
|
||||
/// the section has nothing to reset.
|
||||
const GroupHeader = struct {
|
||||
editor: *PaletteEditor,
|
||||
@@ -81,14 +81,14 @@ body: *gtk.Box,
|
||||
/// Says which scheme is being edited, and is rewritten when that changes.
|
||||
scheme_hint: *gtk.Label,
|
||||
|
||||
/// The colour the rest of the palette is generated from, the hex it currently
|
||||
/// The color the rest of the palette is generated from, the hex it currently
|
||||
/// stands at, and the button that drops it. Assigned in `build`, since the
|
||||
/// swatch has to be handed its dialog at construction.
|
||||
base: *gtk.ColorDialogButton,
|
||||
base_hex: *gtk.Label,
|
||||
base_clear: *gtk.Button,
|
||||
|
||||
/// How far apart to spread the colours built from the base. Insensitive until
|
||||
/// How far apart to spread the colors built from the base. Insensitive until
|
||||
/// there is a base, since on its own it has nothing to spread.
|
||||
contrast: *gtk.Scale,
|
||||
|
||||
@@ -96,7 +96,7 @@ contrast: *gtk.Scale,
|
||||
reset: *gtk.Button,
|
||||
|
||||
/// Indexed by `@intFromEnum(key)`, so a row is found without searching. Inline
|
||||
/// in the struct rather than allocated: there is exactly one row per colour, the
|
||||
/// in the struct rather than allocated: there is exactly one row per color, the
|
||||
/// count is known at compile time, and the addresses have to be stable because
|
||||
/// every swatch's callback holds one.
|
||||
rows: [palette.count]Row,
|
||||
@@ -124,7 +124,7 @@ pub fn create(alloc: std.mem.Allocator, opts: Options) !*PaletteEditor {
|
||||
// The step is what the arrow keys move by; the range is the one
|
||||
// `palette.Tint.contrast` is defined over.
|
||||
.contrast = gtk.Scale.newWithRange(.horizontal, -1, 1, 0.05),
|
||||
.reset = gtk.Button.newWithLabel("Reset every colour"),
|
||||
.reset = gtk.Button.newWithLabel("Reset every color"),
|
||||
.rows = undefined,
|
||||
.groups = undefined,
|
||||
.on_report = opts.on_report,
|
||||
@@ -160,8 +160,8 @@ pub fn refresh(self: *PaletteEditor) void {
|
||||
fn build(self: *PaletteEditor) void {
|
||||
self.root.append(self.buildHeader());
|
||||
|
||||
// Above the base colour rather than inside the disclosure with the
|
||||
// swatches, because it is true of the base colour too: both palettes can be
|
||||
// Above the base color rather than inside the disclosure with the
|
||||
// swatches, because it is true of the base color too: both palettes can be
|
||||
// given one, and this is the only thing on the page that says which of them
|
||||
// the control below is about to change.
|
||||
self.scheme_hint.setXalign(0);
|
||||
@@ -176,8 +176,8 @@ fn build(self: *PaletteEditor) void {
|
||||
self.reset.as(gtk.Widget).addCssClass("flat");
|
||||
self.reset.as(gtk.Widget).setHalign(.start);
|
||||
self.reset.as(gtk.Widget).setTooltipText(
|
||||
"Put every colour in this scheme back to the one Playpen ships with, " ++
|
||||
"and drop the base colour above",
|
||||
"Put every color in this scheme back to the one Playpen ships with, " ++
|
||||
"and drop the base color above",
|
||||
);
|
||||
_ = gtk.Button.signals.clicked.connect(self.reset, *PaletteEditor, &onReset, self, .{});
|
||||
self.body.append(self.reset.as(gtk.Widget));
|
||||
@@ -201,7 +201,7 @@ fn buildHeader(self: *PaletteEditor) *gtk.Widget {
|
||||
name.as(gtk.Widget).addCssClass("playpen-dialog-label");
|
||||
labels.append(name.as(gtk.Widget));
|
||||
|
||||
const sub = gtk.Label.new("Every colour the window and the terminal are painted from.");
|
||||
const sub = gtk.Label.new("Every color the window and the terminal are painted from.");
|
||||
sub.setXalign(0);
|
||||
sub.setWrap(1);
|
||||
sub.as(gtk.Widget).addCssClass("playpen-dialog-sublabel");
|
||||
@@ -217,7 +217,7 @@ fn buildHeader(self: *PaletteEditor) *gtk.Widget {
|
||||
wrap.as(gtk.Widget).addCssClass("playpen-settings-choice");
|
||||
wrap.as(gtk.Widget).setValign(.center);
|
||||
|
||||
const toggle = gtk.ToggleButton.newWithLabel("Customise");
|
||||
const toggle = gtk.ToggleButton.newWithLabel("Customize");
|
||||
_ = gtk.ToggleButton.signals.toggled.connect(
|
||||
toggle,
|
||||
*PaletteEditor,
|
||||
@@ -232,7 +232,7 @@ fn buildHeader(self: *PaletteEditor) *gtk.Widget {
|
||||
return row.as(gtk.Widget);
|
||||
}
|
||||
|
||||
/// The two controls that are always on offer: the colour the palette is built
|
||||
/// The two controls that are always on offer: the color the palette is built
|
||||
/// out of, and how far apart to spread what gets built.
|
||||
///
|
||||
/// Outside the disclosure below on purpose. This is the answer for someone who
|
||||
@@ -244,16 +244,16 @@ fn buildBase(self: *PaletteEditor) *gtk.Widget {
|
||||
const group = gtk.Box.new(.vertical, 6);
|
||||
|
||||
// The same class the swatch sections below carry, which is where the
|
||||
// stylesheet hangs the "a colour button is a rectangle of the colour, not a
|
||||
// button holding one" rule. This row has a colour button in it and wants to
|
||||
// look like the ones under Customise.
|
||||
// stylesheet hangs the "a color button is a rectangle of the color, not a
|
||||
// button holding one" rule. This row has a color button in it and wants to
|
||||
// look like the ones under Customize.
|
||||
group.as(gtk.Widget).addCssClass("playpen-color-group");
|
||||
|
||||
// ---- the colour ----
|
||||
// ---- the color ----
|
||||
const row = gtk.Box.new(.horizontal, 8);
|
||||
row.as(gtk.Widget).addCssClass("playpen-color-row");
|
||||
|
||||
const label = gtk.Label.new("Base colour");
|
||||
const label = gtk.Label.new("Base color");
|
||||
label.setXalign(0);
|
||||
label.as(gtk.Widget).setHexpand(1);
|
||||
label.as(gtk.Widget).addCssClass("playpen-dialog-label");
|
||||
@@ -265,7 +265,7 @@ fn buildBase(self: *PaletteEditor) *gtk.Widget {
|
||||
const dialog = gtk.ColorDialog.new();
|
||||
dialog.setWithAlpha(0);
|
||||
dialog.setModal(1);
|
||||
dialog.setTitle("Base colour");
|
||||
dialog.setTitle("Base color");
|
||||
|
||||
self.base = gtk.ColorDialogButton.new(dialog);
|
||||
self.base.as(gtk.Widget).setValign(.center);
|
||||
@@ -280,7 +280,7 @@ fn buildBase(self: *PaletteEditor) *gtk.Widget {
|
||||
row.append(self.base.as(gtk.Widget));
|
||||
|
||||
self.base_clear.as(gtk.Widget).addCssClass("flat");
|
||||
self.base_clear.as(gtk.Widget).setTooltipText("Back to Playpen's own colours");
|
||||
self.base_clear.as(gtk.Widget).setTooltipText("Back to Playpen's own colors");
|
||||
_ = gtk.Button.signals.clicked.connect(
|
||||
self.base_clear,
|
||||
*PaletteEditor,
|
||||
@@ -293,15 +293,15 @@ fn buildBase(self: *PaletteEditor) *gtk.Widget {
|
||||
group.append(row.as(gtk.Widget));
|
||||
group.append(hint(
|
||||
"Pick one and the rest follows: a very dark version of it behind the window, " ++
|
||||
"lighter ones for the panes and the text on them, and the colour itself as the accent. " ++
|
||||
"Anything you set under Customise stays where you put it.",
|
||||
"lighter ones for the panes and the text on them, and the color itself as the accent. " ++
|
||||
"Anything you set under Customize stays where you put it.",
|
||||
));
|
||||
|
||||
// ---- how far apart ----
|
||||
const contrast_row = gtk.Box.new(.horizontal, 8);
|
||||
contrast_row.as(gtk.Widget).addCssClass("playpen-color-row");
|
||||
contrast_row.as(gtk.Widget).setTooltipText(
|
||||
"How far apart to spread the colours built from the base.\n" ++
|
||||
"How far apart to spread the colors built from the base.\n" ++
|
||||
"Starker pulls the backdrop darker and the text brighter; softer draws them together.",
|
||||
);
|
||||
|
||||
@@ -324,7 +324,7 @@ fn buildBase(self: *PaletteEditor) *gtk.Widget {
|
||||
self.contrast.as(gtk.Range).setIncrements(0.05, 0.25);
|
||||
self.contrast.as(gtk.Range).setRoundDigits(2);
|
||||
|
||||
// The unlabelled middle mark is what makes the shipped spacing findable
|
||||
// The unlabeled middle mark is what makes the shipped spacing findable
|
||||
// again after a drag: GTK snaps the handle to a mark it passes near.
|
||||
self.contrast.addMark(-1, .bottom, "Soft");
|
||||
self.contrast.addMark(0, .bottom, null);
|
||||
@@ -350,8 +350,8 @@ fn buildGroups(self: *PaletteEditor) void {
|
||||
var current: ?palette.Group = null;
|
||||
var section: *gtk.Box = undefined;
|
||||
|
||||
// The ANSI colours are a grid of bare swatches rather than sixteen labelled
|
||||
// rows: they are a palette people recognise by position — eight normal, then
|
||||
// The ANSI colors are a grid of bare swatches rather than sixteen labeled
|
||||
// rows: they are a palette people recognize by position — eight normal, then
|
||||
// eight bright — and a list of their names says less than the strip does.
|
||||
var ansi: *gtk.Grid = undefined;
|
||||
|
||||
@@ -396,7 +396,7 @@ fn buildGroupHeader(self: *PaletteEditor, group: palette.Group) *gtk.Widget {
|
||||
|
||||
const reset = gtk.Button.newFromIconName("edit-undo-symbolic");
|
||||
reset.as(gtk.Widget).addCssClass("flat");
|
||||
reset.as(gtk.Widget).setTooltipText("Reset the colours in this section");
|
||||
reset.as(gtk.Widget).setTooltipText("Reset the colors in this section");
|
||||
|
||||
const header = &self.groups[@intFromEnum(group)];
|
||||
header.* = .{ .editor = self, .group = group, .reset = reset };
|
||||
@@ -407,7 +407,7 @@ fn buildGroupHeader(self: *PaletteEditor, group: palette.Group) *gtk.Widget {
|
||||
return row.as(gtk.Widget);
|
||||
}
|
||||
|
||||
/// One colour as a row: what it is, what it currently is, and the two controls
|
||||
/// One color as a row: what it is, what it currently is, and the two controls
|
||||
/// for changing that.
|
||||
fn buildRow(self: *PaletteEditor, key: palette.Key) *gtk.Widget {
|
||||
const row = gtk.Box.new(.horizontal, 8);
|
||||
@@ -419,7 +419,7 @@ fn buildRow(self: *PaletteEditor, key: palette.Key) *gtk.Widget {
|
||||
label.as(gtk.Widget).addCssClass("playpen-dialog-label");
|
||||
row.append(label.as(gtk.Widget));
|
||||
|
||||
// The tooltip carries what the colour is *for*, plus the name it goes by in
|
||||
// The tooltip carries what the color is *for*, plus the name it goes by in
|
||||
// the settings file, since anyone hand-editing that file is looking at this
|
||||
// list to find out what to call things.
|
||||
var tip: [256]u8 = undefined;
|
||||
@@ -454,8 +454,8 @@ fn buildRow(self: *PaletteEditor, key: palette.Key) *gtk.Widget {
|
||||
/// A swatch, and the row record that goes with it.
|
||||
///
|
||||
/// Alpha is off: `style.css` derives every wash it needs from `alpha()` on the
|
||||
/// colour it belongs to, so a translucent palette entry would be a second way
|
||||
/// of saying the same thing, and one the terminal renderer could not honour.
|
||||
/// color it belongs to, so a translucent palette entry would be a second way
|
||||
/// of saying the same thing, and one the terminal renderer could not honor.
|
||||
fn newSwatch(
|
||||
self: *PaletteEditor,
|
||||
key: palette.Key,
|
||||
@@ -507,7 +507,7 @@ fn reload(self: *PaletteEditor) void {
|
||||
|
||||
// With no base of its own, the swatch shows the accent this scheme is
|
||||
// already painted with — so the first click on it starts from the theme in
|
||||
// front of you rather than from whatever colour a picker opens on.
|
||||
// front of you rather than from whatever color a picker opens on.
|
||||
const base = if (changes.tint) |tint|
|
||||
tint.base
|
||||
else
|
||||
@@ -524,9 +524,9 @@ fn reload(self: *PaletteEditor) void {
|
||||
self.contrast.as(gtk.Widget).setSensitive(@intFromBool(changes.tint != null));
|
||||
|
||||
for (&self.rows) |*row| {
|
||||
// The colour, not the override: a terminal colour that is following the
|
||||
// surface above it, or any colour at all under a base, has to show the
|
||||
// colour it is actually painted in.
|
||||
// The color, not the override: a terminal color that is following the
|
||||
// surface above it, or any color at all under a base, has to show the
|
||||
// color it is actually painted in.
|
||||
const color = palette.resolve(row.key, scheme, changes);
|
||||
|
||||
var rgba = toRgba(color);
|
||||
@@ -538,7 +538,7 @@ fn reload(self: *PaletteEditor) void {
|
||||
}
|
||||
|
||||
// Whether there is anything to revert *to*, which is a different
|
||||
// question: neither an inherited colour nor a generated one is a colour
|
||||
// question: neither an inherited color nor a generated one is a color
|
||||
// that has been set.
|
||||
if (row.revert) |button| {
|
||||
button.as(gtk.Widget).setSensitive(@intFromBool(changes.overrides.get(row.key) != null));
|
||||
@@ -565,7 +565,7 @@ fn groupIsSet(group: palette.Group, overrides: *const palette.Overrides) bool {
|
||||
///
|
||||
/// In that order on purpose: repainting is what the user asked for and cannot
|
||||
/// fail, while the write can — and a settings file that couldn't be written is
|
||||
/// worth saying so about without also refusing the colour for this session.
|
||||
/// worth saying so about without also refusing the color for this session.
|
||||
fn applied(self: *PaletteEditor) void {
|
||||
appearance.refresh();
|
||||
self.reload();
|
||||
@@ -580,7 +580,7 @@ fn applied(self: *PaletteEditor) void {
|
||||
/// Show the change and leave the file for later.
|
||||
///
|
||||
/// For the contrast slider alone. Every other control here settles on a value
|
||||
/// once — a colour dialog reports when it is dismissed, a button when it is
|
||||
/// once — a color dialog reports when it is dismissed, a button when it is
|
||||
/// clicked — but a slider reports on every pixel of a drag, and writing the
|
||||
/// settings file at that rate would be an fsync per frame of an animation. The
|
||||
/// settings page writes on close, so nothing is lost by waiting; see the note at
|
||||
@@ -608,7 +608,7 @@ fn onColorPicked(
|
||||
// The scheme is read here rather than remembered from the last reload: a
|
||||
// desktop that flips to light under a `system` preference while this page is
|
||||
// open changes which palette an edit belongs to, and writing to the one the
|
||||
// rows were last filled from would put the colour in the palette that isn't
|
||||
// rows were last filled from would put the color in the palette that isn't
|
||||
// on screen.
|
||||
const scheme = theme.currentScheme();
|
||||
Settings.get().colors.of(scheme).overrides.set(row.key, fromRgba(swatch.getRgba()));
|
||||
@@ -660,9 +660,9 @@ fn onBasePicked(
|
||||
}
|
||||
|
||||
fn onBaseCleared(_: *gtk.Button, self: *PaletteEditor) callconv(.c) void {
|
||||
// Only the generated palette goes. Colours set by hand were set against
|
||||
// Only the generated palette goes. Colors set by hand were set against
|
||||
// what was underneath them and are still what their owner asked for, so
|
||||
// dropping those too is `onReset`'s job and is labelled as such.
|
||||
// dropping those too is `onReset`'s job and is labeled as such.
|
||||
Settings.get().colors.of(theme.currentScheme()).tint = null;
|
||||
self.applied();
|
||||
}
|
||||
@@ -702,10 +702,10 @@ fn toRgba(color: palette.Rgb) gdk.RGBA {
|
||||
};
|
||||
}
|
||||
|
||||
/// GTK works in floats and the palette works in bytes, so a colour that came
|
||||
/// GTK works in floats and the palette works in bytes, so a color that came
|
||||
/// out of the picker's own HSV wheel or its eyedropper has to be rounded to
|
||||
/// something a hex triplet can hold. Rounding rather than truncating, so that a
|
||||
/// colour typed in as hex comes back as the same hex.
|
||||
/// color typed in as hex comes back as the same hex.
|
||||
fn fromRgba(rgba: *const gdk.RGBA) palette.Rgb {
|
||||
return .{
|
||||
.r = channel(rgba.f_red),
|
||||
|
||||
Reference in New Issue
Block a user