diff options
| author | AlexanderCurl <alexc@alexc.hu> | 2026-04-18 17:46:06 +0100 |
|---|---|---|
| committer | AlexanderCurl <alexc@alexc.hu> | 2026-04-18 17:46:06 +0100 |
| commit | 70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 (patch) | |
| tree | ab1123d4067c1b086dd6faa7ee4ea643236b565a /raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml | |
| parent | 5d94c0a7d44a2255b81815a52a7056a94a39842d (diff) | |
| download | RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.tar.gz RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.zip | |
Replaced file structures for themes in the correct format
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml')
| -rw-r--r-- | raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml | 1893 |
1 files changed, 0 insertions, 1893 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml deleted file mode 100644 index 7fb9362..0000000 --- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml +++ /dev/null @@ -1,1893 +0,0 @@ -import QtQuick -import Quickshell -import Quickshell.Hyprland -import Quickshell.Services.SystemTray -import Quickshell.Wayland -import Quickshell.Widgets -import qs.Common -import qs.Modules.Plugins -import qs.Services -import qs.Widgets - -BasePill { - id: root - - enableBackgroundHover: false - enableCursor: false - - property var parentWindow: null - property var widgetData: null - property string section: "right" - property bool isAtBottom: false - property bool isAutoHideBar: false - property bool useOverflowPopup: !widgetData?.trayUseInlineExpansion - readonly property var hiddenTrayIds: { - const envValue = Quickshell.env("DMS_HIDE_TRAYIDS") || ""; - return envValue ? envValue.split(",").map(id => id.trim().toLowerCase()) : []; - } - readonly property var allTrayItems: { - if (!hiddenTrayIds.length) { - return SystemTray.items.values; - } - return SystemTray.items.values.filter(item => { - const itemId = item?.id || ""; - return !hiddenTrayIds.includes(itemId.toLowerCase()); - }); - } - function getTrayItemKey(item) { - const id = item?.id || ""; - const tooltipTitle = item?.tooltipTitle || ""; - if (!tooltipTitle || tooltipTitle === id) { - return id; - } - return `${id}::${tooltipTitle}`; - } - - function trayIconSourceFor(trayItem) { - let icon = trayItem && trayItem.icon; - if (typeof icon === 'string' || icon instanceof String) { - if (icon === "") - return ""; - if (icon.includes("?path=")) { - const split = icon.split("?path="); - if (split.length !== 2) - return icon; - const name = split[0]; - const path = split[1]; - let fileName = name.substring(name.lastIndexOf("/") + 1); - if (fileName.startsWith("dropboxstatus")) { - fileName = `hicolor/16x16/status/${fileName}`; - } - return `file://${path}/${fileName}`; - } - if (icon.startsWith("/") && !icon.startsWith("file://")) - return `file://${icon}`; - return icon; - } - return ""; - } - - function activateInlineTrayItem(trayItem, anchorItem) { - if (!trayItem) - return; - if (!trayItem.onlyMenu) { - trayItem.activate(); - return; - } - if (!trayItem.hasMenu) - return; - root.showForTrayItem(trayItem, anchorItem, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); - } - - function openInlineTrayContextMenu(trayItem, areaItem, mouse, anchorItem) { - if (!trayItem) { - return; - } - if (!trayItem.hasMenu) { - const gp = areaItem.mapToGlobal(mouse.x, mouse.y); - root.callContextMenuFallback(trayItem.id, Math.round(gp.x), Math.round(gp.y)); - return; - } - root.showForTrayItem(trayItem, anchorItem, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); - } - - function toggleIconName() { - const edge = root.axis?.edge; - if (root.useOverflowPopup) { - switch (edge) { - case "left": - return root.menuOpen ? "keyboard_arrow_left" : "keyboard_arrow_right"; - case "right": - return root.menuOpen ? "keyboard_arrow_right" : "keyboard_arrow_left"; - case "bottom": - return root.menuOpen ? "keyboard_arrow_down" : "keyboard_arrow_up"; - case "top": - return root.menuOpen ? "keyboard_arrow_up" : "keyboard_arrow_down"; - } - } - - if (edge === "left" || edge === "right") { - return root.menuOpen == (root.section !== "right") ? "keyboard_arrow_up" : "keyboard_arrow_down"; - } - - return root.menuOpen != (root.section === "right") ? "keyboard_arrow_left" : "keyboard_arrow_right"; - } - - // ! TODO - replace with either native dbus client (like plugins use) or just a DMS cli or something - function callContextMenuFallback(trayItemId, globalX, globalY) { - const script = ['ITEMS=$(dbus-send --session --print-reply --dest=org.kde.StatusNotifierWatcher /StatusNotifierWatcher org.freedesktop.DBus.Properties.Get string:org.kde.StatusNotifierWatcher string:RegisteredStatusNotifierItems 2>/dev/null)', 'while IFS= read -r line; do', ' line="${line#*\\\"}"', ' line="${line%\\\"*}"', ' [ -z "$line" ] && continue', ' BUS="${line%%/*}"', ' OBJ="/${line#*/}"', ' ID=$(dbus-send --session --print-reply --dest="$BUS" "$OBJ" org.freedesktop.DBus.Properties.Get string:org.kde.StatusNotifierItem string:Id 2>/dev/null | grep -oP "(?<=\\\")(.*?)(?=\\\")" | tail -1)', ' if [ "$ID" = "$1" ]; then', ' dbus-send --session --type=method_call --dest="$BUS" "$OBJ" org.kde.StatusNotifierItem.ContextMenu int32:"$2" int32:"$3"', ' exit 0', ' fi', 'done <<< "$ITEMS"',].join("\n"); - Quickshell.execDetached(["bash", "-c", script, "_", trayItemId, String(globalX), String(globalY)]); - } - - property int _trayOrderTrigger: 0 - - Connections { - target: SessionData - function onTrayItemOrderChanged() { - root._trayOrderTrigger++; - } - } - - function sortByPreferredOrder(items, trigger) { - void trigger; - const savedOrder = SessionData.trayItemOrder || []; - const orderMap = new Map(); - savedOrder.forEach((key, idx) => orderMap.set(key, idx)); - - return [...items].sort((a, b) => { - const keyA = getTrayItemKey(a); - const keyB = getTrayItemKey(b); - const orderA = orderMap.has(keyA) ? orderMap.get(keyA) : 10000 + items.indexOf(a); - const orderB = orderMap.has(keyB) ? orderMap.get(keyB) : 10000 + items.indexOf(b); - return orderA - orderB; - }); - } - - readonly property var allSortedTrayItems: sortByPreferredOrder(allTrayItems, _trayOrderTrigger) - readonly property var allSortedTrayItemKeys: allSortedTrayItems.map(item => getTrayItemKey(item)) - readonly property var mainBarItemsRaw: allSortedTrayItems.filter(item => !SessionData.isHiddenTrayId(root.getTrayItemKey(item))) - readonly property var mainBarItems: mainBarItemsRaw.map((item, idx) => ({ - key: getTrayItemKey(item), - item: item - })) - readonly property var hiddenBarItems: allSortedTrayItems.filter(item => SessionData.isHiddenTrayId(root.getTrayItemKey(item))) - readonly property bool reverseInlineHorizontal: !useOverflowPopup && !isVerticalOrientation && section === "right" - readonly property bool reverseInlineVertical: !useOverflowPopup && isVerticalOrientation && section === "right" - readonly property var displayedMainBarItems: reverseInlineHorizontal ? [...mainBarItems].reverse() : mainBarItems - readonly property var displayedInlineExpandedItems: (reverseInlineHorizontal ? [...hiddenBarItems].reverse() : hiddenBarItems).map(item => ({ - key: getTrayItemKey(item), - item: item - })) - - function moveTrayItemInFullOrder(visibleFromIndex, visibleToIndex) { - if (visibleFromIndex === visibleToIndex || visibleFromIndex < 0 || visibleToIndex < 0) - return; - - const fromKey = mainBarItems[visibleFromIndex]?.key ?? null; - const toKey = mainBarItems[visibleToIndex]?.key ?? null; - if (!fromKey || !toKey) - return; - - const fullOrder = [...allSortedTrayItemKeys]; - const fullFromIndex = fullOrder.indexOf(fromKey); - const fullToIndex = fullOrder.indexOf(toKey); - if (fullFromIndex < 0 || fullToIndex < 0) - return; - - const movedKey = fullOrder.splice(fullFromIndex, 1)[0]; - fullOrder.splice(fullToIndex, 0, movedKey); - SessionData.setTrayItemOrder(fullOrder); - } - - property int draggedIndex: -1 - property int dropTargetIndex: -1 - property bool suppressShiftAnimation: false - readonly property bool hasHiddenItems: allTrayItems.length > mainBarItems.length - readonly property bool inlineExpanded: hasHiddenItems && !useOverflowPopup && menuOpen - visible: allTrayItems.length > 0 - opacity: allTrayItems.length > 0 ? 1 : 0 - - states: [ - State { - name: "hidden_horizontal" - when: allTrayItems.length === 0 && !isVerticalOrientation - PropertyChanges { - target: root - width: 0 - } - }, - State { - name: "hidden_vertical" - when: allTrayItems.length === 0 && isVerticalOrientation - PropertyChanges { - target: root - height: 0 - } - } - ] - - transitions: [ - Transition { - NumberAnimation { - properties: "width,height" - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - } - ] - - Behavior on opacity { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - } - - readonly property real trayItemSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) + 6 - - readonly property real minTooltipY: { - if (!parentScreen || !isVerticalOrientation) { - return 0; - } - - if (isAutoHideBar) { - return 0; - } - - if (parentScreen.y > 0) { - const estimatedTopBarHeight = barThickness + barSpacing; - return estimatedTopBarHeight; - } - - return 0; - } - - readonly property string autoBarShadowDirection: { - const edge = root.axis?.edge; - switch (edge) { - case "top": - return "top"; - case "bottom": - return "bottom"; - case "left": - return "left"; - case "right": - return "right"; - default: - return "bottom"; - } - } - readonly property string effectiveShadowDirection: Theme.elevationLightDirection === "autoBar" ? autoBarShadowDirection : Theme.elevationLightDirection - - property bool menuOpen: false - property var currentTrayMenu: null - - content: Component { - Item { - implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0 - implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0 - - Loader { - id: layoutLoader - anchors.centerIn: parent - sourceComponent: root.isVerticalOrientation ? columnComp : rowComp - } - } - } - - Component { - id: rowComp - Row { - spacing: 0 - layoutDirection: root.reverseInlineHorizontal ? Qt.RightToLeft : Qt.LeftToRight - - Repeater { - model: ScriptModel { - values: root.displayedMainBarItems - objectProp: "key" - } - - delegate: Item { - id: delegateRoot - property var trayItem: modelData.item - property string itemKey: modelData.key - property string iconSource: root.trayIconSourceFor(trayItem) - - width: root.trayItemSize - height: root.barThickness - z: dragHandler.dragging ? 100 : 0 - - property real shiftOffset: { - if (root.draggedIndex < 0) - return 0; - if (index === root.draggedIndex) - return 0; - const dragIdx = root.draggedIndex; - const dropIdx = root.dropTargetIndex; - const shiftAmount = root.trayItemSize; - if (dropIdx < 0) - return 0; - if (dragIdx < dropIdx && index > dragIdx && index <= dropIdx) - return -shiftAmount; - if (dragIdx > dropIdx && index >= dropIdx && index < dragIdx) - return shiftAmount; - return 0; - } - - transform: Translate { - x: delegateRoot.shiftOffset - Behavior on x { - enabled: !root.suppressShiftAnimation - NumberAnimation { - duration: 150 - easing.type: Easing.OutCubic - } - } - } - - Item { - id: dragHandler - anchors.fill: parent - property bool dragging: false - property point dragStartPos: Qt.point(0, 0) - property real dragAxisOffset: 0 - property bool longPressing: false - - Timer { - id: longPressTimer - interval: 400 - repeat: false - onTriggered: dragHandler.longPressing = true - } - } - - Rectangle { - id: visualContent - width: root.trayItemSize - height: root.trayItemSize - anchors.centerIn: parent - radius: Theme.cornerRadius - color: trayItemArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent" - border.width: dragHandler.dragging ? 2 : 0 - border.color: Theme.primary - opacity: dragHandler.dragging ? 0.8 : 1.0 - - transform: Translate { - x: dragHandler.dragging ? dragHandler.dragAxisOffset : 0 - } - - IconImage { - id: iconImg - anchors.centerIn: parent - width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) - height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) - source: delegateRoot.iconSource - asynchronous: true - smooth: true - mipmap: true - visible: status === Image.Ready - } - - Text { - anchors.centerIn: parent - visible: !iconImg.visible - text: { - const itemId = trayItem?.id || ""; - if (!itemId) - return "?"; - return itemId.charAt(0).toUpperCase(); - } - font.pixelSize: 10 - color: Theme.widgetTextColor - } - - DankRipple { - id: itemRipple - cornerRadius: Theme.cornerRadius - } - } - - MouseArea { - id: trayItemArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - cursorShape: dragHandler.longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor - - onPressed: mouse => { - const pos = mapToItem(visualContent, mouse.x, mouse.y); - itemRipple.trigger(pos.x, pos.y); - if (mouse.button === Qt.LeftButton) { - dragHandler.dragStartPos = Qt.point(mouse.x, mouse.y); - longPressTimer.start(); - } - } - - onReleased: mouse => { - longPressTimer.stop(); - const wasDragging = dragHandler.dragging; - const didReorder = wasDragging && root.dropTargetIndex >= 0 && root.dropTargetIndex !== root.draggedIndex; - - if (didReorder) { - root.suppressShiftAnimation = true; - root.moveTrayItemInFullOrder(root.draggedIndex, root.dropTargetIndex); - Qt.callLater(() => root.suppressShiftAnimation = false); - } - - dragHandler.longPressing = false; - dragHandler.dragging = false; - dragHandler.dragAxisOffset = 0; - root.draggedIndex = -1; - root.dropTargetIndex = -1; - - if (wasDragging || mouse.button !== Qt.LeftButton) - return; - - if (!delegateRoot.trayItem) - return; - if (!delegateRoot.trayItem.onlyMenu) { - delegateRoot.trayItem.activate(); - return; - } - if (!delegateRoot.trayItem.hasMenu) - return; - if (root.useOverflowPopup) - root.menuOpen = false; - root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); - } - - onPositionChanged: mouse => { - if (dragHandler.longPressing && !dragHandler.dragging) { - const distance = Math.abs(mouse.x - dragHandler.dragStartPos.x); - if (distance > 5) { - dragHandler.dragging = true; - root.draggedIndex = root.reverseInlineHorizontal ? (root.mainBarItems.length - 1 - index) : index; - root.dropTargetIndex = root.draggedIndex; - } - } - if (!dragHandler.dragging) - return; - - const axisOffset = mouse.x - dragHandler.dragStartPos.x; - dragHandler.dragAxisOffset = axisOffset; - const itemSize = root.trayItemSize; - const slotOffset = Math.round(axisOffset / itemSize); - const visualTargetIndex = Math.max(0, Math.min(root.mainBarItems.length - 1, index + slotOffset)); - const newTargetIndex = root.reverseInlineHorizontal ? (root.mainBarItems.length - 1 - visualTargetIndex) : visualTargetIndex; - if (newTargetIndex !== root.dropTargetIndex) { - root.dropTargetIndex = newTargetIndex; - } - } - - onClicked: mouse => { - if (dragHandler.dragging) - return; - if (mouse.button !== Qt.RightButton) - return; - if (!delegateRoot.trayItem?.hasMenu) { - const gp = trayItemArea.mapToGlobal(mouse.x, mouse.y); - root.callContextMenuFallback(delegateRoot.trayItem.id, Math.round(gp.x), Math.round(gp.y)); - return; - } - if (root.useOverflowPopup) - root.menuOpen = false; - root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); - } - } - } - } - - Item { - width: root.trayItemSize - height: root.barThickness - visible: root.hasHiddenItems - - Rectangle { - id: caretButton - width: root.trayItemSize - height: root.trayItemSize - anchors.centerIn: parent - radius: Theme.cornerRadius - color: caretArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent" - - DankIcon { - anchors.centerIn: parent - name: root.toggleIconName() - size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) - color: Theme.widgetTextColor - } - - DankRipple { - id: caretRipple - cornerRadius: Theme.cornerRadius - } - - MouseArea { - id: caretArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onPressed: mouse => { - caretRipple.trigger(mouse.x, mouse.y); - } - onClicked: root.menuOpen = !root.menuOpen - } - } - } - - Repeater { - model: ScriptModel { - values: root.displayedInlineExpandedItems - objectProp: "key" - } - - delegate: inlineExpandedTrayItemDelegate - } - } - } - - Component { - id: inlineExpandedTrayItemDelegate - - Item { - property var trayItem: modelData.item - property string itemKey: modelData.key - property string iconSource: root.trayIconSourceFor(trayItem) - - width: root.isVerticalOrientation ? root.barThickness : (root.inlineExpanded ? root.trayItemSize : 0) - height: root.isVerticalOrientation ? (root.inlineExpanded ? root.trayItemSize : 0) : root.barThickness - visible: width > 0 || height > 0 - - Behavior on width { - enabled: !root.isVerticalOrientation - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - } - - Behavior on height { - enabled: root.isVerticalOrientation - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - } - - Rectangle { - id: inlineVisualContent - width: root.trayItemSize - height: root.trayItemSize - x: root.isVerticalOrientation ? Math.round((parent.width - width) / 2) : (root.reverseInlineHorizontal ? parent.width - width : 0) - y: root.isVerticalOrientation ? (root.reverseInlineVertical ? parent.height - height : 0) : Math.round((parent.height - height) / 2) - radius: Theme.cornerRadius - color: inlineTrayItemArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent" - opacity: root.inlineExpanded ? 1 : 0 - - Behavior on opacity { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - } - - IconImage { - id: inlineIconImg - anchors.centerIn: parent - width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) - height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) - source: iconSource - asynchronous: true - smooth: true - mipmap: true - visible: status === Image.Ready - } - - Text { - anchors.centerIn: parent - visible: !inlineIconImg.visible - text: { - const itemId = trayItem?.id || ""; - if (!itemId) - return "?"; - return itemId.charAt(0).toUpperCase(); - } - font.pixelSize: 10 - color: Theme.widgetTextColor - } - - DankRipple { - id: inlineItemRipple - cornerRadius: Theme.cornerRadius - } - } - - MouseArea { - id: inlineTrayItemArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - cursorShape: Qt.PointingHandCursor - enabled: root.inlineExpanded - - onPressed: mouse => { - const pos = mapToItem(inlineVisualContent, mouse.x, mouse.y); - inlineItemRipple.trigger(pos.x, pos.y); - } - - onClicked: mouse => { - if (mouse.button === Qt.LeftButton) { - root.activateInlineTrayItem(trayItem, inlineVisualContent); - return; - } - if (mouse.button !== Qt.RightButton) - return; - root.openInlineTrayContextMenu(trayItem, inlineTrayItemArea, mouse, inlineVisualContent); - } - } - } - } - - Component { - id: verticalMainTrayItemDelegate - - Item { - property var trayItem: modelData.item - property string itemKey: modelData.key - property string iconSource: root.trayIconSourceFor(trayItem) - - width: root.barThickness - height: root.trayItemSize - z: dragHandler.dragging ? 100 : 0 - - property real shiftOffset: { - if (root.draggedIndex < 0) - return 0; - if (index === root.draggedIndex) - return 0; - const dragIdx = root.draggedIndex; - const dropIdx = root.dropTargetIndex; - const shiftAmount = root.trayItemSize; - if (dropIdx < 0) - return 0; - if (dragIdx < dropIdx && index > dragIdx && index <= dropIdx) - return -shiftAmount; - if (dragIdx > dropIdx && index >= dropIdx && index < dragIdx) - return shiftAmount; - return 0; - } - - transform: Translate { - y: shiftOffset - Behavior on y { - enabled: !root.suppressShiftAnimation - NumberAnimation { - duration: 150 - easing.type: Easing.OutCubic - } - } - } - - Item { - id: dragHandler - anchors.fill: parent - property bool dragging: false - property point dragStartPos: Qt.point(0, 0) - property real dragAxisOffset: 0 - property bool longPressing: false - - Timer { - id: longPressTimer - interval: 400 - repeat: false - onTriggered: dragHandler.longPressing = true - } - } - - Rectangle { - id: visualContent - width: root.trayItemSize - height: root.trayItemSize - anchors.centerIn: parent - radius: Theme.cornerRadius - color: trayItemArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent" - border.width: dragHandler.dragging ? 2 : 0 - border.color: Theme.primary - opacity: dragHandler.dragging ? 0.8 : 1.0 - - transform: Translate { - y: dragHandler.dragging ? dragHandler.dragAxisOffset : 0 - } - - IconImage { - id: iconImg - anchors.centerIn: parent - width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) - height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) - source: iconSource - asynchronous: true - smooth: true - mipmap: true - visible: status === Image.Ready - } - - Text { - anchors.centerIn: parent - visible: !iconImg.visible - text: { - const itemId = trayItem?.id || ""; - if (!itemId) - return "?"; - return itemId.charAt(0).toUpperCase(); - } - font.pixelSize: 10 - color: Theme.widgetTextColor - } - - DankRipple { - id: itemRipple - cornerRadius: Theme.cornerRadius - } - } - - MouseArea { - id: trayItemArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - cursorShape: dragHandler.longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor - - onPressed: mouse => { - const pos = mapToItem(visualContent, mouse.x, mouse.y); - itemRipple.trigger(pos.x, pos.y); - if (mouse.button === Qt.LeftButton) { - dragHandler.dragStartPos = Qt.point(mouse.x, mouse.y); - longPressTimer.start(); - } - } - - onReleased: mouse => { - longPressTimer.stop(); - const wasDragging = dragHandler.dragging; - const didReorder = wasDragging && root.dropTargetIndex >= 0 && root.dropTargetIndex !== root.draggedIndex; - - if (didReorder) { - root.suppressShiftAnimation = true; - root.moveTrayItemInFullOrder(root.draggedIndex, root.dropTargetIndex); - Qt.callLater(() => root.suppressShiftAnimation = false); - } - - dragHandler.longPressing = false; - dragHandler.dragging = false; - dragHandler.dragAxisOffset = 0; - root.draggedIndex = -1; - root.dropTargetIndex = -1; - - if (wasDragging || mouse.button !== Qt.LeftButton) - return; - - if (!trayItem) - return; - if (!trayItem.onlyMenu) { - trayItem.activate(); - return; - } - if (!trayItem.hasMenu) - return; - if (root.useOverflowPopup) - root.menuOpen = false; - root.showForTrayItem(trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); - } - - onPositionChanged: mouse => { - if (dragHandler.longPressing && !dragHandler.dragging) { - const distance = Math.abs(mouse.y - dragHandler.dragStartPos.y); - if (distance > 5) { - dragHandler.dragging = true; - root.draggedIndex = index; - root.dropTargetIndex = root.draggedIndex; - } - } - if (!dragHandler.dragging) - return; - - const axisOffset = mouse.y - dragHandler.dragStartPos.y; - dragHandler.dragAxisOffset = axisOffset; - const itemSize = root.trayItemSize; - const slotOffset = Math.round(axisOffset / itemSize); - const newTargetIndex = Math.max(0, Math.min(root.mainBarItems.length - 1, index + slotOffset)); - if (newTargetIndex !== root.dropTargetIndex) { - root.dropTargetIndex = newTargetIndex; - } - } - - onClicked: mouse => { - if (dragHandler.dragging) - return; - if (mouse.button !== Qt.RightButton) - return; - root.openInlineTrayContextMenu(trayItem, trayItemArea, mouse, visualContent); - } - } - } - } - - Component { - id: columnComp - Column { - spacing: 0 - - // Column lacks layoutDirection, so we use four repeaters with mutually exclusive models to control whether main items or expanded items appear above/ below the toggle button. - // When reverseInlineVertical is true the first and third repeaters are empty and the second and fourth are active, and vice-versa. - // Because items are swapped between repeaters rather than reversed within a single list, vertical drag-and-drop indices don't need remapping (unlike the horizontal RightToLeft case). - Repeater { - model: ScriptModel { - values: root.reverseInlineVertical ? [] : root.displayedMainBarItems - objectProp: "key" - } - delegate: verticalMainTrayItemDelegate - } - - Repeater { - model: ScriptModel { - values: root.reverseInlineVertical ? root.displayedInlineExpandedItems : [] - objectProp: "key" - } - delegate: inlineExpandedTrayItemDelegate - } - - Item { - width: root.barThickness - height: root.trayItemSize - visible: root.hasHiddenItems - - Rectangle { - id: caretButtonVert - width: root.trayItemSize - height: root.trayItemSize - anchors.centerIn: parent - radius: Theme.cornerRadius - color: caretAreaVert.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent" - - DankIcon { - anchors.centerIn: parent - name: root.toggleIconName() - size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) - color: Theme.widgetTextColor - } - - DankRipple { - id: caretRippleVert - cornerRadius: Theme.cornerRadius - } - - MouseArea { - id: caretAreaVert - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onPressed: mouse => { - caretRippleVert.trigger(mouse.x, mouse.y); - } - onClicked: root.menuOpen = !root.menuOpen - } - } - } - - Repeater { - model: ScriptModel { - values: root.reverseInlineVertical ? [] : root.displayedInlineExpandedItems - objectProp: "key" - } - delegate: inlineExpandedTrayItemDelegate - } - - Repeater { - model: ScriptModel { - values: root.reverseInlineVertical ? root.displayedMainBarItems : [] - objectProp: "key" - } - delegate: verticalMainTrayItemDelegate - } - } - } - - PanelWindow { - id: overflowMenu - - WindowBlur { - targetWindow: overflowMenu - blurX: menuContainer.x - blurY: menuContainer.y - blurWidth: root.menuOpen ? menuContainer.width : 0 - blurHeight: root.menuOpen ? menuContainer.height : 0 - blurRadius: Theme.cornerRadius - } - - visible: root.useOverflowPopup && root.menuOpen - screen: root.parentScreen - WlrLayershell.layer: WlrLayershell.Top - WlrLayershell.exclusiveZone: -1 - WlrLayershell.keyboardFocus: { - if (!root.menuOpen) - return WlrKeyboardFocus.None; - if (CompositorService.useHyprlandFocusGrab) - return WlrKeyboardFocus.OnDemand; - return WlrKeyboardFocus.Exclusive; - } - WlrLayershell.namespace: "dms:tray-overflow-menu" - color: "transparent" - - HyprlandFocusGrab { - windows: [overflowMenu] - active: CompositorService.useHyprlandFocusGrab && root.useOverflowPopup && root.menuOpen - } - - Connections { - target: PopoutManager - function onPopoutOpening() { - if (root.useOverflowPopup) - root.menuOpen = false; - } - } - - Component.onDestruction: { - if (root.parentScreen) { - TrayMenuManager.unregisterMenu(root.parentScreen.name); - } - } - - function close() { - root.menuOpen = false; - } - - anchors { - top: true - left: true - right: true - bottom: true - } - - readonly property real dpr: (typeof CompositorService !== "undefined" && CompositorService.getScreenScale) ? CompositorService.getScreenScale(overflowMenu.screen) : (screen?.devicePixelRatio || 1) - property point anchorPos: Qt.point(screen.width / 2, screen.height / 2) - - property var barBounds: { - if (!overflowMenu.screen || !root.barConfig) { - return { - "x": 0, - "y": 0, - "width": 0, - "height": 0, - "wingSize": 0 - }; - } - const barPosition = root.axis?.edge === "left" ? 2 : (root.axis?.edge === "right" ? 3 : (root.axis?.edge === "top" ? 0 : 1)); - return SettingsData.getBarBounds(overflowMenu.screen, root.barThickness + root.barSpacing, barPosition, root.barConfig); - } - - property real barX: barBounds.x - property real barY: barBounds.y - property real barWidth: barBounds.width - property real barHeight: barBounds.height - - readonly property int barPosition: root.axis?.edge === "left" ? 2 : (root.axis?.edge === "right" ? 3 : (root.axis?.edge === "top" ? 0 : 1)) - readonly property var adjacentBarInfo: parentScreen ? SettingsData.getAdjacentBarInfo(parentScreen, barPosition, root.barConfig) : ({ - "topBar": 0, - "bottomBar": 0, - "leftBar": 0, - "rightBar": 0 - }) - readonly property real effectiveBarSize: root.barThickness + root.barSpacing - - readonly property real maskX: { - const triggeringBarX = (barPosition === 2) ? effectiveBarSize : 0; - const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0; - return Math.max(triggeringBarX, adjacentLeftBar); - } - - readonly property real maskY: { - const triggeringBarY = (barPosition === 0) ? effectiveBarSize : 0; - const adjacentTopBar = adjacentBarInfo?.topBar ?? 0; - return Math.max(triggeringBarY, adjacentTopBar); - } - - readonly property real maskWidth: { - const triggeringBarRight = (barPosition === 3) ? effectiveBarSize : 0; - const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0; - const rightExclusion = Math.max(triggeringBarRight, adjacentRightBar); - return Math.max(100, width - maskX - rightExclusion); - } - - readonly property real maskHeight: { - const triggeringBarBottom = (barPosition === 1) ? effectiveBarSize : 0; - const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0; - const bottomExclusion = Math.max(triggeringBarBottom, adjacentBottomBar); - return Math.max(100, height - maskY - bottomExclusion); - } - - mask: Region { - item: Rectangle { - x: overflowMenu.maskX - y: overflowMenu.maskY - width: overflowMenu.maskWidth - height: overflowMenu.maskHeight - } - } - - onVisibleChanged: { - if (visible) { - if (currentTrayMenu) { - currentTrayMenu.showMenu = false; - } - if (root.parentScreen) { - TrayMenuManager.registerMenu(root.parentScreen.name, overflowMenu); - } - PopoutManager.closeAllPopouts(); - ModalManager.closeAllModalsExcept(null); - updatePosition(); - } else if (!visible && root.parentScreen) { - TrayMenuManager.unregisterMenu(root.parentScreen.name); - } - } - - MouseArea { - x: overflowMenu.maskX - y: overflowMenu.maskY - width: overflowMenu.maskWidth - height: overflowMenu.maskHeight - z: -1 - enabled: root.menuOpen - acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton - onClicked: mouse => { - const clickX = mouse.x + overflowMenu.maskX; - const clickY = mouse.y + overflowMenu.maskY; - const outsideContent = clickX < menuContainer.x || clickX > menuContainer.x + menuContainer.width || clickY < menuContainer.y || clickY > menuContainer.y + menuContainer.height; - - if (!outsideContent) - return; - root.menuOpen = false; - } - } - - FocusScope { - id: overflowFocusScope - anchors.fill: parent - focus: true - - Keys.onEscapePressed: { - root.menuOpen = false; - } - } - - function updatePosition() { - const globalPos = root.mapToGlobal(0, 0); - const screenX = screen.x || 0; - const screenY = screen.y || 0; - const relativeX = globalPos.x - screenX; - const relativeY = globalPos.y - screenY; - - if (root.isVerticalOrientation) { - const edge = root.axis?.edge; - let targetX = edge === "left" ? root.barThickness + root.barSpacing + Theme.popupDistance : screen.width - (root.barThickness + root.barSpacing + Theme.popupDistance); - const adjustedY = relativeY + root.height / 2 + root.minTooltipY; - anchorPos = Qt.point(targetX, adjustedY); - } else { - let targetY = root.isAtBottom ? screen.height - (root.barThickness + root.barSpacing + (barConfig?.bottomGap ?? 0) + Theme.popupDistance) : root.barThickness + root.barSpacing + (barConfig?.bottomGap ?? 0) + Theme.popupDistance; - anchorPos = Qt.point(relativeX + root.width / 2, targetY); - } - } - - Item { - id: menuContainer - objectName: "overflowMenuContainer" - - readonly property real rawWidth: { - const itemCount = root.hiddenBarItems.length; - const cols = Math.min(5, itemCount); - const itemSize = root.trayItemSize + 4; - const spacing = 2; - return cols * itemSize + (cols - 1) * spacing + Theme.spacingS * 2; - } - readonly property real rawHeight: { - const itemCount = root.hiddenBarItems.length; - const cols = Math.min(5, itemCount); - const rows = Math.ceil(itemCount / cols); - const itemSize = root.trayItemSize + 4; - const spacing = 2; - return rows * itemSize + (rows - 1) * spacing + Theme.spacingS * 2; - } - - readonly property real alignedWidth: Theme.px(rawWidth, overflowMenu.dpr) - readonly property real alignedHeight: Theme.px(rawHeight, overflowMenu.dpr) - - width: alignedWidth - height: alignedHeight - - x: Theme.snap((() => { - if (root.isVerticalOrientation) { - const edge = root.axis?.edge; - if (edge === "left") { - const targetX = overflowMenu.anchorPos.x; - return Math.min(overflowMenu.screen.width - alignedWidth - 10, targetX); - } else { - const targetX = overflowMenu.anchorPos.x - alignedWidth; - return Math.max(10, targetX); - } - } else { - const left = 10; - const right = overflowMenu.width - alignedWidth - 10; - const want = overflowMenu.anchorPos.x - alignedWidth / 2; - return Math.max(left, Math.min(right, want)); - } - })(), overflowMenu.dpr) - - y: Theme.snap((() => { - if (root.isVerticalOrientation) { - const top = Math.max(overflowMenu.barY, 10); - const bottom = overflowMenu.height - alignedHeight - 10; - const want = overflowMenu.anchorPos.y - alignedHeight / 2; - return Math.max(top, Math.min(bottom, want)); - } else { - if (root.isAtBottom) { - const targetY = overflowMenu.anchorPos.y - alignedHeight; - return Math.max(10, targetY); - } else { - const targetY = overflowMenu.anchorPos.y; - return Math.min(overflowMenu.screen.height - alignedHeight - 10, targetY); - } - } - })(), overflowMenu.dpr) - - opacity: root.menuOpen ? 1 : 0 - scale: root.menuOpen ? 1 : 0.85 - - Behavior on opacity { - NumberAnimation { - duration: Theme.mediumDuration - easing.type: Theme.emphasizedEasing - } - } - - Behavior on scale { - NumberAnimation { - duration: Theme.mediumDuration - easing.type: Theme.emphasizedEasing - } - } - - ElevationShadow { - id: bgShadowLayer - anchors.fill: parent - level: Theme.elevationLevel3 - direction: root.effectiveShadowDirection - fallbackOffset: 6 - targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) - targetRadius: Theme.cornerRadius - sourceRect.antialiasing: true - sourceRect.smooth: true - shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled - layer.smooth: true - layer.textureSize: Qt.size(Math.round(width * overflowMenu.dpr * 2), Math.round(height * overflowMenu.dpr * 2)) - layer.textureMirroring: ShaderEffectSource.MirrorVertically - layer.samples: 4 - } - - Rectangle { - anchors.fill: parent - color: "transparent" - radius: Theme.cornerRadius - border.color: BlurService.borderColor - border.width: BlurService.borderWidth - z: 100 - } - - Grid { - id: menuGrid - anchors.centerIn: parent - columns: Math.min(5, root.hiddenBarItems.length) - spacing: 2 - rowSpacing: 2 - - Repeater { - model: root.hiddenBarItems - - delegate: Rectangle { - property var trayItem: modelData - property string iconSource: root.trayIconSourceFor(trayItem) - - width: root.trayItemSize + 4 - height: root.trayItemSize + 4 - radius: Theme.cornerRadius - color: itemArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : Theme.withAlpha(Theme.surfaceContainer, 0) - - IconImage { - id: menuIconImg - anchors.centerIn: parent - width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) - height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale) - source: parent.iconSource - asynchronous: true - smooth: true - mipmap: true - visible: status === Image.Ready - } - - Text { - anchors.centerIn: parent - visible: !menuIconImg.visible - text: { - const itemId = trayItem?.id || ""; - if (!itemId) - return "?"; - return itemId.charAt(0).toUpperCase(); - } - font.pixelSize: 10 - color: Theme.widgetTextColor - } - - MouseArea { - id: itemArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - cursorShape: Qt.PointingHandCursor - onClicked: mouse => { - if (!trayItem) - return; - if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) { - trayItem.activate(); - root.menuOpen = false; - return; - } - if (!trayItem.hasMenu) { - const gp = itemArea.mapToGlobal(mouse.x, mouse.y); - root.callContextMenuFallback(trayItem.id, Math.round(gp.x), Math.round(gp.y)); - return; - } - root.showForTrayItem(trayItem, menuContainer, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); - } - } - } - } - } - } - } - - Component { - id: trayMenuComponent - - Rectangle { - id: menuRoot - - property var trayItem: null - property var anchorItem: null - property var parentScreen: null - property bool isAtBottom: false - property bool isVertical: false - property var axis: null - property bool showMenu: false - property var menuHandle: null - - ListModel { - id: entryStack - } - function topEntry() { - return entryStack.count ? entryStack.get(entryStack.count - 1).handle : null; - } - - function showForTrayItem(item, anchor, screen, atBottom, vertical, axisObj) { - trayItem = item; - anchorItem = anchor; - parentScreen = screen; - isAtBottom = atBottom; - isVertical = vertical; - axis = axisObj; - menuHandle = item?.menu; - - if (parentScreen) { - for (var i = 0; i < Quickshell.screens.length; i++) { - const s = Quickshell.screens[i]; - if (s === parentScreen) { - menuWindow.screen = s; - break; - } - } - } - - showMenu = true; - } - - function close() { - showMenu = false; - } - - Connections { - target: menuWindow - function onVisibleChanged() { - if (menuWindow.visible && parentScreen) { - TrayMenuManager.registerMenu(parentScreen.name, menuRoot); - } else if (!menuWindow.visible && parentScreen) { - TrayMenuManager.unregisterMenu(parentScreen.name); - } - } - } - - Component.onDestruction: { - if (parentScreen) { - TrayMenuManager.unregisterMenu(parentScreen.name); - } - } - - Connections { - target: PopoutManager - function onPopoutOpening() { - menuRoot.close(); - } - } - - function closeWithAction() { - close(); - } - - function showSubMenu(entry) { - if (!entry || !entry.hasChildren) - return; - - entryStack.append({ - handle: entry - }); - - const h = entry.menu || entry; - if (h && typeof h.updateLayout === "function") - h.updateLayout(); - - submenuHydrator.menu = h; - submenuHydrator.open(); - Qt.callLater(() => submenuHydrator.close()); - } - - function goBack() { - if (!entryStack.count) - return; - entryStack.remove(entryStack.count - 1); - } - - width: 0 - height: 0 - color: "transparent" - - PanelWindow { - id: menuWindow - - WindowBlur { - targetWindow: menuWindow - blurX: trayMenuContainer.x - blurY: trayMenuContainer.y - blurWidth: menuRoot.showMenu ? trayMenuContainer.width : 0 - blurHeight: menuRoot.showMenu ? trayMenuContainer.height : 0 - blurRadius: Theme.cornerRadius - } - - WlrLayershell.namespace: "dms:tray-menu-window" - visible: menuRoot.showMenu && (menuRoot.trayItem?.hasMenu ?? false) - WlrLayershell.layer: WlrLayershell.Top - WlrLayershell.exclusiveZone: -1 - WlrLayershell.keyboardFocus: { - if (!menuRoot.showMenu) - return WlrKeyboardFocus.None; - if (CompositorService.useHyprlandFocusGrab) - return WlrKeyboardFocus.OnDemand; - return WlrKeyboardFocus.Exclusive; - } - color: "transparent" - - HyprlandFocusGrab { - windows: [menuWindow] - active: CompositorService.useHyprlandFocusGrab && menuRoot.showMenu - } - - anchors { - top: true - left: true - right: true - bottom: true - } - - readonly property real dpr: (typeof CompositorService !== "undefined" && CompositorService.getScreenScale) ? CompositorService.getScreenScale(menuWindow.screen) : (screen?.devicePixelRatio || 1) - property point anchorPos: Qt.point(screen.width / 2, screen.height / 2) - - property var barBounds: { - if (!menuWindow.screen || !root.barConfig) { - return { - "x": 0, - "y": 0, - "width": 0, - "height": 0, - "wingSize": 0 - }; - } - const barPosition = root.axis?.edge === "left" ? 2 : (root.axis?.edge === "right" ? 3 : (root.axis?.edge === "top" ? 0 : 1)); - return SettingsData.getBarBounds(menuWindow.screen, root.barThickness + root.barSpacing, barPosition, root.barConfig); - } - - property real barX: barBounds.x - property real barY: barBounds.y - property real barWidth: barBounds.width - property real barHeight: barBounds.height - - readonly property int barPosition: root.axis?.edge === "left" ? 2 : (root.axis?.edge === "right" ? 3 : (root.axis?.edge === "top" ? 0 : 1)) - readonly property var adjacentBarInfo: menuRoot.parentScreen ? SettingsData.getAdjacentBarInfo(menuRoot.parentScreen, barPosition, root.barConfig) : ({ - "topBar": 0, - "bottomBar": 0, - "leftBar": 0, - "rightBar": 0 - }) - readonly property real effectiveBarSize: root.barThickness + root.barSpacing - - readonly property real maskX: { - const triggeringBarX = (barPosition === 2) ? effectiveBarSize : 0; - const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0; - return Math.max(triggeringBarX, adjacentLeftBar); - } - - readonly property real maskY: { - const triggeringBarY = (barPosition === 0) ? effectiveBarSize : 0; - const adjacentTopBar = adjacentBarInfo?.topBar ?? 0; - return Math.max(triggeringBarY, adjacentTopBar); - } - - readonly property real maskWidth: { - const triggeringBarRight = (barPosition === 3) ? effectiveBarSize : 0; - const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0; - const rightExclusion = Math.max(triggeringBarRight, adjacentRightBar); - return Math.max(100, width - maskX - rightExclusion); - } - - readonly property real maskHeight: { - const triggeringBarBottom = (barPosition === 1) ? effectiveBarSize : 0; - const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0; - const bottomExclusion = Math.max(triggeringBarBottom, adjacentBottomBar); - return Math.max(100, height - maskY - bottomExclusion); - } - - mask: Region { - item: Rectangle { - x: menuWindow.maskX - y: menuWindow.maskY - width: menuWindow.maskWidth - height: menuWindow.maskHeight - } - } - - onVisibleChanged: { - if (visible) { - updatePosition(); - if (root.useOverflowPopup) - root.menuOpen = false; - PopoutManager.closeAllPopouts(); - ModalManager.closeAllModalsExcept(null); - } - } - - MouseArea { - x: menuWindow.maskX - y: menuWindow.maskY - width: menuWindow.maskWidth - height: menuWindow.maskHeight - z: -1 - enabled: menuRoot.showMenu - acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton - onClicked: mouse => { - const clickX = mouse.x + menuWindow.maskX; - const clickY = mouse.y + menuWindow.maskY; - const outsideContent = clickX < trayMenuContainer.x || clickX > trayMenuContainer.x + trayMenuContainer.width || clickY < trayMenuContainer.y || clickY > trayMenuContainer.y + trayMenuContainer.height; - - if (!outsideContent) - return; - menuRoot.close(); - } - } - - FocusScope { - id: menuFocusScope - anchors.fill: parent - focus: true - - Keys.onEscapePressed: { - if (entryStack.count > 0) { - menuRoot.goBack(); - } else { - menuRoot.close(); - } - } - } - - function updatePosition() { - const targetItem = (typeof menuRoot !== "undefined" && menuRoot.anchorItem) ? menuRoot.anchorItem : root; - - const isFromOverflowMenu = targetItem.objectName === "overflowMenuContainer"; - - if (isFromOverflowMenu) { - if (menuRoot.isVertical) { - const edge = menuRoot.axis?.edge; - let targetX = edge === "left" ? root.barThickness + root.barSpacing + Theme.popupDistance : screen.width - (root.barThickness + root.barSpacing + Theme.popupDistance); - const targetY = targetItem.y + targetItem.height / 2; - anchorPos = Qt.point(targetX, targetY); - } else { - const targetX = targetItem.x + targetItem.width / 2; - let targetY = menuRoot.isAtBottom ? screen.height - (root.barThickness + root.barSpacing + (barConfig?.bottomGap ?? 0) + Theme.popupDistance) : root.barThickness + root.barSpacing + (barConfig?.bottomGap ?? 0) + Theme.popupDistance; - anchorPos = Qt.point(targetX, targetY); - } - } else { - const globalPos = targetItem.mapToGlobal(0, 0); - const screenX = screen.x || 0; - const screenY = screen.y || 0; - const relativeX = globalPos.x - screenX; - const relativeY = globalPos.y - screenY; - - if (menuRoot.isVertical) { - const edge = menuRoot.axis?.edge; - let targetX = edge === "left" ? root.barThickness + root.barSpacing + Theme.popupDistance : screen.width - (root.barThickness + root.barSpacing + Theme.popupDistance); - const adjustedY = relativeY + targetItem.height / 2 + root.minTooltipY; - anchorPos = Qt.point(targetX, adjustedY); - } else { - let targetY = menuRoot.isAtBottom ? screen.height - (root.barThickness + root.barSpacing + (barConfig?.bottomGap ?? 0) + Theme.popupDistance) : root.barThickness + root.barSpacing + (barConfig?.bottomGap ?? 0) + Theme.popupDistance; - anchorPos = Qt.point(relativeX + targetItem.width / 2, targetY); - } - } - } - - Item { - id: trayMenuContainer - - readonly property real rawWidth: Math.min(500, Math.max(250, menuColumn.implicitWidth + Theme.spacingS * 2)) - readonly property real rawHeight: Math.max(40, menuColumn.implicitHeight + Theme.spacingS * 2) - - readonly property real alignedWidth: Theme.px(rawWidth, menuWindow.dpr) - readonly property real alignedHeight: Theme.px(rawHeight, menuWindow.dpr) - - width: alignedWidth - height: alignedHeight - - x: Theme.snap((() => { - if (menuRoot.isVertical) { - const edge = menuRoot.axis?.edge; - if (edge === "left") { - const targetX = menuWindow.anchorPos.x; - return Math.min(menuWindow.screen.width - alignedWidth - 10, targetX); - } else { - const targetX = menuWindow.anchorPos.x - alignedWidth; - return Math.max(10, targetX); - } - } else { - const left = 10; - const right = menuWindow.width - alignedWidth - 10; - const want = menuWindow.anchorPos.x - alignedWidth / 2; - return Math.max(left, Math.min(right, want)); - } - })(), menuWindow.dpr) - - y: Theme.snap((() => { - if (menuRoot.isVertical) { - const top = Math.max(menuWindow.barY, 10); - const bottom = menuWindow.height - alignedHeight - 10; - const want = menuWindow.anchorPos.y - alignedHeight / 2; - return Math.max(top, Math.min(bottom, want)); - } else { - if (menuRoot.isAtBottom) { - const targetY = menuWindow.anchorPos.y - alignedHeight; - return Math.max(10, targetY); - } else { - const targetY = menuWindow.anchorPos.y; - return Math.min(menuWindow.screen.height - alignedHeight - 10, targetY); - } - } - })(), menuWindow.dpr) - - opacity: menuRoot.showMenu ? 1 : 0 - scale: menuRoot.showMenu ? 1 : 0.85 - - Behavior on opacity { - NumberAnimation { - duration: Theme.mediumDuration - easing.type: Theme.emphasizedEasing - } - } - - Behavior on scale { - NumberAnimation { - duration: Theme.mediumDuration - easing.type: Theme.emphasizedEasing - } - } - - ElevationShadow { - id: menuBgShadowLayer - anchors.fill: parent - level: Theme.elevationLevel3 - direction: root.effectiveShadowDirection - fallbackOffset: 6 - targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) - targetRadius: Theme.cornerRadius - sourceRect.antialiasing: true - shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled - layer.smooth: true - layer.textureSize: Qt.size(Math.round(width * menuWindow.dpr), Math.round(height * menuWindow.dpr)) - layer.textureMirroring: ShaderEffectSource.MirrorVertically - } - - Rectangle { - anchors.fill: parent - color: "transparent" - radius: Theme.cornerRadius - border.color: BlurService.borderColor - border.width: BlurService.borderWidth - z: 100 - } - - QsMenuAnchor { - id: submenuHydrator - anchor.window: menuWindow - } - - QsMenuOpener { - id: rootOpener - menu: menuRoot.menuHandle - } - - QsMenuOpener { - id: subOpener - menu: { - const e = menuRoot.topEntry(); - return e ? (e.menu || e) : null; - } - } - - Column { - id: menuColumn - - width: parent.width - Theme.spacingS * 2 - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.topMargin: Theme.spacingS - spacing: 1 - - Rectangle { - visible: entryStack.count === 0 - width: parent.width - height: 28 - radius: Theme.cornerRadius - color: visibilityToggleArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : Theme.withAlpha(Theme.surfaceContainer, 0) - - StyledText { - anchors.left: parent.left - anchors.leftMargin: Theme.spacingS - anchors.verticalCenter: parent.verticalCenter - text: menuRoot.trayItem?.id || "Unknown" - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceTextMedium - elide: Text.ElideMiddle - width: parent.width - Theme.spacingS * 2 - 24 - } - - DankIcon { - anchors.right: parent.right - anchors.rightMargin: Theme.spacingS - anchors.verticalCenter: parent.verticalCenter - name: SessionData.isHiddenTrayId(root.getTrayItemKey(menuRoot.trayItem)) ? "visibility" : "visibility_off" - size: 16 - color: Theme.widgetTextColor - } - - MouseArea { - id: visibilityToggleArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - const itemKey = root.getTrayItemKey(menuRoot.trayItem); - if (!itemKey) - return; - if (SessionData.isHiddenTrayId(itemKey)) { - SessionData.showTrayId(itemKey); - } else { - SessionData.hideTrayId(itemKey); - } - menuRoot.closeWithAction(); - } - } - } - - Rectangle { - visible: entryStack.count === 0 - width: parent.width - height: 1 - color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) - } - - Rectangle { - visible: entryStack.count > 0 - width: parent.width - height: 28 - radius: Theme.cornerRadius - color: backArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : Theme.withAlpha(Theme.surfaceContainer, 0) - - Row { - anchors.left: parent.left - anchors.leftMargin: Theme.spacingS - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingXS - - DankIcon { - name: "arrow_back" - size: 16 - color: Theme.widgetTextColor - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Back") - font.pixelSize: Theme.fontSizeSmall - color: Theme.widgetTextColor - anchors.verticalCenter: parent.verticalCenter - } - } - - MouseArea { - id: backArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: menuRoot.goBack() - } - } - - Rectangle { - visible: entryStack.count > 0 - width: parent.width - height: 1 - color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) - } - - Repeater { - model: entryStack.count ? (subOpener.children ? subOpener.children : (menuRoot.topEntry()?.children || [])) : rootOpener.children - - Rectangle { - property var menuEntry: modelData - - width: menuColumn.width - height: menuEntry?.isSeparator ? 1 : 28 - radius: menuEntry?.isSeparator ? 0 : Theme.cornerRadius - color: { - if (menuEntry?.isSeparator) - return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2); - return itemArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : Theme.withAlpha(Theme.surfaceContainer, 0); - } - - MouseArea { - id: itemArea - anchors.fill: parent - hoverEnabled: true - enabled: !menuEntry?.isSeparator && (menuEntry?.enabled !== false) - cursorShape: Qt.PointingHandCursor - - onClicked: { - if (!menuEntry || menuEntry.isSeparator) - return; - if (menuEntry.hasChildren) { - menuRoot.showSubMenu(menuEntry); - return; - } - - if (typeof menuEntry.activate === "function") { - menuEntry.activate(); - } else if (typeof menuEntry.triggered === "function") { - menuEntry.triggered(); - } - Qt.createQmlObject('import QtQuick; Timer { interval: 80; running: true; repeat: false; onTriggered: menuRoot.closeWithAction() }', menuRoot); - } - } - - Row { - anchors.left: parent.left - anchors.leftMargin: Theme.spacingS - anchors.right: parent.right - anchors.rightMargin: Theme.spacingS - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingXS - visible: !menuEntry?.isSeparator - - Rectangle { - width: 16 - height: 16 - anchors.verticalCenter: parent.verticalCenter - visible: menuEntry?.buttonType !== undefined && menuEntry.buttonType !== 0 - radius: menuEntry?.buttonType === 2 ? 8 : 2 - border.width: 1 - border.color: Theme.outline - color: "transparent" - - Rectangle { - anchors.centerIn: parent - width: parent.width - 6 - height: parent.height - 6 - radius: parent.radius - 3 - color: Theme.primary - visible: menuEntry?.checkState === 2 - } - - DankIcon { - anchors.centerIn: parent - name: "check" - size: 10 - color: Theme.primaryText - visible: menuEntry?.buttonType === 1 && menuEntry?.checkState === 2 - } - } - - Item { - width: 16 - height: 16 - anchors.verticalCenter: parent.verticalCenter - visible: (menuEntry?.icon ?? "") !== "" - - Image { - anchors.fill: parent - source: menuEntry?.icon || "" - sourceSize.width: 16 - sourceSize.height: 16 - fillMode: Image.PreserveAspectFit - smooth: true - } - } - - StyledText { - text: menuEntry?.text || "" - font.pixelSize: Theme.fontSizeSmall - color: (menuEntry?.enabled !== false) ? Theme.surfaceText : Theme.surfaceTextMedium - elide: Text.ElideRight - anchors.verticalCenter: parent.verticalCenter - width: Math.max(150, parent.width - 64) - wrapMode: Text.NoWrap - } - - Item { - width: 16 - height: 16 - anchors.verticalCenter: parent.verticalCenter - - DankIcon { - anchors.centerIn: parent - name: "chevron_right" - size: 14 - color: Theme.widgetTextColor - visible: menuEntry?.hasChildren ?? false - } - } - } - } - } - } - } - } - } - } - - function showForTrayItem(item, anchor, screen, atBottom, vertical, axisObj) { - if (!screen) - return; - if (currentTrayMenu) { - currentTrayMenu.showMenu = false; - currentTrayMenu.destroy(); - currentTrayMenu = null; - } - - PopoutManager.closeAllPopouts(); - ModalManager.closeAllModalsExcept(null); - - currentTrayMenu = trayMenuComponent.createObject(null); - if (!currentTrayMenu) - return; - currentTrayMenu.showForTrayItem(item, anchor, screen, atBottom, vertical ?? false, axisObj); - } -} |