fix(quickshell): keep text sharp under zoom and on the lock screen
Native-rendered glyphs are rasterised at rest size, so anything moved with `scale` stretched a bitmap and came out blurred and stepped. - Icons render as curves, so their pulse and bump stay smooth. - The lock face is laid out at uiScale instead of being drawn at 1080p size and scaled by up to 1.3, and it sits on whole pixels. Its native labels now land on the pixel grid. - The OSD rises into place instead of scaling in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f1d16e5175
commit
b86e584b02
4 files changed
+76
-61
No files matched your search
@@ -13,7 +13,11 @@ Text {
|
||||
color: Theme.primary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
renderType: Text.NativeRendering
|
||||
// Curve glyphs, not native ones. Native glyphs are rasterised once at rest
|
||||
// size and the bitmap is stretched when `scale` moves, so every pulse and
|
||||
// bump came out blurred and stepped. Curves are drawn from the outline at
|
||||
// whatever size the icon is at that frame.
|
||||
renderType: Text.CurveRendering
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.durBase }
|
||||
|
||||
@@ -92,11 +92,12 @@ Item {
|
||||
|
||||
Column {
|
||||
id: face
|
||||
x: parent.width * 0.14
|
||||
y: parent.height * 0.2
|
||||
width: 380
|
||||
scale: surface.uiScale
|
||||
transformOrigin: Item.TopLeft
|
||||
// Sized by uiScale rather than drawn small and stretched with `scale`:
|
||||
// a scale transform blurs native text, and every label here is native.
|
||||
// Whole pixels, too, or the glyphs land between them.
|
||||
x: Math.round(parent.width * 0.14)
|
||||
y: Math.round(parent.height * 0.2)
|
||||
width: Math.round(380 * surface.uiScale)
|
||||
opacity: 0
|
||||
spacing: 0
|
||||
|
||||
@@ -104,42 +105,42 @@ Item {
|
||||
|
||||
// The house mark, the same porthole the bar carries far left.
|
||||
Porthole {
|
||||
size: 26
|
||||
size: Math.round(26 * surface.uiScale)
|
||||
}
|
||||
|
||||
Text {
|
||||
topPadding: Theme.padM
|
||||
topPadding: Math.round(Theme.padM * surface.uiScale)
|
||||
text: Qt.formatDateTime(clock.date, "HH:mm")
|
||||
color: Theme.accent
|
||||
font.family: Theme.fontDisplay
|
||||
font.pixelSize: Theme.fsMega * 1.35
|
||||
font.pixelSize: Math.round(Theme.fsMega * 1.35 * surface.uiScale)
|
||||
font.weight: Font.Light
|
||||
font.letterSpacing: 2
|
||||
// Scaled with the face, so distance-field glyphs: native hinting
|
||||
// at this size under a scale transform comes out stepped.
|
||||
renderType: Text.QtRendering
|
||||
font.letterSpacing: 2 * surface.uiScale
|
||||
// Display size, where curves beat hinted bitmaps.
|
||||
renderType: Text.CurveRendering
|
||||
}
|
||||
|
||||
Text {
|
||||
leftPadding: 4
|
||||
leftPadding: Math.round(4 * surface.uiScale)
|
||||
text: Qt.formatDateTime(clock.date, "dddd · dd MMMM").toUpperCase()
|
||||
color: Theme.subtext
|
||||
font.family: Theme.fontDisplay
|
||||
font.pixelSize: Theme.fsBody
|
||||
font.pixelSize: Math.round(Theme.fsBody * surface.uiScale)
|
||||
font.weight: Theme.weightDisplay
|
||||
font.letterSpacing: 5
|
||||
font.letterSpacing: 5 * surface.uiScale
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
||||
Item { width: 1; height: Theme.padXL }
|
||||
Item { width: 1; height: Math.round(Theme.padXL * surface.uiScale) }
|
||||
|
||||
// ------------------------------------------------------ status log
|
||||
Column {
|
||||
width: parent.width
|
||||
leftPadding: 4
|
||||
leftPadding: Math.round(4 * surface.uiScale)
|
||||
|
||||
StatusRow {
|
||||
width: parent.width - 4
|
||||
width: parent.width - parent.leftPadding
|
||||
uiScale: surface.uiScale
|
||||
visible: Battery.available
|
||||
label: "Battery"
|
||||
detail: Math.round(Battery.percent) + "%"
|
||||
@@ -148,14 +149,16 @@ Item {
|
||||
}
|
||||
|
||||
StatusRow {
|
||||
width: parent.width - 4
|
||||
width: parent.width - parent.leftPadding
|
||||
uiScale: surface.uiScale
|
||||
label: "Network"
|
||||
detail: Sys.netUp ? Net.label : "no link"
|
||||
tone: Sys.netUp ? Theme.accentSoft : Theme.overlay
|
||||
}
|
||||
|
||||
StatusRow {
|
||||
width: parent.width - 4
|
||||
width: parent.width - parent.leftPadding
|
||||
uiScale: surface.uiScale
|
||||
visible: Media.hasPlayer
|
||||
label: Media.playing ? "Playing" : "Paused"
|
||||
detail: Media.artist ? Media.title + " — " + Media.artist : Media.title
|
||||
@@ -163,7 +166,8 @@ Item {
|
||||
}
|
||||
|
||||
StatusRow {
|
||||
width: parent.width - 4
|
||||
width: parent.width - parent.leftPadding
|
||||
uiScale: surface.uiScale
|
||||
visible: Notifs.count > 0
|
||||
label: "Notices"
|
||||
detail: Notifs.count + (Notifs.count === 1 ? " message" : " messages")
|
||||
@@ -171,20 +175,20 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Item { width: 1; height: Theme.padXL }
|
||||
Item { width: 1; height: Math.round(Theme.padXL * surface.uiScale) }
|
||||
|
||||
// ------------------------------------------------------ the field
|
||||
Item {
|
||||
id: authBlock
|
||||
width: parent.width
|
||||
height: field.height + hint.height + Theme.padS
|
||||
height: field.height + hint.height + hint.anchors.topMargin
|
||||
|
||||
transform: Translate { id: nudge }
|
||||
|
||||
Rectangle {
|
||||
id: field
|
||||
width: parent.width
|
||||
height: 48
|
||||
height: Math.round(48 * surface.uiScale)
|
||||
radius: height / 2
|
||||
color: Theme.alpha(Theme.hull, 0.86)
|
||||
border.width: 1
|
||||
@@ -199,23 +203,23 @@ Item {
|
||||
Icon {
|
||||
id: lockGlyph
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.padL
|
||||
anchors.leftMargin: Math.round(Theme.padL * surface.uiScale)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: ""
|
||||
color: surface.failed ? Theme.alarm : Theme.muted
|
||||
font.pixelSize: Theme.fsLarge
|
||||
font.pixelSize: Math.round(Theme.fsLarge * surface.uiScale)
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.left: lockGlyph.right
|
||||
anchors.leftMargin: Theme.padM
|
||||
anchors.leftMargin: Math.round(Theme.padM * surface.uiScale)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: surface.entry.length === 0
|
||||
text: "PASSWORD"
|
||||
color: Theme.muted
|
||||
font.family: Theme.fontDisplay
|
||||
font.pixelSize: Theme.fsSmall
|
||||
font.letterSpacing: Theme.tracking * 2
|
||||
font.pixelSize: Math.round(Theme.fsSmall * surface.uiScale)
|
||||
font.letterSpacing: Theme.tracking * 2 * surface.uiScale
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
||||
@@ -224,11 +228,11 @@ Item {
|
||||
Row {
|
||||
id: dots
|
||||
anchors.left: lockGlyph.right
|
||||
anchors.leftMargin: Theme.padM
|
||||
anchors.leftMargin: Math.round(Theme.padM * surface.uiScale)
|
||||
anchors.right: enter.left
|
||||
anchors.rightMargin: Theme.padM
|
||||
anchors.rightMargin: Math.round(Theme.padM * surface.uiScale)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 7
|
||||
spacing: Math.round(7 * surface.uiScale)
|
||||
clip: true
|
||||
|
||||
Repeater {
|
||||
@@ -238,8 +242,8 @@ Item {
|
||||
id: pip
|
||||
required property int index
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 7
|
||||
height: 7
|
||||
width: Math.round(7 * surface.uiScale)
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: surface.failed ? Theme.alarm : Theme.accent
|
||||
opacity: 0
|
||||
@@ -276,10 +280,10 @@ Item {
|
||||
id: enter
|
||||
readonly property bool ready: surface.entry.length > 0 && !surface.busy
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.rightMargin: Math.round(8 * surface.uiScale)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 32
|
||||
height: 32
|
||||
width: Math.round(32 * surface.uiScale)
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: enter.ready ? Theme.accent : "transparent"
|
||||
border.width: enter.ready ? 0 : 1
|
||||
@@ -293,7 +297,7 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
text: ""
|
||||
color: enter.ready ? Theme.ink : Theme.overlay
|
||||
font.pixelSize: Theme.fsLarge
|
||||
font.pixelSize: Math.round(Theme.fsLarge * surface.uiScale)
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
@@ -330,16 +334,16 @@ Item {
|
||||
Row {
|
||||
id: hint
|
||||
anchors.top: field.bottom
|
||||
anchors.topMargin: Theme.padS
|
||||
anchors.topMargin: Math.round(Theme.padS * surface.uiScale)
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.padL
|
||||
spacing: Theme.padS
|
||||
anchors.leftMargin: Math.round(Theme.padL * surface.uiScale)
|
||||
spacing: Math.round(Theme.padS * surface.uiScale)
|
||||
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 4
|
||||
height: 4
|
||||
radius: 2
|
||||
width: Math.round(4 * surface.uiScale)
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: surface.failed ? Theme.alarm : Theme.overlay
|
||||
}
|
||||
|
||||
@@ -350,8 +354,8 @@ Item {
|
||||
: (surface.busy ? "VERIFYING" : (Quickshell.env("USER") || "user"))
|
||||
color: surface.failed ? Theme.alarm : Theme.muted
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: Theme.fsMicro
|
||||
font.letterSpacing: 2
|
||||
font.pixelSize: Math.round(Theme.fsMicro * surface.uiScale)
|
||||
font.letterSpacing: 2 * surface.uiScale
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,18 @@ Item {
|
||||
property string label: ""
|
||||
property string detail: ""
|
||||
property color tone: Theme.primaryDim
|
||||
// The lock face's size factor. The row is laid out at that size rather
|
||||
// than scaled, so its native text stays sharp.
|
||||
property real uiScale: 1
|
||||
|
||||
implicitHeight: visible ? 26 : 0
|
||||
implicitHeight: visible ? Math.round(26 * row.uiScale) : 0
|
||||
|
||||
Rectangle {
|
||||
id: dot
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Theme.marker
|
||||
height: Theme.marker
|
||||
width: Math.round(Theme.marker * row.uiScale)
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: row.tone
|
||||
}
|
||||
@@ -27,24 +30,24 @@ Item {
|
||||
Text {
|
||||
id: name
|
||||
anchors.left: dot.right
|
||||
anchors.leftMargin: Theme.padS
|
||||
anchors.leftMargin: Math.round(Theme.padS * row.uiScale)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Math.min(implicitWidth, row.width * 0.55)
|
||||
text: row.label.toUpperCase()
|
||||
color: Theme.text
|
||||
elide: Text.ElideRight
|
||||
font.family: Theme.fontDisplay
|
||||
font.pixelSize: Theme.fsSmall
|
||||
font.pixelSize: Math.round(Theme.fsSmall * row.uiScale)
|
||||
font.weight: Theme.weightDisplay
|
||||
font.letterSpacing: Theme.tracking
|
||||
font.letterSpacing: Theme.tracking * row.uiScale
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: name.right
|
||||
anchors.leftMargin: Theme.padS
|
||||
anchors.leftMargin: Math.round(Theme.padS * row.uiScale)
|
||||
anchors.right: value.left
|
||||
anchors.rightMargin: Theme.padS
|
||||
anchors.rightMargin: Math.round(Theme.padS * row.uiScale)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 1
|
||||
color: Theme.alpha(Theme.outline, 0.4)
|
||||
@@ -60,7 +63,7 @@ Item {
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: Theme.fsMicro
|
||||
font.pixelSize: Math.round(Theme.fsMicro * row.uiScale)
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
}
|
||||
@@ -113,16 +113,20 @@ PanelWindow {
|
||||
slashWidth: 5
|
||||
padding: Theme.padM
|
||||
|
||||
// It rises a few pixels into place instead of growing: a scale would
|
||||
// stretch the native text in the panel while it moves.
|
||||
opacity: root.shown ? 1 : 0
|
||||
scale: root.shown ? 1 : 0.9
|
||||
transform: Translate {
|
||||
y: root.shown ? 0 : 8
|
||||
|
||||
Behavior on y {
|
||||
NumberAnimation { duration: Theme.durSlow; easing.type: Easing.OutCubic }
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { id: fade; duration: Theme.durBase }
|
||||
}
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: Theme.durSlow; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
Icon {
|
||||
id: glyphIcon
|
||||
anchors.left: parent.left
|
||||
|
||||
Reference in new issue
Block a user