import QtQuick import QtQuick.Controls import Quickshell import Quickshell.Wayland import Quickshell.Hyprland import "../../config" as Cfg import "sysinfo" import "mpris" import "weather" PanelWindow { id: root required property var modelData screen: modelData color: "transparent" visible: false anchors { top: true; bottom: true; left: true; right: true } WlrLayershell.layer: WlrLayer.Top WlrLayershell.namespace: "main-shell-sidebar" WlrLayershell.exclusiveZone: -1 WlrLayershell.keyboardFocus: _open ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None Region { id: noMask } mask: _open ? null : noMask readonly property var t: Cfg.Config.theme readonly property bool isTop: Cfg.Config.barPosition === "top" readonly property int barEdgePad: Cfg.Config.frameVariant === "bar" ? (isTop ? Cfg.Config.frameBarPadTop : Cfg.Config.frameBarPadBottom) : 0 readonly property int frameTop: isTop ? Cfg.Config.barHeight + barEdgePad : Cfg.Config.margin readonly property int frameBot: isTop ? Cfg.Config.margin : Cfg.Config.barHeight + barEdgePad readonly property int inset: Cfg.Config.margin + Cfg.Config.frameBorderWidth + 8 readonly property int sbWidth: 260 readonly property bool isLeft: Cfg.Config.sideBarPosition === "left" readonly property int slideDist: root.sbWidth + 20 readonly property int slideFrom: isLeft ? -slideDist : slideDist readonly property int panelX: isLeft ? root.inset : (width - root.sbWidth - root.inset) property bool _open: false readonly property bool isOpen: _open GlobalShortcut { name: "sidebar" onPressed: root.toggle() } function open() { var fm = Hyprland.focusedMonitor if (!fm || fm.name !== screen.name) return visible = true _open = true enterAnim.stop() exitAnim.stop() hideTimer.stop() enterAnim.start() } function close() { _open = false enterAnim.stop() exitAnim.start() } function toggle() { if (!_open) { var fm = Hyprland.focusedMonitor if (!fm || fm.name !== screen.name) return } _open ? close() : open() } Timer { id: hideTimer interval: 280 onTriggered: visible = false } Connections { target: root function onActiveChanged() { if (!root.active && root._open) root.close() } } MouseArea { anchors.fill: parent onClicked: root.close() } Rectangle { id: sidebarPanel transform: Translate { id: panelSlide; x: root.slideFrom } x: root.panelX y: root.frameTop + Cfg.Config.frameBorderWidth width: root.sbWidth height: parent.height - root.frameTop - root.frameBot - Cfg.Config.frameBorderWidth * 2 radius: Cfg.Config.popupRadius color: "transparent" opacity: 0 clip: true Item { id: widgetArea anchors { fill: parent topMargin: 10 bottomMargin: 10 leftMargin: 10 rightMargin: 10 } Flickable { id: flick anchors.fill: parent contentWidth: width contentHeight: widgetCol.implicitHeight boundsBehavior: Flickable.StopAtBounds clip: true flickDeceleration: 2000 ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded width: 4 contentItem: Rectangle { radius: 2 color: Qt.alpha(root.t.accent, 0.3) } } Column { id: widgetCol anchors { left: parent.left; right: parent.right } spacing: 10 Loader { active: Cfg.Config.sideBarSysInfo sourceComponent: sysInfoComp anchors { left: parent.left; right: parent.right } } Loader { active: Cfg.Config.sideBarMpris sourceComponent: mprisComp anchors { left: parent.left; right: parent.right } } Loader { active: Cfg.Config.sideBarWeather sourceComponent: weatherComp anchors { left: parent.left; right: parent.right } } } } } } Component { id: sysInfoComp WidgetCard { SysInfo { anchors { left: parent.left; right: parent.right } } } } Component { id: mprisComp WidgetCard { MprisWidget { anchors { left: parent.left; right: parent.right } } } } Component { id: weatherComp WidgetCard { WeatherWidget { anchors { left: parent.left; right: parent.right } } } } ParallelAnimation { id: enterAnim NumberAnimation { target: panelSlide; property: "x" to: 0; duration: 320; easing.type: Easing.OutCubic } NumberAnimation { target: sidebarPanel; property: "opacity" to: 1; duration: 280; easing.type: Easing.OutCubic } } ParallelAnimation { id: exitAnim onStopped: hideTimer.restart() NumberAnimation { target: panelSlide; property: "x" to: root.slideFrom; duration: 250; easing.type: Easing.InCubic } NumberAnimation { target: sidebarPanel; property: "opacity" to: 0; duration: 200; easing.type: Easing.InBack } } }