The `Bar` uses a `Repeater` when building out the models. This has the effect of marshalling the configuration, and causing JSON arrays to become sequence wrappers. A couple of the plugins had been checking for the existence of array values with `Array.isArray`, which fails for the sequence wrappers, causing the values to be ignored. This breaks associated functionality (the "hide" and "pin" actions in the systray had stopped working, as had exit node selection in Tailscale). Switching the guards to use `instanceof Array` works correctly for sequence values (as well as arrays).
648 lines
21 KiB
QML
648 lines
21 KiB
QML
import Quickshell
|
|
import QtQuick
|
|
import QtQuick.Effects
|
|
import Quickshell.Services.SystemTray
|
|
import Quickshell.Widgets
|
|
import qs.Commons
|
|
import qs.Ui
|
|
import "TrayModel.js" as TrayModel
|
|
|
|
BarWidget {
|
|
id: root
|
|
moduleName: "omarchy.tray"
|
|
|
|
property bool expanded: false
|
|
property bool managePopupOpen: false
|
|
property bool trayMenuOpen: false
|
|
property var activeTrayItem: null
|
|
property var activeTrayAnchor: null
|
|
readonly property color foreground: bar ? bar.foreground : Color.foreground
|
|
readonly property string fontFamily: bar ? bar.fontFamily : Style.font.family
|
|
readonly property var pinnedIds: settings.pinned instanceof Array ? settings.pinned : []
|
|
readonly property var hiddenIds: settings.hidden instanceof Array ? settings.hidden : []
|
|
readonly property var pinnedItems: bucket("pinned")
|
|
readonly property var drawerItems: bucket("drawer")
|
|
readonly property var allItems: bucket("all")
|
|
readonly property int drawerCount: drawerItems.length
|
|
readonly property int trayItemExtent: Style.space(16)
|
|
readonly property int trayItemGap: Style.space(9)
|
|
readonly property int trayJoinGap: Style.space(4)
|
|
readonly property int drawerExtent: drawerCount > 0 ? drawerCount * trayItemExtent + (drawerCount - 1) * trayItemGap : 0
|
|
// Match Waybar's group/tray-expander drawer transition-duration.
|
|
readonly property int animationDuration: 600
|
|
property real revealProgress: expanded ? 1 : 0
|
|
readonly property real revealExtent: drawerExtent * revealProgress
|
|
|
|
function close() {
|
|
managePopupOpen = false
|
|
trayMenuOpen = false
|
|
}
|
|
|
|
function openTrayMenu(item, anchorItem, mouse) {
|
|
if (!item || !item.menu) {
|
|
var point = anchorItem.QsWindow.contentItem.mapFromItem(anchorItem, mouse.x, mouse.y)
|
|
item.display(anchorItem.QsWindow.window, point.x, point.y)
|
|
return
|
|
}
|
|
|
|
activeTrayItem = item
|
|
activeTrayAnchor = anchorItem
|
|
trayMenuOpen = true
|
|
}
|
|
|
|
function trayIconSource(icon) {
|
|
// Quickshell already resolves the tray icon into a ready-to-use image://
|
|
// URL, including a "?path=" fallback search dir for apps that ship their
|
|
// tray icon outside a standard theme (e.g. Steam's flat public/ dir). Hand
|
|
// it straight to IconImage; guessing a theme sub-directory here only broke
|
|
// apps whose layout didn't match the guess.
|
|
return String(icon || "")
|
|
}
|
|
|
|
// Symbolic icons ship a fixed fill (often near-white) that the host is meant
|
|
// to recolor to its foreground; detect them by the freedesktop "-symbolic"
|
|
// name suffix so they can be tinted instead of rendered as-is.
|
|
function iconIsSymbolic(icon) {
|
|
var name = String(icon || "").split("?")[0]
|
|
return name.slice(-9) === "-symbolic"
|
|
}
|
|
|
|
function trayTooltip(item) {
|
|
return item.tooltipTitle || item.title || item.id || ""
|
|
}
|
|
|
|
function classifyItem(item) {
|
|
var iid = String(item.id || "")
|
|
if (hiddenIds.indexOf(iid) !== -1) return "hidden"
|
|
if (pinnedIds.indexOf(iid) !== -1) return "pinned"
|
|
return "drawer"
|
|
}
|
|
|
|
function ownedByDedicatedWidget(item) {
|
|
var layout = root.bar && root.bar.layoutConfig ? root.bar.layoutConfig : null
|
|
return TrayModel.ownedByDedicatedWidget(item, layout)
|
|
}
|
|
|
|
function bucket(category) {
|
|
var values = SystemTray.items.values
|
|
var result = []
|
|
for (var i = 0; i < values.length; i++) {
|
|
var item = values[i]
|
|
if (item.status === Status.Passive) continue
|
|
if (ownedByDedicatedWidget(item)) continue
|
|
if (category === "all") {
|
|
result.push(item)
|
|
continue
|
|
}
|
|
if (classifyItem(item) === category) result.push(item)
|
|
}
|
|
return result
|
|
}
|
|
|
|
function persistTrayState(pinned, hidden) {
|
|
if (!root.bar || !root.bar.shell || typeof root.bar.shell.updateEntryInline !== "function") return
|
|
var id = root.moduleName || "omarchy.tray"
|
|
root.bar.shell.updateEntryInline(id, { id: id, pinned: pinned, hidden: hidden })
|
|
}
|
|
|
|
function togglePin(iid) {
|
|
var p = pinnedIds.slice(), h = hiddenIds.slice()
|
|
var idx = p.indexOf(iid)
|
|
if (idx !== -1) p.splice(idx, 1)
|
|
else {
|
|
p.push(iid)
|
|
var hi = h.indexOf(iid)
|
|
if (hi !== -1) h.splice(hi, 1)
|
|
}
|
|
persistTrayState(p, h)
|
|
}
|
|
|
|
function toggleHide(iid) {
|
|
var p = pinnedIds.slice(), h = hiddenIds.slice()
|
|
var idx = h.indexOf(iid)
|
|
if (idx !== -1) h.splice(idx, 1)
|
|
else {
|
|
h.push(iid)
|
|
var pi = p.indexOf(iid)
|
|
if (pi !== -1) p.splice(pi, 1)
|
|
}
|
|
persistTrayState(p, h)
|
|
}
|
|
|
|
visible: pinnedItems.length > 0 || drawerCount > 0
|
|
clip: false
|
|
implicitWidth: root.vertical ? root.barSize : trayContent.implicitWidth
|
|
implicitHeight: root.vertical ? trayContent.implicitHeight : root.barSize
|
|
|
|
Behavior on revealProgress {
|
|
NumberAnimation { duration: root.animationDuration; easing.type: Easing.OutCubic }
|
|
}
|
|
|
|
Loader {
|
|
id: trayContent
|
|
anchors.fill: parent
|
|
sourceComponent: root.vertical ? verticalTray : horizontalTray
|
|
}
|
|
|
|
Component {
|
|
id: horizontalTray
|
|
|
|
Item {
|
|
id: horizontalTrayRoot
|
|
|
|
readonly property int pinnedWidth: pinnedRow.implicitWidth
|
|
readonly property int drawerBlockWidth: root.allItems.length > 0 ? expandIcon.implicitWidth + root.drawerExtent : 0
|
|
|
|
implicitWidth: pinnedWidth + drawerBlockWidth
|
|
implicitHeight: root.barSize
|
|
|
|
// Mask out the empty area the collapsed drawer reserves for its slide-in,
|
|
// so hovering it doesn't trigger expand and clicks pass through.
|
|
containmentMask: QtObject {
|
|
function contains(point: point): bool {
|
|
if (point.y < 0 || point.y > horizontalTrayRoot.height) return false
|
|
// Drawer reveals leftward; chevron sits at the right end when collapsed
|
|
// and slides left as it opens. The visible region starts at the chevron.
|
|
var chevronX = root.drawerExtent - root.revealExtent
|
|
if (point.x >= chevronX && point.x <= horizontalTrayRoot.drawerBlockWidth) return true
|
|
// Pinned items, placed to the right of the drawer block.
|
|
var pinnedStart = horizontalTrayRoot.drawerBlockWidth
|
|
return point.x >= pinnedStart && point.x <= horizontalTrayRoot.implicitWidth
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: drawerArea
|
|
x: 0
|
|
width: horizontalTrayRoot.drawerBlockWidth
|
|
height: root.barSize
|
|
visible: root.allItems.length > 0
|
|
|
|
HoverHandler {
|
|
onHoveredChanged: root.expanded = hovered
|
|
}
|
|
|
|
WidgetButton {
|
|
id: expandIcon
|
|
bar: root.bar
|
|
width: implicitWidth
|
|
height: implicitHeight
|
|
x: root.drawerExtent - root.revealExtent
|
|
text: "\uf053"
|
|
horizontalMargin: 9
|
|
verticalPadding: 6
|
|
onPressed: function(button) {
|
|
if (button === Qt.RightButton) root.managePopupOpen = !root.managePopupOpen
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: trayClip
|
|
x: expandIcon.width
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: root.drawerExtent
|
|
height: root.barSize
|
|
clip: true
|
|
|
|
Row {
|
|
id: trayIcons
|
|
x: root.drawerExtent - root.revealExtent
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: root.trayItemGap
|
|
layer.enabled: true
|
|
|
|
Repeater {
|
|
model: root.drawerItems
|
|
TrayItem {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Row {
|
|
id: pinnedRow
|
|
x: drawerArea.x + horizontalTrayRoot.drawerBlockWidth
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: root.trayItemGap
|
|
leftPadding: root.pinnedItems.length > 0 && root.allItems.length > 0 ? root.trayJoinGap : 0
|
|
Repeater {
|
|
model: root.pinnedItems
|
|
TrayItem {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: verticalTray
|
|
|
|
Item {
|
|
id: verticalTrayRoot
|
|
|
|
readonly property int pinnedHeight: pinnedCol.implicitHeight
|
|
readonly property int drawerBlockHeight: root.allItems.length > 0 ? expandIcon.implicitHeight + root.drawerExtent : 0
|
|
|
|
implicitWidth: root.barSize
|
|
implicitHeight: pinnedHeight + drawerBlockHeight
|
|
|
|
containmentMask: QtObject {
|
|
function contains(point: point): bool {
|
|
if (point.x < 0 || point.x > verticalTrayRoot.width) return false
|
|
var chevronY = root.drawerExtent - root.revealExtent
|
|
if (point.y >= chevronY && point.y <= verticalTrayRoot.drawerBlockHeight) return true
|
|
var pinnedStart = verticalTrayRoot.drawerBlockHeight
|
|
return point.y >= pinnedStart && point.y <= verticalTrayRoot.implicitHeight
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: drawerArea
|
|
y: 0
|
|
width: root.barSize
|
|
height: verticalTrayRoot.drawerBlockHeight
|
|
visible: root.allItems.length > 0
|
|
|
|
HoverHandler {
|
|
onHoveredChanged: root.expanded = hovered
|
|
}
|
|
|
|
WidgetButton {
|
|
id: expandIcon
|
|
bar: root.bar
|
|
width: implicitWidth
|
|
height: implicitHeight
|
|
y: root.drawerExtent - root.revealExtent
|
|
text: "\uf053"
|
|
textRotation: 90
|
|
horizontalMargin: 9
|
|
verticalPadding: 6
|
|
onPressed: function(button) {
|
|
if (button === Qt.RightButton) root.managePopupOpen = !root.managePopupOpen
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: trayClip
|
|
y: expandIcon.height
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
width: root.barSize
|
|
height: root.drawerExtent
|
|
clip: true
|
|
|
|
Column {
|
|
id: trayIcons
|
|
y: root.drawerExtent - root.revealExtent
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: root.trayItemGap
|
|
layer.enabled: true
|
|
|
|
Repeater {
|
|
model: root.drawerItems
|
|
TrayItem {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
id: pinnedCol
|
|
y: drawerArea.y + verticalTrayRoot.drawerBlockHeight
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: root.trayItemGap
|
|
topPadding: root.pinnedItems.length > 0 && root.allItems.length > 0 ? root.trayJoinGap : 0
|
|
Repeater {
|
|
model: root.pinnedItems
|
|
TrayItem {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PopupCard {
|
|
id: managePopup
|
|
anchorItem: root
|
|
owner: root
|
|
bar: root.bar
|
|
open: root.managePopupOpen
|
|
contentWidth: managePopup.fittedContentWidth(Style.space(300))
|
|
contentHeight: managePopup.fittedContentHeight(manageColumn.implicitHeight)
|
|
|
|
Column {
|
|
id: manageColumn
|
|
anchors.fill: parent
|
|
spacing: Style.space(8)
|
|
|
|
Text {
|
|
text: "Tray icons"
|
|
color: root.foreground
|
|
font.family: root.fontFamily
|
|
font.pixelSize: Style.font.body
|
|
font.bold: true
|
|
}
|
|
|
|
Text {
|
|
text: "Pinned icons stay visible. Hidden icons never show."
|
|
color: Qt.darker(root.foreground, 1.4)
|
|
font.family: root.fontFamily
|
|
font.pixelSize: Style.font.caption
|
|
wrapMode: Text.WordWrap
|
|
width: parent.width
|
|
}
|
|
|
|
Text {
|
|
visible: root.allItems.length === 0
|
|
text: "No tray items reporting."
|
|
color: Qt.darker(root.foreground, 1.5)
|
|
font.family: root.fontFamily
|
|
font.pixelSize: Style.font.bodySmall
|
|
font.italic: true
|
|
}
|
|
|
|
Repeater {
|
|
model: root.allItems
|
|
delegate: Item {
|
|
id: rowRoot
|
|
required property var modelData
|
|
required property int index
|
|
width: manageColumn.width
|
|
implicitHeight: 28
|
|
|
|
readonly property string itemId: String(modelData.id || "")
|
|
readonly property string displayName: {
|
|
var t = String(modelData.title || "").trim()
|
|
if (t) return t
|
|
var tt = String(modelData.tooltipTitle || "").trim()
|
|
if (tt) return tt
|
|
var id = String(modelData.id || "")
|
|
var slash = id.lastIndexOf("/")
|
|
return slash !== -1 ? id.substring(slash + 1) : (id || "Unknown")
|
|
}
|
|
readonly property bool isPinned: root.pinnedIds.indexOf(itemId) !== -1
|
|
readonly property bool isHidden: root.hiddenIds.indexOf(itemId) !== -1
|
|
|
|
TrayIcon {
|
|
id: rowIcon
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.left: parent.left
|
|
width: 16
|
|
height: 16
|
|
icon: rowRoot.modelData.icon
|
|
}
|
|
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.left: rowIcon.right
|
|
anchors.leftMargin: Style.space(10)
|
|
anchors.right: rowHideBtn.left
|
|
anchors.rightMargin: Style.space(8)
|
|
text: rowRoot.displayName
|
|
color: root.foreground
|
|
font.family: root.fontFamily
|
|
font.pixelSize: Style.font.bodySmall
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
Button {
|
|
id: rowPinBtn
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.right: parent.right
|
|
iconText: "\uf08d"
|
|
text: rowRoot.isPinned ? "Unpin" : "Pin"
|
|
foreground: root.foreground
|
|
horizontalPadding: 8
|
|
verticalPadding: 3
|
|
iconSize: Style.font.bodySmall
|
|
fontSize: Style.font.bodySmall
|
|
onClicked: root.togglePin(rowRoot.itemId)
|
|
}
|
|
|
|
Button {
|
|
id: rowHideBtn
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.right: rowPinBtn.left
|
|
anchors.rightMargin: Style.space(6)
|
|
iconText: "\uf06e"
|
|
text: rowRoot.isHidden ? "Show" : "Hide"
|
|
foreground: root.foreground
|
|
horizontalPadding: 8
|
|
verticalPadding: 3
|
|
iconSize: Style.font.bodySmall
|
|
fontSize: Style.font.bodySmall
|
|
onClicked: root.toggleHide(rowRoot.itemId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QsMenuOpener {
|
|
id: trayMenuOpener
|
|
menu: root.activeTrayItem ? root.activeTrayItem.menu : null
|
|
}
|
|
|
|
PopupCard {
|
|
id: trayMenuPopup
|
|
anchorItem: root.activeTrayAnchor || root
|
|
owner: root
|
|
bar: root.bar
|
|
open: root.trayMenuOpen
|
|
padding: Style.space(8)
|
|
borderColor: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.45)
|
|
contentWidth: trayMenuPopup.fittedContentWidth(Style.space(232))
|
|
contentHeight: trayMenuPopup.fittedContentHeight(trayMenuColumn.implicitHeight, Style.space(420))
|
|
|
|
Column {
|
|
id: trayMenuColumn
|
|
anchors.fill: parent
|
|
spacing: 0
|
|
|
|
Repeater {
|
|
model: trayMenuOpener.children
|
|
|
|
delegate: Item {
|
|
id: menuRow
|
|
required property var modelData
|
|
required property int index
|
|
|
|
readonly property string rowText: String(modelData.text || "")
|
|
readonly property string activeTitle: root.activeTrayItem ? String(root.activeTrayItem.title || root.activeTrayItem.id || "") : ""
|
|
readonly property bool rootTitleEntry: index === 0 && modelData.hasChildren && rowText.toLowerCase() === activeTitle.toLowerCase()
|
|
readonly property bool leadingSeparator: modelData.isSeparator && index <= 1
|
|
readonly property bool hiddenRow: rootTitleEntry || leadingSeparator
|
|
|
|
visible: !hiddenRow
|
|
width: trayMenuColumn.width
|
|
implicitHeight: hiddenRow ? 0 : (modelData.isSeparator ? Style.space(11) : Style.space(30))
|
|
opacity: modelData.enabled ? 1.0 : 0.45
|
|
|
|
Rectangle {
|
|
visible: menuRow.modelData.isSeparator
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: Style.space(10)
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: Style.space(10)
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
height: 1
|
|
color: Color.popups.border
|
|
opacity: 0.45
|
|
}
|
|
|
|
Rectangle {
|
|
visible: !menuRow.modelData.isSeparator
|
|
anchors.fill: parent
|
|
radius: Math.max(2, Style.cornerRadius)
|
|
color: rowMouse.containsMouse && menuRow.modelData.enabled ? Style.hoverFillFor(root.foreground, root.foreground) : "transparent"
|
|
}
|
|
|
|
Text {
|
|
visible: !menuRow.modelData.isSeparator && menuRow.modelData.buttonType !== QsMenuButtonType.None
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.left: parent.left
|
|
width: Style.space(22)
|
|
horizontalAlignment: Text.AlignHCenter
|
|
text: menuRow.modelData.checkState === Qt.Checked ? "\uf00c" : ""
|
|
color: root.foreground
|
|
font.family: root.fontFamily
|
|
font.pixelSize: Style.font.bodySmall
|
|
}
|
|
|
|
IconImage {
|
|
id: menuIcon
|
|
visible: !menuRow.modelData.isSeparator && String(menuRow.modelData.icon || "") !== ""
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: Style.space(24)
|
|
implicitSize: Style.space(16)
|
|
width: Style.space(16)
|
|
height: Style.space(16)
|
|
source: menuRow.modelData.icon
|
|
}
|
|
|
|
Text {
|
|
visible: !menuRow.modelData.isSeparator
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: menuIcon.visible ? Style.space(46) : Style.space(28)
|
|
anchors.right: submenuGlyph.left
|
|
anchors.rightMargin: Style.space(8)
|
|
text: menuRow.rowText
|
|
color: root.foreground
|
|
font.family: root.fontFamily
|
|
font.pixelSize: Style.font.bodySmall
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
Text {
|
|
id: submenuGlyph
|
|
visible: !menuRow.modelData.isSeparator && menuRow.modelData.hasChildren
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: Style.space(10)
|
|
text: "\u203a"
|
|
color: root.foreground
|
|
font.family: root.fontFamily
|
|
font.pixelSize: Style.font.bodySmall
|
|
}
|
|
|
|
MouseArea {
|
|
id: rowMouse
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
enabled: !menuRow.modelData.isSeparator && menuRow.modelData.enabled
|
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
onClicked: {
|
|
if (menuRow.modelData.hasChildren) {
|
|
var point = menuRow.QsWindow.contentItem.mapFromItem(menuRow, menuRow.width, menuRow.height / 2)
|
|
menuRow.modelData.display(menuRow.QsWindow.window, point.x, point.y)
|
|
} else {
|
|
menuRow.modelData.triggered()
|
|
root.close()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Renders a tray icon, recoloring symbolic icons to the bar foreground so
|
|
// they stay visible on any theme (a raw symbolic icon keeps its baked-in
|
|
// fill and disappears against a matching background).
|
|
component TrayIcon: Item {
|
|
id: trayIconRoot
|
|
required property var icon
|
|
readonly property bool symbolic: root.iconIsSymbolic(icon)
|
|
|
|
IconImage {
|
|
id: trayIconImage
|
|
anchors.fill: parent
|
|
implicitSize: Math.round(Math.min(parent.width, parent.height))
|
|
source: root.trayIconSource(trayIconRoot.icon)
|
|
// Kept as a hidden layer so the effect can sample it as a texture.
|
|
visible: !trayIconRoot.symbolic
|
|
layer.enabled: trayIconRoot.symbolic
|
|
}
|
|
|
|
MultiEffect {
|
|
anchors.fill: trayIconImage
|
|
source: trayIconImage
|
|
visible: trayIconRoot.symbolic
|
|
colorization: 1.0
|
|
colorizationColor: root.foreground
|
|
}
|
|
}
|
|
|
|
component TrayItem: Item {
|
|
id: trayItemRoot
|
|
|
|
required property var modelData
|
|
|
|
visible: modelData.status !== Status.Passive
|
|
implicitWidth: visible ? root.trayItemExtent : 0
|
|
implicitHeight: visible ? root.trayItemExtent : 0
|
|
|
|
function displayMenu(mouse) {
|
|
root.openTrayMenu(trayItemRoot.modelData, trayItemRoot, mouse)
|
|
}
|
|
|
|
TrayIcon {
|
|
anchors.centerIn: parent
|
|
width: Style.space(12)
|
|
height: Style.space(12)
|
|
icon: trayItemRoot.modelData.icon
|
|
}
|
|
|
|
MouseArea {
|
|
id: mouseArea
|
|
anchors.fill: parent
|
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onEntered: if (root.bar) root.bar.showTooltip(trayItemRoot, root.trayTooltip(modelData))
|
|
onExited: if (root.bar) root.bar.hideTooltip(trayItemRoot)
|
|
onPressed: function(mouse) {
|
|
if (mouse.button === Qt.RightButton) {
|
|
trayItemRoot.displayMenu(mouse)
|
|
mouse.accepted = true
|
|
}
|
|
}
|
|
onClicked: function(mouse) {
|
|
if (mouse.button === Qt.RightButton) {
|
|
mouse.accepted = true
|
|
} else if (mouse.button === Qt.MiddleButton) {
|
|
trayItemRoot.modelData.secondaryActivate()
|
|
} else if (trayItemRoot.modelData.onlyMenu) {
|
|
trayItemRoot.displayMenu(mouse)
|
|
} else {
|
|
trayItemRoot.modelData.activate()
|
|
}
|
|
}
|
|
onWheel: function(wheel) {
|
|
trayItemRoot.modelData.scroll(wheel.angleDelta.y, false)
|
|
}
|
|
}
|
|
|
|
readonly property bool tooltipHovered: visible && opacity > 0 && mouseArea.containsMouse
|
|
}
|
|
}
|