Also disable nexus nav fade on rebound start if content smaller than height And force vertical flick only on nexus nav
190 lines
6.7 KiB
QML
190 lines
6.7 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import QtQuick.Layouts
|
|
import Caelestia.Config
|
|
import qs.components
|
|
import qs.components.containers
|
|
import qs.components.effects
|
|
import qs.services
|
|
import qs.modules.nexus
|
|
|
|
StyledFlickable {
|
|
id: root
|
|
|
|
required property NexusState nState
|
|
|
|
property real topFadeOpacity: fadeShouldBeActive(true) ? 0 : 1
|
|
property real bottomFadeOpacity: fadeShouldBeActive(false) ? 0 : 1
|
|
|
|
function fadeShouldBeActive(isStart: bool): bool {
|
|
// When content is smaller than flickable size, hide fade when rebound starts
|
|
if (contentHeight + topMargin + bottomMargin < height && rebound.running && ((isStart ? verticalOvershoot > 0 : verticalOvershoot < 0)))
|
|
return false;
|
|
|
|
if (isStart)
|
|
return visibleArea.yPosition > 0;
|
|
return visibleArea.yPosition + visibleArea.heightRatio < 1;
|
|
}
|
|
|
|
topMargin: Tokens.padding.large
|
|
bottomMargin: Tokens.padding.large
|
|
contentHeight: content.implicitHeight
|
|
flickableDirection: Flickable.VerticalFlick
|
|
|
|
layer.enabled: true
|
|
layer.effect: Mask {
|
|
maskSource: mask
|
|
|
|
Rectangle {
|
|
id: mask
|
|
|
|
anchors.fill: parent
|
|
visible: false
|
|
layer.enabled: true
|
|
|
|
gradient: Gradient {
|
|
orientation: Gradient.Vertical
|
|
|
|
GradientStop {
|
|
position: 0
|
|
color: Qt.rgba(0, 0, 0, root.topFadeOpacity)
|
|
}
|
|
GradientStop {
|
|
position: 0.3
|
|
color: Qt.rgba(0, 0, 0, 1)
|
|
}
|
|
GradientStop {
|
|
position: 0.7
|
|
color: Qt.rgba(0, 0, 0, 1)
|
|
}
|
|
GradientStop {
|
|
position: 1
|
|
color: Qt.rgba(0, 0, 0, root.bottomFadeOpacity)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Behavior on topFadeOpacity {
|
|
Anim {
|
|
type: Anim.SlowEffects
|
|
}
|
|
}
|
|
|
|
Behavior on bottomFadeOpacity {
|
|
Anim {
|
|
type: Anim.SlowEffects
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
id: content
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
spacing: Tokens.spacing.extraSmall
|
|
|
|
Repeater {
|
|
id: list
|
|
|
|
model: PageRegistry.pages
|
|
|
|
StyledRect {
|
|
id: item
|
|
|
|
required property var modelData
|
|
required property int index
|
|
|
|
readonly property bool isCurrentPage: index === root.nState.currentPageIdx
|
|
readonly property bool isCategoryStart: index === 0 || PageRegistry.pages[index - 1].category !== modelData.category
|
|
readonly property bool isCategoryEnd: index === list.model.length - 1 || PageRegistry.pages[index + 1].category !== modelData.category
|
|
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: index !== 0 && isCategoryStart ? Tokens.spacing.medium : 0
|
|
implicitHeight: {
|
|
const h = layout.implicitHeight + layout.anchors.margins * 2;
|
|
return h % 2 === 0 ? h : h + 1;
|
|
}
|
|
|
|
color: isCurrentPage ? Colours.palette.m3secondaryContainer : Colours.palette.m3surfaceContainerHigh
|
|
|
|
topLeftRadius: stateLayer.pressed ? Tokens.rounding.medium : isCurrentPage ? Tokens.rounding.extraLargeIncreased : isCategoryStart ? Tokens.rounding.extraLarge : Tokens.rounding.extraSmall
|
|
topRightRadius: stateLayer.pressed ? Tokens.rounding.medium : isCurrentPage ? Tokens.rounding.extraLargeIncreased : isCategoryStart ? Tokens.rounding.extraLarge : Tokens.rounding.extraSmall
|
|
bottomLeftRadius: stateLayer.pressed ? Tokens.rounding.medium : isCurrentPage ? Tokens.rounding.extraLargeIncreased : isCategoryEnd ? Tokens.rounding.extraLarge : Tokens.rounding.extraSmall
|
|
bottomRightRadius: stateLayer.pressed ? Tokens.rounding.medium : isCurrentPage ? Tokens.rounding.extraLargeIncreased : isCategoryEnd ? Tokens.rounding.extraLarge : Tokens.rounding.extraSmall
|
|
|
|
RadiusBehavior on topLeftRadius {}
|
|
RadiusBehavior on topRightRadius {}
|
|
RadiusBehavior on bottomLeftRadius {}
|
|
RadiusBehavior on bottomRightRadius {}
|
|
|
|
StateLayer {
|
|
id: stateLayer
|
|
|
|
anchors.fill: parent
|
|
topLeftRadius: parent.topLeftRadius
|
|
topRightRadius: parent.topRightRadius
|
|
bottomLeftRadius: parent.bottomLeftRadius
|
|
bottomRightRadius: parent.bottomRightRadius
|
|
|
|
onClicked: root.nState.currentPageIdx = item.index
|
|
}
|
|
|
|
RowLayout {
|
|
id: layout
|
|
|
|
anchors.fill: parent
|
|
anchors.margins: Tokens.padding.large
|
|
spacing: Tokens.spacing.medium
|
|
|
|
StyledRect {
|
|
Layout.fillHeight: true
|
|
Layout.topMargin: -1
|
|
Layout.bottomMargin: -1
|
|
implicitWidth: height
|
|
|
|
radius: Tokens.rounding.full
|
|
color: item.isCurrentPage ? Colours.palette.m3primary : Colours.palette.m3secondaryContainer
|
|
|
|
MaterialIcon {
|
|
anchors.centerIn: parent
|
|
anchors.verticalCenterOffset: 1
|
|
|
|
text: item.modelData.icon
|
|
color: item.isCurrentPage ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondaryContainer
|
|
fontStyle: Tokens.font.icon.medium
|
|
fill: 1
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 0
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
text: item.modelData.label
|
|
font: Tokens.font.title.small
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
text: item.modelData.description
|
|
color: Colours.palette.m3onSurfaceVariant
|
|
font: Tokens.font.label.small
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
component RadiusBehavior: Behavior {
|
|
Anim {
|
|
type: Anim.DefaultEffects
|
|
}
|
|
}
|
|
}
|