pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import QtQuick.Templates import Quickshell import ZShell import ZShell.Components import ZShell.Config import qs.Components import qs.Services Item { id: root required property real nonAnimWidth required property ScreenState screenState required property var tabs readonly property alias count: bar.count readonly property int tabIndicatorSpacing: ZUtils.snap(Tokens.spacing.small, root) function snapLayoutHeight(v) { const dpr = ZUtils.dprFor(root); let h = Math.ceil(v); while (Math.abs(h * dpr - Math.round(h * dpr)) > 1e-6) ++h; return h; } implicitHeight: ZUtils.snap(bar.implicitHeight + bar.anchors.topMargin + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight, root) TabBar { id: bar anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.topMargin: root.tabIndicatorSpacing currentIndex: root.screenState.dashboardTab onCurrentIndexChanged: root.screenState.dashboardTab = currentIndex topPadding: 0 bottomPadding: 0 implicitHeight: implicitContentHeight background: null contentItem: SnapLayout { spacing: 0 Repeater { model: bar.contentModel } } Repeater { model: ScriptModel { values: root.tabs } delegate: Tab { required property var modelData iconName: modelData.iconName text: modelData.text } } } Item { id: indicator anchors.top: bar.bottom anchors.topMargin: root.tabIndicatorSpacing implicitWidth: { const tab = bar.currentItem; if (tab) return tab.implicitWidth; const width = (root.nonAnimWidth - bar.spacing * (bar.count - 1)) / bar.count; return width; } implicitHeight: 3 x: { const tab = bar.currentItem; const width = (root.nonAnimWidth - bar.spacing * (bar.count - 1)) / bar.count; const tabWidth = tab?.implicitWidth ?? width; return width * bar.currentIndex + (width - tabWidth) / 2; } clip: true CustomRect { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right implicitHeight: parent.implicitHeight * 2 color: Colors.palette.m3primary radius: Tokens.rounding.full } Behavior on x { Anim {} } Behavior on implicitWidth { Anim {} } } CustomRect { id: separator anchors.top: indicator.bottom anchors.left: parent.left anchors.right: parent.right implicitHeight: 1 color: Colors.palette.m3outlineVariant } component Tab: TabButton { id: tab required property string iconName readonly property bool current: TabBar.tabBar.currentItem === this Layout.fillWidth: true Layout.fillHeight: true Layout.preferredWidth: 1 implicitWidth: implicitContentWidth implicitHeight: implicitContentHeight background: null topPadding: 0 bottomPadding: 0 contentItem: CustomMouseArea { id: mouse function onWheel(event: WheelEvent): void { if (event.angleDelta.y < 0) root.screenState.dashboardTab = Math.min(root.screenState.dashboardTab + 1, bar.count - 1); else if (event.angleDelta.y > 0) root.screenState.dashboardTab = Math.max(root.screenState.dashboardTab - 1, 0); } implicitWidth: Math.max(icon.implicitWidth, label.implicitWidth) implicitHeight: snapLayout.implicitHeight hoverEnabled: true cursorShape: Qt.PointingHandCursor onPressed: root.screenState.dashboardTab = tab.TabBar.index StateLayer { id: stateLayer anchors.fill: undefined anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter anchors.alignWhenCentered: false implicitHeight: ZUtils.snap(parent.height + Tokens.sizes.dashboard.tabIndicatorSpacing * 2, stateLayer) radius: Tokens.rounding.medium color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurface onClicked: { root.screenState.dashboardTab = tab.TabBar.index; } } SnapLayout { id: snapLayout anchors.fill: parent spacing: 0 orientation: Qt.Vertical MaterialIcon { id: icon text: tab.iconName color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant fill: tab.current ? 1 : 0 fontStyle: Tokens.font.icon.medium Layout.fillHeight: true Layout.alignment: Qt.AlignHCenter Behavior on fill { Anim { type: Anim.DefaultEffects } } } CustomText { id: label text: tab.text color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant Layout.fillHeight: true Layout.alignment: Qt.AlignHCenter } } } } }