fix: reduce shader usage and fix greeter

This commit is contained in:
2026-10-24 00:35:24 +02:00
parent cbd4e6caa7
commit e35c87b3f2
56 changed files with 819 additions and 638 deletions
+4 -2
View File
@@ -2,17 +2,18 @@ import QtQuick
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
CustomClippingRect { Item {
id: track id: track
required property var scrollBar required property var scrollBar
required property var mouseArea required property var mouseArea
ClipRect {
radius: Tokens.rounding.full
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
implicitHeight: handle.implicitHeight implicitHeight: handle.implicitHeight
radius: Tokens.rounding.full
CustomRect { CustomRect {
id: handle id: handle
@@ -54,3 +55,4 @@ CustomClippingRect {
} }
} }
} }
}
+1 -1
View File
@@ -30,7 +30,7 @@ CustomFlickable {
type: Anim.SlowEffects type: Anim.SlowEffects
} }
} }
// layer.textureSize: Qt.size(width * dpr, height * dpr) layer.textureSize: Qt.size(width * dpr, height * dpr)
layer.effect: Mask { layer.effect: Mask {
maskSource: mask maskSource: mask
+4 -2
View File
@@ -2,17 +2,18 @@ import QtQuick
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
CustomClippingRect { Item {
id: track id: track
required property var scrollBar required property var scrollBar
required property var mouseArea required property var mouseArea
ClipRect {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
implicitWidth: handle.implicitWidth
radius: Tokens.rounding.full radius: Tokens.rounding.full
implicitWidth: handle.implicitWidth
CustomRect { CustomRect {
id: handle id: handle
@@ -54,3 +55,4 @@ CustomClippingRect {
} }
} }
} }
}
+1 -2
View File
@@ -59,7 +59,6 @@ Scope {
name: "Bar-Exclusion" name: "Bar-Exclusion"
screen: root.screen screen: root.screen
mask: Region { mask: Region {}
}
} }
} }
+7 -6
View File
@@ -19,15 +19,11 @@ import ZShell.Config
Item { Item {
id: root id: root
required property Item bar
required property real borderThickness
readonly property alias clipboard: clipboard readonly property alias clipboard: clipboard
readonly property alias dashboard: dashboard readonly property alias dashboard: dashboard
readonly property alias dashboardWrapper: dashboardWrapper readonly property alias dashboardWrapper: dashboardWrapper
readonly property alias dock: dock readonly property alias dock: dock
readonly property alias drawing: drawing readonly property alias drawing: drawing
required property var drawingItem
required property EdgeGeometry geometry
readonly property alias launcher: launcher readonly property alias launcher: launcher
readonly property alias notifications: notifications readonly property alias notifications: notifications
readonly property alias osd: osd readonly property alias osd: osd
@@ -36,14 +32,19 @@ Item {
readonly property alias popoutsWrapper: popouts readonly property alias popoutsWrapper: popouts
readonly property alias resources: resources readonly property alias resources: resources
readonly property alias resourcesWrapper: resourcesWrapper readonly property alias resourcesWrapper: resourcesWrapper
required property ShellScreen screen
required property ScreenState screenState
readonly property alias settings: settings readonly property alias settings: settings
readonly property alias settingsWrapper: settingsWrapper readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar readonly property alias sidebar: sidebar
readonly property alias toasts: toasts readonly property alias toasts: toasts
readonly property alias utilities: utilities readonly property alias utilities: utilities
required property Item bar
required property real borderThickness
required property var drawingItem
required property EdgeGeometry geometry
required property ShellScreen screen
required property ScreenState screenState
anchors.bottomMargin: geometry.insetBottom(borderThickness) anchors.bottomMargin: geometry.insetBottom(borderThickness)
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: geometry.insetLeft(borderThickness) anchors.leftMargin: geometry.insetLeft(borderThickness)
+12 -28
View File
@@ -27,27 +27,21 @@ ColumnLayout {
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary color: Colors.palette.m3secondary
font.bold: true font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build()
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: Time.hourStr text: Time.hourStr
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3primary color: Colors.palette.m3primary
font.bold: true font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build()
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: ":" text: ":"
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary color: Colors.palette.m3secondary
font.bold: true font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build()
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: Time.minuteStr text: Time.minuteStr
} }
} }
@@ -56,13 +50,11 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: -Tokens.padding.large * 2 Layout.topMargin: -Tokens.padding.large * 2
color: Colors.palette.m3tertiary color: Colors.palette.m3tertiary
font.bold: true font: Tokens.font.title.builders.medium.weight(Font.DemiBold).build()
font.family: Config.appearance.font.family.mono
font.pointSize: Math.floor(Tokens.font.size.extraLarge * root.centerScale)
text: Time.format("dddd, d MMMM yyyy") text: Time.format("dddd, d MMMM yyyy")
} }
CustomClippingRect { ClipRect {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Tokens.spacing.largeIncreased * 2 Layout.topMargin: Tokens.spacing.largeIncreased * 2
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
@@ -73,7 +65,7 @@ ColumnLayout {
MaterialIcon { MaterialIcon {
anchors.centerIn: parent anchors.centerIn: parent
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
font.pointSize: Math.floor(root.centerWidth / 4) fontStyle: Tokens.font.icon.size(root.centerWidth / 4).build()
text: "person" text: "person"
} }
@@ -88,9 +80,7 @@ ColumnLayout {
CustomText { CustomText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
font.family: Config.appearance.font.family.mono font: Tokens.font.body.large
font.pointSize: Tokens.font.size.normal
font.weight: 600
text: root.greeter.username text: root.greeter.username
visible: text.length > 0 visible: text.length > 0
} }
@@ -155,8 +145,7 @@ ColumnLayout {
} }
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
} }
@@ -191,7 +180,6 @@ ColumnLayout {
anchors.centerIn: parent anchors.centerIn: parent
color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
font.weight: 500
text: root.greeter.launching ? "hourglass_top" : "arrow_forward" text: root.greeter.launching ? "hourglass_top" : "arrow_forward"
} }
} }
@@ -204,8 +192,7 @@ ColumnLayout {
implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight) implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
Behavior on implicitHeight { Behavior on implicitHeight {
Anim { Anim {}
}
} }
CustomText { CustomText {
@@ -245,12 +232,10 @@ ColumnLayout {
wrapMode: Text.WrapAtWordBoundaryOrAnywhere wrapMode: Text.WrapAtWordBoundaryOrAnywhere
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
Behavior on scale { Behavior on scale {
Anim { Anim {}
}
} }
onMsgChanged: { onMsgChanged: {
@@ -292,8 +277,7 @@ ColumnLayout {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: isError ? Colors.palette.m3error : Colors.palette.m3primary color: isError ? Colors.palette.m3error : Colors.palette.m3primary
font.family: Config.appearance.font.family.mono font: Tokens.font.mono.medium
font.pointSize: Tokens.font.size.small
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
opacity: 0 opacity: 0
scale: 0.7 scale: 0.7
+1 -1
View File
@@ -20,7 +20,7 @@ BusyIndicator {
property int animState property int animState
property color bgColor: Colors.palette.m3secondaryContainer property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary property color fgColor: Colors.palette.m3primary
property real implicitSize: Tokens.font.size.normal * 3 property real implicitSize: Tokens.font.body.medium.pointSize * 3
property real internalStrokeWidth: strokeWidth property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress readonly property alias progress: manager.progress
property real strokeWidth: Tokens.padding.extraSmall * 0.8 property real strokeWidth: Tokens.padding.extraSmall * 0.8
+41
View File
@@ -0,0 +1,41 @@
import QtQuick
import ZShell.Components
Item {
id: root
property real radius
property real topLeftRadius: radius
property real topRightRadius: radius
property real bottomRightRadius: radius
property real bottomLeftRadius: radius
property color color: "transparent"
default property alias contentData: contentItem.data
Item {
id: contentItem
anchors.fill: parent
}
ShaderEffectSource {
id: source
hideSource: true
sourceItem: contentItem
}
RoundedMask {
anchors.fill: parent
source: source
topLeftRadius: root.topLeftRadius
topRightRadius: root.topRightRadius
bottomRightRadius: root.bottomRightRadius
bottomLeftRadius: root.bottomLeftRadius
color: root.color
}
Behavior on color {
CAnim {}
}
}
+23
View File
@@ -1,13 +1,36 @@
import QtQuick import QtQuick
import qs.Services
Flickable { Flickable {
id: root id: root
property bool doneFakeFlick
interactive: !ShellState.forActive().isDrawing
maximumFlickVelocity: 3000 maximumFlickVelocity: 3000
rebound: Transition { rebound: Transition {
onRunningChanged: {
if (!running && !root.doneFakeFlick) {
root.doneFakeFlick = true;
root.flick(1, 1);
root.flick(-1, -1);
Qt.callLater(() => root.cancelFlick());
}
}
Anim { Anim {
properties: "x,y" properties: "x,y"
} }
} }
Timer {
interval: 10
running: root.doneFakeFlick
onTriggered: {
root.doneFakeFlick = false;
root.returnToBounds();
}
}
} }
+23
View File
@@ -1,13 +1,36 @@
import QtQuick import QtQuick
import qs.Services
ListView { ListView {
id: root id: root
property bool doneFakeFlick
interactive: !ShellState.forActive()?.isDrawing
maximumFlickVelocity: 3000 maximumFlickVelocity: 3000
rebound: Transition { rebound: Transition {
// onRunningChanged: {
// if (!running && !root.doneFakeFlick) {
// root.doneFakeFlick = true;
// root.flick(1, 1);
// root.flick(-1, -1);
// Qt.callLater(() => root.cancelFlick());
// }
// }
Anim { Anim {
properties: "x,y" properties: "x,y"
type: Anim.DefaultEffects
} }
} }
// Timer {
// interval: 10
// running: root.doneFakeFlick
//
// onTriggered: {
// root.doneFakeFlick = false;
// }
// }
} }
+1
View File
@@ -4,6 +4,7 @@ MouseArea {
property int scrollAccumulatedY: 0 property int scrollAccumulatedY: 0
function onWheel(event: WheelEvent): void { function onWheel(event: WheelEvent): void {
event.accepted = false;
} }
onWheel: event => { onWheel: event => {
+1 -2
View File
@@ -6,7 +6,6 @@ Rectangle {
color: "transparent" color: "transparent"
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
} }
+9 -7
View File
@@ -14,10 +14,9 @@ Text {
property real animateTo: 1 property real animateTo: 1
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
font.family: Config.appearance.font.family.sans font: Tokens.font.body.small
font.pointSize: Tokens.font.size.normal
linkColor: Colors.palette.m3onPrimaryFixedVariant linkColor: Colors.palette.m3onPrimaryFixedVariant
renderType: Text.NativeRendering renderType: Text.QtRendering
textFormat: Text.PlainText textFormat: Text.PlainText
Behavior on color { Behavior on color {
@@ -28,22 +27,25 @@ Text {
enabled: root.animate enabled: root.animate
SequentialAnimation { SequentialAnimation {
Anim { TAnim {
target: root
to: root.animateFrom to: root.animateFrom
type: Anim.FastEffects
} }
PropertyAction { PropertyAction {
} }
Anim { TAnim {
target: root
to: root.animateTo to: root.animateTo
type: Anim.DefaultEffects
} }
} }
} }
component Anim: NumberAnimation { component TAnim: Anim {
duration: root.animateDuration / 2 duration: root.animateDuration / 2
easing.type: Easing.BezierSpline
properties: root.animateProp.split(",").length > 1 ? root.animateProp : "" properties: root.animateProp.split(",").length > 1 ? root.animateProp : ""
property: root.animateProp.split(",").length === 1 ? root.animateProp : "" property: root.animateProp.split(",").length === 1 ? root.animateProp : ""
target: root target: root
+271 -42
View File
@@ -1,76 +1,305 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Shapes
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
TextField { TextFieldBase {
id: root id: root
background: null enum TextFieldType {
color: Colors.palette.m3onSurface Outlined,
cursorVisible: !readOnly Filled
font.family: Config.appearance.font.family.sans }
font.pointSize: Tokens.font.size.smaller
placeholderTextColor: Colors.palette.m3outline
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
Behavior on color { readonly property int clampedRadius: Math.min(horizontalPadding, Math.min(width, height) / 2, radius)
CAnim { readonly property string effectiveSupportingText: isError && errorText ? errorText : supportingText
readonly property int filledOffset: type === CustomTextField.Filled ? Tokens.spacing.small : 0
readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0
readonly property real smallFontScale: smallFontSize / font.pointSize
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Tokens.spacing.extraSmall : 0
readonly property int trailingOffset: trailingIcon ? trailingIconLoader.width + trailingIconLoader.anchors.rightMargin : 0
readonly property bool valid: !validate || (!text && emptyIsValid) || (validate instanceof RegExp ? validate.test(text) : !!validate(text))
property int horizontalPadding: Tokens.padding.large
property int verticalPadding: Tokens.padding.large
property int radius: Tokens.rounding.small
property string supportingText
property string trailingIcon
property int smallFontSize: Tokens.font.label.small.pointSize
property int type: CustomTextField.Outlined
property var validate // Regex or function
property bool emptyIsValid: true
property string errorText
property bool isError
property string leadingIcon
bottomPadding: verticalPadding + supportingTextOffset - filledOffset
leftPadding: horizontalPadding + leadingOffset
rightPadding: horizontalPadding + trailingOffset
topPadding: verticalPadding + filledOffset
background: Loader {
anchors.bottomMargin: root.supportingTextOffset
anchors.fill: parent
sourceComponent: root.type === CustomTextField.Filled ? filledComp : outlineComp
StateLayer {
id: stateLayer
cursorShape: Qt.IBeamCursor
enabled: !root.activeFocus
manualPressOverride: tapHandler.pressed
radius: root.type === CustomTextField.Outlined ? root.clampedRadius : 0
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
onClicked: root.focus = true
} }
} }
cursorDelegate: CustomRect {
id: cursor
property bool disableBlink onEditingFinished: {
if (!valid)
isError = true;
}
onPressed: {
if (stateLayer.enabled)
stateLayer.press(stateLayer.mouseX, stateLayer.mouseY);
}
onTextEdited: {
if (isError)
isError = false;
}
color: Colors.palette.m3primary Item {
implicitWidth: 2 id: contentWrapper
radius: Tokens.rounding.medium
Behavior on opacity { anchors.bottomMargin: root.supportingTextOffset
anchors.fill: parent
CustomText {
id: placeholder
anchors.left: parent.left
anchors.leftMargin: root.leftPadding
anchors.topMargin: Tokens.padding.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : root.text ? Colors.palette.m3outline : root.placeholderTextColor)
font.family: root.font.family
font.pointSize: root.font.pointSize
font.variableAxes: root.font.variableAxes
font.weight: root.font.weight
renderType: Text.QtRendering
text: root.placeholderText
states: [
State {
name: "smallOutlined"
when: root.type === CustomTextField.Outlined && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding
placeholder.scale: root.smallFontScale
}
AnchorChanges {
anchors.verticalCenter: contentWrapper.top
target: placeholder
}
},
State {
name: "smallFilled"
when: root.type === CustomTextField.Filled && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding + root.leadingOffset
placeholder.scale: root.smallFontScale
}
AnchorChanges {
anchors.top: contentWrapper.top
anchors.verticalCenter: undefined
target: placeholder
}
}
]
transitions: Transition {
Anim { Anim {
duration: Tokens.anim.durations.small properties: "scale,leftMargin"
type: Anim.DefaultEffects
}
AnchorAnim {
duration: Tokens.anim.durations.expressiveDefaultEffects
easing: Tokens.anim.expressiveDefaultEffects
}
} }
} }
Connections { Loader {
function onCursorPositionChanged(): void { id: leadingIconLoader
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1; active: root.leadingIcon
cursor.disableBlink = true; anchors.left: parent.left
enableBlink.restart(); anchors.leftMargin: Tokens.padding.small
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
text: root.leadingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
} }
} }
target: root Loader {
id: trailingIconLoader
active: root.trailingIcon
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
text: root.trailingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
}
}
} }
Timer { Loader {
id: enableBlink id: supportingTextLoader
interval: 100 active: root.effectiveSupportingText
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: root.horizontalPadding
onTriggered: cursor.disableBlink = false sourceComponent: CustomText {
color: root.isError ? Colors.palette.m3error : Colors.palette.m3onSurfaceVariant
font: Tokens.font.label.small
text: root.effectiveSupportingText
}
} }
Timer { TapHandler {
interval: 500 id: tapHandler
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
} }
Binding { Component {
cursor.opacity: 0 id: outlineComp
when: !root.activeFocus || !root.cursorVisible
Shape {
id: bg
asynchronous: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
id: path
readonly property real inset: strokeWidth / 2
readonly property real outlineGap: placeholder.width * root.smallFontScale + Tokens.spacing.small * 2
property real outlineGapScale: root.activeFocus || root.text ? 1 : 0
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: path.inset + root.horizontalPadding - root.clampedRadius + path.outlineGap * (1 - path.outlineGapScale) / 2 + path.outlineGap * path.outlineGapScale
startY: path.inset
strokeColor: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : Colors.palette.m3outline)
strokeWidth: root.activeFocus ? 2 : 1
Behavior on outlineGapScale {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on strokeColor {
CAnim {}
}
Behavior on strokeWidth {
Anim {}
}
PathLine {
x: bg.width - path.inset - root.clampedRadius
y: path.inset
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - path.inset
y: path.inset + root.clampedRadius
}
PathLine {
x: bg.width - path.inset
y: bg.height - path.inset - root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - path.inset - root.clampedRadius
y: bg.height - path.inset
}
PathLine {
x: path.inset + root.clampedRadius
y: bg.height - path.inset
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: path.inset
y: bg.height - path.inset - root.clampedRadius
}
PathLine {
x: path.inset
y: path.inset + root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: path.inset + root.clampedRadius
y: path.inset
}
PathLine {
x: path.inset + root.horizontalPadding - root.clampedRadius / 2 + path.outlineGap * (1 - path.outlineGapScale) / 2
y: path.inset
}
}
}
}
Component {
id: filledComp
CustomRect {
color: root.activeFocus ? Colors.tPalette.m3surfaceContainerHighest : Colors.tPalette.m3surfaceContainerHigh
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : Colors.palette.m3outline)
implicitHeight: root.activeFocus ? 2 : 1
Behavior on implicitHeight {
Anim {}
} }
} }
Behavior on placeholderTextColor {
CAnim {
} }
} }
} }
+3 -2
View File
@@ -1,15 +1,16 @@
import QtQuick import QtQuick
import QtQuick.Controls
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
TextInput { TextInput {
renderType: Text.NativeRendering renderType: Text.NativeRendering
selectedTextColor: Colors.palette.m3onSecondaryContainer selectedTextColor: Colors.palette.m3onSecondaryContainer
selectionColor: Colors.tPalette.m3onSecondaryContainer selectionColor: Colors.tPalette.colSecondaryContainer
font { font {
family: Config.appearance?.font.family.sans ?? "sans-serif" family: Config.appearance?.font.family.sans ?? "sans-serif"
hintingPreference: Font.PreferFullHinting hintingPreference: Font.PreferFullHinting
pixelSize: Tokens?.font.size.normal ?? 15 pixelSize: Config.appearance?.font.size.normal ?? 15
} }
} }
+79
View File
@@ -0,0 +1,79 @@
import QtQuick
import Quickshell
import ZShell.Config
Image {
id: root
property int fadeInAnim: Anim.DefaultEffects
property int fadeInLargeAnim: Anim.StandardLarge
property int fadeOutAnim: Anim.FastEffects
property bool fadingOut
property bool hadPrevious
property bool preventInit
function maybeStartInAnim(): void {
if (!preventInit && !opacityInAnim.running && status === Image.Ready) {
opacityInAnim.type = hadPrevious ? fadeInAnim : fadeInLargeAnim;
opacityInAnim.start();
}
}
asynchronous: true
fillMode: Image.PreserveAspectCrop
opacity: 0
retainWhileLoading: true
sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr);
}
Anim on opacity {
id: opacityInAnim
running: false
to: 1
}
Behavior on source {
SequentialAnimation {
ScriptAction {
script: opacityInAnim.stop()
}
PropertyAction {
property: "fadingOut"
target: root
value: true
}
Anim {
property: "opacity"
target: root
to: 0
type: root.fadeOutAnim
}
PropertyAction {
property: "fadingOut"
target: root
value: false
}
PropertyAction {
property: "hadPrevious"
target: root
value: root.source
}
PropertyAction {
}
ScriptAction {
script: root.maybeStartInAnim()
}
}
}
onPreventInitChanged: maybeStartInAnim()
onStatusChanged: maybeStartInAnim()
}
+115
View File
@@ -0,0 +1,115 @@
import QtQuick
import Quickshell
import M3Shapes
import ZShell.Config
import qs.Services
MaterialShape {
id: root
property bool animated: true
property real cRotation
property bool containsIcon
property real dampingRatio: 0.6
property real lRotation
property int morphAnimRotation: 60
property real morphScale: 0.14
property alias rotateAnimDuration: rotateAnim.duration
property int shapeIndex
property list<int> shapes: {
if (containsIcon)
return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pentagon, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
}
readonly property real springDuration: {
const wn = Math.sqrt(stiffness);
const r = -dampingRatio * wn;
const c = 1 / Math.sqrt(1 - dampingRatio * dampingRatio);
return Math.log(visibilityThreshold / c) / r;
}
readonly property real springMaxVelocity: {
const wn = Math.sqrt(stiffness);
const factor = Math.exp(-z * Math.acos(z) / Math.sqrt(1 - z * z));
return wn * factor;
}
property bool springSettled: true
property real stiffness: 180
property real thisLRotation
property real visibilityThreshold: 0.075
function spring(t: real): var {
const wn = Math.sqrt(stiffness);
const za = dampingRatio * wn;
const wd = wn * Math.sqrt(1 - dampingRatio * dampingRatio);
const r = za / wd;
const pos = 1 - Math.exp(-za * t) * (Math.cos(wd * t) + r * Math.sin(wd * t));
const vel = Math.exp(-za * t) * (wn * wn / wd) * Math.sin(wd * t);
return [pos, vel];
}
color: Colors.palette.m3primary
implicitSize: 38
toShape: shapes[0]
RotationAnimation on cRotation {
id: rotateAnim
duration: 4666
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
running: root.animated
to: 360
}
Behavior on color {
CAnim {
}
}
ElapsedTimer {
id: timer
}
FrameAnimation {
running: root.animated && !root.springSettled
onTriggered: {
const t = timer.elapsed();
if (t >= root.springDuration) {
root.springSettled = true;
} else {
const [pos, vel] = root.spring(t);
root.morphProgress = Math.min(1, pos); // Overshooting the morph looks weird
root.thisLRotation = pos * root.morphAnimRotation;
root.scale = 1 + vel * root.morphScale / root.springMaxVelocity;
}
}
}
Timer {
interval: 650
repeat: true
running: root.animated
triggeredOnStart: true
onTriggered: {
root.beginBatchUpdate();
root.fromShape = root.toShape;
root.shapeIndex = (root.shapeIndex + 1) % root.shapes.length;
root.toShape = root.shapes[root.shapeIndex];
root.morphProgress = 0;
root.rotation = root.rotation;
root.lRotation = (root.lRotation + root.thisLRotation) % 360;
root.thisLRotation = 0;
root.rotation = Qt.binding(() => root.cRotation + root.lRotation + root.thisLRotation);
root.springSettled = false;
timer.restart();
root.endBatchUpdate();
}
}
}
+8 -10
View File
@@ -1,16 +1,14 @@
import QtQuick
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
CustomText { CustomText {
property real fill id: root
property int grade: Colors.light ? 0 : -25
font.family: "Material Symbols Rounded" property real fill: 0
font.pointSize: Tokens.font.size.larger property int grade: Colors.light ? 0 : -25
font.variableAxes: ({ property font fontStyle: Tokens.font.icon.small
FILL: fill.toFixed(1),
GRAD: grade, renderType: Text.NativeRendering
opsz: fontInfo.pixelSize, font: Tokens.font.icon.size(fontStyle.pointSize).weight(fontStyle.weight).vaxes(fontStyle.variableAxes).fill(fill.toFixed(1)).grade(grade).build()
wght: fontInfo.weight
})
} }
+6 -8
View File
@@ -1,8 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Shapes import QtQuick.Shapes
import ZShell import ZShell
import ZShell.Components
import qs.Helpers
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -13,7 +11,6 @@ MouseArea {
property alias bottomRightRadius: base.bottomRightRadius property alias bottomRightRadius: base.bottomRightRadius
property real circleRadius property real circleRadius
property alias color: base.color property alias color: base.color
property bool disabled
readonly property real endRadius: { readonly property real endRadius: {
const d1 = distSq(0, 0); const d1 = distSq(0, 0);
const d2 = distSq(width, 0); const d2 = distSq(width, 0);
@@ -22,6 +19,7 @@ MouseArea {
return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3; return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
} }
property real endRadiusAtPress property real endRadiusAtPress
property bool manualHoverOverride
property bool manualPressOverride property bool manualPressOverride
property real pressX: width / 2 property real pressX: width / 2
property real pressY: height / 2 property real pressY: height / 2
@@ -29,7 +27,7 @@ MouseArea {
readonly property alias rect: base readonly property alias rect: base
property bool shapeMorph property bool shapeMorph
property bool showHoverBackground: true property bool showHoverBackground: true
property real stateOpacity: containsMouse ? 0.08 : 0 property real stateOpacity: containsMouse || manualHoverOverride ? 0.08 : 0
property alias topLeftRadius: base.topLeftRadius property alias topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius property alias topRightRadius: base.topRightRadius
@@ -52,8 +50,8 @@ MouseArea {
} }
anchors.fill: parent anchors.fill: parent
cursorShape: disabled ? undefined : Qt.PointingHandCursor cursorShape: !enabled ? undefined : Qt.PointingHandCursor
enabled: !disabled enabled: parent.enabled
hoverEnabled: true hoverEnabled: true
Behavior on stateOpacity { Behavior on stateOpacity {
@@ -100,13 +98,13 @@ MouseArea {
id: base id: base
anchors.fill: parent anchors.fill: parent
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
opacity: root.stateOpacity opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props) // Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property // qmllint disable missing-property
radius: root.parent?.radius ?? 0 radius: root.parent?.radius ?? 0
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0 topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0 topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property // qmllint enable missing-property
+1 -24
View File
@@ -13,27 +13,10 @@ RowLayout {
spacing: Tokens.spacing.largeIncreased * 2 spacing: Tokens.spacing.largeIncreased * 2
ColumnLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.medium
CustomRect { CustomRect {
Layout.fillWidth: true
color: Colors.tPalette.m3surfaceContainer
implicitHeight: weather.implicitHeight
radius: Tokens.rounding.small
topLeftRadius: Tokens.rounding.largeIncreased
WeatherInfo {
id: weather
rootHeight: root.height
}
}
CustomClippingRect {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
topLeftRadius: Tokens.rounding.largeIncreased
bottomLeftRadius: Tokens.rounding.largeIncreased bottomLeftRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small radius: Tokens.rounding.small
@@ -42,17 +25,12 @@ RowLayout {
greeter: root.greeter greeter: root.greeter
} }
} }
}
Center { Center {
greeter: root.greeter greeter: root.greeter
screenHeight: root.screenHeight screenHeight: root.screenHeight
} }
ColumnLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.medium
CustomRect { CustomRect {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
@@ -66,4 +44,3 @@ RowLayout {
} }
} }
} }
}
+1 -2
View File
@@ -134,8 +134,7 @@ Scope {
id: lockIcon id: lockIcon
anchors.centerIn: parent anchors.centerIn: parent
font.bold: true fontStyle: Tokens.font.icon.builders.extraLarge.size(Tokens.font.icon.extraLarge.pointSize * 3).build()
font.pointSize: Tokens.font.size.extraLarge * 4
text: "lock" text: "lock"
} }
+8 -15
View File
@@ -39,8 +39,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
animate: true animate: true
color: root.greeter.launching ? Colors.palette.m3secondary : Colors.palette.m3outline color: root.greeter.launching ? Colors.palette.m3secondary : Colors.palette.m3outline
font.family: Config.appearance.font.family.mono font: Tokens.font.mono.medium
font.pointSize: Tokens.font.size.normal
opacity: root.buffer ? 0 : 1 opacity: root.buffer ? 0 : 1
text: { text: {
if (root.greeter.launching) if (root.greeter.launching)
@@ -51,8 +50,7 @@ Item {
} }
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
} }
@@ -64,15 +62,13 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
elide: Text.ElideLeft elide: Text.ElideLeft
font.family: Config.appearance.font.family.mono font: Tokens.font.mono.medium
font.pointSize: Tokens.font.size.normal
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
opacity: root.greeter.echoResponse && root.buffer ? 1 : 0 opacity: root.greeter.echoResponse && root.buffer ? 1 : 0
text: root.buffer text: root.buffer
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
} }
@@ -89,7 +85,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0 anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0
implicitHeight: Tokens.font.size.normal implicitHeight: Tokens.font.body.medium
implicitWidth: fullWidth implicitWidth: fullWidth
interactive: false interactive: false
opacity: root.greeter.echoResponse ? 0 : 1 opacity: root.greeter.echoResponse ? 0 : 1
@@ -107,8 +103,7 @@ Item {
scale: 0 scale: 0
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
Behavior on scale { Behavior on scale {
Anim { Anim {
@@ -156,15 +151,13 @@ Item {
Behavior on implicitWidth { Behavior on implicitWidth {
id: imWidthBehavior id: imWidthBehavior
Anim { Anim {}
}
} }
model: ScriptModel { model: ScriptModel {
values: root.buffer.split("") values: root.buffer.split("")
} }
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
} }
} }
+11 -17
View File
@@ -13,22 +13,19 @@ ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small spacing: Tokens.spacing.large
CustomText { CustomText {
Layout.fillWidth: true Layout.fillWidth: true
color: Colors.palette.m3outline color: Colors.palette.m3outline
elide: Text.ElideRight elide: Text.ElideRight
font.family: Config.appearance.font.family.mono font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
font.weight: 500
text: root.greeter.sessions.length > 0 ? qsTr("%1 session%2").arg(root.greeter.sessions.length).arg(root.greeter.sessions.length === 1 ? "" : "s") : qsTr("Sessions") text: root.greeter.sessions.length > 0 ? qsTr("%1 session%2").arg(root.greeter.sessions.length).arg(root.greeter.sessions.length === 1 ? "" : "s") : qsTr("Sessions")
} }
CustomClippingRect { Item {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader { Loader {
active: opacity > 0 active: opacity > 0
@@ -46,16 +43,14 @@ ColumnLayout {
MaterialIcon { MaterialIcon {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
font.pointSize: Tokens.font.size.extraLarge * 2 fontStyle: Tokens.font.icon.large
text: "desktop_windows" text: "desktop_windows"
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
font.family: Config.appearance.font.family.mono font: Tokens.font.headline.builders.small.width(90).build()
font.pointSize: Tokens.font.size.large
font.weight: 500
text: qsTr("No Sessions Found") text: qsTr("No Sessions Found")
} }
} }
@@ -68,6 +63,7 @@ ColumnLayout {
clip: true clip: true
currentIndex: root.greeter.sessionIndex currentIndex: root.greeter.sessionIndex
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
boundsBehavior: ListView.StopAtBounds
model: root.greeter.sessions model: root.greeter.sessions
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
@@ -80,7 +76,7 @@ ColumnLayout {
anchors.left: parent?.left anchors.left: parent?.left
anchors.right: parent?.right anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2 implicitHeight: row.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.medium - Tokens.padding.small radius: Tokens.rounding.large
StateLayer { StateLayer {
onClicked: { onClicked: {
@@ -97,7 +93,7 @@ ColumnLayout {
MaterialIcon { MaterialIcon {
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.extraLarge fontStyle: Tokens.font.icon.extraLarge
text: modelData.kind === "x11" ? "tv" : "desktop_windows" text: modelData.kind === "x11" ? "tv" : "desktop_windows"
} }
@@ -109,8 +105,7 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
elide: Text.ElideRight elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal font: Tokens.font.body.builders.medium.weight(Font.Bold).build()
font.weight: 600
text: modelData.name text: modelData.name
} }
@@ -118,8 +113,7 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimaryFixedVariant : Colors.palette.m3onSurfaceVariant color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimaryFixedVariant : Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight elide: Text.ElideRight
font.family: Config.appearance.font.family.mono font: Tokens.font.mono.small
font.pointSize: Tokens.font.size.small
text: modelData.kind text: modelData.kind
} }
} }
@@ -129,7 +123,7 @@ ColumnLayout {
color: Colors.palette.m3primary color: Colors.palette.m3primary
implicitHeight: sessions.currentItem?.implicitHeight ?? 0 implicitHeight: sessions.currentItem?.implicitHeight ?? 0
implicitWidth: sessions.width implicitWidth: sessions.width
radius: Tokens.rounding.medium - Tokens.padding.small radius: Tokens.rounding.large
y: sessions.currentItem?.y ?? 0 y: sessions.currentItem?.y ?? 0
Behavior on y { Behavior on y {
+11 -16
View File
@@ -14,22 +14,19 @@ ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small spacing: Tokens.spacing.large
CustomText { CustomText {
Layout.fillWidth: true Layout.fillWidth: true
color: Colors.palette.m3outline color: Colors.palette.m3outline
elide: Text.ElideRight elide: Text.ElideRight
font.family: Config.appearance.font.family.mono font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
font.weight: 500
text: root.greeter.users.length > 0 ? qsTr("%1 user%2").arg(root.greeter.users.length).arg(root.greeter.users.length === 1 ? "" : "s") : qsTr("Users") text: root.greeter.users.length > 0 ? qsTr("%1 user%2").arg(root.greeter.users.length).arg(root.greeter.users.length === 1 ? "" : "s") : qsTr("Users")
} }
CustomClippingRect { Item {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader { Loader {
active: opacity > 0 active: opacity > 0
@@ -48,16 +45,14 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
fill: 1 fill: 1
font.pointSize: Tokens.font.size.extraLarge * 2 fontStyle: Tokens.font.icon.large
text: "account_circle" text: "account_circle"
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
font.family: Config.appearance.font.family.mono font: Tokens.font.headline.builders.small.width(90).build()
font.pointSize: Tokens.font.size.large
font.weight: 500
text: qsTr("No Users Found") text: qsTr("No Users Found")
} }
} }
@@ -70,6 +65,7 @@ ColumnLayout {
clip: true clip: true
currentIndex: root.greeter.selectedUser ? root.greeter.users.indexOf(root.greeter.selectedUser) : -1 currentIndex: root.greeter.selectedUser ? root.greeter.users.indexOf(root.greeter.selectedUser) : -1
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
boundsBehavior: ListView.StopAtBounds
model: root.greeter.users model: root.greeter.users
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
@@ -82,7 +78,7 @@ ColumnLayout {
anchors.left: parent?.left anchors.left: parent?.left
anchors.right: parent?.right anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2 implicitHeight: row.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.medium - Tokens.padding.small radius: Tokens.rounding.large
StateLayer { StateLayer {
onClicked: { onClicked: {
@@ -97,7 +93,7 @@ ColumnLayout {
anchors.margins: Tokens.padding.small anchors.margins: Tokens.padding.small
spacing: Tokens.spacing.medium spacing: Tokens.spacing.medium
CustomClippingRect { ClipRect {
Layout.preferredHeight: 50 Layout.preferredHeight: 50
Layout.preferredWidth: 50 Layout.preferredWidth: 50
radius: Tokens.rounding.full radius: Tokens.rounding.full
@@ -106,7 +102,7 @@ ColumnLayout {
anchors.centerIn: parent anchors.centerIn: parent
color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant
fill: 1 fill: 1
font.pointSize: Tokens.font.size.extraLarge fontStyle: Tokens.font.icon.extraLarge
text: modelData.kind === "x11" ? "tv" : "account_circle" text: modelData.kind === "x11" ? "tv" : "account_circle"
} }
@@ -124,8 +120,7 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
elide: Text.ElideRight elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal font: Tokens.font.body.builders.medium.weight(Font.Bold).build()
font.weight: 600
text: modelData.username text: modelData.username
} }
} }
@@ -135,7 +130,7 @@ ColumnLayout {
color: Colors.palette.m3primary color: Colors.palette.m3primary
implicitHeight: users.currentItem?.implicitHeight ?? 0 implicitHeight: users.currentItem?.implicitHeight ?? 0
implicitWidth: users.width implicitWidth: users.width
radius: Tokens.rounding.medium - Tokens.padding.small radius: Tokens.rounding.large
y: users.currentItem?.y ?? 0 y: users.currentItem?.y ?? 0
Behavior on y { Behavior on y {
-171
View File
@@ -1,171 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Helpers
import qs.Services
ColumnLayout {
id: root
required property int rootHeight
anchors.left: parent.left
anchors.margins: Tokens.padding.large * 2
anchors.right: parent.right
spacing: Tokens.spacing.small
Loader {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: -Tokens.padding.large
Layout.topMargin: Tokens.padding.large * 2
active: root.rootHeight > 610
visible: active
sourceComponent: CustomText {
color: Colors.palette.m3primary
font.pointSize: Tokens.font.size.extraLarge
font.weight: 500
text: qsTr("Weather")
}
}
RowLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.largeIncreased
MaterialIcon {
animate: true
color: Colors.palette.m3secondary
font.pointSize: Tokens.font.size.extraLarge * 2.5
text: Weather.icon
}
ColumnLayout {
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3secondary
elide: Text.ElideRight
font.pointSize: Tokens.font.size.large
font.weight: 500
text: Weather.description
}
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal
text: qsTr("Humidity: %1%").arg(Weather.humidity)
}
}
Loader {
Layout.rightMargin: Tokens.padding.small
active: root.width > 400
visible: active
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3primary
elide: Text.ElideLeft
font.pointSize: Tokens.font.size.extraLarge
font.weight: 500
horizontalAlignment: Text.AlignRight
text: Weather.temp
}
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3outline
elide: Text.ElideLeft
font.pointSize: Tokens.font.size.smaller
horizontalAlignment: Text.AlignRight
text: qsTr("Feels like: %1").arg(Weather.feelsLike)
}
}
}
}
Loader {
id: forecastLoader
Layout.bottomMargin: Tokens.padding.large * 2
Layout.fillWidth: true
Layout.topMargin: Tokens.spacing.small
active: root.rootHeight > 820
visible: active
sourceComponent: RowLayout {
spacing: Tokens.spacing.largeIncreased
Repeater {
model: {
const forecast = Weather.hourlyForecast;
const count = root.width < 320 ? 3 : root.width < 400 ? 4 : 5;
if (!forecast)
return Array.from({
length: count
}, () => null);
const start = Math.max(0, Weather.currentHourIndex);
return forecast.slice(start, start + count);
}
ColumnLayout {
id: forecastHour
required property var modelData
Layout.fillWidth: true
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3outline
font.pointSize: Tokens.font.size.larger
horizontalAlignment: Text.AlignHCenter
text: {
const hour = forecastHour.modelData?.hour ?? 0;
return hour > 12 ? `${(hour - 12).toString().padStart(2, "0")} PM` : `${hour.toString().padStart(2, "0")} AM`;
}
}
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
font.pointSize: Tokens.font.size.extraLarge * 1.5
font.weight: 500
text: forecastHour.modelData?.icon ?? "cloud_alert"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3secondary
font.pointSize: Tokens.font.size.larger
text: Config.services.useFahrenheit ? `${forecastHour.modelData?.tempF ?? 0}°F` : `${forecastHour.modelData?.tempC ?? 0}°C`
}
}
}
}
}
Timer {
interval: 900000 // 15 minutes
repeat: true
running: true
triggeredOnStart: true
onTriggered: Weather.reload()
}
}
-49
View File
@@ -1,49 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Effects
import ZShell.Config
import qs.Components
import qs.Services
Item {
id: root
required property Item bar
required property ScreenState screenState
anchors.fill: parent
CustomRect {
anchors.fill: parent
anchors.margins: -1
color: Colors.palette.m3surface
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: mask
maskSpreadAtMin: 1
maskThresholdMin: 0.5
}
}
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
anchors.margins: Config.bar.border + 1
anchors.topMargin: root.bar.implicitHeight + 1
radius: Config.bar.border > 0 ? Config.bar.rounding : 0
topLeftRadius: Config.bar.rounding
topRightRadius: Config.bar.rounding
}
}
}
+2 -2
View File
@@ -134,7 +134,7 @@ Item {
node: Audio.source node: Audio.source
} }
CustomClippingRect { ClipRect {
anchors.fill: parent anchors.fill: parent
radius: parent.radius radius: parent.radius
@@ -249,7 +249,7 @@ Item {
node: appBox.modelData node: appBox.modelData
} }
CustomClippingRect { ClipRect {
anchors.fill: parent anchors.fill: parent
radius: parent.radius radius: parent.radius
+3 -3
View File
@@ -25,7 +25,7 @@ Item {
searchField.forceActiveFocus(); searchField.forceActiveFocus();
} }
CustomClippingRect { ClipRect {
id: search id: search
anchors.left: parent.left anchors.left: parent.left
@@ -54,7 +54,7 @@ Item {
} }
} }
CustomClippingRect { ClipRect {
id: preview id: preview
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@@ -228,7 +228,7 @@ Item {
anchors.fill: parent anchors.fill: parent
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
CustomClippingRect { ClipRect {
id: textRect id: textRect
Layout.fillHeight: true Layout.fillHeight: true
+1 -1
View File
@@ -14,7 +14,7 @@ Row {
padding: 20 padding: 20
spacing: 12 spacing: 12
CustomClippingRect { ClipRect {
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2) color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
implicitHeight: info.implicitHeight implicitHeight: info.implicitHeight
implicitWidth: info.implicitHeight implicitWidth: info.implicitHeight
+1 -1
View File
@@ -16,7 +16,7 @@ Item {
implicitWidth: 1000 implicitWidth: 1000
implicitHeight: 320 implicitHeight: 320
CustomClippingRect { ClipRect {
anchors.fill: parent anchors.fill: parent
radius: root.radius radius: root.radius
@@ -46,7 +46,7 @@ CustomListView {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
spacing: Tokens.spacing.extraSmall / 2 spacing: Tokens.spacing.extraSmall / 2
CustomClippingRect { ClipRect {
id: iconClip id: iconClip
Layout.fillHeight: true Layout.fillHeight: true
@@ -25,7 +25,7 @@ Item {
onClicked: root.close() onClicked: root.close()
} }
CustomClippingRect { ClipRect {
id: popupBackground id: popupBackground
readonly property real padding: 4 readonly property real padding: 4
@@ -40,7 +40,7 @@ Item {
visible: false visible: false
z: 999 z: 999
CustomClippingRect { ClipRect {
id: popupBackground id: popupBackground
readonly property real padding: Tokens.padding.extraSmall readonly property real padding: Tokens.padding.extraSmall
+1 -1
View File
@@ -19,7 +19,7 @@ Item {
implicitHeight: tileHeight + Tokens.padding.large * 2 implicitHeight: tileHeight + Tokens.padding.large * 2
implicitWidth: tileWidth implicitWidth: tileWidth
CustomClippingRect { ClipRect {
id: image id: image
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
+1 -1
View File
@@ -55,7 +55,7 @@ ColumnLayout {
text: Time.format("dddd, d MMMM yyyy") text: Time.format("dddd, d MMMM yyyy")
} }
CustomClippingRect { ClipRect {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Tokens.spacing.largeIncreased * 2 Layout.topMargin: Tokens.spacing.largeIncreased * 2
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
+1 -1
View File
@@ -5,7 +5,7 @@ import qs.Helpers
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
CustomClippingRect { ClipRect {
id: root id: root
required property var lock required property var lock
+2 -2
View File
@@ -2,12 +2,12 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
import ZShell.Components import ZShell.Components
import qs.Components
import ZShell.Config import ZShell.Config
import qs.Components
import qs.Helpers import qs.Helpers
import qs.Services import qs.Services
CustomClippingRect { ClipRect {
id: root id: root
required property var lock required property var lock
+2 -3
View File
@@ -24,12 +24,11 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
color: Colors.palette.m3outline color: Colors.palette.m3outline
elide: Text.ElideRight elide: Text.ElideRight
font.family: Config.appearance.font.family.mono font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
font.weight: 500
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications") text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
} }
ClippingRectangle { ClipRect {
id: clipRect id: clipRect
Layout.fillHeight: true Layout.fillHeight: true
+1 -1
View File
@@ -85,7 +85,7 @@ CustomRect {
} }
} }
ClippingRectangle { ClipRect {
anchors.fill: parent anchors.fill: parent
color: root.urgency === "critical" ? Colors.palette.m3error : root.urgency === "low" ? Colors.layer(Colors.palette.m3surfaceContainerHighest, 3) : Colors.palette.m3secondaryContainer color: root.urgency === "critical" ? Colors.palette.m3error : root.urgency === "low" ? Colors.layer(Colors.palette.m3surfaceContainerHighest, 3) : Colors.palette.m3secondaryContainer
radius: Tokens.rounding.full radius: Tokens.rounding.full
+2 -1
View File
@@ -1,12 +1,13 @@
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import QtQuick import QtQuick
import qs.Components
import qs.Paths import qs.Paths
Item { Item {
id: root id: root
ClippingRectangle { ClipRect {
anchors.fill: parent anchors.fill: parent
radius: Tokens.rounding.full radius: Tokens.rounding.full
+1 -1
View File
@@ -108,7 +108,7 @@ CustomRect {
visible: root.hasImage || root.hasAppIcon visible: root.hasImage || root.hasAppIcon
width: Config.notifs.sizes.image width: Config.notifs.sizes.image
sourceComponent: CustomClippingRect { sourceComponent: ClipRect {
color: root.modelData.urgency === NotificationUrgency.Critical ? Colors.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? Colors.layer(Colors.palette.m3surfaceContainerHighest, 2) : Colors.palette.m3secondaryContainer color: root.modelData.urgency === NotificationUrgency.Critical ? Colors.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? Colors.layer(Colors.palette.m3surfaceContainerHighest, 2) : Colors.palette.m3secondaryContainer
implicitHeight: Config.notifs.sizes.image implicitHeight: Config.notifs.sizes.image
implicitWidth: Config.notifs.sizes.image implicitWidth: Config.notifs.sizes.image
+1 -1
View File
@@ -5,7 +5,7 @@ import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
CustomClippingRect { ClipRect {
id: root id: root
property real animPerc: UPower.displayDevice.percentage property real animPerc: UPower.displayDevice.percentage
+1 -1
View File
@@ -19,7 +19,7 @@ CustomRect {
implicitWidth: 450 implicitWidth: 450
radius: Tokens.rounding.largeIncreased radius: Tokens.rounding.largeIncreased
CustomClippingRect { ClipRect {
anchors.fill: parent anchors.fill: parent
radius: parent.radius radius: parent.radius
+1 -1
View File
@@ -149,7 +149,7 @@ ColumnLayout {
} }
} }
Flickable { CustomFlickable {
id: flickable id: flickable
Layout.fillHeight: true Layout.fillHeight: true
+1 -1
View File
@@ -25,7 +25,7 @@ Item {
anchors.fill: parent anchors.fill: parent
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
CustomClippingRect { ClipRect {
id: imgWrapper id: imgWrapper
Layout.fillWidth: true Layout.fillWidth: true
+15 -13
View File
@@ -100,16 +100,13 @@ Item {
visible: opacity > 0 visible: opacity > 0
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
Behavior on scale { Behavior on scale {
Anim { Anim {}
}
} }
Behavior on y { Behavior on y {
Anim { Anim {}
}
} }
Component.onCompleted: { Component.onCompleted: {
@@ -120,9 +117,8 @@ Item {
Qt.callLater(syncCropToScreen); Qt.callLater(syncCropToScreen);
} }
ButtonRow { Loader {
id: screenSelector id: selectorLoader
anchors.bottom: imageSlot.top anchors.bottom: imageSlot.top
anchors.bottomMargin: Tokens.spacing.medium anchors.bottomMargin: Tokens.spacing.medium
anchors.left: parent.left anchors.left: parent.left
@@ -130,6 +126,12 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Tokens.padding.extraLarge * 2 anchors.rightMargin: Tokens.padding.extraLarge * 2
anchors.top: parent.top anchors.top: parent.top
active: wrapper.screens.length > 1
sourceComponent: ButtonRow {
id: screenSelector
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
Repeater { Repeater {
@@ -152,6 +154,7 @@ Item {
} }
} }
} }
}
RowLayout { RowLayout {
id: sliderLayout id: sliderLayout
@@ -222,9 +225,9 @@ Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: wrapper.buttonRowHeight + Tokens.spacing.medium anchors.topMargin: selectorLoader.active ? wrapper.buttonRowHeight + Tokens.spacing.medium : Tokens.spacing.medium
CustomClippingRect { ClipRect {
id: fitBox id: fitBox
readonly property real aspect: scaledImg.implicitHeight > 0 ? scaledImg.implicitWidth / scaledImg.implicitHeight : 1 readonly property real aspect: scaledImg.implicitHeight > 0 ? scaledImg.implicitWidth / scaledImg.implicitHeight : 1
@@ -357,8 +360,7 @@ Item {
width: Math.round(baseWidth / zoom) width: Math.round(baseWidth / zoom)
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
Component.onCompleted: { Component.onCompleted: {
+2
View File
@@ -38,6 +38,7 @@ Item {
} }
BlobInvertedRect { BlobInvertedRect {
id: blobRect
anchors.fill: parent anchors.fill: parent
borderBottom: Tokens.padding.small borderBottom: Tokens.padding.small
borderLeft: navPane.width + navPane.anchors.margins * 2 borderLeft: navPane.width + navPane.anchors.margins * 2
@@ -65,6 +66,7 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: navPane.right anchors.left: navPane.right
anchors.leftMargin: navPane.anchors.margins + anchors.margins anchors.leftMargin: navPane.anchors.margins + anchors.margins
anchors.bottomMargin: blobRect.borderBottom
anchors.margins: Tokens.padding.extraLarge anchors.margins: Tokens.padding.extraLarge
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
+1 -1
View File
@@ -436,7 +436,7 @@ Item {
width: attachments.cellWidth width: attachments.cellWidth
height: width height: width
opacity: modelData ? 1 : 0 opacity: modelData ? 1 : 0
CustomClippingRect { ClipRect {
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.spacing.extraSmall anchors.margins: Tokens.spacing.extraSmall
@@ -337,7 +337,7 @@ Item {
} }
} }
CustomClippingRect { ClipRect {
id: expandedTool id: expandedTool
anchors.top: collapsedRow.bottom anchors.top: collapsedRow.bottom
@@ -252,7 +252,7 @@ CustomRect {
} }
} }
CustomClippingRect { CustomRect {
id: codeRect id: codeRect
anchors.left: parent.left anchors.left: parent.left
@@ -265,6 +265,7 @@ CustomRect {
implicitWidth: codeText.implicitWidth + codeFlick.anchors.margins * 2 implicitWidth: codeText.implicitWidth + codeFlick.anchors.margins * 2
implicitHeight: codeText.implicitHeight + codeFlick.anchors.margins * 2 implicitHeight: codeText.implicitHeight + codeFlick.anchors.margins * 2
color: CodeColors.active.bg color: CodeColors.active.bg
clip: true
Flickable { Flickable {
id: codeFlick id: codeFlick
@@ -272,6 +273,9 @@ CustomRect {
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.small anchors.margins: Tokens.padding.small
boundsBehavior: Flickable.StopAtBounds
contentWidth: codeText.implicitWidth
CustomScrollBar.horizontal: CustomScrollBar { CustomScrollBar.horizontal: CustomScrollBar {
flickable: codeFlick flickable: codeFlick
parent: codeFlick.parent parent: codeFlick.parent
@@ -280,16 +284,13 @@ CustomRect {
anchors.right: parent.right anchors.right: parent.right
} }
TextAreaBase.flickable: TextAreaBase { CustomText {
id: codeText id: codeText
color: CodeColors.active.normal color: CodeColors.active.normal
layer.enabled: true
clip: false
font: Tokens.font.mono.small font: Tokens.font.mono.small
textFormat: root.colored ? Text.RichText : Text.PlainText textFormat: Text.RichText
text: root.colored ? root.html : root.plainForDisplay(root.code) text: root.colored ? root.html : root.plainForDisplay(root.code)
readOnly: true
} }
} }
} }
@@ -125,7 +125,7 @@ Item {
current: root.current current: root.current
} }
CustomClippingRect { ClipRect {
id: expandedRect id: expandedRect
anchors.left: parent.left anchors.left: parent.left
+1 -1
View File
@@ -86,7 +86,7 @@ Item {
} }
} }
CustomClippingRect { ClipRect {
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: invertedRect.borderLeft anchors.leftMargin: invertedRect.borderLeft
anchors.topMargin: invertedRect.borderTop anchors.topMargin: invertedRect.borderTop
-70
View File
@@ -17,76 +17,6 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: flickable.contentHeight implicitHeight: flickable.contentHeight
layer.enabled: true
layer.smooth: true
layer.effect: Mask {
maskSource: gradientMask
}
Item {
id: gradientMask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
color: Qt.rgba(0, 0, 0, 0)
position: 0
}
GradientStop {
color: Qt.rgba(0, 0, 0, 1)
position: 0.1
}
GradientStop {
color: Qt.rgba(0, 0, 0, 1)
position: 0.9
}
GradientStop {
color: Qt.rgba(0, 0, 0, 0)
position: 1
}
}
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
implicitWidth: parent.width / 2
opacity: flickable.contentX > 0 ? 0 : 1
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
Rectangle {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
implicitWidth: parent.width / 2
opacity: flickable.contentX < flickable.contentWidth - parent.width ? 0 : 1
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
}
CustomFlickable { CustomFlickable {
id: flickable id: flickable
+1 -1
View File
@@ -112,7 +112,7 @@ CustomRect {
} }
} }
CustomClippingRect { ClipRect {
anchors.fill: parent anchors.fill: parent
color: root.urgency === NotificationUrgency.Critical ? Colors.palette.m3error : root.urgency === NotificationUrgency.Low ? Colors.layer(Colors.palette.m3surfaceContainerHigh, 3) : Colors.palette.m3secondaryContainer color: root.urgency === NotificationUrgency.Critical ? Colors.palette.m3error : root.urgency === NotificationUrgency.Low ? Colors.layer(Colors.palette.m3surfaceContainerHigh, 3) : Colors.palette.m3secondaryContainer
radius: Tokens.rounding.full radius: Tokens.rounding.full
+14 -1
View File
@@ -662,6 +662,17 @@ void WallpaperService::drawSurface(WallpaperSurface* surface) {
return; return;
} }
// Release the context when this scope ends (see FrameService::drawSurface).
// Holding it across the draw loop's sleep serializes the two raw-EGL clients on
// NVIDIA, which makes eglSwapBuffers fail and starves other processes' contexts.
struct ContextRelease {
EGLDisplay display;
~ContextRelease() {
eglMakeCurrent(
display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
} contextRelease{m_eglDisplay};
QVariantMap crops; QVariantMap crops;
QSize size; QSize size;
bool upload = false; bool upload = false;
@@ -701,7 +712,9 @@ void WallpaperService::drawSurface(WallpaperSurface* surface) {
glDrawArrays(GL_TRIANGLES, 0, 3); glDrawArrays(GL_TRIANGLES, 0, 3);
if (!eglSwapBuffers(m_eglDisplay, target)) if (!eglSwapBuffers(m_eglDisplay, target))
qWarning() << "WallpaperService: eglSwapBuffers failed"; qWarning()
<< "WallpaperService: eglSwapBuffers failed, eglGetError = 0x"
<< QString::number(unsigned(eglGetError()), 16);
} }
void WallpaperService::addSurface(const QString& screen) { void WallpaperService::addSurface(const QString& screen) {
-2
View File
@@ -1,7 +1,5 @@
//@ pragma UseQApplication //@ pragma UseQApplication
//@ pragma DefaultEnv QSG_RENDER_LOOP=threaded //@ pragma DefaultEnv QSG_RENDER_LOOP=threaded
// @ pragma DefaultEnv QSG_RHI_BACKEND=vulkan
//@ pragma DefaultEnv QSG_NO_VSYNC=1
//@ pragma DefaultEnv QS_NO_RELOAD_POPUP=1 //@ pragma DefaultEnv QS_NO_RELOAD_POPUP=1
//@ pragma DefaultEnv QT_QUICK_FLICKABLE_WHEEL_DECELERATION=10000 //@ pragma DefaultEnv QT_QUICK_FLICKABLE_WHEEL_DECELERATION=10000
//@ pragma DropExpensiveFonts //@ pragma DropExpensiveFonts