Expose 19 more compositor options that only looks.lua could reach

Measured the gap first: of the 38 real Hyprland options Panama's own Lua
sets, only 16 were editable in Settings. Everything else required a text
editor, which is the thing this app exists to stop. This closes most of
that: 66 mapped options now, from 47.

Window shape and shadows on Appearance: corner shape (rounding_power),
focused and fullscreen opacity, shadow falloff and hard-edged shadows.
Window edges, master layout and Hyprland's own notices on Desktop & Dock.

Three of these are corrections rather than additions.

Master layout options existed nowhere, while Settings has offered "Master
and stack" as a choice since this morning -- a layout you can select and
cannot configure is barely a choice. Its card is hidden unless that
layout is actually selected, since settings that do nothing under the
layout you are running are worse than not offering the layout at all.

The four Hyprland notices -- logo, splash, update news, donation nag --
are all turned off by looks.lua on the user's behalf. Defensible as a
default, but not a decision anyone could reverse. They are stored
positively ("show this") and written as Hyprland's `disable_*` through a
new `invert` flag, because a switch labelled "Disable splash text" that
must be ON to hide something is a small cruelty. The Lua does the same
inversion so both sides agree.

Everything new also reads from prefs in looks.lua. Without that these
would apply live and silently revert on the next compositor reload,
which is the failure this codebase keeps designing against.

Two shapes the write path had never seen. Border colours are gradients
and shadow offsets are vec2, and the verifier understood neither -- it
returned false for anything outside int/bool/float/str/css, so both
would have reported every write as rejected. Gradients also need real
care: the stubs declare them as `string|{colors,angle}`, and the string
form carries only ONE stop, so writing "rgba(a) rgba(b) 45deg" as a
string is accepted and keeps the previous value. Verified that directly.
They are also written in one notation and read back in another
(`{colors={"rgba(3b426199)"},angle=45}` becomes `993b4261 45deg`), so
comparison normalises both sides.

Border COLOUR is deliberately not exposed yet. col.inactive_border is
written by ColorScheme on every scheme change, so a user's choice would
be silently overwritten, and col.active_border is the Prism gradient,
which needs a colour control this app does not have. Shadow offset is
left out for the same reason -- the vec2 support is in place for
whenever the widget exists.

Verified each new option applies and reverts against the live
compositor, and that the schema, enum-map, nav, write and commit/reset
contracts all pass.

Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
This commit is contained in:
Gabriel Brown
2026-08-18 12:27:34 -04:00
parent b7ce2c6e43
commit ccf46c40ef
6 changed files with 333 additions and 13 deletions
+36 -11
View File
@@ -36,7 +36,9 @@ hl.config({
and "rgba(a8aecb99)" or "rgba(3b426199)",
},
resize_on_border = true,
resize_on_border = prefs.get("resizeOnBorder", true),
extend_border_grab_area = prefs.getInt("borderGrabArea", 15),
hover_icon_on_border = prefs.get("hoverIconOnBorder", true),
-- Enables the per-window "immediate" rule used for games in rules.lua.
-- Harmless on its own; tearing only happens where a rule opts in.
@@ -44,16 +46,22 @@ hl.config({
layout = "dwindle",
snap = { enabled = true },
snap = {
enabled = true,
window_gap = prefs.getInt("snapWindowGap", 10),
monitor_gap = prefs.getInt("snapMonitorGap", 10),
respect_gaps = prefs.get("snapRespectGaps", false),
},
},
decoration = {
-- 18 to match the shell's popover radius, so a window and a panel sitting
-- next to each other read as the same object family.
rounding = prefs.get("windowRounding", 18),
rounding_power = 2,
rounding_power = prefs.get("roundingPower", 2),
active_opacity = 1.0,
active_opacity = prefs.get("activeOpacity", 1.0),
fullscreen_opacity = prefs.get("fullscreenOpacity", 1.0),
inactive_opacity = prefs.get("inactiveOpacity", 1.0),
blur = {
@@ -83,9 +91,13 @@ hl.config({
shadow = {
enabled = prefs.get("shadowEnabled", true),
range = prefs.get("shadowRange", 20),
render_power = 3,
sharp = false,
render_power = prefs.getInt("shadowRenderPower", 3),
sharp = prefs.get("shadowSharp", false),
color = "rgba(15161eee)",
-- Deliberately not a setting: a two-axis offset needs a control we
-- do not have, and a slider bound to half a value is worse than
-- leaving it alone. SystemSettings understands the vec2 shape
-- already, so adding it later is only a matter of the widget.
offset = { 0, 4 },
scale = 1.0,
},
@@ -110,14 +122,27 @@ hl.config({
dwindle = {
-- Keep the split orientation a window was created with. Closest match
-- to how the Forge extension behaved on GNOME.
preserve_split = true,
preserve_split = prefs.get("preserveSplit", true),
smart_resizing = true,
},
-- Only in effect when the tiling layout is "master". Panama ships dwindle,
-- but Settings offers master as a choice, and a layout you can select and
-- cannot configure is barely a choice at all.
master = {
mfact = prefs.get("masterFactor", 0.55),
orientation = prefs.get("masterOrientation", "left"),
new_status = prefs.get("masterNewStatus", "slave"),
new_on_top = prefs.get("masterNewOnTop", false),
},
misc = {
force_default_wallpaper = 0,
disable_hyprland_logo = true,
disable_splash_rendering = true,
-- Stored as "show the logo / show the splash" and written as Hyprland's
-- `disable_*`, matching the `invert` flag on these entries in
-- PreferenceSchema so both sides agree about which way round they are.
disable_hyprland_logo = not prefs.get("hyprlandLogo", false),
disable_splash_rendering = not prefs.get("hyprlandSplash", false),
-- Same setting as Theme.fontFamily in the shell. If only the QML side
-- followed the preference, the compositor and the shell would disagree
@@ -172,8 +197,8 @@ hl.config({
},
ecosystem = {
no_update_news = true,
no_donation_nag = true,
no_update_news = not prefs.get("hyprlandUpdateNews", false),
no_donation_nag = not prefs.get("hyprlandDonationNag", false),
},
xwayland = {