pragma ComponentBehavior: Bound import Quickshell import Quickshell.Widgets import QtQuick import QtQuick.Layouts import ZShell.Config import ZShell import qs.Components Item { id: root readonly property real nonAnimWidth: view.implicitWidth + viewWrapper.anchors.margins * 2 readonly property real nonAnimHeight: tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + viewWrapper.anchors.margins + viewWrapper.anchors.topMargin required property ScreenState screenState readonly property var dashboardTabs: { const allTabs = [ { component: dashComponent, iconName: "dashboard", text: qsTr("Dashboard"), enabled: Config.dashboard.showDashboard }, { component: weatherComponent, iconName: "cloud", text: qsTr("Weather"), enabled: Config.dashboard.showWeather }, { component: mediaComponent, iconName: "queue_music", text: qsTr("Media"), enabled: Config.dashboard.showMedia }, { component: procsComponent, iconName: "speed", text: qsTr("Processes"), enabled: Config.dashboard.showProcesses }, ]; return allTabs.filter(tab => tab.enabled); } implicitWidth: nonAnimWidth implicitHeight: nonAnimHeight Behavior on implicitHeight { Anim { type: Anim.SlowEffects } } Behavior on implicitWidth { Anim { type: Anim.SlowEffects } } Tabs { id: tabs anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right nonAnimWidth: view.implicitWidth screenState: root.screenState tabs: root.dashboardTabs anchors.topMargin: ZUtils.clamp(anchors.margins - Config.bar.border, 0, anchors.margins) anchors.margins: Tokens.padding.small } Item { id: viewWrapper property int radius: Tokens.rounding.extraLarge - anchors.margins anchors.top: tabs.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom anchors.topMargin: Tokens.spacing.small anchors.margins: Tokens.padding.small clip: true Flickable { id: view readonly property int currentIndex: root.screenState.dashboardTab readonly property Item currentItem: { repeater.count; return repeater.itemAt(currentIndex); } anchors.fill: parent implicitWidth: currentItem?.implicitWidth ?? 0 implicitHeight: currentItem?.implicitHeight ?? 0 contentX: currentItem?.x ?? 0 contentWidth: row.implicitWidth contentHeight: row.implicitHeight interactive: false onContentXChanged: { if (!moving || !currentItem) return; const x = contentX - currentItem.x; if (x > currentItem.implicitWidth / 2) root.screenState.dashboardTab = Math.min(root.screenState.dashboardTab + 1, tabs.count - 1); else if (x < -currentItem.implicitWidth / 2) root.screenState.dashboardTab = Math.max(root.screenState.dashboardTab - 1, 0); } RowLayout { id: row Repeater { id: repeater model: ScriptModel { values: root.dashboardTabs } delegate: Loader { id: paneLoader required property int index required property var modelData Layout.alignment: Qt.AlignTop sourceComponent: modelData.component Component.onCompleted: active = Qt.binding(() => { if (index === view.currentIndex) return true; const vx = Math.floor(view.visibleArea.xPosition * view.contentWidth); const vex = Math.floor(vx + view.visibleArea.widthRatio * view.contentWidth); return (vx >= x && vx <= x + implicitWidth) || (vex >= x && vex <= x + implicitWidth); }) } } } Component { id: dashComponent Dash { screenState: root.screenState radius: viewWrapper.radius } } Component { id: weatherComponent WeatherTab { radius: viewWrapper.radius } } Component { id: mediaComponent Media { screenState: root.screenState radius: viewWrapper.radius } } Component { id: procsComponent ProcessesTab { radius: viewWrapper.radius } } Behavior on contentX { Anim { type: Anim.DefaultEffects } } } } }