aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--desktop/modules/deckit/DeckItDashboardModule.qml3
-rw-r--r--desktop/modules/deckit/DeckItDashboardPage.qml6
-rw-r--r--desktop/modules/fonts/Fonts.js35
-rw-r--r--desktop/modules/fonts/FontsModule.qml5
-rw-r--r--desktop/modules/fonts/FontsPage.qml71
-rw-r--r--desktop/modules/fonts/README.md10
-rw-r--r--desktop/modules/fonts/test_fonts.js13
7 files changed, 118 insertions, 25 deletions
diff --git a/desktop/modules/deckit/DeckItDashboardModule.qml b/desktop/modules/deckit/DeckItDashboardModule.qml
index 78a1e5a..ca033f8 100644
--- a/desktop/modules/deckit/DeckItDashboardModule.qml
+++ b/desktop/modules/deckit/DeckItDashboardModule.qml
@@ -22,11 +22,10 @@ Module {
group: "deck-it"
dirty: DeckIt.store.dirty
keywords: ["deck-it", "tablet", "dashboard", "modules", "panels", "actions", "icons", "hardware",
- "network", "battery", "cpu", "gpu", "sensor", "dim", "brightness", "backlight", "clock", "font", "date"]
+ "network", "battery", "cpu", "gpu", "sensor", "dim", "brightness", "backlight"]
readonly property var fields: [
["modules", "", "Dashboard parts"], ["dashboard", "actions", "Dashboard actions"],
["tablet", "dim_after", "Dim after"], ["tablet", "dim_brightness", "Dim brightness"],
- ["tablet", "clock_font", "Clock font"], ["tablet", "clock_font_date", "Date in clock font"],
["hardware", "net", "Network interface"], ["hardware", "battery", "Battery"],
["hardware", "cpu_sensor", "CPU sensor"], ["hardware", "gpu", "GPU"],
["hardware", "gpu_mem_total", "GPU memory"]]
diff --git a/desktop/modules/deckit/DeckItDashboardPage.qml b/desktop/modules/deckit/DeckItDashboardPage.qml
index 20581a8..14aba95 100644
--- a/desktop/modules/deckit/DeckItDashboardPage.qml
+++ b/desktop/modules/deckit/DeckItDashboardPage.qml
@@ -74,12 +74,6 @@ Column {
hint: "Seconds without a touch before the tablet's backlight goes down; a touch brings it back. 0 turns it off." }
Setting { store: col.st; section: "tablet"; key: "dim_brightness"; label: "Dim brightness"; kind: "int"; min: 0; max: 255
placeholder: col.st.placeholder("tablet", "dim_brightness"); hint: "Backlight while dimmed, 0 to 255." }
- Setting { store: col.st; section: "tablet"; key: "clock_font"; label: "Clock font"
- placeholder: col.st.placeholder("tablet", "clock_font") || "page font"
- hint: "A font family installed on the PC (fc-list), for the clock on the dim cover. Empty keeps the page font." }
- BoolSetting { store: col.st; section: "tablet"; key: "clock_font_date"; label: "Date in clock font"
- fallback: col.def("tablet", "clock_font_date") ?? true
- hint: "the date under the clock uses the clock font too" }
Heading { text: "Hardware" }
Small { text: "Each is auto, or a fixed value such as wlan0, BAT1 or coretemp." }
diff --git a/desktop/modules/fonts/Fonts.js b/desktop/modules/fonts/Fonts.js
index 5b7d303..d42c784 100644
--- a/desktop/modules/fonts/Fonts.js
+++ b/desktop/modules/fonts/Fonts.js
@@ -12,9 +12,10 @@
// Pure helpers for the fonts page, tested by test_fonts.js under node.
//
-// The model is fonts.json: {base, override}. base is one pixel size for the
-// whole desktop; override maps a system ("qt") or one entry of a section
-// ("rofi:kf-launcher-entry") to its own pixel size. Everything is stored in
+// The model is fonts.json: {base, override, family}. base is one pixel size
+// for the whole desktop; override maps a system ("qt") or one entry of a
+// section ("rofi:kf-launcher-entry") to its own pixel size; family maps a
+// font slot no other config holds (FAMILIES) to an installed family. Everything is stored in
// px and converted where a system wants points: at scale 1 Wayland's logical
// DPI is 96 (gtk-xft-dpi reads 98304, 96 * 1024), so px = pt * 96 / 72.
@@ -30,6 +31,14 @@ const SYSTEMS = [
{ id: "gtk", label: "GTK", unit: "pt", where: "gtk-3.0 and gtk-4.0 settings.ini, .gtkrc-2.0, gsettings", when: "at once for GTK 3 and 4" },
{ id: "kitty", label: "kitty", unit: "pt", where: "kitty.conf, through an include", when: "at once" },
{ id: "groupbar", label: "Hyprland groupbar", unit: "px", where: "keystone.lua, group:groupbar:font_size", when: "at once" },
+ { id: "deckit", label: "deck-it", unit: "px", where: "deck-it's theme.css, from fonts.json", when: "on the tablet within 5 s" },
+];
+
+// deck-it's dim cover clock, sized from deck-it's text as deck.css designs
+// it (8rem, 1.6rem); an override is "deckit:clock" or "deckit:date".
+const DECKIT_CLOCK = [
+ { id: "clock", label: "Clock time", ratio: 8 },
+ { id: "date", label: "Clock date", ratio: 1.6 },
];
// A section is a set of designed sizes that scale together: rofi's fonts,
@@ -40,6 +49,12 @@ const SECTIONS = [
{ id: "hyprlock", label: "hyprlock", unit: "pt", when: "next lock" },
];
+// Font families set here, read by their program from fonts.json itself.
+const FAMILIES = [
+ { id: "deckit-clock", label: "deck-it clock", where: "the time on the tablet's dim cover" },
+ { id: "deckit-date", label: "deck-it date", where: "the date under it" },
+];
+
// A system's or a section's size in px.
function size(m, id) {
const o = m.override[id];
@@ -62,13 +77,21 @@ function withOverride(m, id, px) {
const o = {};
for (const k in m.override) if (k !== id) o[k] = m.override[k];
if (px !== null) o[id] = px;
- return { base: m.base, override: o };
+ return { base: m.base, override: o, family: m.family };
+}
+
+// The model with family id set to name, or unset when name is empty.
+function withFamily(m, id, name) {
+ const f = {};
+ for (const k in m.family) if (k !== id) f[k] = m.family[k];
+ if (name.trim() !== "") f[id] = name.trim();
+ return { base: m.base, override: m.override, family: f };
}
function parseModel(text) {
try {
const j = JSON.parse(text);
- if (typeof j.base === "number") return { base: j.base, override: j.override || {} };
+ if (typeof j.base === "number") return { base: j.base, override: j.override || {}, family: j.family || {} };
} catch (e) {}
return null;
}
@@ -78,7 +101,7 @@ function parseModel(text) {
// from it is an override at its current size. Kept to 0.01px: at 0.1, an
// 11pt body stored as 14.7px scaled rofi's 72pt glyph to 72.2.
function seed(now) {
- const m = { base: 16, override: {} };
+ const m = { base: 16, override: {}, family: {} };
for (const id in now) if (now[id] !== null && Math.abs(now[id] - 16) > 0.05) m.override[id] = Math.round(now[id] * 100) / 100;
return m;
}
diff --git a/desktop/modules/fonts/FontsModule.qml b/desktop/modules/fonts/FontsModule.qml
index 016d85d..76f6ae9 100644
--- a/desktop/modules/fonts/FontsModule.qml
+++ b/desktop/modules/fonts/FontsModule.qml
@@ -33,7 +33,7 @@ Module {
settings: true
group: "Appearance"
dirty: fm.touched
- keywords: ["font", "size", "text", "px", "pt", "qt6ct", "gtk", "kitty", "rofi", "hyprlock", "groupbar", "scale"]
+ keywords: ["font", "size", "text", "px", "pt", "qt6ct", "gtk", "kitty", "rofi", "hyprlock", "groupbar", "scale", "family", "deck-it", "clock", "date", "tablet"]
readonly property string home: Quickshell.env("HOME")
readonly property string config: Quickshell.env("XDG_CONFIG_HOME") || fm.home + "/.config"
@@ -76,7 +76,7 @@ Module {
}
readonly property var saved: { fm.version; return F.parseModel(fm.texts.model ?? "") ?? F.seed(fm.now()); }
- property var draft: ({ base: 16, override: {} })
+ property var draft: ({ base: 16, override: {}, family: {} })
// A reload replaces the draft only while it holds no edits.
onSavedChanged: if (!fm.touched) fm.draft = fm.saved
property bool touched: false
@@ -85,6 +85,7 @@ Module {
function setBase(px) { fm.touched = true; fm.draft = { base: px, override: fm.draft.override }; }
function setOverride(id, px) { fm.touched = true; fm.draft = F.withOverride(fm.draft, id, px); }
+ function setFamily(id, name) { fm.touched = true; fm.draft = F.withFamily(fm.draft, id, name); }
function discard() { fm.touched = false; fm.draft = fm.saved; fm.error = ""; }
function refresh() {
diff --git a/desktop/modules/fonts/FontsPage.qml b/desktop/modules/fonts/FontsPage.qml
index fd2e672..1a58b11 100644
--- a/desktop/modules/fonts/FontsPage.qml
+++ b/desktop/modules/fonts/FontsPage.qml
@@ -11,6 +11,7 @@
import QtQuick
import "../.."
+import "../appearance" as A
import "Fonts.js" as F
// The base size, then a row per system and per section entry: what it is,
@@ -27,9 +28,9 @@ Column {
spacing: 8
- function clamp(px) { return Math.max(page.minPx, Math.min(page.maxPx, px)); }
+ function clamp(px, max) { return Math.max(page.minPx, Math.min(max, px)); }
// A step of 1 in the row's own unit, so a pt row moves by whole points.
- function step(px, unit, d) { return page.clamp(unit === "pt" ? F.toPx(Math.round(F.toPt(px)) + d) : Math.round(px) + d); }
+ function step(px, unit, d, max) { return page.clamp(unit === "pt" ? F.toPx(Math.round(F.toPt(px)) + d) : Math.round(px) + d, max); }
component Small: Text {
color: Theme.subtext
@@ -60,13 +61,14 @@ Column {
property real px: 16
property string unit: "px"
property bool enabled: true
+ property real max: page.maxPx
signal changed(real px)
spacing: 6
opacity: st.enabled ? 1 : 0.45
- Button { text: "−"; enabled: st.enabled; onClicked: st.changed(page.step(st.px, st.unit, -1)) }
+ Button { text: "−"; enabled: st.enabled; onClicked: st.changed(page.step(st.px, st.unit, -1, st.max)) }
Unit { text: F.round(st.px) + " px"; own: st.unit === "px" }
Unit { text: F.round(F.toPt(st.px)) + " pt"; own: st.unit === "pt" }
- Button { text: "+"; enabled: st.enabled; onClicked: st.changed(page.step(st.px, st.unit, 1)) }
+ Button { text: "+"; enabled: st.enabled; onClicked: st.changed(page.step(st.px, st.unit, 1, st.max)) }
}
// A system or a section entry. id is its override key; inherited is the
@@ -79,6 +81,7 @@ Column {
property string unit: "px"
required property real inherited
property string follows: "base"
+ property real max: page.maxPx
readonly property bool own: page.m.override[sr.key] !== undefined
readonly property real px: sr.own ? page.m.override[sr.key] : sr.inherited
objectName: "font:" + sr.key
@@ -116,6 +119,7 @@ Column {
anchors.verticalCenter: parent.verticalCenter
px: sr.px
unit: sr.unit
+ max: sr.max
enabled: sr.own
onChanged: px => page.fonts.setOverride(sr.key, px)
}
@@ -147,7 +151,7 @@ Column {
Heading { text: "Systems" }
Repeater {
- model: F.SYSTEMS.filter(s => s.id !== "groupbar")
+ model: F.SYSTEMS.filter(s => s.id !== "groupbar" && s.id !== "deckit")
SizeRow {
required property var modelData
key: modelData.id
@@ -166,6 +170,26 @@ Column {
inherited: page.m.base
}
+ Heading { text: "deck-it" }
+ SizeRow {
+ key: "deckit"
+ title: "deck-it text"
+ detail: "The tablet page's rem. deck-it reads fonts.json itself; seen on the tablet within 5 s."
+ inherited: page.m.base
+ }
+ Repeater {
+ model: F.DECKIT_CLOCK
+ SizeRow {
+ required property var modelData
+ key: "deckit:" + modelData.id
+ title: modelData.label
+ detail: "On the dim cover, designed at " + modelData.ratio + "× deck-it's text."
+ follows: "deck-it"
+ max: 400
+ inherited: F.size(page.m, "deckit") * modelData.ratio
+ }
+ }
+
// rofi and hyprlock: the section's size, which is its body text, then
// each entry at its designed ratio to the body.
Repeater {
@@ -209,4 +233,41 @@ Column {
}
}
}
+
+ // Families no other config holds; the program reads fonts.json itself.
+ Heading { text: "Families" }
+ Small {
+ x: 8
+ width: parent.width - 16
+ text: "A family installed on this PC, as fc-list names it. Empty keeps the program's own font."
+ }
+ Repeater {
+ model: F.FAMILIES
+ Item {
+ id: fr
+ required property var modelData
+ objectName: "font:" + fr.modelData.id
+ width: page.width
+ height: 48
+ Column {
+ anchors { left: parent.left; leftMargin: 8; right: field.left; rightMargin: 12; verticalCenter: parent.verticalCenter }
+ Text {
+ text: fr.modelData.label
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
+ }
+ Small { width: parent.width; text: fr.modelData.where }
+ }
+ A.Field {
+ id: field
+ anchors { right: parent.right; rightMargin: 8; verticalCenter: parent.verticalCenter }
+ width: 240
+ value: page.m.family[fr.modelData.id] ?? ""
+ placeholder: "program's font"
+ // Every keystroke, not editingFinished: a click on Save takes no
+ // focus, so a name typed and saved would never reach the draft.
+ onValueChanged: if (value.trim() !== (page.m.family[fr.modelData.id] ?? "")) page.fonts.setFamily(fr.modelData.id, value)
+ }
+ }
+ }
}
diff --git a/desktop/modules/fonts/README.md b/desktop/modules/fonts/README.md
index b887060..c3e2232 100644
--- a/desktop/modules/fonts/README.md
+++ b/desktop/modules/fonts/README.md
@@ -4,7 +4,8 @@ One pixel size for the desktop, in the settings window under Appearance >
Fonts, and an override wherever a system needs its own. Saved as
`~/.config/keystone/fonts.json`:
- { "base": 16, "override": { "qt": 18.67, "rofi:kf-launcher-entry": 22 } }
+ { "base": 16, "override": { "qt": 18.67, "rofi:kf-launcher-entry": 22 },
+ "family": { "deckit-clock": "Some Font" } }
Everything is stored in px. At scale 1 Wayland's logical DPI is 96
(`gtk-xft-dpi` reads 98304, 96 * 1024), so a point is 4/3 of a pixel and
@@ -17,6 +18,7 @@ Everything is stored in px. At scale 1 Wayland's logical DPI is 96
| GTK | gtk-3.0 and gtk-4.0 `settings.ini`, `.gtkrc-2.0`, gsettings `font-name` | pt | at once for GTK 3 and 4 |
| kitty | `~/.config/keystone/kitty-fonts.conf`, included last by `kitty.conf`; SIGUSR1 reloads | pt | at once |
| Hyprland groupbar | `group:groupbar:font_size` line in `keystone.lua` | px | at once |
+| deck-it | reads `fonts.json` itself into its `theme.css`: `deckit`, and the dim cover's `deckit:clock` and `deckit:date` (8 and 1.6 times `deckit` unless set) | px | on the tablet within 5 s |
| rofi | `~/.config/keystone/rofi-fonts.rasi`, imported by udt's `common.rasi` | pt | next open |
| hyprlock | `~/.config/keystone/hyprlock-fonts.conf`, sourced by `hyprlock.conf` | pt | next lock |
@@ -39,7 +41,11 @@ With no `fonts.json`, the page starts from what the files say: base 16 px,
and an override for every system at its current size, so the first save
changes nothing.
-Not handled: font families; the hard-coded `pixelSize` values in the shells
+`family` names a font for a slot no other config holds, read by its
+program from `fonts.json` itself: `deckit-clock` and `deckit-date`, the
+time and the date on deck-it's dim cover. Empty leaves the program's own.
+
+Not handled: the families of the systems above; the hard-coded `pixelSize` values in the shells
(21 of them), which do not follow `Theme.fontSize`; and a `keystone.lua`
save from this page and the Options page at the same moment, which can drop
one line.
diff --git a/desktop/modules/fonts/test_fonts.js b/desktop/modules/fonts/test_fonts.js
index 947bd42..773da64 100644
--- a/desktop/modules/fonts/test_fonts.js
+++ b/desktop/modules/fonts/test_fonts.js
@@ -17,7 +17,7 @@ const fs = require("fs");
const assert = require("assert");
const src = fs.readFileSync(__dirname + "/Fonts.js", "utf8").replace(/^\.pragma library$/m, "");
-const F = new Function(src + "\nreturn { size, entryPx, withOverride, parseModel, seed, qtPt, gtkPt, kittyPt, "
+const F = new Function(src + "\nreturn { DECKIT_CLOCK, size, entryPx, withOverride, withFamily, parseModel, seed, qtPt, gtkPt, kittyPt, "
+ "rofiEntries, hyprlockEntries, setQt, setGtk, gtkFamily, kittyFile, rofiFile, hyprlockFile };")();
const qt = '[Fonts]\nfixed="Inconsolata Nerd Font Mono,14,-1,5,400"\ngeneral="Noto Sans,14,-1,5,400,Regular"\n';
@@ -36,11 +36,16 @@ assert.strictEqual(F.setGtk(gtk2, 12), 'gtk-font-name="Noto Sans, 12"\n');
// The first save changes nothing: what differs from 16px is an override.
const m = F.seed({ quickshell: 16, qt: 14 * 4 / 3, gtk: 10 * 4 / 3, kitty: 20 });
-assert.deepStrictEqual(m, { base: 16, override: { qt: 18.67, gtk: 13.33, kitty: 20 } });
+assert.deepStrictEqual(m, { base: 16, override: { qt: 18.67, gtk: 13.33, kitty: 20 }, family: {} });
assert.strictEqual(F.size(m, "quickshell"), 16);
assert.strictEqual(F.size(F.withOverride(m, "qt", null), "qt"), 16);
assert.strictEqual(F.parseModel("nope"), null);
assert.deepStrictEqual(F.parseModel(JSON.stringify(m)), m);
+// A family survives a size edit; an empty one is dropped.
+const fm = F.withOverride(F.withFamily(m, "deckit-date", " Sedgwick Ave "), "qt", 20);
+assert.deepStrictEqual(fm.family, { "deckit-date": "Sedgwick Ave" });
+assert.deepStrictEqual(F.withFamily(fm, "deckit-date", "").family, {});
+assert.deepStrictEqual(F.parseModel('{"base": 16}').family, {});
const rasi = ['* { font: var(kf-common-font, "Noto Sans 11"); }',
'entry { font: var(kf-launcher-entry, "Noto Sans Bold 15"); }',
@@ -64,4 +69,8 @@ const sr = F.seed({ rofi: 11 * 4 / 3 });
assert.ok(F.rofiFile(sr, [{ name: "kf-a", family: "M", pt: 11 }, { name: "kf-b", family: "M", pt: 72 }]).includes('kf-b: "M 72"'));
assert.strictEqual(F.kittyFile(m).split("\n")[1], "font_size 15");
+// The clock follows deck-it's text at its designed ratio.
+const d = { base: 16, override: { deckit: 20 }, family: {} };
+assert.strictEqual(F.size(d, "deckit") * F.DECKIT_CLOCK[0].ratio, 160);
+
console.log("ok");