From e5e85db75d69ff6cf6a8e87065d75efb68610cb0 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:56:08 +0100 Subject: [PATCH] refractor secondary tab bar --- .../common/widgets/SecondaryTabBar.qml | 53 ++++++++++++ .../common/widgets/SecondaryTabButton.qml | 22 ++--- .../sidebarRight/pomodoro/PomodoroWidget.qml | 82 ++----------------- .../modules/sidebarRight/todo/TodoWidget.qml | 80 ++---------------- 4 files changed, 78 insertions(+), 159 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/SecondaryTabBar.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/SecondaryTabBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/SecondaryTabBar.qml new file mode 100644 index 000000000..dcb490048 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/SecondaryTabBar.qml @@ -0,0 +1,53 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.models + +TabBar { + id: root + property real indicatorPadding: 8 + Layout.fillWidth: true + + background: Item { + WheelHandler { + onWheel: (event) => { + if (event.angleDelta.y < 0) root.incrementCurrentIndex(); + else if (event.angleDelta.y > 0) root.decrementCurrentIndex(); + } + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + } + + Rectangle { + id: activeIndicator + z: 9999 + anchors.bottom: parent.bottom + topLeftRadius: height + topRightRadius: height + bottomLeftRadius: 0 + bottomRightRadius: 0 + color: Appearance.colors.colPrimary + // Animation + property real baseWidth: root.width / root.count + AnimatedTabIndexPair { + id: idxPair + index: root.currentIndex + } + height: 3 + x: Math.min(idxPair.idx1, idxPair.idx2) * baseWidth + root.indicatorPadding + width: ((Math.max(idxPair.idx1, idxPair.idx2) + 1) * baseWidth - root.indicatorPadding) - x + } + + Rectangle { // Tabbar bottom border + id: tabBarBottomBorder + z: 9998 + anchors.bottom: parent.bottom + height: 1 + anchors { + left: parent.left + right: parent.right + } + color: Appearance.colors.colOutlineVariant + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/SecondaryTabButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/SecondaryTabButton.qml index b1036c777..b0bc9b4de 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/SecondaryTabButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/SecondaryTabButton.qml @@ -1,6 +1,6 @@ import qs.modules.common -import qs.modules.common.widgets import qs.modules.common.functions +import qs.modules.common.widgets import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls @@ -10,14 +10,12 @@ TabButton { id: root property string buttonText property string buttonIcon - property bool selected: false property int rippleDuration: 1200 - height: buttonBackground.height property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2 - property color colBackground: ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1) - property color colBackgroundHover: Appearance.colors.colLayer1Hover - property color colRipple: Appearance.colors.colLayer1Active + property color colBackground: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer) + property color colBackgroundHover: ColorUtils.transparentize(Appearance.colors.colOnSurface, root.checked ? 1 : 0.95) + property color colRipple: ColorUtils.transparentize(Appearance.colors.colOnSurface, 0.95) PointingHandInteraction {} @@ -91,8 +89,12 @@ TabButton { background: Rectangle { id: buttonBackground + anchors { + fill: parent + margins: 3 + } radius: Appearance?.rounding.normal - implicitHeight: 37 + implicitHeight: 42 color: (root.hovered ? root.colBackgroundHover : root.colBackground) layer.enabled: true layer.effect: OpacityMask { @@ -156,8 +158,8 @@ TabButton { verticalAlignment: Text.AlignVCenter text: buttonIcon iconSize: Appearance.font.pixelSize.huge - fill: selected ? 1 : 0 - color: selected ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1 + fill: root.checked ? 1 : 0 + color: root.checked ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1 Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } @@ -167,7 +169,7 @@ TabButton { id: buttonTextWidget verticalAlignment: Text.AlignVCenter font.pixelSize: Appearance.font.pixelSize.small - color: selected ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1 + color: root.checked ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1 text: buttonText Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/dots/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml index ca7723db3..a62a9616c 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml @@ -7,7 +7,6 @@ import QtQuick.Layouts Item { id: root - property int currentTab: 0 property var tabButtonList: [ {"name": Translation.tr("Pomodoro"), "icon": "search_activity"}, {"name": Translation.tr("Stopwatch"), "icon": "timer"} @@ -17,20 +16,20 @@ Item { Keys.onPressed: (event) => { if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) { // Switch tabs if (event.key === Qt.Key_PageDown) { - currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1) + tabBar.incrementCurrentIndex(); } else if (event.key === Qt.Key_PageUp) { - currentTab = Math.max(currentTab - 1, 0) + tabBar.decrementCurrentIndex(); } event.accepted = true } else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S - if (currentTab === 0) { + if (tabBar.currentIndex === 0) { TimerService.togglePomodoro() } else { TimerService.toggleStopwatch() } event.accepted = true } else if (event.key === Qt.Key_R) { // Reset with R - if (currentTab === 0) { + if (tabBar.currentIndex === 0) { TimerService.resetPomodoro() } else { TimerService.stopwatchReset() @@ -46,82 +45,19 @@ Item { anchors.fill: parent spacing: 0 - TabBar { + SecondaryTabBar { id: tabBar - Layout.fillWidth: true - currentIndex: currentTab - onCurrentIndexChanged: currentTab = currentIndex - - background: Item { - WheelHandler { - onWheel: (event) => { - if (event.angleDelta.y < 0) - tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1) - else if (event.angleDelta.y > 0) - tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0) - } - acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad - } - } + currentIndex: swipeView.currentIndex Repeater { model: root.tabButtonList delegate: SecondaryTabButton { - selected: (index == currentTab) buttonText: modelData.name buttonIcon: modelData.icon } } } - Item { // Tab indicator - id: tabIndicator - Layout.fillWidth: true - height: 3 - property bool enableIndicatorAnimation: false - Connections { - target: root - function onCurrentTabChanged() { - tabIndicator.enableIndicatorAnimation = true - } - } - - Rectangle { - id: indicator - property int tabCount: root.tabButtonList.length - property real fullTabSize: root.width / tabCount; - property real targetWidth: tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth - - implicitWidth: targetWidth - anchors { - top: parent.top - bottom: parent.bottom - } - - x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2 - - color: Appearance.colors.colPrimary - radius: Appearance.rounding.full - - Behavior on x { - enabled: tabIndicator.enableIndicatorAnimation - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } - - Behavior on implicitWidth { - enabled: tabIndicator.enableIndicatorAnimation - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } - } - } - - Rectangle { // Tabbar bottom border - id: tabBarBottomBorder - Layout.fillWidth: true - height: 1 - color: Appearance.colors.colOutlineVariant - } - SwipeView { id: swipeView Layout.topMargin: 10 @@ -129,11 +65,7 @@ Item { Layout.fillHeight: true spacing: 10 clip: true - currentIndex: currentTab - onCurrentIndexChanged: { - tabIndicator.enableIndicatorAnimation = true - currentTab = currentIndex - } + currentIndex: tabBar.currentIndex // Tabs PomodoroTimer {} diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/todo/TodoWidget.qml b/dots/.config/quickshell/ii/modules/sidebarRight/todo/TodoWidget.qml index 99b0100f5..36c885523 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/todo/TodoWidget.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/todo/TodoWidget.qml @@ -7,7 +7,6 @@ import QtQuick.Layouts Item { id: root - property int currentTab: 0 property var tabButtonList: [{"icon": "checklist", "name": Translation.tr("Unfinished")}, {"name": Translation.tr("Done"), "icon": "check_circle"}] property bool showAddDialog: false property int dialogMargins: 20 @@ -17,9 +16,9 @@ Item { Keys.onPressed: (event) => { if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) { if (event.key === Qt.Key_PageDown) { - currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1) + tabBar.incrementCurrentIndex(); } else if (event.key === Qt.Key_PageUp) { - currentTab = Math.max(currentTab - 1, 0) + tabBar.decrementCurrentIndex(); } event.accepted = true; } @@ -39,82 +38,19 @@ Item { anchors.fill: parent spacing: 0 - TabBar { + SecondaryTabBar { id: tabBar - Layout.fillWidth: true - currentIndex: currentTab - onCurrentIndexChanged: currentTab = currentIndex - - background: Item { - WheelHandler { - onWheel: (event) => { - if (event.angleDelta.y < 0) - tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1) - else if (event.angleDelta.y > 0) - tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0) - } - acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad - } - } + currentIndex: swipeView.currentIndex Repeater { model: root.tabButtonList delegate: SecondaryTabButton { - selected: (index == currentTab) buttonText: modelData.name buttonIcon: modelData.icon } } } - Item { // Tab indicator - id: tabIndicator - Layout.fillWidth: true - height: 3 - property bool enableIndicatorAnimation: false - Connections { - target: root - function onCurrentTabChanged() { - tabIndicator.enableIndicatorAnimation = true - } - } - - Rectangle { - id: indicator - property int tabCount: root.tabButtonList.length - property real fullTabSize: root.width / tabCount; - property real targetWidth: tabBar?.contentItem?.children[0]?.children[tabBar.currentIndex]?.tabContentWidth ?? 0 - - implicitWidth: targetWidth - anchors { - top: parent.top - bottom: parent.bottom - } - - x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2 - - color: Appearance.colors.colPrimary - radius: Appearance.rounding.full - - Behavior on x { - enabled: tabIndicator.enableIndicatorAnimation - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } - - Behavior on implicitWidth { - enabled: tabIndicator.enableIndicatorAnimation - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } - } - } - - Rectangle { // Tabbar bottom border - id: tabBarBottomBorder - Layout.fillWidth: true - height: 1 - color: Appearance.colors.colOutlineVariant - } - SwipeView { id: swipeView Layout.topMargin: 10 @@ -122,11 +58,7 @@ Item { Layout.fillHeight: true spacing: 10 clip: true - currentIndex: currentTab - onCurrentIndexChanged: { - tabIndicator.enableIndicatorAnimation = true - currentTab = currentIndex - } + currentIndex: tabBar.currentIndex // To Do tab TaskList { @@ -215,7 +147,7 @@ Item { Todo.addTask(todoInput.text) todoInput.text = "" root.showAddDialog = false - root.currentTab = 0 // Show unfinished tasks + tabBar.setCurrentIndex(0) // Show unfinished tasks } }