diff --git a/modules/nexus/NexusState.qml b/modules/nexus/NexusState.qml index 015f844d..3e2afc72 100644 --- a/modules/nexus/NexusState.qml +++ b/modules/nexus/NexusState.qml @@ -5,7 +5,22 @@ QtObject { property ShellScreen screen property bool isWindow property int currentPageIdx + property list subPageIdxStack property bool searchOpen signal close + signal subPageOpened(idx: int) + signal subPageClosed + + function openSubPage(idx: int): void { + subPageIdxStack.push(idx); + subPageOpened(idx); + } + + function closeSubPage(): void { + subPageClosed(); + subPageIdxStack.pop(); + } + + onCurrentPageIdxChanged: subPageIdxStack.length = 0 } diff --git a/modules/nexus/PageCompRegistry.qml b/modules/nexus/PageCompRegistry.qml new file mode 100644 index 00000000..8fcdfca3 --- /dev/null +++ b/modules/nexus/PageCompRegistry.qml @@ -0,0 +1,20 @@ +pragma Singleton + +import QtQuick +import qs.modules.nexus.common +import qs.modules.nexus.pages + +QtObject { + id: root + + readonly property list pageComps: [ + // Appearance + Component { + StackPage { + Component { + WallpaperAndStyle {} + } + } + } + ] +} diff --git a/modules/nexus/Pages.qml b/modules/nexus/Pages.qml index b2f03aac..ab30bd50 100644 --- a/modules/nexus/Pages.qml +++ b/modules/nexus/Pages.qml @@ -1,6 +1,5 @@ pragma ComponentBehavior: Bound -import "pages" import QtQuick import QtQuick.Layouts import Caelestia.Config @@ -13,23 +12,39 @@ Item { required property NexusState nState - readonly property list pageComps: [ - Component { - WallpaperAndStyle { - nState: root.nState - } - } - ] - property int lastPageIdx property int animOff + property Item currentItem - Loader { - id: loader + function loadPage(idx: int): void { + if (currentItem) + currentItem.destroy(); + + const comp = PageCompRegistry.pageComps[idx] ?? placeholderComp; + const incubator = comp.incubateObject(container, { + nState + }); + + const attach = () => { + incubator.object.anchors.fill = container; + currentItem = incubator.object; + }; + + if (incubator.status === Component.Ready) + attach(); + else + incubator.onStatusChanged = status => { + if (status === Component.Ready) + attach(); + }; + } + + Item { + id: container anchors.fill: parent layer.enabled: opacity < 1 - Component.onCompleted: sourceComponent = root.pageComps[root.nState.currentPageIdx] ?? placeholderComp + Component.onCompleted: root.loadPage(root.nState.currentPageIdx) } Connections { @@ -47,36 +62,34 @@ Item { id: switchAnim Anim { - target: loader + target: container property: "opacity" to: 0 type: Anim.DefaultEffects } - PropertyAction { - target: loader - property: "sourceComponent" - value: root.pageComps[root.nState.currentPageIdx] ?? placeholderComp + ScriptAction { + script: root.loadPage(root.nState.currentPageIdx) } PropertyAction { - target: loader.anchors + target: container.anchors property: "topMargin" value: root.animOff } PropertyAction { - target: loader.anchors + target: container.anchors property: "bottomMargin" value: -root.animOff } ParallelAnimation { Anim { - target: loader + target: container property: "opacity" from: 0 to: 1 type: Anim.SlowEffects } Anim { - target: loader.anchors + target: container.anchors properties: "topMargin,bottomMargin" to: 0 type: Anim.SlowEffects @@ -88,6 +101,8 @@ Item { id: placeholderComp Item { + property NexusState nState // To avoid the warning from non-existent property + ColumnLayout { anchors.centerIn: parent spacing: Tokens.padding.extraSmall diff --git a/modules/nexus/common/PageBase.qml b/modules/nexus/common/PageBase.qml index ba329340..0b5e1cf7 100644 --- a/modules/nexus/common/PageBase.qml +++ b/modules/nexus/common/PageBase.qml @@ -19,8 +19,6 @@ ColumnLayout { default property Item contentChild - signal close - spacing: Tokens.spacing.extraExtraLarge RowLayout { @@ -37,7 +35,7 @@ ColumnLayout { isRound: true inactiveColour: Colours.tPalette.m3surfaceContainerHigh inactiveOnColour: Colours.palette.m3onSurface - onClicked: root.close() + onClicked: root.nState.closeSubPage() } } diff --git a/modules/nexus/common/StackPage.qml b/modules/nexus/common/StackPage.qml new file mode 100644 index 00000000..f104652e --- /dev/null +++ b/modules/nexus/common/StackPage.qml @@ -0,0 +1,122 @@ +import QtQuick +import QtQuick.Controls +import Caelestia.Config +import qs.components +import qs.modules.nexus + +StackView { + id: root + + required property NexusState nState + default property list pages + readonly property int animMovement: Tokens.padding.extraExtraLarge * 2 + + function openSubPage(idx: int, immediate: bool): void { + const page = pages[idx]; + if (page) { + push(page, { + nState + }, immediate ? StackView.Immediate : StackView.PushTransition); + } else { + console.warn(logCat, "Attempted to open invalid sub-page index", idx); + nState.closeSubPage(); + } + } + + clip: busy + + Component.onCompleted: { + openSubPage(0, true); + for (const page of nState.subPageIdxStack) + openSubPage(page, true); + } + + pushEnter: Transition { + SequentialAnimation { + PropertyAction { + property: "opacity" + value: 0 + } + PauseAnimation { + duration: Tokens.anim.durations.expressiveDefaultEffects + } + ParallelAnimation { + Anim { + property: "opacity" + to: 1 + type: Anim.SlowEffects + } + Anim { + property: "x" + from: root.animMovement + to: 0 + type: Anim.SlowEffects + } + } + } + } + + pushExit: Transition { + Anim { + property: "opacity" + to: 0 + type: Anim.DefaultEffects + } + } + + popEnter: Transition { + SequentialAnimation { + PropertyAction { + property: "opacity" + value: 0 + } + PauseAnimation { + duration: Tokens.anim.durations.expressiveDefaultEffects + } + ParallelAnimation { + Anim { + property: "opacity" + to: 1 + type: Anim.SlowEffects + } + Anim { + property: "x" + from: -root.animMovement + to: 0 + type: Anim.SlowEffects + } + } + } + } + + popExit: Transition { + Anim { + property: "opacity" + to: 0 + type: Anim.DefaultEffects + } + } + + LoggingCategory { + id: logCat + + name: "caelestia.nexus" + defaultLogLevel: LoggingCategory.Info + } + + Connections { + function onSubPageOpened(idx: int): void { + root.openSubPage(idx, false); + } + + function onSubPageClosed(): void { + if (root.depth < root.nState.subPageIdxStack.length) { + console.log(logCat, "Attempted to close page while depth < stack depth. Ignoring."); + return; + } + root.pop(); + } + + target: root.nState + } +}