feat: tray item menus
This commit is contained in:
parent
c3a91e7554
commit
17fb60f216
6 changed files with 278 additions and 1 deletions
|
|
@ -12,6 +12,7 @@ Singleton {
|
||||||
component Sizes: QtObject {
|
component Sizes: QtObject {
|
||||||
property int innerHeight: 30
|
property int innerHeight: 30
|
||||||
property int windowPreviewSize: 400
|
property int windowPreviewSize: 400
|
||||||
|
property int trayMenuWidth: 300
|
||||||
}
|
}
|
||||||
|
|
||||||
component Workspaces: QtObject {
|
component Workspaces: QtObject {
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,31 @@ StyledRect {
|
||||||
const spacing = Appearance.spacing.small;
|
const spacing = Appearance.spacing.small;
|
||||||
const aw = activeWindow.child;
|
const aw = activeWindow.child;
|
||||||
const awy = activeWindow.y + aw.y;
|
const awy = activeWindow.y + aw.y;
|
||||||
|
|
||||||
|
const ty = tray.y;
|
||||||
|
const th = tray.implicitHeight;
|
||||||
|
const trayItems = tray.items;
|
||||||
|
|
||||||
const n = statusIconsInner.network;
|
const n = statusIconsInner.network;
|
||||||
const ny = statusIcons.y + statusIconsInner.y + n.y - spacing / 2;
|
const ny = statusIcons.y + statusIconsInner.y + n.y - spacing / 2;
|
||||||
|
|
||||||
const bls = statusIcons.y + statusIconsInner.y + statusIconsInner.bs - spacing / 2;
|
const bls = statusIcons.y + statusIconsInner.y + statusIconsInner.bs - spacing / 2;
|
||||||
const ble = statusIcons.y + statusIconsInner.y + statusIconsInner.be + spacing / 2;
|
const ble = statusIcons.y + statusIconsInner.y + statusIconsInner.be + spacing / 2;
|
||||||
|
|
||||||
const b = statusIconsInner.battery;
|
const b = statusIconsInner.battery;
|
||||||
const by = statusIcons.y + statusIconsInner.y + b.y - spacing / 2;
|
const by = statusIcons.y + statusIconsInner.y + b.y - spacing / 2;
|
||||||
|
|
||||||
if (y >= awy && y <= awy + aw.implicitHeight) {
|
if (y >= awy && y <= awy + aw.implicitHeight) {
|
||||||
Popouts.currentName = "activewindow";
|
Popouts.currentName = "activewindow";
|
||||||
Popouts.currentCenter = Qt.binding(() => activeWindow.y + aw.y + aw.implicitHeight / 2);
|
Popouts.currentCenter = Qt.binding(() => activeWindow.y + aw.y + aw.implicitHeight / 2);
|
||||||
Popouts.hasCurrent = true;
|
Popouts.hasCurrent = true;
|
||||||
|
} else if (y > ty && y < ty + th) {
|
||||||
|
const index = Math.floor(((y - ty) / th) * trayItems.count);
|
||||||
|
const item = trayItems.itemAt(index);
|
||||||
|
|
||||||
|
Popouts.currentName = `traymenu${index}`;
|
||||||
|
Popouts.currentCenter = Qt.binding(() => tray.y + item.y + item.implicitHeight / 2);
|
||||||
|
Popouts.hasCurrent = true;
|
||||||
} else if (y >= ny && y <= ny + n.implicitHeight + spacing) {
|
} else if (y >= ny && y <= ny + n.implicitHeight + spacing) {
|
||||||
Popouts.currentName = "network";
|
Popouts.currentName = "network";
|
||||||
Popouts.currentCenter = Qt.binding(() => statusIcons.y + statusIconsInner.y + n.y + n.implicitHeight / 2);
|
Popouts.currentCenter = Qt.binding(() => statusIcons.y + statusIconsInner.y + n.y + n.implicitHeight / 2);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import QtQuick
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property Repeater items: items
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
visible: width > 0 && height > 0 // To avoid warnings about being visible with no size
|
visible: width > 0 && height > 0 // To avoid warnings about being visible with no size
|
||||||
|
|
||||||
|
|
@ -28,6 +30,8 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
|
id: items
|
||||||
|
|
||||||
model: SystemTray.items
|
model: SystemTray.items
|
||||||
|
|
||||||
TrayItem {}
|
TrayItem {}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import "root:/services"
|
import "root:/services"
|
||||||
import "root:/config"
|
import "root:/config"
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
@ -40,6 +43,24 @@ Item {
|
||||||
name: "battery"
|
name: "battery"
|
||||||
source: "Battery.qml"
|
source: "Battery.qml"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ScriptModel {
|
||||||
|
values: [...SystemTray.items.values]
|
||||||
|
}
|
||||||
|
|
||||||
|
Popout {
|
||||||
|
id: trayMenu
|
||||||
|
|
||||||
|
required property SystemTrayItem modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
name: `traymenu${index}`
|
||||||
|
sourceComponent: TrayMenu {
|
||||||
|
trayItem: trayMenu.modelData.menu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
|
|
@ -65,6 +86,7 @@ Item {
|
||||||
|
|
||||||
opacity: 0
|
opacity: 0
|
||||||
scale: 0.8
|
scale: 0.8
|
||||||
|
active: false
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
states: State {
|
states: State {
|
||||||
|
|
|
||||||
235
modules/bar/popouts/TrayMenu.qml
Normal file
235
modules/bar/popouts/TrayMenu.qml
Normal file
|
|
@ -0,0 +1,235 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import "root:/widgets"
|
||||||
|
import "root:/services"
|
||||||
|
import "root:/config"
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
StackView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property QsMenuHandle trayItem
|
||||||
|
|
||||||
|
implicitWidth: currentItem.implicitWidth
|
||||||
|
implicitHeight: currentItem.implicitHeight
|
||||||
|
|
||||||
|
initialItem: SubMenu {
|
||||||
|
handle: root.trayItem
|
||||||
|
}
|
||||||
|
|
||||||
|
pushEnter: Anim {}
|
||||||
|
pushExit: Anim {}
|
||||||
|
popEnter: Anim {}
|
||||||
|
popExit: Anim {}
|
||||||
|
|
||||||
|
component Anim: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 0 // Appearance.anim.durations.normal
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component SubMenu: Column {
|
||||||
|
id: menu
|
||||||
|
|
||||||
|
required property QsMenuHandle handle
|
||||||
|
property bool isSubMenu
|
||||||
|
property bool shown
|
||||||
|
|
||||||
|
padding: Appearance.padding.smaller
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
opacity: shown ? 1 : 0
|
||||||
|
scale: shown ? 1 : 0.8
|
||||||
|
|
||||||
|
Component.onCompleted: shown = true
|
||||||
|
StackView.onActivating: shown = true
|
||||||
|
StackView.onDeactivating: shown = false
|
||||||
|
StackView.onRemoved: destroy()
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QsMenuOpener {
|
||||||
|
id: menuOpener
|
||||||
|
|
||||||
|
menu: menu.handle
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: menuOpener.children
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: item
|
||||||
|
|
||||||
|
required property QsMenuEntry modelData
|
||||||
|
|
||||||
|
implicitWidth: BarConfig.sizes.trayMenuWidth
|
||||||
|
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
|
||||||
|
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
color: modelData.isSeparator ? Colours.palette.m3outlineVariant : "transparent"
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: children
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
active: !item.modelData.isSeparator
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: Item {
|
||||||
|
implicitHeight: label.implicitHeight
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.margins: -Appearance.padding.small / 2
|
||||||
|
anchors.leftMargin: -Appearance.padding.smaller
|
||||||
|
anchors.rightMargin: -Appearance.padding.smaller
|
||||||
|
|
||||||
|
radius: item.radius
|
||||||
|
disabled: !item.modelData.enabled
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
const entry = item.modelData;
|
||||||
|
if (entry.hasChildren)
|
||||||
|
root.push(subMenuComp.createObject(null, {
|
||||||
|
handle: entry,
|
||||||
|
isSubMenu: true
|
||||||
|
}));
|
||||||
|
else
|
||||||
|
item.modelData.triggered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: icon
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
active: item.modelData.icon !== ""
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: IconImage {
|
||||||
|
implicitSize: label.implicitHeight
|
||||||
|
|
||||||
|
source: item.modelData.icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: label
|
||||||
|
|
||||||
|
anchors.left: icon.right
|
||||||
|
anchors.leftMargin: icon.active ? Appearance.spacing.smaller : 0
|
||||||
|
|
||||||
|
text: labelMetrics.elidedText
|
||||||
|
color: item.modelData.enabled ? Colours.palette.m3onSurface : Colours.palette.m3outline
|
||||||
|
}
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: labelMetrics
|
||||||
|
|
||||||
|
text: item.modelData.text
|
||||||
|
font.pointSize: label.font.pointSize
|
||||||
|
font.family: label.font.family
|
||||||
|
|
||||||
|
elide: Text.ElideRight
|
||||||
|
elideWidth: BarConfig.sizes.trayMenuWidth - (icon.active ? icon.implicitWidth + label.anchors.leftMargin : 0) - (expand.active ? expand.implicitWidth + Appearance.spacing.normal : 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: expand
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
active: item.modelData.hasChildren
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: MaterialIcon {
|
||||||
|
text: "chevron_right"
|
||||||
|
color: item.modelData.enabled ? Colours.palette.m3onSurface : Colours.palette.m3outline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: menu.isSubMenu
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: Item {
|
||||||
|
implicitWidth: back.implicitWidth
|
||||||
|
implicitHeight: back.implicitHeight + Appearance.spacing.small / 2
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
implicitWidth: back.implicitWidth
|
||||||
|
implicitHeight: back.implicitHeight
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: -Appearance.padding.small / 2
|
||||||
|
anchors.leftMargin: -Appearance.padding.smaller
|
||||||
|
anchors.rightMargin: -Appearance.padding.smaller * 2
|
||||||
|
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
color: Colours.palette.m3secondaryContainer
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
radius: parent.radius
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
root.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: back
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: "chevron_left"
|
||||||
|
color: Colours.palette.m3onSecondaryContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: qsTr("Back")
|
||||||
|
color: Colours.palette.m3onSecondaryContainer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: subMenuComp
|
||||||
|
|
||||||
|
SubMenu {}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
run.fish
2
run.fish
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/fish
|
#!/bin/fish
|
||||||
|
|
||||||
set -l dbus 'quickshell.dbus.properties.warning = false' # System tray dbus property errors
|
set -l dbus 'quickshell.dbus.properties.warning = false;quickshell.dbus.dbusmenu.warning = false' # System tray dbus property errors
|
||||||
set -l notifs 'quickshell.service.notifications.warning = false' # Notification server warnings on reload
|
set -l notifs 'quickshell.service.notifications.warning = false' # Notification server warnings on reload
|
||||||
set -l sni 'quickshell.service.sni.host.warning = false' # StatusNotifierItem warnings on reload
|
set -l sni 'quickshell.service.sni.host.warning = false' # StatusNotifierItem warnings on reload
|
||||||
set -l process 'QProcess: Destroyed while process' # Long running processes on reload
|
set -l process 'QProcess: Destroyed while process' # Long running processes on reload
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue