Merge remote-tracking branch 'refs/remotes/origin/main'

This commit is contained in:
clsty
2025-11-04 00:45:19 +08:00
10 changed files with 210 additions and 51 deletions
@@ -334,7 +334,6 @@ Variants {
font {
pixelSize: Appearance.font.pixelSize.normal
weight: 350
italic: true
}
color: bgRoot.colText
style: Text.Raised
@@ -1,6 +1,7 @@
import qs
import qs.services
import qs.modules.common
import qs.modules.common.models
import qs.modules.common.widgets
import qs.modules.common.functions
import QtQuick
@@ -163,12 +164,12 @@ Item {
horizontalCenter: vertical ? parent.horizontalCenter : undefined
}
// idx1 is the "leading" indicator position, idx2 is the "following" one
// The former animates faster than the latter, see the NumberAnimations below
property real idx1: workspaceIndexInGroup
property real idx2: workspaceIndexInGroup
property real indicatorPosition: Math.min(idx1, idx2) * workspaceButtonWidth + root.activeWorkspaceMargin
property real indicatorLength: Math.abs(idx1 - idx2) * workspaceButtonWidth + workspaceButtonWidth - root.activeWorkspaceMargin * 2
AnimatedTabIndexPair {
id: idxPair
index: root.workspaceIndexInGroup
}
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * workspaceButtonWidth + root.activeWorkspaceMargin
property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * workspaceButtonWidth + workspaceButtonWidth - root.activeWorkspaceMargin * 2
property real indicatorThickness: workspaceButtonWidth - root.activeWorkspaceMargin * 2
x: root.vertical ? null : indicatorPosition
@@ -176,18 +177,6 @@ Item {
y: root.vertical ? indicatorPosition : null
implicitHeight: root.vertical ? indicatorLength : indicatorThickness
Behavior on idx1 {
NumberAnimation {
duration: 100
easing.type: Easing.OutSine
}
}
Behavior on idx2 {
NumberAnimation {
duration: 300
easing.type: Easing.OutSine
}
}
}
// Workspaces - numbers
@@ -0,0 +1,26 @@
import QtQuick
// idx1 is the "leading" indicator position, idx2 is the "following" one
// The former animates faster than the latter, see the NumberAnimations below
QtObject {
id: root
required property int index
property real idx1: index
property real idx2: index
property int idx1Duration: 100
property int idx2Duration: 300
Behavior on idx1 {
NumberAnimation {
duration: root.idx1Duration
easing.type: Easing.OutSine
}
}
Behavior on idx2 {
NumberAnimation {
duration: root.idx2Duration
easing.type: Easing.OutSine
}
}
}
@@ -13,7 +13,7 @@ ToolbarButton {
contentItem: Row {
anchors.centerIn: parent
spacing: 6
spacing: 4
MaterialSymbol {
anchors.verticalCenter: parent.verticalCenter
@@ -9,7 +9,7 @@ ColumnLayout {
id: root
spacing: 0
required property var tabButtonList // Something like [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}]
property int currentIndex
property alias currentIndex: tabBar.currentIndex
property bool enableIndicatorAnimation: false
property color colIndicator: Appearance?.colors.colPrimary ?? "#65558F"
property color colBorder: Appearance?.m3colors.m3outlineVariant ?? "#C6C6D0"
@@ -26,17 +26,14 @@ ColumnLayout {
TabBar {
id: tabBar
Layout.fillWidth: true
Synchronizer on currentIndex {
property alias source: root.currentIndex
}
background: Item {
WheelHandler {
onWheel: (event) => {
if (event.angleDelta.y < 0)
tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1)
tabBar.incrementCurrentIndex()
else if (event.angleDelta.y > 0)
tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0)
tabBar.decrementCurrentIndex()
}
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
}
@@ -49,7 +46,7 @@ ColumnLayout {
buttonText: modelData.name
buttonIcon: modelData.icon
minimumWidth: 160
onClicked: root.currentIndex = index
onClicked: tabBar.setCurrentIndex(index)
}
}
}
@@ -10,6 +10,7 @@ import qs.modules.common.widgets
Item {
id: root
property bool enableShadow: true
property real padding: 8
property alias colBackground: background.color
property alias spacing: toolbarLayout.spacing
@@ -18,15 +19,20 @@ Item {
implicitHeight: background.implicitHeight
property alias radius: background.radius
StyledRectangularShadow {
target: background
Loader {
active: root.enableShadow
anchors.fill: background
sourceComponent: StyledRectangularShadow {
target: background
anchors.fill: undefined
}
}
Rectangle {
id: background
anchors.fill: parent
color: Appearance.m3colors.m3surfaceContainer
implicitHeight: Math.max(toolbarLayout.implicitHeight + root.padding * 2, 56)
implicitHeight: 56
implicitWidth: toolbarLayout.implicitWidth + root.padding * 2
radius: height / 2
@@ -0,0 +1,103 @@
pragma ComponentBehavior: Bound
import qs.modules.common
import qs.modules.common.models
import qs.services
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.synchronizer
Item {
id: root
property alias currentIndex: tabBar.currentIndex
required property var tabButtonList
function incrementCurrentIndex() {
tabBar.incrementCurrentIndex()
}
function decrementCurrentIndex() {
tabBar.decrementCurrentIndex()
}
function setCurrentIndex(index) {
tabBar.setCurrentIndex(index)
}
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
implicitWidth: contentItem.implicitWidth
implicitHeight: 40
Row {
id: contentItem
z: 1
anchors.centerIn: parent
spacing: 4
Repeater {
model: root.tabButtonList
delegate: ToolbarTabButton {
required property int index
required property var modelData
current: index == root.currentIndex
text: modelData.name
materialSymbol: modelData.icon
onClicked: {
root.setCurrentIndex(index)
}
}
}
}
Rectangle {
id: activeIndicator
z: 0
color: Appearance.colors.colSecondaryContainer
implicitWidth: contentItem.children[root.currentIndex].implicitWidth
implicitHeight: contentItem.children[root.currentIndex].implicitHeight
radius: height / 2
// Animation
property Item targetItem: contentItem.children[root.currentIndex]
AnimatedTabIndexPair {
id: leftBound
idx1Duration: 50
idx2Duration: 200
index: activeIndicator.targetItem.x
}
AnimatedTabIndexPair {
id: rightBound
idx1Duration: 50
idx2Duration: 200
index: activeIndicator.targetItem.x + activeIndicator.targetItem.width
}
x: Math.min(leftBound.idx1, leftBound.idx2)
width: Math.max(rightBound.idx1, rightBound.idx2) - x
}
MouseArea {
anchors.fill: parent
z: 2
acceptedButtons: Qt.NoButton
cursorShape: Qt.PointingHandCursor
onWheel: (event) => {
if (event.angleDelta.y < 0) {
root.incrementCurrentIndex();
}
else {
root.decrementCurrentIndex();
}
}
}
// TabBar doesn't allow tabs to be of different sizes. Literally unusable.
// We use it only for the logic and draw stuff manually
TabBar {
id: tabBar
z: -1
background: null
Repeater { // This is to fool the TabBar that it has tabs so it does the indices properly
model: root.tabButtonList.length
delegate: TabButton {
background: null
}
}
}
}
@@ -0,0 +1,40 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import Qt5Compat.GraphicalEffects
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
RippleButton {
id: root
required property string materialSymbol
required property bool current
horizontalPadding: 10
implicitHeight: 40
implicitWidth: implicitContentWidth + horizontalPadding * 2
buttonRadius: height / 2
colBackground: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer)
colBackgroundHover: ColorUtils.transparentize(Appearance.colors.colOnSurface, current ? 1 : 0.95)
colRipple: ColorUtils.transparentize(Appearance.colors.colOnSurface, 0.95)
contentItem: Row {
id: contentRow
anchors.centerIn: parent
spacing: 6
MaterialSymbol {
id: icon
anchors.verticalCenter: parent.verticalCenter
iconSize: 22
text: root.materialSymbol
}
StyledText {
id: label
anchors.verticalCenter: parent.verticalCenter
text: root.text
}
}
}
@@ -21,7 +21,6 @@ Item {
...(root.translatorEnabled ? [{"icon": "translate", "name": Translation.tr("Translator")}] : []),
...((root.animeEnabled && !root.animeCloset) ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : [])
]
property int selectedTab: 0
property int tabCount: swipeView.count
function focusActiveItem() {
@@ -31,36 +30,39 @@ Item {
Keys.onPressed: (event) => {
if (event.modifiers === Qt.ControlModifier) {
if (event.key === Qt.Key_PageDown) {
root.selectedTab = Math.min(root.selectedTab + 1, root.tabCount - 1)
swipeView.incrementCurrentIndex()
event.accepted = true;
}
}
else if (event.key === Qt.Key_PageUp) {
root.selectedTab = Math.max(root.selectedTab - 1, 0)
swipeView.decrementCurrentIndex()
event.accepted = true;
}
else if (event.key === Qt.Key_Tab) {
root.selectedTab = (root.selectedTab + 1) % root.tabCount;
swipeView.setCurrentIndex((swipeView.currentIndex + 1) % swipeView.count);
event.accepted = true;
}
else if (event.key === Qt.Key_Backtab) {
root.selectedTab = (root.selectedTab - 1 + root.tabCount) % root.tabCount;
swipeView.setCurrentIndex((swipeView.currentIndex - 1 + swipeView.count) % swipeView.count);
event.accepted = true;
}
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: sidebarPadding
anchors {
fill: parent
margins: sidebarPadding
}
spacing: sidebarPadding
PrimaryTabBar { // Tab strip
id: tabBar
visible: root.tabButtonList.length > 1
tabButtonList: root.tabButtonList
Synchronizer on currentIndex {
property alias source: root.selectedTab
Toolbar {
Layout.alignment: Qt.AlignHCenter
enableShadow: false
ToolbarTabBar {
id: tabBar
Layout.alignment: Qt.AlignHCenter
tabButtonList: root.tabButtonList
currentIndex: swipeView.currentIndex
}
}
@@ -70,12 +72,7 @@ Item {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 10
currentIndex: root.selectedTab
onCurrentIndexChanged: {
tabBar.enableIndicatorAnimation = true
root.selectedTab = currentIndex
}
currentIndex: tabBar.currentIndex
clip: true
layer.enabled: true
@@ -186,6 +186,8 @@ MouseArea {
colBackgroundToggled: Appearance.colors.colSecondaryContainer
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
colRippleToggled: Appearance.colors.colSecondaryContainerActive
buttonRadius: height / 2
implicitHeight: 38
contentItem: RowLayout {
MaterialSymbol {