Files
quickshell/Components/Container.qml

256 lines
4.7 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import Quickshell.Hyprland
import "../Color.js" as Colors
Item {
id: root
property bool _fullyHidden: false
property bool _widthCollapsed: false
property double animOffset: 0
property color boxColor: Colors.surface0
property double boxHeight: 28
property double boxHeightOpened: 150
property bool boxOpened: false
property double boxRadius: 9
property double boxRadiusOpened: 12
property double boxWidth: 100
property double boxWidthOpened: 200
readonly property double calculatedTopMargin: {
if (!root.exclusiveToScreen) {
return visibleTopMargin;
}
if (Hyprland.focusedMonitor == root.exclusiveMonitor) {
if (!root.forceHidden) {
return visibleTopMargin;
} else if (root.hoverableWhenHidden) {
return 7 - root.boxHeight - 5;
}
}
return -4 - root.boxHeight - 5;
}
property Component defaultItem
required property HyprlandMonitor exclusiveMonitor
property bool exclusiveToScreen: false
property bool forceHidden: false
property alias hover: hoverHandler
property bool hoverableWhenHidden: false
property bool hovered: false
property Component openedItem
property bool overriden: false
property alias rect: container
property alias stack: containerContent
property alias tap: tapHandler
property double visibleTopMargin: 0
implicitHeight: boxHeight
implicitWidth: root._widthCollapsed ? 0 : root.boxWidth
visible: !root._fullyHidden
Behavior on anchors.topMargin {
animation: defaultCurve
}
Behavior on boxRadius {
SpringAnimation {
damping: 0.3
spring: 4
}
}
Behavior on implicitHeight {
SpringAnimation {
damping: 0.3
spring: 4
}
}
Behavior on implicitWidth {
SpringAnimation {
damping: 0.3
spring: 4
}
}
states: [
State {
name: "closed"
when: root.hovered == false && root.boxOpened == false && root.overriden == false
PropertyChanges {
container.radius: root.boxRadius
root.implicitHeight: root.boxHeight
root.implicitWidth: root.boxWidth
}
StateChangeScript {
script: {
stack.replace(root.defaultItem);
}
}
},
State {
name: "hovered"
when: root.hovered == true && root.boxOpened == false && root.overriden == false
PropertyChanges {
container.radius: root.boxRadius + 3
root.implicitHeight: root.boxHeight + 2
root.implicitWidth: root.boxWidth + 2
}
StateChangeScript {
script: {
stack.replace(root.defaultItem);
}
}
},
State {
name: "opened"
when: root.boxOpened == true && root.hovered == true && root.overriden == false
PropertyChanges {
container.radius: root.boxRadiusOpened
root.implicitHeight: root.boxHeightOpened
root.implicitWidth: root.boxWidthOpened
}
StateChangeScript {
script: {
stack.replace(root.openedItem);
}
}
}
]
onForceHiddenChanged: {
if (root.forceHidden == true) {
hideTimer.restart();
} else {
hideTimer.stop();
subHideTimer.stop();
root._fullyHidden = false;
root._widthCollapsed = false;
}
}
Timer {
id: hideTimer
interval: 250
repeat: false
running: false
onTriggered: {
root._widthCollapsed = true;
subHideTimer.restart();
}
}
Timer {
id: subHideTimer
interval: 200
repeat: false
running: false
onTriggered: {
root._fullyHidden = true;
}
}
anchors {
top: parent.top
topMargin: calculatedTopMargin
}
NumberAnimation {
id: defaultCurve
duration: 200 + root.animOffset
easing: Easing.InOutBack
}
HoverHandler {
id: hoverHandler
onHoveredChanged: {
if (hoverHandler.hovered == true) {
root.hovered = true;
} else {
root.hovered = false;
root.boxOpened = false;
if (root.state == "closed") {
root.stack.replace(root.defaultItem);
}
}
}
}
TapHandler {
id: tapHandler
onTapped: {
if (root.openedItem) {
root.boxOpened = true;
}
}
}
Rectangle {
id: container
anchors.fill: parent
clip: true
color: root.boxColor
radius: root.boxRadius
Behavior on radius {
NumberAnimation {
duration: 300
}
}
StackView {
id: containerContent
anchors.fill: parent
replaceEnter: Transition {
PropertyAnimation {
duration: 100
from: 0
property: "opacity"
to: 1
}
PropertyAnimation {
duration: 100
from: -5
property: "anchors.topMargin"
to: 0
}
}
replaceExit: Transition {
PropertyAnimation {
duration: 100
from: 1
property: "opacity"
to: 0
}
}
Component.onCompleted: containerContent.push(root.defaultItem)
onCurrentItemChanged: {
if (currentItem) {
// Dynamically center the incoming child to the StackView
currentItem.anchors.horizontalCenter = containerContent.horizontalCenter;
currentItem.anchors.verticalCenter = containerContent.verticalCenter;
}
}
}
}
}