feat(quickshell): "Take your time." glows and flickers
A new GlowText component lights a line from inside: a soft two-layer halo that breathes slowly, and every 4-14 seconds a short, uneven stutter like a tube catching. Only opacity and the halo move, so the glyphs stay on the pixel grid. The lock screen and the power menu use it; the power menu's stops while it is closed.
This commit is contained in:
1 parent
c36a74e861
commit
e9735d1129
4 files changed
+124
-16
No files matched your search
@@ -252,7 +252,8 @@ session.
|
|||||||
|
|
||||||
It is the wallpaper, barely softened, with the face set in the dark of the
|
It is the wallpaper, barely softened, with the face set in the dark of the
|
||||||
porthole: the mark, the time in light display type, the date, "Take your
|
porthole: the mark, the time in light display type, the date, "Take your
|
||||||
time.", a status log
|
time." glowing faintly with the odd flicker (`components/GlowText.qml`), a
|
||||||
|
status log
|
||||||
(battery, network, what is playing, unread notices), and a rounded field that
|
(battery, network, what is playing, unread notices), and a rounded field that
|
||||||
fills with dots as you type. The dots breathe while PAM checks; a wrong
|
fills with dots as you type. The dots breathe while PAM checks; a wrong
|
||||||
password turns them and the field's rim to rust and nudges the field once.
|
password turns them and the field's rim to rust and nudges the field once.
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import "root:/config"
|
||||||
|
|
||||||
|
// A line of type lit from inside, like a tube in a dark console: a soft halo
|
||||||
|
// that breathes slowly, and every so often a short, uneven stutter where the
|
||||||
|
// light catches — two or three dips, then steady again for a while. The
|
||||||
|
// stutters come at random intervals so it never settles into a rhythm you
|
||||||
|
// could count.
|
||||||
|
//
|
||||||
|
// Only the halo and the opacity move, never the geometry, so the glyphs stay
|
||||||
|
// on the pixel grid. Set `active` false while the text is off screen to stop
|
||||||
|
// the animation.
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string text: ""
|
||||||
|
property color color: Theme.accent
|
||||||
|
property color glowColor: root.color
|
||||||
|
property string family: Theme.fontDisplay
|
||||||
|
property int pixelSize: Theme.fsLarge
|
||||||
|
property int weight: Font.Light
|
||||||
|
property real letterSpacing: 0
|
||||||
|
property bool active: true
|
||||||
|
|
||||||
|
// 0..1, how lit the tube is at this instant.
|
||||||
|
property real lit: 1
|
||||||
|
// Slow breathing on the halo alone, between `low` and 1.
|
||||||
|
property real breath: 1
|
||||||
|
|
||||||
|
implicitWidth: label.implicitWidth
|
||||||
|
implicitHeight: label.implicitHeight
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
visible: false
|
||||||
|
text: root.text
|
||||||
|
color: root.color
|
||||||
|
font.family: root.family
|
||||||
|
font.pixelSize: root.pixelSize
|
||||||
|
font.weight: root.weight
|
||||||
|
font.letterSpacing: root.letterSpacing
|
||||||
|
// Greyscale antialiasing: the glyphs are drawn into the effect's
|
||||||
|
// texture, and native subpixel rendering there leaves colour fringes.
|
||||||
|
renderType: Text.CurveRendering
|
||||||
|
}
|
||||||
|
|
||||||
|
// A wide, faint halo under a tight, brighter one, so the light both
|
||||||
|
// spreads into the dark and hugs the strokes.
|
||||||
|
MultiEffect {
|
||||||
|
source: label
|
||||||
|
anchors.fill: label
|
||||||
|
autoPaddingEnabled: true
|
||||||
|
opacity: root.lit
|
||||||
|
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: root.glowColor
|
||||||
|
shadowHorizontalOffset: 0
|
||||||
|
shadowVerticalOffset: 0
|
||||||
|
shadowBlur: 1.0
|
||||||
|
blurMax: Math.max(32, Math.round(root.pixelSize * 2))
|
||||||
|
shadowOpacity: 0.7 * root.breath
|
||||||
|
shadowScale: 1.02
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
source: label
|
||||||
|
anchors.fill: label
|
||||||
|
autoPaddingEnabled: true
|
||||||
|
opacity: 0.55 + 0.45 * root.lit
|
||||||
|
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: root.glowColor
|
||||||
|
shadowHorizontalOffset: 0
|
||||||
|
shadowVerticalOffset: 0
|
||||||
|
shadowBlur: 1.0
|
||||||
|
blurMax: Math.max(8, Math.round(root.pixelSize * 0.35))
|
||||||
|
shadowOpacity: 0.9 * root.lit
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation on breath {
|
||||||
|
running: root.active
|
||||||
|
loops: Animation.Infinite
|
||||||
|
NumberAnimation { to: 0.6; duration: 2600; easing.type: Easing.InOutSine }
|
||||||
|
NumberAnimation { to: 1.0; duration: 2600; easing.type: Easing.InOutSine }
|
||||||
|
}
|
||||||
|
|
||||||
|
// One stutter: uneven dips of uneven length, then back to full.
|
||||||
|
SequentialAnimation {
|
||||||
|
id: stutter
|
||||||
|
NumberAnimation { target: root; property: "lit"; to: 0.25; duration: 40 }
|
||||||
|
NumberAnimation { target: root; property: "lit"; to: 0.9; duration: 60 }
|
||||||
|
PauseAnimation { duration: 70 }
|
||||||
|
NumberAnimation { target: root; property: "lit"; to: 0.4; duration: 30 }
|
||||||
|
NumberAnimation { target: root; property: "lit"; to: 1.0; duration: 180; easing.type: Easing.OutCubic }
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: nextStutter
|
||||||
|
running: root.active
|
||||||
|
repeat: true
|
||||||
|
interval: 6000
|
||||||
|
onTriggered: {
|
||||||
|
stutter.restart();
|
||||||
|
nextStutter.interval = 4000 + Math.random() * 10000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveChanged: if (!root.active) { stutter.stop(); root.lit = 1; }
|
||||||
|
}
|
||||||
@@ -132,15 +132,14 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The one line the face says to you. Nothing here is in a hurry.
|
// The one line the face says to you. Nothing here is in a hurry.
|
||||||
Text {
|
Item { width: 1; height: Math.round(Theme.padL * surface.uiScale) }
|
||||||
topPadding: Math.round(Theme.padL * surface.uiScale)
|
|
||||||
leftPadding: Math.round(4 * surface.uiScale)
|
GlowText {
|
||||||
|
x: Math.round(4 * surface.uiScale)
|
||||||
text: "Take your time."
|
text: "Take your time."
|
||||||
color: Theme.subtext
|
color: Theme.subtext
|
||||||
font.family: Theme.fontDisplay
|
glowColor: Theme.accent
|
||||||
font.pixelSize: Math.round(Theme.fsLarge * surface.uiScale)
|
pixelSize: Math.round(Theme.fsLarge * surface.uiScale)
|
||||||
font.weight: Font.Light
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { width: 1; height: Math.round(Theme.padXL * surface.uiScale) }
|
Item { width: 1; height: Math.round(Theme.padXL * surface.uiScale) }
|
||||||
|
|||||||
@@ -98,15 +98,13 @@ PanelWindow {
|
|||||||
size: 26
|
size: 26
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Item { width: 1; height: Theme.padM }
|
||||||
topPadding: Theme.padM
|
|
||||||
|
GlowText {
|
||||||
text: "Take your time."
|
text: "Take your time."
|
||||||
color: Theme.accent
|
pixelSize: Theme.fsHuge
|
||||||
font.family: Theme.fontDisplay
|
letterSpacing: 1
|
||||||
font.pixelSize: Theme.fsHuge
|
active: root.open
|
||||||
font.weight: Font.Light
|
|
||||||
font.letterSpacing: 1
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
|||||||
Reference in new issue
Block a user