theme: strip the remaining Persona punch from Hyprland and the popouts

Hyprland:
- Windows round at 8px, concentric with the porthole's 18px corners at
  the 10px gap, instead of square corners inside a rounded opening.
- Group tabs are solid hull slabs; the titles used to float over the
  wallpaper. The current tab is lifted a step with its title in the light.
- Motion rewritten to glide and settle: no overshoot curve, gentler
  popin, workspaces slide-fade a short way instead of sweeping the whole
  screen, and layers only fade so they never stack on quickshell's own
  slides.

Quickshell:
- Album art in the bar and the media popout is square in a hairline frame,
  not masked to a leaning chamfered shape; the accent slab under the
  popout art is gone, and a track change dips the title instead of
  tilting the art.
- The bar's equaliser is a five-hairline signal meter that rests as dots.
- Segment meters (system, battery, OSD) are 4px rows instead of 10-12px
  blocks; the toast fuse is a hairline; the clock's accent slab is a
  neutral divider.
- No hover pops: rail icons, tray icons, media transport buttons and
  P5Button no longer scale up or lunge sideways; the icon bump is reduced.

Terminal: ANSI bright black drops to #464d44. TUIs use it as a panel fill,
and at the outline grey it read as a flat light slab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
nvrlandClaude Opus 5.5 committed 2026-10-04 20:45:24 +02:00
1 parent a27dd6f93b
commit ae6539218e
18 files changed
+144 -177

No files matched your search

