summaryrefslogtreecommitdiff
path: root/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankTabBar.qml
diff options
context:
space:
mode:
authornippy <you@example.com>2026-04-18 13:49:56 +0200
committernippy <you@example.com>2026-04-18 13:49:56 +0200
commit5d94c0a7d44a2255b81815a52a7056a94a39842d (patch)
tree759bdea9645c6a62f9e1e4c001f7d81cccd120d2 /raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankTabBar.qml
parente79cdf210b267f21a186255ce1a4d50029439d54 (diff)
downloadRaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.tar.gz
RaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.zip
update Raveos themes
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.qml263
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)
+}