summaryrefslogtreecommitdiffstats
path: root/desktop/modules/fonts/FontsPage.qml
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/modules/fonts/FontsPage.qml')
-rw-r--r--desktop/modules/fonts/FontsPage.qml212
1 files changed, 212 insertions, 0 deletions
diff --git a/desktop/modules/fonts/FontsPage.qml b/desktop/modules/fonts/FontsPage.qml
new file mode 100644
index 0000000..fd2e672
--- /dev/null
+++ b/desktop/modules/fonts/FontsPage.qml
@@ -0,0 +1,212 @@
+// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+import QtQuick
+import "../.."
+import "Fonts.js" as F
+
+// The base size, then a row per system and per section entry: what it is,
+// its size in its own unit and in px, and a switch to give it its own size
+// instead of following the base (or, inside rofi and hyprlock, its share of
+// the section's size). Edits are a draft until Save.
+Column {
+ id: page
+
+ required property QtObject fonts
+ readonly property var m: page.fonts.draft
+ readonly property real minPx: 8
+ readonly property real maxPx: 160
+
+ spacing: 8
+
+ function clamp(px) { return Math.max(page.minPx, Math.min(page.maxPx, 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); }
+
+ component Small: Text {
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ wrapMode: Text.Wrap
+ }
+
+ component Heading: Text {
+ topPadding: 10
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1; bold: true }
+ }
+
+ // One column of the stepper: px always first, pt second, so every row
+ // lines up; the unit the system's config is written in is the bright one.
+ component Unit: Text {
+ property bool own: false
+ width: 72
+ anchors.verticalCenter: parent.verticalCenter
+ horizontalAlignment: Text.AlignRight
+ color: own ? Theme.accent : Theme.subtext
+ font { family: Theme.iconFamily; pixelSize: Theme.fontSize - 3; bold: own }
+ }
+
+ // − px pt +, the system's own unit highlighted.
+ component Stepper: Row {
+ id: st
+ property real px: 16
+ property string unit: "px"
+ property bool enabled: true
+ 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)) }
+ 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)) }
+ }
+
+ // A system or a section entry. id is its override key; inherited is the
+ // size it has when it follows.
+ component SizeRow: Item {
+ id: sr
+ required property string key
+ required property string title
+ property string detail: ""
+ property string unit: "px"
+ required property real inherited
+ property string follows: "base"
+ 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
+ width: page.width
+ height: Math.max(48, texts.implicitHeight + 12)
+
+ Column {
+ id: texts
+ anchors { left: parent.left; leftMargin: 8; right: controls.left; rightMargin: 12; verticalCenter: parent.verticalCenter }
+ Text {
+ width: parent.width
+ text: sr.title
+ color: Theme.text
+ elide: Text.ElideRight
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
+ }
+ Small { width: parent.width; visible: sr.detail !== ""; text: sr.detail }
+ }
+
+ Row {
+ id: controls
+ anchors { right: parent.right; rightMargin: 8; verticalCenter: parent.verticalCenter }
+ spacing: 12
+ Small {
+ anchors.verticalCenter: parent.verticalCenter
+ wrapMode: Text.NoWrap
+ text: sr.own ? "own size" : "follows " + sr.follows
+ }
+ Switch {
+ anchors.verticalCenter: parent.verticalCenter
+ checked: sr.own
+ onToggled: on => page.fonts.setOverride(sr.key, on ? F.round(sr.inherited) : null)
+ }
+ Stepper {
+ anchors.verticalCenter: parent.verticalCenter
+ px: sr.px
+ unit: sr.unit
+ enabled: sr.own
+ onChanged: px => page.fonts.setOverride(sr.key, px)
+ }
+ }
+ }
+
+ Small {
+ width: parent.width
+ text: "One size for the whole desktop, in pixels. At scale 1 a point is 4/3 of a pixel (96 dpi), so 16 px is 12 pt. "
+ + "A system with its own size keeps it when the base moves."
+ }
+
+ Item {
+ objectName: "font:base"
+ width: parent.width
+ height: 48
+ Text {
+ anchors { left: parent.left; leftMargin: 8; verticalCenter: parent.verticalCenter }
+ text: "Base size"
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize; bold: true }
+ }
+ Stepper {
+ anchors { right: parent.right; rightMargin: 8; verticalCenter: parent.verticalCenter }
+ px: page.m.base
+ onChanged: px => page.fonts.setBase(px)
+ }
+ }
+
+ Heading { text: "Systems" }
+ Repeater {
+ model: F.SYSTEMS.filter(s => s.id !== "groupbar")
+ SizeRow {
+ required property var modelData
+ key: modelData.id
+ title: modelData.label
+ detail: modelData.where + ". Seen " + modelData.when + "."
+ unit: modelData.unit
+ inherited: page.m.base
+ }
+ }
+
+ Heading { text: "Hyprland groupbar" }
+ SizeRow {
+ key: "groupbar"
+ title: "Group tab titles"
+ detail: "group:groupbar:font_size, a keystone.lua line. Seen at once. Its text_offset is tuned for 14 px."
+ inherited: page.m.base
+ }
+
+ // rofi and hyprlock: the section's size, which is its body text, then
+ // each entry at its designed ratio to the body.
+ Repeater {
+ model: F.SECTIONS
+ Column {
+ id: sec
+ required property var modelData
+ readonly property var entries: sec.modelData.id === "rofi" ? page.fonts.rofiEntries : page.fonts.hyprlockEntries
+ readonly property real body: sec.entries.length ? Math.min.apply(null, sec.entries.map(e => e.pt)) : 0
+ width: page.width
+ spacing: 8
+
+ Heading { text: sec.modelData.label }
+ Small {
+ x: 8
+ width: parent.width - 16
+ visible: sec.entries.length === 0
+ text: "No sizes found to scale: " + (sec.modelData.id === "rofi"
+ ? "no var(kf-..., \"...\") in ~/.config/rofi/udt."
+ : "no $kf_... = size in ~/.config/hypr/hyprlock.conf.")
+ }
+ SizeRow {
+ visible: sec.entries.length > 0
+ key: sec.modelData.id
+ title: sec.modelData.label + " body text"
+ detail: "Designed at " + sec.body + " pt; everything below keeps its ratio to it. Seen " + sec.modelData.when + "."
+ unit: "pt"
+ inherited: page.m.base
+ }
+ Repeater {
+ model: sec.entries
+ SizeRow {
+ required property var modelData
+ key: sec.modelData.id + ":" + modelData.name
+ title: modelData.name.replace(/^kf[-_]/, "")
+ detail: (modelData.family ? modelData.family + ", " : "") + "designed " + modelData.pt + " pt"
+ unit: "pt"
+ follows: sec.modelData.label
+ inherited: F.entryPx(page.m, sec.modelData.id, sec.entries, { name: "", pt: modelData.pt })
+ }
+ }
+ }
+ }
+}