+4 -1
View File
@@ -53,8 +53,11 @@ magenta = "#a48ba0"
cyan = "#86a8a0" cyan = "#86a8a0"
white = "#bcc1bf" white = "#bcc1bf"
# Bright black doubles as a panel fill in TUIs (Claude Code's side pane, fzf
# previews), so it sits one step under the outline grey rather than at it:
# light enough for comments, dark enough not to read as a slab.
[colors.bright] [colors.bright]
black = "#545c52" black = "#464d44"
red = "#d98068" red = "#d98068"
green = "#a5b893" green = "#a5b893"
yellow = "#d8c491" yellow = "#d8c491"
+35 -27
View File
@@ -1,11 +1,13 @@
-- https://wiki.hypr.land/Configuring/Advanced-and-Cool/Animations/ -- https://wiki.hypr.land/Configuring/Advanced-and-Cool/Animations/
-- --
-- Quick and clean. Motion should get out of the way, not perform. -- YoRHa motion: calm and mechanical. Things glide in, settle and stop; nothing
-- overshoots, nothing pops. Matches Theme.easeSnap / easeGlide in
-- ~/.config/quickshell/config/Theme.qml.
-- --
-- An earlier pass overdid it: a 1.56 overshoot on every window, an 88% popin -- Windows fade in from a hair smaller, the way a NieR menu panel resolves
-- scale, and a borderangle looping forever so the focused border was in -- rather than arrives. Workspaces slide a short way and fade instead of
-- constant motion. The bounce is dialled back to a hint, the popin barely -- sweeping the whole screen across, which on a 2560px panel is a lot of
-- scales, and the border angle does not animate at all. -- motion for "you are somewhere else now". The border angle never animates.
hl.config({ hl.config({
animations = { animations = {
@@ -14,34 +16,40 @@ hl.config({
}) })
-- ------------------------------------------------------------------- curves -- ------------------------------------------------------------------- curves
hl.curve("snap", { type = "bezier", points = { { 0.16, 1.00 }, { 0.30, 1.00 } } }) -- outQuint: fast off the mark, long quiet settle.
hl.curve("overshoot", { type = "bezier", points = { { 0.34, 1.12 }, { 0.64, 1.00 } } }) hl.curve("glide", { type = "bezier", points = { { 0.22, 1.00 }, { 0.36, 1.00 } } })
hl.curve("hard", { type = "bezier", points = { { 0.55, 0.00 }, { 0.90, 0.35 } } }) -- Material standard, for things that move from one place to another.
hl.curve("standard", { type = "bezier", points = { { 0.40, 0.00 }, { 0.20, 1.00 } } })
-- Leaving: ease in, so the exit gathers pace and is gone.
hl.curve("exit", { type = "bezier", points = { { 0.55, 0.00 }, { 0.90, 0.35 } } })
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } }) hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
-- --------------------------------------------------------------------- tree -- --------------------------------------------------------------------- tree
-- speed is in ds (1ds = 100ms). Unset leaves inherit their parent. -- speed is in ds (1ds = 100ms). Unset leaves inherit their parent.
hl.animation({ leaf = "global", enabled = true, speed = 4, bezier = "snap" }) hl.animation({ leaf = "global", enabled = true, speed = 4, bezier = "glide" })
hl.animation({ leaf = "border", enabled = true, speed = 5, bezier = "snap" }) hl.animation({ leaf = "border", enabled = true, speed = 4, bezier = "glide" })
hl.animation({ leaf = "borderangle", enabled = false })
hl.animation({ leaf = "windows", enabled = true, speed = 4, bezier = "snap" }) hl.animation({ leaf = "windows", enabled = true, speed = 4, bezier = "glide" })
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4, bezier = "overshoot", style = "popin 96%" }) hl.animation({ leaf = "windowsIn", enabled = true, speed = 4, bezier = "glide", style = "popin 94%" })
hl.animation({ leaf = "windowsOut", enabled = true, speed = 2.6, bezier = "hard", style = "popin 97%" }) hl.animation({ leaf = "windowsOut", enabled = true, speed = 2.4, bezier = "exit", style = "popin 96%" })
hl.animation({ leaf = "windowsMove", enabled = true, speed = 3.6, bezier = "snap" }) hl.animation({ leaf = "windowsMove", enabled = true, speed = 4, bezier = "standard" })
hl.animation({ leaf = "fade", enabled = true, speed = 3, bezier = "snap" }) hl.animation({ leaf = "fade", enabled = true, speed = 3.4, bezier = "glide" })
hl.animation({ leaf = "fadeIn", enabled = true, speed = 2.4, bezier = "snap" }) hl.animation({ leaf = "fadeIn", enabled = true, speed = 3.4, bezier = "glide" })
hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.8, bezier = "hard" }) hl.animation({ leaf = "fadeOut", enabled = true, speed = 2.2, bezier = "exit" })
hl.animation({ leaf = "fadeSwitch", enabled = true, speed = 2, bezier = "snap" }) hl.animation({ leaf = "fadeSwitch", enabled = true, speed = 3, bezier = "glide" })
hl.animation({ leaf = "fadeShadow", enabled = true, speed = 3, bezier = "snap" }) hl.animation({ leaf = "fadeShadow", enabled = true, speed = 3.4, bezier = "glide" })
hl.animation({ leaf = "fadeDim", enabled = true, speed = 2.4, bezier = "snap" }) hl.animation({ leaf = "fadeDim", enabled = true, speed = 3, bezier = "glide" })
hl.animation({ leaf = "layers", enabled = true, speed = 3, bezier = "snap" }) -- Quickshell animates its own surfaces; the compositor only fades them, so the
hl.animation({ leaf = "layersIn", enabled = true, speed = 3, bezier = "snap", style = "slide" }) -- two motions never stack.
hl.animation({ leaf = "layersOut", enabled = true, speed = 2, bezier = "hard", style = "slide" }) hl.animation({ leaf = "layers", enabled = true, speed = 2.6, bezier = "glide" })
hl.animation({ leaf = "layersIn", enabled = true, speed = 2.6, bezier = "glide", style = "fade" })
hl.animation({ leaf = "layersOut", enabled = true, speed = 2, bezier = "exit", style = "fade" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 3, bezier = "snap", style = "slidevert" }) hl.animation({ leaf = "workspaces", enabled = true, speed = 4, bezier = "glide", style = "slidefadevert 12%" })
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 3, bezier = "snap", style = "slidevert" }) hl.animation({ leaf = "workspacesIn", enabled = true, speed = 4, bezier = "glide", style = "slidefadevert 12%" })
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 2.4, bezier = "hard", style = "slidevert" }) hl.animation({ leaf = "workspacesOut", enabled = true, speed = 3, bezier = "exit", style = "slidefadevert 12%" })
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 3, bezier = "snap", style = "slidevert" }) hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 4, bezier = "glide", style = "slidefadevert 20%" })
+31 -15
View File
@@ -35,9 +35,12 @@ hl.config({
}, },
decoration = { decoration = {
-- NieR panels are plain rectangles. The only curve on screen is the -- Concentric with the porthole: the opening's corners are 18px and
-- porthole the quickshell hull cuts round the desktop. -- the windows sit 10px inside them, so 8px keeps every gap the same
rounding = 0, -- width all the way round the curve. Matches Theme.frameRadius -
-- gaps_out; change one and the other has to follow.
rounding = 8,
rounding_power = 2,
-- Fully opaque, both states. Flat ink on flat ground. -- Fully opaque, both states. Flat ink on flat ground.
active_opacity = 1.0, active_opacity = 1.0,
@@ -76,27 +79,40 @@ hl.config({
group = { group = {
col = { col = {
border_active = c.activeBorder, border_active = c.activeBorder,
border_inactive = c.surfaceAlt, border_inactive = c.overlay,
}, },
-- Tabs are solid slabs of the hull, not titles painted over the
-- wallpaper. Hyprland paints a tab's fill and its indicator from the
-- same colour, so the current tab is told apart by being lifted a step
-- and by its title taking the light, not by an accent fill — a light
-- fill would swallow its own title.
groupbar = { groupbar = {
font_family = "JetBrainsMono Nerd Font", font_family = "Noto Sans",
font_size = 11, font_size = 10,
font_weight_active = "medium",
font_weight_inactive = "normal",
height = 24, height = 22,
gradients = false, indicator_height = 1,
indicator_gap = 0,
gradients = true,
rounding = 6,
gradient_rounding = 6,
round_only_edges = true,
gradient_round_only_edges = true,
keep_upper_gap = false,
gaps_in = 2,
gaps_out = 0,
-- --- Active tab ---
col = { col = {
active = c.accent, active = c.overlay,
inactive = c.surface, inactive = c.surface,
locked_active = c.alarm,
locked_inactive = c.surface,
}, },
text_color = c.ink, text_color = c.accent,
-- --- Inactive tab ---
text_color_inactive = c.muted, text_color_inactive = c.muted,
indicator_height = 3,
}, },
}, },
+14 -6
View File
@@ -296,13 +296,18 @@ network and bluetooth, but keep running as the secret and pairing agents.
## The rest of the desktop ## The rest of the desktop
- **Hyprland** — `hypr/lua/look.lua`. No rounding, no blur, both opacities at - **Hyprland** — `hypr/lua/look.lua`. Windows round at 8px, concentric with
`1.0`, 1px borders. The active border runs from the light into the suit's the opening's 18px corners at a 10px gap (`Theme.frameRadius - gaps_out`);
grey; the inactive border is `overlay`. The shadow is soft and wide, so change one and the others follow. No blur, both opacities at `1.0`, 1px
windows float in the opening rather than being printed onto it. `gaps_out` borders running from the light into the suit's grey. The shadow is soft and
is measured from the hull's opening, since the hull reserves its own zones. wide, so windows float in the opening. Group tabs are solid hull slabs, the
current one lifted a step with its title in the light. Motion
(`hypr/lua/animations.lua`) glides and settles with no overshoot; layers only
fade, since quickshell animates its own surfaces.
- **Alacritty** — `alacritty/yorha.toml`, selected by the `import` in - **Alacritty** — `alacritty/yorha.toml`, selected by the `import` in
`alacritty.toml`. Muted earth hues on the void; selection inverts. `alacritty.toml`. Muted earth hues on the void; selection inverts.
Alacritty only live-reloads when `alacritty.toml` itself changes, so touch
it after editing the palette.
The Catppuccin and `takemi.toml` files are left in place, unreferenced. The Catppuccin and `takemi.toml` files are left in place, unreferenced.
- **Neovim** — still the `catppuccin` plugin, flavour mocha, configured in - **Neovim** — still the `catppuccin` plugin, flavour mocha, configured in
`nvim/lua/plugins/ui.lua`. Not yet moved to the YoRHa palette. `nvim/lua/plugins/ui.lua`. Not yet moved to the YoRHa palette.
@@ -333,7 +338,10 @@ rectangles; the knobs are kept so a shape can be brought back on purpose. The
porthole's curve is the only one on screen, which is what lets it read. porthole's curve is the only one on screen, which is what lets it read.
**Motion glides.** No overshoot anywhere: things ease in on `OutCubic` or **Motion glides.** No overshoot anywhere: things ease in on `OutCubic` or
`OutExpo` and settle. NieR's interface is calm and mechanical. `OutExpo` and settle. Nothing scales up or nudges sideways on hover either;
hover brightens and marks, the way NieR's menus do. Meters are thin segment
rows, album art sits square in a hairline frame, and the bar's player shows a
five-hairline signal meter.
Each surface is **one continuous slab**, with modules divided by hairline rules Each surface is **one continuous slab**, with modules divided by hairline rules
rather than by gaps. A row of separate floating cards reads as clutter; that is rather than by gaps. A row of separate floating cards reads as clutter; that is
+6 -7
View File
@@ -53,16 +53,15 @@ BarPill {
} }
} }
// Declared with room for its own lean, so the slash sits inside the box // A hairline between the date and the time.
// the Row gave it instead of hanging several px into the date column. Rectangle {
Skew {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 22 * Theme.skew + 3 width: 1
height: 22 height: 18
color: Theme.alpha(Theme.accent, 0.6) color: Theme.alpha(Theme.outline, 0.6)
} }
// Archivo Black's digits are not all the same width, so a clock sized to // The display face's digits are not all the same width, so a clock sized to
// its own text changed width as the minutes rolled over — 11:11 is // its own text changed width as the minutes rolled over — 11:11 is
// several px narrower than 08:48 — and shoved the rest of the bar // several px narrower than 08:48 — and shoved the rest of the bar
// around. Reserve the widest possible reading and centre inside it. // around. Reserve the widest possible reading and centre inside it.
+25 -31
View File
@@ -41,40 +41,27 @@ BarPill {
anchors.left: parent.left anchors.left: parent.left
spacing: Theme.padS spacing: Theme.padS
// Album art, chopped into the house shape. // Album art in a hairline frame.
Item { Rectangle {
id: artFrame id: artFrame
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 24 width: 24
height: 24 height: 24
color: Theme.surface
Shape { border.width: 1
id: artMask border.color: Theme.overlay
anchors.fill: parent
visible: false
layer.enabled: true
layer.smooth: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
fillColor: "white"
strokeColor: "transparent"
PathPolyline {
path: Geom.chamfer(Geom.parallelogram(24, 24, 0.18), 5, [1, 3])
}
}
}
Image { Image {
id: art id: art
anchors.fill: parent anchors.fill: parent
anchors.margins: 1
source: Media.artUrl source: Media.artUrl
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
cache: true cache: true
asynchronous: true asynchronous: true
visible: status === Image.Ready visible: status === Image.Ready
layer.enabled: true sourceSize.width: 48
layer.smooth: true sourceSize.height: 48
layer.effect: MaskedArt { maskItem: artMask }
} }
// Fallback: the source app's glyph. // Fallback: the source app's glyph.
@@ -124,8 +111,10 @@ BarPill {
} }
} }
// Compact play-state equaliser. It moves only while audio is playing // Signal meter: five hairline bars, NieR's audio readout rather than a
// and freezes into three quiet bars when paused. // chunky equaliser. It moves only while audio is playing and settles
// into a row of square dots when paused — the channel is open, there
// is just nothing on it.
// //
// Sampled from a timer rather than tweened by an infinite // Sampled from a timer rather than tweened by an infinite
// NumberAnimation. DP-1 runs at 240 Hz, and a frame-synced animation // NumberAnimation. DP-1 runs at 240 Hz, and a frame-synced animation
@@ -144,7 +133,7 @@ BarPill {
Row { Row {
id: eq id: eq
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
spacing: 2 spacing: 3
// Sampling interval — the one knob trading smoothness against cost. // Sampling interval — the one knob trading smoothness against cost.
// Frames are the whole expense: a full-bar repaint runs about 0.3% of // Frames are the whole expense: a full-bar repaint runs about 0.3% of
@@ -163,8 +152,8 @@ BarPill {
// sine would, is three times the motion and reads far busier than // sine would, is three times the motion and reads far busier than
// this meter is supposed to. // this meter is supposed to.
function level(i: int): real { function level(i: int): real {
const lo = 4 + (i * 5) % 13; // old first NumberAnimation `to` const lo = 2 + (i * 3) % 5;
const hi = 14 - (i * 4) % 9; // old second NumberAnimation `to` const hi = 14 - (i * 4) % 7;
const d1 = 320 + i * 90; // ...and their durations const d1 = 320 + i * 90; // ...and their durations
const d2 = 280 + i * 70; const d2 = 280 + i * 70;
@@ -188,12 +177,12 @@ BarPill {
} }
Repeater { Repeater {
model: 3 model: 5
Rectangle { Rectangle {
required property int index required property int index
width: 3 width: 1
height: Media.playing ? eq.level(index) : 6 height: Media.playing ? eq.level(index) : 2
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: Media.playing ? Theme.primary : Theme.muted color: Media.playing ? Theme.primary : Theme.muted
@@ -212,11 +201,16 @@ BarPill {
function onTrackChanged() { titleGlitch.restart(); } function onTrackChanged() { titleGlitch.restart(); }
} }
// A new track resolves in, the way NieR text does: a brief dip and back.
SequentialAnimation { SequentialAnimation {
id: titleGlitch id: titleGlitch
NumberAnimation { NumberAnimation {
target: artFrame; property: "rotation" target: meta; property: "opacity"
from: -8; to: 0; duration: Theme.durSlow; easing.type: Easing.OutCubic to: 0.25; duration: 60
}
NumberAnimation {
target: meta; property: "opacity"
to: 1; duration: Theme.durSlow; easing.type: Easing.OutCubic
} }
} }
+3 -9
View File
@@ -122,12 +122,10 @@ Item {
} }
// Leading-edge wipe. On the rail this runs vertically down the left side, // Leading-edge wipe. On the rail this runs vertically down the left side,
// mirroring the slash P5Panel puts on horizontal modules. // mirroring the accent bar P5Panel puts on horizontal modules.
// //
// Held off the edge by `wipeInset`: the icon spans the rail's full width, so // Held off the edge by `wipeInset`: the icon spans the rail's full width, so
// at x = 0 this landed exactly on the slab's own border, and the 1.08 hover // at x = 0 this landed exactly on the slab's own border.
// scale then pushed it out past the silhouette onto the desktop. The inset
// has to survive that scale, which is why it is not simply 1 or 2.
// //
// It is the accent rather than `tint` because it only ever appears on // It is the accent rather than `tint` because it only ever appears on
// hover, and hover is the accent everywhere else in the shell (Theme.glow). // hover, and hover is the accent everywhere else in the shell (Theme.glow).
@@ -137,7 +135,7 @@ Item {
anchors.leftMargin: root.wipeInset anchors.leftMargin: root.wipeInset
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 2 width: 2
height: root.hovered ? root.height - 4 : 0 height: root.hovered ? Theme.marker * 2 : 0
color: Theme.glow color: Theme.glow
Behavior on height { Behavior on height {
@@ -145,10 +143,6 @@ Item {
} }
} }
scale: root.hovered ? 1.08 : 1.0
Behavior on scale {
NumberAnimation { duration: Theme.durBase; easing.type: Easing.OutCubic }
}
onAlarmChanged: if (alarm) glyph.bump() onAlarmChanged: if (alarm) glyph.bump()
onLiveChanged: if (live) glyph.bump() onLiveChanged: if (live) glyph.bump()
-4
View File
@@ -77,10 +77,6 @@ Column {
} }
} }
scale: itemHover.hovered ? 1.18 : 1.0
Behavior on scale {
NumberAnimation { duration: Theme.durBase; easing.type: Easing.OutCubic }
}
HoverHandler { HoverHandler {
id: itemHover id: itemHover
-1
View File
@@ -76,7 +76,6 @@ Item {
NumberAnimation { NumberAnimation {
duration: Theme.durSlow duration: Theme.durSlow
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
easing.overshoot: 1.6
} }
} }
+1 -1
View File
@@ -35,7 +35,7 @@ Text {
id: bumpAnim id: bumpAnim
NumberAnimation { NumberAnimation {
target: root; property: "scale" target: root; property: "scale"
to: 1.35; duration: Theme.durFast; easing.type: Easing.OutExpo to: 1.12; duration: Theme.durFast; easing.type: Easing.OutExpo
} }
NumberAnimation { NumberAnimation {
target: root; property: "scale" target: root; property: "scale"
+3 -3
View File
@@ -2,8 +2,8 @@ import QtQuick
import "root:/config" import "root:/config"
import "root:/components" import "root:/components"
// One metric in a popout: a label and its reading on a line, with a wide // One metric in a popout: a label and its reading on a line, with a thin
// sheared segmented meter underneath. // segmented meter underneath.
// //
// This replaces the radial Gauge. A 270° dial was the only curved thing in a // This replaces the radial Gauge. A 270° dial was the only curved thing in a
// shell whose whole geometry is sheared slabs and hard chamfers, so it read as // shell whose whole geometry is sheared slabs and hard chamfers, so it read as
@@ -78,7 +78,7 @@ Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
height: 12 height: 4
segments: root.segments segments: root.segments
spacing: root.segmentSpacing spacing: root.segmentSpacing
// Fill the row exactly rather than leaving a ragged tail on the right. // Fill the row exactly rather than leaving a ragged tail on the right.
+3 -1
View File
@@ -12,7 +12,9 @@ FocusScope {
property bool destructive: false property bool destructive: false
property bool selected: false property bool selected: false
property real lean: Theme.skew property real lean: Theme.skew
property real lunge: 4 // Sideways nudge on hover. Zero: NieR buttons do not move, the marker
// and the inversion carry the state.
property real lunge: 0
property bool wide: false property bool wide: false
signal clicked() signal clicked()
+5 -5
View File
@@ -171,8 +171,8 @@ PanelWindow {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
// Natural width, not a fixed one — SplitText offsets its // Natural width, not a fixed one — SplitText offsets its
// ghosts with left/right anchor margins, so pinning the // ghosts with left/right anchor margins, so pinning the
// width and right-aligning the text would flatten the RGB // width and right-aligning the text would flatten the
// split away. The title's left edge is anchored, so a // glitch flicker away. The title's left edge is anchored, so a
// widening readout only moves its own left edge. // widening readout only moves its own left edge.
text: Math.round(root.level * 100) + "%" text: Math.round(root.level * 100) + "%"
pixelSize: Theme.fsBody pixelSize: Theme.fsBody
@@ -181,10 +181,10 @@ PanelWindow {
MeterBar { MeterBar {
width: parent.width width: parent.width
height: 10 height: 4
segments: 20 segments: 20
segmentWidth: (width - 19 * 3) / 20 segmentWidth: (width - 19 * 2) / 20
spacing: 3 spacing: 2
value: root.level * 100 value: root.level * 100
activeColor: root.tone activeColor: root.tone
} }
-1
View File
@@ -179,7 +179,6 @@ PanelWindow {
selected: root.selectedAction === entry.index selected: root.selectedAction === entry.index
accent: entry.modelData.danger ? Theme.alarm : Theme.primary accent: entry.modelData.danger ? Theme.alarm : Theme.primary
lean: Theme.skew lean: Theme.skew
lunge: -10
onClicked: root.invoke(entry.modelData.act) onClicked: root.invoke(entry.modelData.act)
onHoveredChanged: if (hovered) root.selectedAction = entry.index onHoveredChanged: if (hovered) root.selectedAction = entry.index
onActiveFocusChanged: if (actionButton.activeFocus) root.selectedAction = entry.index onActiveFocusChanged: if (actionButton.activeFocus) root.selectedAction = entry.index
+4 -9
View File
@@ -80,14 +80,9 @@ PanelWindow {
} }
} }
// Countdown rule that drains along the bottom edge. // Countdown hairline that drains along the bottom edge. The
// // chop and lean terms are zero under the YoRHa theme; they are
// It used to be a full-bleed rectangle across the whole toast, // kept so the fuse still fits if a shape is brought back.
// which meant it ran straight through the card's chopped
// bottom-left corner and out the other side — a stray bar
// hanging off the card at the one place the silhouette is
// supposed to be cut. It lives inside the cut now, and ends
// where the leaning right edge does.
readonly property real fuseSpan: Math.max(0, readonly property real fuseSpan: Math.max(0,
width - card.chop - card.implicitHeight * card.lean) width - card.chop - card.implicitHeight * card.lean)
@@ -96,7 +91,7 @@ PanelWindow {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: 1 anchors.bottomMargin: 1
x: card.chop x: card.chop
height: 2 height: 1
width: toast.fuseSpan width: toast.fuseSpan
lean: card.lean lean: card.lean
color: Theme.alpha(card.tone, 0.7) color: Theme.alpha(card.tone, 0.7)
+3 -3
View File
@@ -99,10 +99,10 @@ Popout {
anchors.topMargin: Theme.padM anchors.topMargin: Theme.padM
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
height: 12 height: 4
segments: 20 segments: 20
segmentWidth: (width - 19 * 3) / 20 segmentWidth: (width - 19 * 2) / 20
spacing: 3 spacing: 2
value: Battery.percent value: Battery.percent
activeColor: Battery.tint activeColor: Battery.tint
} }
+6 -52
View File
@@ -32,62 +32,30 @@ Popout {
subtitle: Media.identity subtitle: Media.identity
} }
// Cover art, chopped. // Cover art in a hairline frame.
Item { Rectangle {
id: artFrame id: artFrame
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: Theme.padM anchors.topMargin: Theme.padM
anchors.left: parent.left anchors.left: parent.left
width: 108 width: 108
height: 108 height: 108
color: Theme.surface
Shape { border.width: 1
id: artMask border.color: Theme.overlay
anchors.fill: parent
visible: false
layer.enabled: true
// Without this the mask texture is point-sampled, so the
// antialiased pixels along the cuts get snapped back to hard
// steps before MultiEffect ever sees them — see MaskedArt.
layer.smooth: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
fillColor: "white"
strokeColor: "transparent"
PathPolyline {
path: Geom.chamfer(Geom.parallelogram(108, 108, 0.1), 18, [1, 3])
}
}
}
Image { Image {
id: art id: art
anchors.fill: parent anchors.fill: parent
anchors.margins: 1
source: Media.artUrl source: Media.artUrl
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
asynchronous: true asynchronous: true
cache: true cache: true
visible: status === Image.Ready visible: status === Image.Ready
layer.enabled: true
layer.smooth: true
layer.effect: MaskedArt { maskItem: artMask }
} }
// Placeholder when the player exposes no artwork. // Placeholder when the player exposes no artwork.
Shape {
anchors.fill: parent
visible: art.status !== Image.Ready
preferredRendererType: Shape.CurveRenderer
ShapePath {
fillColor: Theme.surface
strokeColor: Theme.surfaceAlt
strokeWidth: 2
PathPolyline {
path: Geom.chamfer(Geom.parallelogram(108, 108, 0.1), 18, [1, 3])
}
}
}
Icon { Icon {
anchors.centerIn: parent anchors.centerIn: parent
visible: art.status !== Image.Ready visible: art.status !== Image.Ready
@@ -95,16 +63,6 @@ Popout {
font.pixelSize: 42 font.pixelSize: 42
color: Theme.muted color: Theme.muted
} }
// Spinning rule around the art while playing.
Rectangle {
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 30
height: 3
color: Theme.accent
visible: Media.playing
}
} }
Column { Column {
@@ -363,10 +321,6 @@ Popout {
} }
} }
scale: tbHover.hovered && tb.enabled ? 1.12 : 1.0
Behavior on scale {
NumberAnimation { duration: Theme.durBase; easing.type: Easing.OutCubic }
}
HoverHandler { HoverHandler {
id: tbHover id: tbHover
+1 -1
View File
@@ -17,7 +17,7 @@ Item {
Row { Row {
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
spacing: Theme.padS spacing: Theme.padM
Icon { Icon {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter