feat: add nexus sub pages

This commit is contained in:
2 * r + 2 * t 2026-06-05 19:22:09 +10:00
parent 550eb0bf1a
commit 4a77746393
5 changed files with 194 additions and 24 deletions

View file

@ -5,7 +5,22 @@ QtObject {
property ShellScreen screen property ShellScreen screen
property bool isWindow property bool isWindow
property int currentPageIdx property int currentPageIdx
property list<int> subPageIdxStack
property bool searchOpen property bool searchOpen
signal close 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
} }

View file

@ -0,0 +1,20 @@
pragma Singleton
import QtQuick
import qs.modules.nexus.common
import qs.modules.nexus.pages
QtObject {
id: root
readonly property list<Component> pageComps: [
// Appearance
Component {
StackPage {
Component {
WallpaperAndStyle {}
}
}
}
]
}

View file

@ -1,6 +1,5 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import "pages"
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Caelestia.Config import Caelestia.Config
@ -13,23 +12,39 @@ Item {
required property NexusState nState required property NexusState nState
readonly property list<Component> pageComps: [
Component {
WallpaperAndStyle {
nState: root.nState
}
}
]
property int lastPageIdx property int lastPageIdx
property int animOff property int animOff
property Item currentItem
Loader { function loadPage(idx: int): void {
id: loader 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 anchors.fill: parent
layer.enabled: opacity < 1 layer.enabled: opacity < 1
Component.onCompleted: sourceComponent = root.pageComps[root.nState.currentPageIdx] ?? placeholderComp Component.onCompleted: root.loadPage(root.nState.currentPageIdx)
} }
Connections { Connections {
@ -47,36 +62,34 @@ Item {
id: switchAnim id: switchAnim
Anim { Anim {
target: loader target: container
property: "opacity" property: "opacity"
to: 0 to: 0
type: Anim.DefaultEffects type: Anim.DefaultEffects
} }
PropertyAction { ScriptAction {
target: loader script: root.loadPage(root.nState.currentPageIdx)
property: "sourceComponent"
value: root.pageComps[root.nState.currentPageIdx] ?? placeholderComp
} }
PropertyAction { PropertyAction {
target: loader.anchors target: container.anchors
property: "topMargin" property: "topMargin"
value: root.animOff value: root.animOff
} }
PropertyAction { PropertyAction {
target: loader.anchors target: container.anchors
property: "bottomMargin" property: "bottomMargin"
value: -root.animOff value: -root.animOff
} }
ParallelAnimation { ParallelAnimation {
Anim { Anim {
target: loader target: container
property: "opacity" property: "opacity"
from: 0 from: 0
to: 1 to: 1
type: Anim.SlowEffects type: Anim.SlowEffects
} }
Anim { Anim {
target: loader.anchors target: container.anchors
properties: "topMargin,bottomMargin" properties: "topMargin,bottomMargin"
to: 0 to: 0
type: Anim.SlowEffects type: Anim.SlowEffects
@ -88,6 +101,8 @@ Item {
id: placeholderComp id: placeholderComp
Item { Item {
property NexusState nState // To avoid the warning from non-existent property
ColumnLayout { ColumnLayout {
anchors.centerIn: parent anchors.centerIn: parent
spacing: Tokens.padding.extraSmall spacing: Tokens.padding.extraSmall

View file

@ -19,8 +19,6 @@ ColumnLayout {
default property Item contentChild default property Item contentChild
signal close
spacing: Tokens.spacing.extraExtraLarge spacing: Tokens.spacing.extraExtraLarge
RowLayout { RowLayout {
@ -37,7 +35,7 @@ ColumnLayout {
isRound: true isRound: true
inactiveColour: Colours.tPalette.m3surfaceContainerHigh inactiveColour: Colours.tPalette.m3surfaceContainerHigh
inactiveOnColour: Colours.palette.m3onSurface inactiveOnColour: Colours.palette.m3onSurface
onClicked: root.close() onClicked: root.nState.closeSubPage()
} }
} }

View file

@ -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<Component> 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
}
}