aboutsummaryrefslogtreecommitdiffstats
path: root/appearance/README.md
blob: 85fe5d75024a416db18a654f11a138754350e46e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# appearance

Wallpapers and colour scheme in one drawer. `SUPER+Return` opens it on the
Wallpaper tab; Tab switches tabs, Escape closes.

    ┌─[ Wallpaper ]─[ Theme ]─[ Sunset ]─[ Idle ]─[ Icons ]──┐
    │  Set on [Horizontal] [Vertical]   261 wallpapers       │
    │  ┌────┐ ┌────┐ ┌────┐ ┌────┐        ┌──┐ ┌─────┐       │
    │  │    │ │    │ │    │ │    │        │  │ │     │       │
    │  └────┘ └────┘ └────┘ └────┘        └┬─┘ └──┬──┘       │
    │  ┌────┐ ┌────┐ ┌────┐ ┌────┐        ═╧═  ══╧══         │
    │  │    │ │    │ │    │ │    │      [Apply] [Reset]      │
    │  └────┘ └────┘ └────┘ └────┘                           │
    └────────────────────────────────────────────────────────┘

## Running it

    qs -p .

It is started from `autostart.lua` and reached over IPC, so the shell has to
be running for the keybind to work:

    hl.bind(mainMod .. " + Return", hl.dsp.exec_cmd(
        "qs -p ~/Programming/GIT/quickshell/appearance ipc call appearance wallpaper"))

Write that path out in full in the real config: `exec_cmd` has no shell to
expand `~`. `ipc call appearance theme` opens the Theme tab.

IPC verbs: `wallpaper`, `theme`, `sunset`, `idle`, `icons`.

## Sunset

A port of `hyprsunset-qt` (`~/Programming/GIT/sunset-qt`): profiles in
`~/.config/hypr/hyprsunset.conf`, location in
`~/.config/hyprsunset-qt/config`, sunrise/sunset from the same API and cache.
Both apps read and write the identical file format, so either can edit it.

## Idle

Timeout and enabled only; the commands in `~/.config/hypr/hypridle.conf` are
fixed. Everything before the first `listener` (the `general` block and the
comments explaining the design) is preserved verbatim. A disabled listener is
written commented out. Save restarts `hypridle`, which resets its timers.

## Icons

Switches the icon and cursor theme, and the pointer size. Icon previews come
from a GTK lookup per theme. Cursor previews come from one python pass per
open: for every theme it extracts four shapes (arrow, hand, text, horizontal
resize) out of a hyprcursor `.hlc`, a shape directory's SVG, or a legacy
Xcursor binary via `xcur2png`, so a card shows the cases that tell themes
apart. Hovering a card changes the real cursor and reverts it on leave.

Applying writes `gsettings` (which GTK3 and GTK4 both read), the Qt configs
(`qt6ct`/`qt5ct`), and, for cursors, `environment.lua`. The GTK theme names do
not come from `gsettings` alone here, so the drawer also rewrites
`gtk-3.0/settings.ini` and `gtk-4.0/settings.ini` to match. Apps and a relogin
are needed to see the rest. The theme name is still hardcoded in
`unified-desktop-theme`, `waybar-theme-udt` and rofi, which is tracked as a
follow-up in those repos.

## Wallpapers

Clicking a thumbnail **stages** it rather than setting it, so both screens can
be composed before anything changes, and Apply then makes a single
`wallp --set H=… V=…` call. The mock screens on the right show what the desk
would look like: the staged pick where there is one, what is currently set
where there is not, and the hovered thumbnail on the targeted screen while the
pointer is over it.

`wallp` does the actual work, including running `udt-accent`, so the accent
follows the new wallpaper exactly as it does from a terminal. This panel
replaces its qarma file dialog, not the script.

The two monitors are drawn at their real proportions, read from
`hyprctl monitors`: DP-1 is 2560x1080 and DP-3 is 1920x1080 with
`transform=1`, which makes it 1080x1920 on the desk. They are centred against
each other because that is how Hyprland has them, both spanning y=0.

The bezel and stand are Rectangles rather than an SVG: no asset to ship, sharp
at any size, and the case stays a fixed near-black while the accent marks
which screen is targeted. Their proportions come from the panel **width**, not
its height. Scaling the stand off height gave the wide monitor a 20px stand
that was invisible and would have given the vertical one an absurd long neck.

## Themes

Each scheme shows its real colours, parsed from the pair of files
unified-desktop-theme keeps for it: `palette/<name>.conf` holds the colours
under the scheme's own names, `palette/roles-<name>.conf` says what each
colour is for, so a role is resolved by looking its value up as a key in the
first file. Beside the swatches is a small mock of a panel painted in that
scheme, which says how the colours sit together rather than only what they
are.

The list comes from the palette directory rather than a hardcoded set, so
adding a scheme to unified-desktop-theme is enough to make it appear here.
Both the scheme list and the wallpaper scan are redone every time the panel
opens: this shell is autostarted and runs all session, so a scan done only at
startup would go stale the first time anything was added.

Applying writes the `scheme =` line in `~/.config/udt/roles.conf` and runs
`install.sh`, which regenerates every themed config. **It reloads nothing**,
because `install.sh` reloads nothing: the panel reports what is still showing
the old scheme instead of pretending the switch is complete. `hyprctl reload`,
waybar, kitty and conky each need a nudge, and Qt and GTK apps only reread a
theme when they restart.

## Two QML traps met here

**There is no `String.matchAll`.** QML's JS engine does not have it. It threw
inside a `try` and left every swatch empty with nothing in the log. The
palette parser uses an `exec` loop instead.

**Assigning `running = true` to a Process that is already running does
nothing.** The scheme loader reuses one `Process` for each scheme in turn and
stopped after the first until it set `running = false` immediately before.

## Theme and blur

`Theme.qml` is the shared one: the palette comes from
`~/.cache/udt/udt-palette.qml` and is watched. Frosting is Hyprland's, matched
on this window's namespace:

    hl.layer_rule({
        name         = "blur-appearance",
        match        = { namespace = "^(quickshell-appearance)$" },
        blur         = true,
        xray         = false,
        ignore_alpha = 0.1,
    })