Files
Panama/config/dot/quickshell/modules/settings/AppearancePage.qml
Gabriel Brown 9c574f9eb7 Add a light mode that keeps the same identity
Light is Tokyo Night Day, the palette's own light variant, rather than
one invented to merely not be dark. Both share the same hues at
different lightness, which is what lets the Prism signature survive the
switch: blue still leads into orchid, it is simply a darker blue on a
lighter ground.

Every colour token became a binding on one boolean, so flipping it
repaints the whole shell without any component needing to know it
happened. That only worked because nothing outside Theme.qml defines a
colour; the two places that did are fixed here.

Surface alphas differ by scheme. The 0.34 that reads as glass over a
dark desktop reads as haze over a light one, and text stops being
legible on it.

Two things that draw on this desktop do not read Panama's store: GTK
applications, which read gsettings, and the compositor, which draws
window borders. A toolbar or a border still wearing the other scheme is
more jarring than either scheme on its own, so ColorScheme pushes the
choice to both. It also pushes at startup, since a scheme chosen in a
previous session would otherwise be in effect only for the shell.

The unfocused window border follows too. It is a flat neutral, and a
dark neutral is invisible against a light desktop. The focused border is
the prism gradient and needs no variant.

The live preview follows the scheme as well. A preview that stayed dark
while the shell around it went light did not read as "your desktop", it
read as a screenshot of someone else's.

Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
2026-08-18 07:20:35 -04:00

193 lines
7.2 KiB
QML

