tray menu sub-menus, animations, better width/height calculations
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// thanks to Soramane :>
|
||||||
|
// expressive curves => thanks end cutie ;)
|
||||||
|
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
|
||||||
|
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
|
||||||
|
readonly property int emphasizedAccelTime: 200
|
||||||
|
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
|
||||||
|
readonly property int emphasizedDecelTime: 400
|
||||||
|
readonly property int emphasizedTime: 500
|
||||||
|
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1.00, 1, 1]
|
||||||
|
readonly property int expressiveDefaultSpatialTime: 500
|
||||||
|
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1]
|
||||||
|
readonly property int expressiveEffectsTime: 200
|
||||||
|
readonly property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.90, 1, 1]
|
||||||
|
readonly property int expressiveFastSpatialTime: 350
|
||||||
|
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
|
||||||
|
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
|
||||||
|
readonly property int standardAccelTime: 200
|
||||||
|
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
|
||||||
|
readonly property int standardDecelTime: 250
|
||||||
|
readonly property int standardTime: 300
|
||||||
|
}
|
||||||
+13
-3
@@ -14,7 +14,6 @@ MouseArea {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property SystemTrayItem item
|
required property SystemTrayItem item
|
||||||
property var menuHandle
|
|
||||||
|
|
||||||
implicitWidth: 22
|
implicitWidth: 22
|
||||||
implicitHeight: 22
|
implicitHeight: 22
|
||||||
@@ -46,14 +45,16 @@ MouseArea {
|
|||||||
ImageAnalyser {
|
ImageAnalyser {
|
||||||
id: analyser
|
id: analyser
|
||||||
sourceItem: icon
|
sourceItem: icon
|
||||||
rescaleSize: 20
|
rescaleSize: 22
|
||||||
}
|
}
|
||||||
|
|
||||||
TrayMenu {
|
TrayMenu {
|
||||||
id: trayMenu
|
id: trayMenu
|
||||||
menu: root.item.menu
|
menu: menuOpener
|
||||||
anchor.item: root
|
anchor.item: root
|
||||||
anchor.edges: Edges.Bottom
|
anchor.edges: Edges.Bottom
|
||||||
|
anchor.rect.x: 11
|
||||||
|
anchor.rect.y: 25
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if ( grab.active && !visible ) {
|
if ( grab.active && !visible ) {
|
||||||
grab.active = false;
|
grab.active = false;
|
||||||
@@ -74,8 +75,17 @@ MouseArea {
|
|||||||
if ( mouse.button === Qt.LeftButton ) {
|
if ( mouse.button === Qt.LeftButton ) {
|
||||||
root.item.activate();
|
root.item.activate();
|
||||||
} else if ( mouse.button === Qt.RightButton ) {
|
} else if ( mouse.button === Qt.RightButton ) {
|
||||||
|
if ( trayMenu.menu != menuOpener ) {
|
||||||
|
trayMenu.menu = menuOpener;
|
||||||
|
}
|
||||||
trayMenu.visible = !trayMenu.visible;
|
trayMenu.visible = !trayMenu.visible;
|
||||||
grab.active = true;
|
grab.active = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QsMenuOpener {
|
||||||
|
id: menuOpener
|
||||||
|
|
||||||
|
menu: root.item?.menu
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+126
-57
@@ -10,75 +10,119 @@ PopupWindow {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
signal menuActionTriggered()
|
signal menuActionTriggered()
|
||||||
required property QsMenuHandle menu
|
required property QsMenuOpener menu
|
||||||
property int height: {
|
property var menuStack: []
|
||||||
|
|
||||||
|
function calcHeight() {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (let i = 0; i < repeater.count; i++) {
|
let separatorCount = 0;
|
||||||
if (!repeater.itemAt(i).modelData.isSeparator) count++;
|
for (let i = 0; i < listLayout.count; i++) {
|
||||||
|
if (!listLayout.model.values[i].isSeparator) {
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
separatorCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return count * (entryHeight + 3);
|
if ( root.menuStack.length > 0 ) {
|
||||||
}
|
count++;
|
||||||
property int entryHeight: 30
|
}
|
||||||
property int maxWidth: 100
|
return (count * entryHeight) + ((count - 1) * 2) + (separatorCount * 3) + 10;
|
||||||
implicitWidth: Math.max(100, maxWidth + 20)
|
|
||||||
implicitHeight: height
|
|
||||||
color: "transparent"
|
|
||||||
anchor.margins {
|
|
||||||
left: -implicitWidth
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QsMenuOpener {
|
function calcWidth() {
|
||||||
id: menuOpener
|
let menuWidth = 0;
|
||||||
menu: root.menu
|
for ( let i = 0; i < listLayout.count; i++ ) {
|
||||||
|
if ( !listLayout.model.values[i].isSeparator ) {
|
||||||
|
let entry = listLayout.model.values[i];
|
||||||
|
tempMetrics.text = entry.text;
|
||||||
|
let textWidth = tempMetrics.width + 20 + (entry.icon ?? "" ? 30 : 0) + (entry.hasChildren ? 30 : 0);
|
||||||
|
if ( textWidth > menuWidth ) {
|
||||||
|
menuWidth = textWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return menuWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function goBack() {
|
||||||
|
if ( root.menuStack.length > 0 ) {
|
||||||
|
root.menu = root.menuStack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if ( visible ) {
|
||||||
|
root.menuStack = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: tempMetrics
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
property int height: calcHeight()
|
||||||
|
property int entryHeight: 30
|
||||||
|
property int maxWidth: calcWidth()
|
||||||
|
implicitWidth: maxWidth + 20
|
||||||
|
implicitHeight: height
|
||||||
|
color: "transparent"
|
||||||
|
anchor.gravity: Edges.Bottom
|
||||||
|
|
||||||
|
Behavior on implicitHeight {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: MaterialEasing.standardTime
|
||||||
|
easing.bezierCurve: MaterialEasing.standard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: MaterialEasing.standardTime
|
||||||
|
easing.bezierCurve: MaterialEasing.standard
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: menuRect
|
id: menuRect
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "#90000000"
|
color: "#80151515"
|
||||||
radius: 8
|
radius: 8
|
||||||
border.color: "#10FFFFFF"
|
border.color: "#40FFFFFF"
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: columnLayout
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 5
|
anchors.margins: 5
|
||||||
spacing: 2
|
spacing: 0
|
||||||
Repeater {
|
ListView {
|
||||||
id: repeater
|
id: listLayout
|
||||||
model: menuOpener.children
|
Layout.fillWidth: true
|
||||||
Rectangle {
|
Layout.preferredHeight: root.calcHeight() - ( root.menuStack.length > 0 ? root.entryHeight + 10 : 0 )
|
||||||
|
spacing: 2
|
||||||
|
model: ScriptModel {
|
||||||
|
values: [...root.menu?.children.values]
|
||||||
|
}
|
||||||
|
delegate: Rectangle {
|
||||||
id: menuItem
|
id: menuItem
|
||||||
|
required property QsMenuEntry modelData
|
||||||
|
property var child: QsMenuOpener {
|
||||||
|
menu: menuItem.modelData
|
||||||
|
}
|
||||||
property bool containsMouseAndEnabled: mouseArea.containsMouse && menuItem.modelData.enabled
|
property bool containsMouseAndEnabled: mouseArea.containsMouse && menuItem.modelData.enabled
|
||||||
property bool containsMouseAndNotEnabled: mouseArea.containsMouse && !menuItem.modelData.enabled
|
property bool containsMouseAndNotEnabled: mouseArea.containsMouse && !menuItem.modelData.enabled
|
||||||
width: root.implicitWidth
|
width: root.implicitWidth
|
||||||
Layout.maximumWidth: parent.width
|
anchors.left: parent.left
|
||||||
Layout.fillHeight: true
|
anchors.right: parent.right
|
||||||
height: root.entryHeight
|
height: menuItem.modelData.isSeparator ? 1 : root.entryHeight
|
||||||
color: modelData.isSeparator ? "transparent" : containsMouseAndEnabled ? "#15FFFFFF" : containsMouseAndNotEnabled ? "#08FFFFFF" : "transparent"
|
color: menuItem.modelData.isSeparator ? "#20FFFFFF" : containsMouseAndEnabled ? "#15FFFFFF" : containsMouseAndNotEnabled ? "#08FFFFFF" : "transparent"
|
||||||
radius: 4
|
radius: 4
|
||||||
visible: modelData.isSeparator ? false : true
|
visible: true
|
||||||
required property QsMenuEntry modelData
|
|
||||||
|
|
||||||
TextMetrics {
|
|
||||||
id: textMetrics
|
|
||||||
font: menuText.font
|
|
||||||
text: menuItem.modelData.text
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
// Measure text width to determine maximumWidth
|
|
||||||
var textWidth = textMetrics.width + 20 + (iconImage.source ? iconImage.width + 10 : 0);
|
|
||||||
if ( textWidth > 0 && textWidth > root.maxWidth ) {
|
|
||||||
root.maxWidth = textWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
propagateComposedEvents: true
|
propagateComposedEvents: true
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if ( !menuItem.modelData.hasChildren ) {
|
if ( !menuItem.modelData.hasChildren ) {
|
||||||
@@ -88,14 +132,8 @@ propagateComposedEvents: true
|
|||||||
root.visible = false;
|
root.visible = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
subMenuComponent.createObject( null, {
|
root.menuStack.push(root.menu);
|
||||||
menu: menuItem.modelData,
|
root.menu = menuItem.child;
|
||||||
anchor: {
|
|
||||||
item: menuItem,
|
|
||||||
edges: Edges.Right
|
|
||||||
},
|
|
||||||
visible: true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,13 +162,44 @@ propagateComposedEvents: true
|
|||||||
color: menuItem.modelData.enabled ? "white" : "gray"
|
color: menuItem.modelData.enabled ? "white" : "gray"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Text {
|
||||||
|
id: textArrow
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||||
|
Layout.rightMargin: 10
|
||||||
|
Layout.bottomMargin: 5
|
||||||
|
Layout.maximumWidth: 20
|
||||||
|
Layout.maximumHeight: 20
|
||||||
|
text: ""
|
||||||
|
color: menuItem.modelData.enabled ? "white" : "gray"
|
||||||
|
visible: menuItem.modelData.hasChildren ?? false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Rectangle {
|
||||||
|
visible: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: root.entryHeight
|
||||||
|
color: mouseAreaBack.containsMouse ? "#15FFFFFF" : "transparent"
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseAreaBack
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: {
|
||||||
|
root.goBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
text: "Back "
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Component {
|
|
||||||
id: subMenuComponent
|
|
||||||
SubMenu {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user