diff options
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankTabBar.qml')
| -rw-r--r-- | raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankTabBar.qml | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankTabBar.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankTabBar.qml new file mode 100644 index 0000000..d3b72f1 --- /dev/null +++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankTabBar.qml @@ -0,0 +1,263 @@ +import QtQuick +import qs.Common +import qs.Widgets + +FocusScope { + id: tabBar + + property alias model: tabRepeater.model + property int currentIndex: 0 + property int spacing: Theme.spacingL + property int tabHeight: 56 + property bool showIcons: true + property bool equalWidthTabs: true + property bool enableArrowNavigation: true + property Item nextFocusTarget: null + property Item previousFocusTarget: null + + signal tabClicked(int index) + signal actionTriggered(int index) + + focus: false + activeFocusOnTab: true + height: tabHeight + + KeyNavigation.tab: nextFocusTarget + KeyNavigation.down: nextFocusTarget + KeyNavigation.backtab: previousFocusTarget + KeyNavigation.up: previousFocusTarget + + Keys.onPressed: event => { + if (!tabBar.activeFocus || tabRepeater.count === 0) + return; + function findSelectableIndex(startIndex, step) { + let idx = startIndex; + for (let i = 0; i < tabRepeater.count; i++) { + idx = (idx + step + tabRepeater.count) % tabRepeater.count; + const item = tabRepeater.itemAt(idx); + if (item && !item.isAction) + return idx; + } + return -1; + } + + const goToIndex = nextIndex => { + if (nextIndex >= 0 && nextIndex !== tabBar.currentIndex) { + tabBar.currentIndex = nextIndex; + tabBar.tabClicked(nextIndex); + } + }; + + const resolveTarget = item => { + if (!item) + return null; + + if (item.focusTarget) + return resolveTarget(item.focusTarget); + + return item; + }; + + const focusItem = item => { + const target = resolveTarget(item); + if (!target) + return false; + + if (target.requestFocus) { + Qt.callLater(() => target.requestFocus()); + return true; + } + + if (target.forceActiveFocus) { + Qt.callLater(() => target.forceActiveFocus()); + return true; + } + + return false; + }; + + if (event.key === Qt.Key_Right && tabBar.enableArrowNavigation) { + const baseIndex = (tabBar.currentIndex >= 0 && tabBar.currentIndex < tabRepeater.count) ? tabBar.currentIndex : -1; + const nextIndex = findSelectableIndex(baseIndex, 1); + if (nextIndex >= 0) { + goToIndex(nextIndex); + event.accepted = true; + } + } else if (event.key === Qt.Key_Left && tabBar.enableArrowNavigation) { + const baseIndex = (tabBar.currentIndex >= 0 && tabBar.currentIndex < tabRepeater.count) ? tabBar.currentIndex : 0; + const nextIndex = findSelectableIndex(baseIndex, -1); + if (nextIndex >= 0) { + goToIndex(nextIndex); + event.accepted = true; + } + } else if (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier)) { + if (focusItem(tabBar.previousFocusTarget)) { + event.accepted = true; + } + } else if (event.key === Qt.Key_Tab || event.key === Qt.Key_Down) { + if (focusItem(tabBar.nextFocusTarget)) { + event.accepted = true; + } + } else if (event.key === Qt.Key_Up) { + if (focusItem(tabBar.previousFocusTarget)) { + event.accepted = true; + } + } + } + + Row { + id: tabRow + anchors.fill: parent + spacing: tabBar.spacing + + Repeater { + id: tabRepeater + + Item { + id: tabItem + property bool isAction: modelData && modelData.isAction === true + property bool isActive: !isAction && tabBar.currentIndex === index + property bool hasIcon: tabBar.showIcons && modelData && modelData.icon && modelData.icon.length > 0 + property bool hasText: modelData && modelData.text && modelData.text.length > 0 + + width: tabBar.equalWidthTabs ? (tabBar.width - tabBar.spacing * Math.max(0, tabRepeater.count - 1)) / Math.max(1, tabRepeater.count) : Math.max(contentCol.implicitWidth + Theme.spacingXL, 64) + height: tabBar.tabHeight + + Column { + id: contentCol + anchors.centerIn: parent + spacing: Theme.spacingXS + + DankIcon { + name: modelData.icon || "" + anchors.horizontalCenter: parent.horizontalCenter + size: Theme.iconSize + color: tabItem.isActive ? Theme.primary : Theme.surfaceText + visible: hasIcon + } + + StyledText { + text: modelData.text || "" + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: Theme.fontSizeMedium + color: tabItem.isActive ? Theme.primary : Theme.surfaceText + font.weight: Font.Medium + visible: hasText + } + } + + Rectangle { + id: stateLayer + anchors.fill: parent + color: Theme.surfaceTint + opacity: tabArea.pressed ? 0.12 : (tabArea.containsMouse ? 0.08 : 0) + visible: opacity > 0 + radius: Theme.cornerRadius + Behavior on opacity { + NumberAnimation { + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } + } + } + + DankRipple { + id: tabRipple + cornerRadius: Theme.cornerRadius + } + + MouseArea { + id: tabArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onPressed: mouse => tabRipple.trigger(mouse.x, mouse.y) + onClicked: { + if (tabItem.isAction) { + tabBar.actionTriggered(index); + } else { + tabBar.tabClicked(index); + } + } + } + } + } + } + + Rectangle { + id: indicator + y: parent.height + 7 + height: 3 + width: 60 + topLeftRadius: Theme.cornerRadius + topRightRadius: Theme.cornerRadius + bottomLeftRadius: 0 + bottomRightRadius: 0 + color: Theme.primary + visible: false + + property bool animationEnabled: false + property bool initialSetupComplete: false + + Behavior on x { + enabled: indicator.animationEnabled + NumberAnimation { + duration: Theme.mediumDuration + easing.type: Theme.standardEasing + } + } + + Behavior on width { + enabled: indicator.animationEnabled + NumberAnimation { + duration: Theme.mediumDuration + easing.type: Theme.standardEasing + } + } + } + + Rectangle { + width: parent.width + height: 1 + y: parent.height + 10 + color: Theme.outlineStrong + } + + function updateIndicator() { + if (tabRepeater.count === 0 || currentIndex < 0 || currentIndex >= tabRepeater.count) { + return; + } + + const item = tabRepeater.itemAt(currentIndex); + if (!item || item.isAction) { + return; + } + + const tabPos = item.mapToItem(tabBar, 0, 0); + const tabCenterX = tabPos.x + item.width / 2; + const indicatorWidth = 60; + + if (tabPos.x < 10 && currentIndex > 0) { + Qt.callLater(updateIndicator); + return; + } + + if (!indicator.initialSetupComplete) { + indicator.animationEnabled = false; + indicator.width = indicatorWidth; + indicator.x = tabCenterX - indicatorWidth / 2; + indicator.visible = true; + indicator.initialSetupComplete = true; + indicator.animationEnabled = true; + } else { + indicator.width = indicatorWidth; + indicator.x = tabCenterX - indicatorWidth / 2; + indicator.visible = true; + } + } + + onCurrentIndexChanged: { + Qt.callLater(updateIndicator); + } + onWidthChanged: Qt.callLater(updateIndicator) +} |