// Appearance — the page the rest of the settings vocabulary was built for.
//
// Before Stage 3 this page adjusted a clock format and three vitals toggles,
// and its "Theme" card was two rows of text pretending to be controls. It now
// drives the compositor directly: every row here is applied through
// `hyprctl eval` and confirmed by reading the value back before it is stored.
//
// The preview is pinned above the controls rather than sitting inline, because
// the numbers are meaningless on their own -- "outer gaps 24" only means
// something once you have watched the windows move apart by that much, at the
// scale of the display you actually use.
import QtQuick
import qs.config
import qs.services
SettingsPage {
id: root
title: "Appearance"
lede: "Drag anything below. The preview above is your real geometry, to scale."
header: Component {
Column {
spacing: 9
DesktopPreview {
width: parent.width
}
Text {
width: parent.width
horizontalAlignment: Text.AlignHCenter
text: SystemSettings.lastError !== ""
? SystemSettings.lastError
: "Live preview — the focused window wears the prism border"
color: SystemSettings.lastError !== "" ? Theme.warn : Theme.fgMuted
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
elide: Text.ElideRight
}
}
}
SettingsCard {
title: "Background"
subtitle: Wallpaper.lastError !== ""
? Wallpaper.lastError
: "Applied to every display. Panama looks in ~/Pictures/Wallpapers, ~/Pictures/Backgrounds, ~/.local/share/backgrounds, and /usr/share/backgrounds."
WallpaperPicker {
id: wallpapers
width: parent.width
}
ActionRow {
visible: wallpapers.hidden > 0 || wallpapers.expanded
label: wallpapers.expanded ? "Showing every image" : wallpapers.hidden + " more available"
detail: "The grid is kept short so the rest of this page stays reachable"
action: wallpapers.expanded ? "Show fewer" : "Show all"
onTriggered: wallpapers.expanded = !wallpapers.expanded
}
ActionRow {
label: "Look for new images"
detail: Wallpaper.scanning
? "Scanning…"
: Wallpaper.available.length + " image" + (Wallpaper.available.length === 1 ? "" : "s") + " found"
action: "Rescan"
divider: false
enabled: !Wallpaper.scanning
onTriggered: Wallpaper.rescan()
}
}
SettingsCard {
title: "Colour scheme"
subtitle: ColorScheme.lastError !== ""
? ColorScheme.lastError
: "Light is Tokyo Night Day, the official light variant — the same hues at a different lightness, so the blue-into-orchid signature survives the switch. Applications and window borders follow."
ChoiceRow { setting: "colorScheme"; divider: false }
}
SettingsCard {
title: "Typography"
subtitle: Fonts.lastError !== ""
? Fonts.lastError
: "Every piece of text in the shell. Samples are drawn in the font they name."
SliderRow { setting: "interfaceFontSize" }
TextRow {
label: "Interface font"
detail: Fonts.interfaceMissing
? "Not installed on this machine — fontconfig is substituting something else"
: "Used for all shell text"
value: Fonts.interfaceFont
}
FontPicker {
width: parent.width
families: Fonts.interfaceFonts
current: Fonts.interfaceFont
emptyText: Fonts.scanning ? "Reading installed fonts…" : "No interface fonts found"
onPicked: family => Fonts.setInterface(family)
}
TextRow {
label: "Icon font"
detail: Fonts.iconMissing
? "Not installed — the shell's glyphs will not draw correctly"
: "Draws the shell's glyphs, so only Nerd Fonts are offered"
value: Fonts.iconFont
}
FontPicker {
width: parent.width
families: Fonts.iconFonts
current: Fonts.iconFont
emptyText: "No Nerd Fonts installed"
onPicked: family => Fonts.setIcon(family)
}
}
SettingsCard {
title: "Windows"
subtitle: "Spacing and shape of tiled windows. Each change is applied to the compositor and confirmed before it is saved."
SliderRow { setting: "windowRounding" }
SliderRow { setting: "gapsIn" }
SliderRow { setting: "gapsOut" }
SliderRow { setting: "borderSize"; zeroLabel: "None" }
SliderRow { setting: "inactiveOpacity"; divider: false }
}
SettingsCard {
title: "Effects"
subtitle: "Each of these costs frame time. Turning one off is a legitimate way to buy it back while gaming."
ToggleRow { setting: "blurEnabled" }
SliderRow { setting: "blurSize" }
SliderRow { setting: "blurPasses" }
ToggleRow { setting: "shadowEnabled" }
SliderRow { setting: "shadowRange"; zeroLabel: "None" }
ToggleRow { setting: "glowEnabled" }
SliderRow { setting: "glowRange"; zeroLabel: "None" }
ToggleRow { setting: "animationsEnabled"; divider: false }
}
SettingsCard {
title: "Clock"
ToggleRow { setting: "use24Hour" }
ToggleRow { setting: "showSeconds" }
ToggleRow { setting: "showWeekday"; divider: false }
}
SettingsCard {
title: "System vitals"
subtitle: "Choose what appears beside the workspace indicator."
ToggleRow { setting: "showCpu" }
ToggleRow { setting: "showMemory" }
ToggleRow { setting: "showGpu"; divider: GraphicsDevices.devices.length > 1 || GraphicsDevices.selectionMissing }
// Only worth asking when there is a choice to make.
ChoiceGrid {
visible: GraphicsDevices.devices.length > 1 || GraphicsDevices.selectionMissing
width: parent.width
label: "Graphics device"
detail: GraphicsDevices.selectionMissing
? "The stored device is not present on this machine, so the graphics readout is hidden. Choose one below."
: "Which GPU the graphics readout measures."
options: GraphicsDevices.devices.map(device => ({
value: device.path,
label: GraphicsDevices.shortName(device.name)
}))
current: GraphicsDevices.selectedPath
divider: false
onPicked: value => GraphicsDevices.select(value)
}
}
SettingsCard {
title: "Theme"
subtitle: "Panama has one curated visual identity rather than a matrix of partially compatible themes. The controls above adjust its parameters — how much space, how soft, how much motion — without replacing it."
TextRow { label: "Color palette"; detail: "Tokyo Night Moon"; value: "Prism" }
TextRow { label: "Interface type"; detail: "Adwaita Sans"; value: "System"; divider: false }
}
}