settings: load all pages asynchronously

This commit is contained in:
bbedward
2026-09-21 16:47:09 -04:00
parent bb20528f68
commit c9032926d7
13 changed files with 116 additions and 40 deletions
Generated
+3 -3
View File
@@ -3,11 +3,11 @@
"dank-qml-common": {
"flake": false,
"locked": {
"lastModified": 1790019586,
"narHash": "sha256-ChWD8MBgZLeNMQu0xjlPF3Lpz1Rw5rLGX0X7CfPHjns=",
"lastModified": 1790023369,
"narHash": "sha256-BwL+cY6QjWTjKZ13feTjRpkPg4J4B0YGcl3gMPBK6pI=",
"owner": "AvengeMedia",
"repo": "dank-qml-common",
"rev": "1a531ca78180f1fdedcdcf1696e3f662f47d325f",
"rev": "c30ef9f1abd09bae6b2e70939104b59b659187db",
"type": "github"
},
"original": {
+1
View File
@@ -228,6 +228,7 @@ DankModal {
name: Theme.getPowerProfileIcon(modelData)
size: Theme.iconSize + 16
color: isActive ? Theme.primary : Theme.surfaceText
filled: isActive
anchors.horizontalCenter: parent.horizontalCenter
}
+68 -12
View File
@@ -115,25 +115,59 @@ FocusScope {
Component {
id: pageComponent
Loader {
// StackView writes visible and opacity on the element it manages, so the presentation gate lives on the inner Loader
Item {
id: host
required property string page
readonly property bool pageActive: root.sessionVisible && pageStack.currentItem === host
readonly property alias item: loader.item
readonly property int status: loader.status
property bool presented: false
enabled: pageActive
asynchronous: page === "dankbar_widgets" || page === "window_rules"
Component.onCompleted: {
const file = root._fileFor(page);
if (file)
setSource(Qt.resolvedUrl("../../Modules/Settings/" + file), root._propertiesFor(page));
Loader {
id: loader
anchors.fill: parent
asynchronous: true
opacity: host.presented ? 1 : 0
Component.onCompleted: {
const file = root._fileFor(host.page);
if (file)
setSource(Qt.resolvedUrl("../../Modules/Settings/" + file), root._propertiesFor(host.page));
}
onLoaded: {
if (item.pageActive !== undefined)
item.pageActive = Qt.binding(() => host.pageActive);
root._focusPage();
}
}
onLoaded: {
if (item.pageActive !== undefined)
item.pageActive = Qt.binding(() => host.pageActive);
root._focusPage();
FrameAnimation {
id: settleWatch
property real lastHeight: -1
property int stableFrames: 0
running: loader.status === Loader.Ready && !host.presented
onTriggered: {
const h = loader.item?.contentHeight ?? loader.item?.height ?? 0;
if (h === lastHeight)
stableFrames++;
else {
lastHeight = h;
stableFrames = 0;
}
if (stableFrames < SettingsMetrics.pageSettleFrames && elapsedTime * 1000 < SettingsMetrics.pageSettleDeadline)
return;
host.presented = true;
}
}
onPageActiveChanged: {
if (pageActive)
root._focusPage();
@@ -216,9 +250,13 @@ FocusScope {
Item {
id: backSlot
readonly property real glyphInset: (Theme.iconButtonSize - Theme.iconSize) / 2
anchors.left: parent.left
anchors.leftMargin: root.showBack ? -glyphInset : 0
anchors.verticalCenter: parent.verticalCenter
width: root.showBack ? Theme.iconButtonSize + Theme.spacingL : 0
width: root.showBack ? Theme.iconButtonSize + Theme.spacingM - glyphInset : 0
height: Theme.iconButtonSize
DankActionButton {
@@ -273,8 +311,26 @@ FocusScope {
}
DankSpinner {
id: pageSpinner
readonly property bool loading: pageStack.currentItem?.presented === false
anchors.centerIn: parent
visible: pageStack.currentItem?.status === Loader.Loading
visible: false
onLoadingChanged: {
if (!loading) {
spinnerDelay.stop();
visible = false;
return;
}
spinnerDelay.restart();
}
Timer {
id: spinnerDelay
interval: SettingsMetrics.pageSettleDeadline + SettingsMetrics.fadeDuration
onTriggered: pageSpinner.visible = pageSpinner.loading
}
}
}
}
@@ -137,13 +137,10 @@ Column {
anchors.rightMargin: SettingsMetrics.rowPaddingH
spacing: Theme.spacingM
Rectangle {
DankColorSwatch {
width: Theme.avatarSize
height: Theme.avatarSize
radius: width / 2
color: root.customColor
border.color: Theme.outline
border.width: Theme.outlineWidth
swatchColor: root.customColor
anchors.verticalCenter: parent.verticalCenter
DankIcon {
+2 -5
View File
@@ -119,13 +119,10 @@ Item {
clickable: true
onClicked: root.openShadowColorPicker()
Rectangle {
DankColorSwatch {
width: Theme.iconSizeMedium
height: width
radius: width / 2
color: SettingsData.m3ElevationCustomColor ?? "#000000"
border.color: Theme.outline
border.width: Theme.outlineWidth
swatchColor: SettingsData.m3ElevationCustomColor ?? "#000000"
anchors.verticalCenter: parent.verticalCenter
}
}
@@ -182,6 +182,8 @@ Column {
SettingsRow {
settingKey: "colorMode"
tags: ["light", "dark", "mode", "theme", "color", "palette"]
paddingH: SettingsMetrics.heroPadding
paddingV: SettingsMetrics.heroPadding
body: Flow {
id: hero
@@ -192,8 +194,8 @@ Column {
spacing: Theme.spacingL
SettingsWallpaperThumb {
id: thumb
width: hero.thumbWidth
height: hero.stacked ? width * SettingsMetrics.wallpaperThumbRatio : side.implicitHeight
path: root.currentWallpaper
onBrowse: root.openBrowser()
onPickColor: root.pickColor()
@@ -202,10 +204,14 @@ Column {
Column {
id: side
readonly property real navRowHeight: (thumb.height - modeGroup.height - spacing - navGroup.spacing) / 2
width: hero.stacked ? hero.width : hero.width - hero.thumbWidth - hero.spacing
spacing: Theme.spacingM
DankButtonGroup {
id: modeGroup
width: parent.width
fillWidth: true
checkEnabled: false
@@ -233,6 +239,7 @@ Column {
}
SettingsGroup {
id: navGroup
width: parent.width
slotColor: Theme.foregroundColor(Theme.chipSurface, true)
@@ -243,6 +250,8 @@ Column {
title: I18n.tr("Dark mode")
hint: root.colorModeStatus
iconName: "schedule"
iconBox: true
height: hero.stacked ? implicitHeight : Math.max(implicitHeight, side.navRowHeight)
paddingH: Theme.spacingM
paddingV: Theme.spacingM
onClicked: root.parentModal?.navigateTo("theme_schedule")
@@ -254,12 +263,13 @@ Column {
settingKey: "themeNav"
title: I18n.tr("Theme & colors")
hint: Theme.currentThemeLabel
height: hero.stacked ? implicitHeight : Math.max(implicitHeight, side.navRowHeight)
paddingH: Theme.spacingM
paddingV: Theme.spacingM
onClicked: root.parentModal?.navigateTo("theme")
leading: DankPaletteSwatch {
width: Theme.iconSize
width: SettingsMetrics.heroLeadingSize
height: width
primaryColor: root.themePalette.primary
secondaryColor: root.themePalette.secondary
@@ -69,13 +69,10 @@ Item {
anchors.centerIn: parent
spacing: Theme.spacingXS
Rectangle {
DankColorSwatch {
width: Theme.iconSize
height: Theme.iconSize
radius: Theme.fullRadius(width, height)
color: modelData.color
border.color: Theme.outline
border.width: Theme.outlineWidth
swatchColor: modelData.color
anchors.horizontalCenter: parent.horizontalCenter
DankIcon {
@@ -19,6 +19,8 @@ Singleton {
readonly property real rowPaddingH: 20
readonly property real rowPaddingV: 16
readonly property real rowContentSpacing: Theme.spacingL
readonly property real heroPadding: rowPaddingV
readonly property real heroLeadingSize: Theme.avatarSize
readonly property real sectionLabelTopGap: Theme.spacingS
readonly property real sectionLabelBottomGap: Theme.spacingM
readonly property real navIconSize: Theme.avatarSize
@@ -42,4 +44,6 @@ Singleton {
readonly property color selectedRowColor: Theme.blend(rowColor, Theme.onSurface, Theme.stateLayerDrag)
readonly property int transitionDuration: Theme.expressiveDurations.expressiveFastSpatial
readonly property int fadeDuration: Theme.expressiveDurations.expressiveEffects
readonly property int pageSettleFrames: 2
readonly property int pageSettleDeadline: 250
}
@@ -24,6 +24,7 @@ T.Control {
property string subtitle: ""
property color subtitleColor: Theme.surfaceVariantText
property string iconName: ""
property bool iconBox: false
property bool clickable: false
property bool showChevron: false
property string trailingBadge: ""
@@ -198,8 +199,7 @@ T.Control {
id: mainColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: root.paddingV
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: root.paddingH
anchors.rightMargin: root.paddingH
spacing: Theme.spacingM
@@ -226,13 +226,21 @@ T.Control {
visible: children.length > 0
}
DankIcon {
Rectangle {
id: leadingIcon
anchors.verticalCenter: parent.verticalCenter
name: root.iconName
size: Theme.iconSize
color: root.iconColor
width: root.iconBox ? SettingsMetrics.heroLeadingSize : Theme.iconSize
height: width
radius: Theme.fullRadius(width, height)
color: root.iconBox ? Theme.withAlpha(root.iconColor, Theme.tonalTintAlpha) : "transparent"
visible: root.iconName !== ""
DankIcon {
anchors.centerIn: parent
name: root.iconName
size: root.iconBox ? Theme.iconSizeMedium : Theme.iconSize
color: root.iconColor
}
}
}
@@ -108,6 +108,7 @@ Rectangle {
name: root.iconName
size: Theme.iconSizeMedium
color: root.glyphColor
filled: root.active
}
}
@@ -36,6 +36,7 @@ Rectangle {
sourceComponent: CachingImage {
imagePath: root.path
maxCacheSize: SettingsMetrics.wallpaperThumbCache
asynchronous: false
animate: false
}
}
+5 -1
View File
@@ -73,8 +73,12 @@ ShellRoot {
function open(page) {
root.content.currentPage = page;
try {
tryVerify(() => scrollable() !== null, 5000);
} catch (error) {
throw new Error(page + " has a scrollable page");
}
settle();
check(scrollable() !== null, page + " has a scrollable page");
}
function expectPosition(y, label) {