mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-17 00:34:01 -04:00
settings: change page transition
This commit is contained in:
@@ -17,15 +17,12 @@ FocusScope {
|
||||
readonly property bool sessionVisible: parentModal?.shouldBeVisible ?? false
|
||||
readonly property list<string> pagePath: (parentModal?.pageHistory ?? []).concat([currentPage])
|
||||
readonly property Item currentPageItem: pageStack.currentItem?.item ?? null
|
||||
readonly property bool currentPageSettled: pageStack.currentItem?.settled ?? false
|
||||
readonly property var pageInfo: SettingsTabs.page(currentPage)
|
||||
readonly property bool currentPageSettled: pageStack.currentItem?.page === currentPage && pageStack.currentItem.settled
|
||||
property Item pendingPage: null
|
||||
readonly property bool isPluginPage: SettingsTabs.isPluginPage(currentPage)
|
||||
readonly property bool isHubPage: pageInfo?.kind === "hub"
|
||||
readonly property bool isCompactMode: parentModal?.isCompactMode ?? false
|
||||
readonly property bool menuHidden: isCompactMode && !(parentModal?.menuVisible ?? true)
|
||||
readonly property bool showBack: (parentModal?.canGoBack ?? false) || isPluginPage || menuHidden
|
||||
readonly property string pageTitle: (pageInfo?.titleFrom ? SettingsUiState[pageInfo.titleFrom] : "") || (pageInfo?.text ?? "")
|
||||
readonly property real pageContentMaxWidth: currentPageItem?.contentMaxWidth ?? SettingsMetrics.contentMaxWidth
|
||||
readonly property bool animationsEnabled: Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None
|
||||
// Mouse navigation lands parked; only keyboard navigation highlights a control on the page
|
||||
property bool keyboardNavigation: true
|
||||
@@ -50,7 +47,7 @@ FocusScope {
|
||||
return;
|
||||
if (parentModal?.modalFocusScope?.transientOwnsFocus())
|
||||
return;
|
||||
if (!sessionVisible || !currentPageItem || !pageStack.currentItem.presented)
|
||||
if (!sessionVisible || !currentPageItem || pageStack.currentItem.page !== currentPage || !pageStack.currentItem.presented)
|
||||
return;
|
||||
if (parentModal?.focusPane === "sidebar")
|
||||
return;
|
||||
@@ -208,51 +205,88 @@ FocusScope {
|
||||
return properties;
|
||||
}
|
||||
|
||||
function _discardPendingPage() {
|
||||
if (!pendingPage)
|
||||
return;
|
||||
pendingPage.destroy();
|
||||
pendingPage = null;
|
||||
}
|
||||
|
||||
function _createPage(page, index) {
|
||||
return pageComponent.createObject(pageStack, {
|
||||
page,
|
||||
pathIndex: index
|
||||
});
|
||||
}
|
||||
|
||||
function _syncPages() {
|
||||
if (!sessionVisible) {
|
||||
_discardPendingPage();
|
||||
pageStack.clear(StackView.Immediate);
|
||||
return;
|
||||
}
|
||||
if (!currentPage)
|
||||
if (!currentPage || pageStack.busy)
|
||||
return;
|
||||
if (pendingPage && (pendingPage.page !== currentPage || pendingPage.pathIndex !== pagePath.length - 1))
|
||||
_discardPendingPage();
|
||||
let shared = 0;
|
||||
while (shared < Math.min(pageStack.depth, pagePath.length) && pageStack.get(shared).page === pagePath[shared])
|
||||
shared++;
|
||||
if (shared === pagePath.length) {
|
||||
_discardPendingPage();
|
||||
if (shared < pageStack.depth)
|
||||
pageStack.pop(pageStack.get(shared - 1), animationsEnabled ? StackView.PopTransition : StackView.Immediate);
|
||||
return;
|
||||
}
|
||||
const drillDown = animationsEnabled && shared > 0 && shared === pageStack.depth && shared === pagePath.length - 1;
|
||||
const deferred = parentModal?.visible ?? false;
|
||||
let index = shared;
|
||||
if (index < pageStack.depth)
|
||||
pageStack.replace(pageStack.get(index), pageComponent, {
|
||||
page: pagePath[index++]
|
||||
}, StackView.Immediate).load(deferred);
|
||||
for (; index < pagePath.length; index++)
|
||||
pageStack.push(pageComponent, {
|
||||
page: pagePath[index]
|
||||
}, drillDown ? StackView.PushTransition : StackView.Immediate).load(deferred);
|
||||
if (!pendingPage) {
|
||||
pendingPage = _createPage(currentPage, pagePath.length - 1);
|
||||
pendingPage.load(parentModal?.visible ?? false);
|
||||
}
|
||||
if (!pendingPage.settled)
|
||||
return;
|
||||
const animate = animationsEnabled && pageStack.depth > 0 && (parentModal?.visible ?? false);
|
||||
const pages = [];
|
||||
for (let index = shared; index < pagePath.length - 1; index++) {
|
||||
const page = _createPage(pagePath[index], index);
|
||||
page.load(false);
|
||||
pages.push(page);
|
||||
}
|
||||
pages.push(pendingPage);
|
||||
pendingPage = null;
|
||||
if (shared < pageStack.depth) {
|
||||
pageStack.replace(pageStack.get(shared), pages, animate ? StackView.ReplaceTransition : StackView.Immediate);
|
||||
return;
|
||||
}
|
||||
pageStack.push(pages, animate ? StackView.PushTransition : StackView.Immediate);
|
||||
}
|
||||
|
||||
onPagePathChanged: Qt.callLater(_syncPages)
|
||||
onSessionVisibleChanged: Qt.callLater(_syncPages)
|
||||
|
||||
component Slide: Transition {
|
||||
id: slide
|
||||
component PageTransition: Transition {
|
||||
id: transition
|
||||
|
||||
property bool entering: true
|
||||
property real fromX: 0
|
||||
property real toX: 0
|
||||
|
||||
NumberAnimation {
|
||||
property: "x"
|
||||
from: slide.fromX
|
||||
to: slide.toX
|
||||
from: transition.fromX
|
||||
to: transition.toX
|
||||
duration: SettingsMetrics.transitionDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.standard
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
from: transition.entering ? 0 : 1
|
||||
to: transition.entering ? 1 : 0
|
||||
duration: SettingsMetrics.fadeDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
function _scrollerOf(item) {
|
||||
@@ -282,21 +316,35 @@ FocusScope {
|
||||
Component {
|
||||
id: pageComponent
|
||||
|
||||
// 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
|
||||
required property int pathIndex
|
||||
readonly property bool pageActive: root.sessionVisible && page === root.currentPage && pageStack.currentItem === host
|
||||
readonly property alias item: loader.item
|
||||
readonly property int status: loader.status
|
||||
readonly property bool settled: !pending && loader.status !== Loader.Loading && (presented || loader.status !== Loader.Ready)
|
||||
|
||||
readonly property var pageInfo: SettingsTabs.page(page)
|
||||
readonly property string pageTitle: (pageInfo?.titleFrom ? SettingsUiState[pageInfo.titleFrom] : "") || (pageInfo?.text ?? "")
|
||||
readonly property bool showBack: canGoBack || SettingsTabs.isPluginPage(page) || root.menuHidden
|
||||
readonly property string parentId: SettingsTabs.parentOf(page)
|
||||
readonly property bool canGoBack: pathIndex > 0 || (parentId !== "" && (SettingsTabs.isPluginPage(page) || SettingsTabs.visibleLeaves(parentId).length > 1 || !!SettingsTabs.page(parentId)?.hubHeader))
|
||||
property Item rememberedFocus: null
|
||||
property bool pending: true
|
||||
property bool presented: false
|
||||
|
||||
enabled: pageActive
|
||||
width: pageStack.width
|
||||
height: pageStack.height
|
||||
opacity: 0
|
||||
enabled: pageActive && presented
|
||||
|
||||
StackView.onRemoved: destroy()
|
||||
onSettledChanged: {
|
||||
if (settled && root.pendingPage === host)
|
||||
Qt.callLater(root._syncPages);
|
||||
}
|
||||
|
||||
function load(deferred) {
|
||||
const file = root._fileFor(page);
|
||||
@@ -308,11 +356,56 @@ FocusScope {
|
||||
pending = false;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: pageHeader
|
||||
width: Math.min(host.item?.contentMaxWidth ?? SettingsMetrics.contentMaxWidth, parent.width - SettingsMetrics.panePadding * 2)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: Math.max(SettingsMetrics.pageHeaderHeight, pageHeading.implicitHeight + Theme.spacingM * 2)
|
||||
|
||||
Item {
|
||||
id: backSlot
|
||||
|
||||
readonly property real glyphInset: (Theme.iconButtonSize - Theme.iconSize) / 2
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: host.showBack ? -glyphInset : 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: host.showBack ? Theme.iconButtonSize + Theme.spacingM - glyphInset : 0
|
||||
height: Theme.iconButtonSize
|
||||
|
||||
DActionButton {
|
||||
buttonSize: Theme.iconButtonSize
|
||||
iconName: I18n.isRtl ? "arrow_forward" : "arrow_back"
|
||||
Accessible.name: I18n.tr("Back")
|
||||
iconSize: Theme.iconSize
|
||||
iconColor: Theme.surfaceText
|
||||
visible: host.showBack
|
||||
onClicked: root.goBack()
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: pageHeading
|
||||
anchors.left: backSlot.right
|
||||
anchors.right: parent.right
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: host.pageTitle
|
||||
font.pixelSize: Theme.fontSizeXXLarge
|
||||
color: Theme.surfaceText
|
||||
visible: host.pageTitle !== ""
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader
|
||||
|
||||
anchors.fill: parent
|
||||
opacity: host.presented ? 1 : 0
|
||||
anchors.top: pageHeader.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.leftMargin: SettingsMetrics.panePadding
|
||||
anchors.rightMargin: SettingsMetrics.panePadding
|
||||
enabled: host.presented
|
||||
|
||||
onLoaded: {
|
||||
@@ -428,95 +521,59 @@ FocusScope {
|
||||
|
||||
readonly property var pagesWithParentModal: ["dankbar_widgets", "window_rules", "notification_rules", "display_config", "users", "time_weather", "weather", "lock_screen", "greeter", "dank_dash", "wallpaper_cycling", "theme_schedule", "surface_shadows", "keybinds", "dankbar_settings", "dankbar_appearance", "bar_widget", "dock_general", "dock_widgets", "dock_appearance", "dock_advanced", "launcher", "theme", "theme_apps", "media_player", "desktop_widgets", "desktop_widget", "autostart", "compositor_layout", "updater", "display_gamma"]
|
||||
|
||||
Column {
|
||||
StackView {
|
||||
id: pageStack
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: SettingsMetrics.panePadding
|
||||
anchors.rightMargin: SettingsMetrics.panePadding
|
||||
spacing: 0
|
||||
clip: true
|
||||
|
||||
Item {
|
||||
id: pageHeader
|
||||
width: Math.min(root.pageContentMaxWidth, parent.width)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: Math.max(SettingsMetrics.pageHeaderHeight, pageHeading.implicitHeight + Theme.spacingM * 2)
|
||||
readonly property real pageOffset: SettingsData.reduceMotion ? 0 : (I18n.isRtl ? -Theme.spacingXL : Theme.spacingXL)
|
||||
|
||||
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.spacingM - glyphInset : 0
|
||||
height: Theme.iconButtonSize
|
||||
|
||||
DActionButton {
|
||||
buttonSize: Theme.iconButtonSize
|
||||
iconName: I18n.isRtl ? "arrow_forward" : "arrow_back"
|
||||
Accessible.name: I18n.tr("Back")
|
||||
iconSize: Theme.iconSize
|
||||
iconColor: Theme.surfaceText
|
||||
visible: root.showBack
|
||||
onClicked: root.goBack()
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: pageHeading
|
||||
anchors.left: backSlot.right
|
||||
anchors.right: parent.right
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.pageTitle
|
||||
font.pixelSize: Theme.fontSizeXXLarge
|
||||
color: Theme.surfaceText
|
||||
visible: root.pageTitle !== ""
|
||||
}
|
||||
onBusyChanged: {
|
||||
if (!busy)
|
||||
Qt.callLater(root._syncPages);
|
||||
}
|
||||
|
||||
StackView {
|
||||
id: pageStack
|
||||
width: parent.width
|
||||
height: parent.height - pageHeader.height
|
||||
clip: true
|
||||
pushEnter: PageTransition {
|
||||
fromX: pageStack.pageOffset
|
||||
}
|
||||
pushExit: PageTransition {
|
||||
entering: false
|
||||
toX: -pageStack.pageOffset
|
||||
}
|
||||
popEnter: PageTransition {
|
||||
fromX: -pageStack.pageOffset
|
||||
}
|
||||
popExit: PageTransition {
|
||||
entering: false
|
||||
toX: pageStack.pageOffset
|
||||
}
|
||||
replaceEnter: PageTransition {}
|
||||
replaceExit: PageTransition {
|
||||
entering: false
|
||||
}
|
||||
|
||||
readonly property real slideOffscreen: I18n.isRtl ? -width : width
|
||||
DSpinner {
|
||||
id: pageSpinner
|
||||
|
||||
pushEnter: Slide {
|
||||
fromX: pageStack.slideOffscreen
|
||||
}
|
||||
pushExit: Slide {
|
||||
toX: -pageStack.slideOffscreen
|
||||
}
|
||||
popEnter: Slide {
|
||||
fromX: -pageStack.slideOffscreen
|
||||
}
|
||||
popExit: Slide {
|
||||
toX: pageStack.slideOffscreen
|
||||
}
|
||||
readonly property bool loading: root.pendingPage !== null
|
||||
|
||||
DSpinner {
|
||||
id: pageSpinner
|
||||
|
||||
readonly property bool loading: pageStack.currentItem?.presented === false
|
||||
|
||||
anchors.centerIn: parent
|
||||
visible: false
|
||||
onLoadingChanged: {
|
||||
if (!loading) {
|
||||
spinnerDelay.stop();
|
||||
visible = false;
|
||||
return;
|
||||
}
|
||||
spinnerDelay.restart();
|
||||
anchors.centerIn: parent
|
||||
z: pageStack.depth
|
||||
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
|
||||
}
|
||||
Timer {
|
||||
id: spinnerDelay
|
||||
interval: SettingsMetrics.pageSettleDeadline + SettingsMetrics.fadeDuration
|
||||
onTriggered: pageSpinner.visible = pageSpinner.loading
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +442,7 @@ DFloatingWindow {
|
||||
clip: true
|
||||
|
||||
Behavior on x {
|
||||
enabled: settingsModal.isCompactMode && Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None
|
||||
enabled: settingsModal.isCompactMode && !SettingsData.reduceMotion && Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None
|
||||
NumberAnimation {
|
||||
duration: SettingsMetrics.transitionDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
|
||||
Reference in New Issue
Block a user