systray
This commit is contained in:
@@ -5,6 +5,7 @@ import Quickshell.Hyprland
|
|||||||
|
|
||||||
import "Windows/QuickSettings"
|
import "Windows/QuickSettings"
|
||||||
import "Windows/AppLauncher"
|
import "Windows/AppLauncher"
|
||||||
|
import "Windows/PowerMenu"
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: overlay
|
id: overlay
|
||||||
@@ -76,4 +77,9 @@ PanelWindow {
|
|||||||
id: appLauncherWindow
|
id: appLauncherWindow
|
||||||
manager: ShellStateManager
|
manager: ShellStateManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PowerMenu {
|
||||||
|
id: powerMenuWindow
|
||||||
|
manager: ShellStateManager
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ QtObject {
|
|||||||
context = "appLauncher";
|
context = "appLauncher";
|
||||||
else if (ShellStateManager.quickSettingsOpen)
|
else if (ShellStateManager.quickSettingsOpen)
|
||||||
context = "quickSettings";
|
context = "quickSettings";
|
||||||
|
else if (ShellStateManager.powerMenuOpen)
|
||||||
|
context = "powerMenu";
|
||||||
var handler = handlers[context];
|
var handler = handlers[context];
|
||||||
if (handler) {
|
if (handler) {
|
||||||
var handled = handler(key);
|
var handled = handler(key);
|
||||||
@@ -47,6 +49,8 @@ QtObject {
|
|||||||
ShellStateManager.closeAppLauncher();
|
ShellStateManager.closeAppLauncher();
|
||||||
} else if (context === "quickSettings") {
|
} else if (context === "quickSettings") {
|
||||||
ShellStateManager.closeQuickSettings();
|
ShellStateManager.closeQuickSettings();
|
||||||
|
} else if (context === "powerMenu") {
|
||||||
|
ShellStateManager.closePowerMenu();
|
||||||
} else if (context === "topbar") {
|
} else if (context === "topbar") {
|
||||||
ShellStateManager.closeShell();
|
ShellStateManager.closeShell();
|
||||||
Hyprland.dispatch("submap reset");
|
Hyprland.dispatch("submap reset");
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ QtObject {
|
|||||||
property bool shellOpen: false
|
property bool shellOpen: false
|
||||||
property bool quickSettingsOpen: false
|
property bool quickSettingsOpen: false
|
||||||
property bool appLauncherOpen: false
|
property bool appLauncherOpen: false
|
||||||
|
property bool powerMenuOpen: false
|
||||||
property var appUsageCounts: ({})
|
property var appUsageCounts: ({})
|
||||||
property string appUsagePath: {
|
property string appUsagePath: {
|
||||||
var homeDir = Quickshell.env("HOME");
|
var homeDir = Quickshell.env("HOME");
|
||||||
@@ -26,6 +27,8 @@ QtObject {
|
|||||||
signal quickSettingsClosed
|
signal quickSettingsClosed
|
||||||
signal appLauncherOpened
|
signal appLauncherOpened
|
||||||
signal appLauncherClosed
|
signal appLauncherClosed
|
||||||
|
signal powerMenuOpened
|
||||||
|
signal powerMenuClosed
|
||||||
signal windowRequested(string name, var payload)
|
signal windowRequested(string name, var payload)
|
||||||
|
|
||||||
function openShell() {
|
function openShell() {
|
||||||
@@ -47,6 +50,9 @@ QtObject {
|
|||||||
if (appLauncherOpen) {
|
if (appLauncherOpen) {
|
||||||
closeAppLauncher();
|
closeAppLauncher();
|
||||||
}
|
}
|
||||||
|
if (powerMenuOpen) {
|
||||||
|
closePowerMenu();
|
||||||
|
}
|
||||||
shellOpen = false;
|
shellOpen = false;
|
||||||
shellClosed();
|
shellClosed();
|
||||||
}
|
}
|
||||||
@@ -72,6 +78,9 @@ QtObject {
|
|||||||
if (appLauncherOpen) {
|
if (appLauncherOpen) {
|
||||||
closeAppLauncher();
|
closeAppLauncher();
|
||||||
}
|
}
|
||||||
|
if (powerMenuOpen) {
|
||||||
|
closePowerMenu();
|
||||||
|
}
|
||||||
if (!quickSettingsOpen) {
|
if (!quickSettingsOpen) {
|
||||||
quickSettingsOpen = true;
|
quickSettingsOpen = true;
|
||||||
quickSettingsOpened();
|
quickSettingsOpened();
|
||||||
@@ -97,6 +106,9 @@ QtObject {
|
|||||||
if (quickSettingsOpen) {
|
if (quickSettingsOpen) {
|
||||||
closeQuickSettings();
|
closeQuickSettings();
|
||||||
}
|
}
|
||||||
|
if (powerMenuOpen) {
|
||||||
|
closePowerMenu();
|
||||||
|
}
|
||||||
if (!appLauncherOpen) {
|
if (!appLauncherOpen) {
|
||||||
appLauncherOpen = true;
|
appLauncherOpen = true;
|
||||||
appLauncherOpened();
|
appLauncherOpened();
|
||||||
@@ -116,6 +128,34 @@ QtObject {
|
|||||||
appLauncherOpen ? closeAppLauncher() : openAppLauncher(payload);
|
appLauncherOpen ? closeAppLauncher() : openAppLauncher(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openPowerMenu(payload) {
|
||||||
|
var normalizedPayload = payload || ({});
|
||||||
|
console.log("ShellStateManager: openPowerMenu", normalizedPayload);
|
||||||
|
if (quickSettingsOpen) {
|
||||||
|
closeQuickSettings();
|
||||||
|
}
|
||||||
|
if (appLauncherOpen) {
|
||||||
|
closeAppLauncher();
|
||||||
|
}
|
||||||
|
if (!powerMenuOpen) {
|
||||||
|
powerMenuOpen = true;
|
||||||
|
powerMenuOpened();
|
||||||
|
}
|
||||||
|
requestWindow("powerMenu", normalizedPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePowerMenu() {
|
||||||
|
if (powerMenuOpen) {
|
||||||
|
console.log("ShellStateManager: closePowerMenu");
|
||||||
|
powerMenuOpen = false;
|
||||||
|
powerMenuClosed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePowerMenu(payload) {
|
||||||
|
powerMenuOpen ? closePowerMenu() : openPowerMenu(payload);
|
||||||
|
}
|
||||||
|
|
||||||
function bumpAppUsage(id) {
|
function bumpAppUsage(id) {
|
||||||
if (!id)
|
if (!id)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ Item {
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (iconSources[selectedIndex] === "power.png") {
|
||||||
|
ShellStateManager.openPowerMenu({
|
||||||
|
source: "Topbar"
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -90,7 +96,7 @@ Item {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
selected: topbar.selectedIndex == repeatitem.index
|
selected: topbar.selectedIndex == repeatitem.index
|
||||||
iconSource: topbar.iconSources[repeatitem.index]
|
iconSource: topbar.iconSources[repeatitem.index]
|
||||||
showSoul: !topbar.manager.quickSettingsOpen && !topbar.manager.appLauncherOpen
|
showSoul: !topbar.manager.quickSettingsOpen && !topbar.manager.appLauncherOpen && !topbar.manager.powerMenuOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -107,6 +113,11 @@ Item {
|
|||||||
source: "Topbar"
|
source: "Topbar"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (topbar.iconSources[repeatitem.index] === "power.png") {
|
||||||
|
ShellStateManager.openPowerMenu({
|
||||||
|
source: "Topbar"
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
44
Shell/Windows/PowerMenu/PowerMenu.qml
Normal file
44
Shell/Windows/PowerMenu/PowerMenu.qml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import QtQuick
|
||||||
|
import "../.."
|
||||||
|
import "../../Window" as ShellWindow
|
||||||
|
|
||||||
|
ShellWindow.Window {
|
||||||
|
id: powerMenuWindow
|
||||||
|
property var manager: ShellStateManager
|
||||||
|
|
||||||
|
property var powerMenuApp: null
|
||||||
|
|
||||||
|
width: 1217 + 52
|
||||||
|
height: 767 + 52
|
||||||
|
visible: manager ? manager.powerMenuOpen : false
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: powerMenuLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
asynchronous: true
|
||||||
|
source: "./PowerMenuApp.qml"
|
||||||
|
onLoaded: {
|
||||||
|
powerMenuApp = item;
|
||||||
|
if (powerMenuApp)
|
||||||
|
powerMenuApp.manager = manager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onManagerChanged: {
|
||||||
|
if (powerMenuApp)
|
||||||
|
powerMenuApp.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: powerMenuKeyHandler
|
||||||
|
function handle(key) {
|
||||||
|
if (powerMenuApp && powerMenuApp.handleKey)
|
||||||
|
return powerMenuApp.handleKey(key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: ShellInputManager.registerHandler("powerMenu", handle)
|
||||||
|
Component.onDestruction: ShellInputManager.unregisterHandler("powerMenu")
|
||||||
|
}
|
||||||
|
}
|
||||||
522
Shell/Windows/PowerMenu/PowerMenuApp.qml
Normal file
522
Shell/Windows/PowerMenu/PowerMenuApp.qml
Normal file
@@ -0,0 +1,522 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
width: parent ? parent.width : 1280
|
||||||
|
height: parent ? parent.height : 820
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
property ShellStateManager manager: null
|
||||||
|
|
||||||
|
// ITEM/APPS-like layout constants
|
||||||
|
property int menuTop: 140
|
||||||
|
property int lineHeight: 79
|
||||||
|
property int iconSize: 36
|
||||||
|
property int textStartX: 239
|
||||||
|
property int textRightPadding: 96
|
||||||
|
property int visibleRows: Math.max(1, Math.floor((height - menuTop - 64) / lineHeight))
|
||||||
|
|
||||||
|
property bool inSubmenu: false
|
||||||
|
property int traySelection: 0
|
||||||
|
property int submenuSelection: 0
|
||||||
|
property int scrollOffset: 0
|
||||||
|
property var menuStack: []
|
||||||
|
property var currentMenuHandle: null
|
||||||
|
property var submenuSourceTrayItem: null
|
||||||
|
property int submenuPollTicks: 0
|
||||||
|
|
||||||
|
readonly property int activeSelection: inSubmenu ? submenuSelection : traySelection
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: submenuData
|
||||||
|
}
|
||||||
|
|
||||||
|
QsMenuOpener {
|
||||||
|
id: opener
|
||||||
|
menu: root.currentMenuHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw source entries from current DBus menu (hidden)
|
||||||
|
Repeater {
|
||||||
|
id: submenuSourceRepeater
|
||||||
|
model: opener.children
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
visible: false
|
||||||
|
width: 0
|
||||||
|
height: 0
|
||||||
|
property var entry: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: submenuPollTimer
|
||||||
|
interval: 120
|
||||||
|
repeat: true
|
||||||
|
running: false
|
||||||
|
onTriggered: {
|
||||||
|
if (!root.currentMenuHandle && root.submenuSourceTrayItem && root.submenuSourceTrayItem.menu)
|
||||||
|
root.currentMenuHandle = root.submenuSourceTrayItem.menu;
|
||||||
|
|
||||||
|
root.rebuildSubmenuData();
|
||||||
|
root.submenuPollTicks = root.submenuPollTicks + 1;
|
||||||
|
if (submenuData.count > 0 || root.submenuPollTicks >= 20)
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAll() {
|
||||||
|
if (manager && manager.closeShell)
|
||||||
|
manager.closeShell();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clamp(v, lo, hi) {
|
||||||
|
return Math.max(lo, Math.min(hi, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
function trayCount() {
|
||||||
|
return trayRepeater ? trayRepeater.count : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function submenuCount() {
|
||||||
|
return submenuData.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentCount() {
|
||||||
|
return inSubmenu ? submenuCount() : trayCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapIndex(i) {
|
||||||
|
const length = currentCount();
|
||||||
|
if (length === 0)
|
||||||
|
return 0;
|
||||||
|
return (i + length) % length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedTrayDelegate() {
|
||||||
|
if (trayCount() <= 0)
|
||||||
|
return null;
|
||||||
|
return trayRepeater.itemAt(traySelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedTrayItem() {
|
||||||
|
const delegate = selectedTrayDelegate();
|
||||||
|
return delegate ? delegate.trayItem : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedMenuEntry() {
|
||||||
|
if (submenuSelection < 0 || submenuSelection >= submenuData.count)
|
||||||
|
return null;
|
||||||
|
const row = submenuData.get(submenuSelection);
|
||||||
|
return row ? row.entry : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSelection() {
|
||||||
|
const length = currentCount();
|
||||||
|
if (length <= 0) {
|
||||||
|
if (inSubmenu)
|
||||||
|
submenuSelection = 0;
|
||||||
|
else
|
||||||
|
traySelection = 0;
|
||||||
|
scrollOffset = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inSubmenu)
|
||||||
|
submenuSelection = clamp(submenuSelection, 0, length - 1);
|
||||||
|
else
|
||||||
|
traySelection = clamp(traySelection, 0, length - 1);
|
||||||
|
|
||||||
|
const currentSelection = activeSelection;
|
||||||
|
const maxOffset = Math.max(0, length - visibleRows);
|
||||||
|
|
||||||
|
if (currentSelection < scrollOffset)
|
||||||
|
scrollOffset = currentSelection;
|
||||||
|
else if (currentSelection >= scrollOffset + visibleRows)
|
||||||
|
scrollOffset = Math.max(0, currentSelection - visibleRows + 1);
|
||||||
|
|
||||||
|
scrollOffset = clamp(scrollOffset, 0, maxOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebuildSubmenuData() {
|
||||||
|
submenuData.clear();
|
||||||
|
|
||||||
|
const count = submenuSourceRepeater ? submenuSourceRepeater.count : 0;
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const sourceItem = submenuSourceRepeater.itemAt(i);
|
||||||
|
const entry = sourceItem ? sourceItem.entry : null;
|
||||||
|
if (!entry || entry.isSeparator)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
submenuData.append({
|
||||||
|
entry: entry
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
submenuSelection = clamp(submenuSelection, 0, Math.max(0, submenuData.count - 1));
|
||||||
|
normalizeSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
function enterSubmenu(menuHandle) {
|
||||||
|
currentMenuHandle = menuHandle;
|
||||||
|
inSubmenu = true;
|
||||||
|
submenuSelection = 0;
|
||||||
|
scrollOffset = 0;
|
||||||
|
submenuData.clear();
|
||||||
|
submenuPollTicks = 0;
|
||||||
|
Qt.callLater(function () {
|
||||||
|
rebuildSubmenuData();
|
||||||
|
submenuPollTimer.restart();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSelectedTraySubmenu() {
|
||||||
|
const trayItem = selectedTrayItem();
|
||||||
|
if (!trayItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!trayItem.hasMenu) {
|
||||||
|
trayItem.activate();
|
||||||
|
closeAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
menuStack = [];
|
||||||
|
submenuSourceTrayItem = trayItem;
|
||||||
|
enterSubmenu(trayItem.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerSelectedSubmenuEntry() {
|
||||||
|
const entry = selectedMenuEntry();
|
||||||
|
if (!entry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (entry.hasChildren) {
|
||||||
|
const stackCopy = menuStack.slice(0);
|
||||||
|
stackCopy.push(currentMenuHandle);
|
||||||
|
menuStack = stackCopy;
|
||||||
|
submenuSourceTrayItem = null;
|
||||||
|
enterSubmenu(entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.triggered();
|
||||||
|
closeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function backAction() {
|
||||||
|
if (inSubmenu) {
|
||||||
|
if (menuStack.length > 0) {
|
||||||
|
const stackCopy = menuStack.slice(0);
|
||||||
|
const parentHandle = stackCopy.pop();
|
||||||
|
menuStack = stackCopy;
|
||||||
|
submenuSourceTrayItem = null;
|
||||||
|
enterSubmenu(parentHandle);
|
||||||
|
} else {
|
||||||
|
inSubmenu = false;
|
||||||
|
currentMenuHandle = null;
|
||||||
|
submenuSourceTrayItem = null;
|
||||||
|
submenuData.clear();
|
||||||
|
submenuPollTimer.stop();
|
||||||
|
scrollOffset = 0;
|
||||||
|
normalizeSelection();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manager && manager.closePowerMenu)
|
||||||
|
manager.closePowerMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKey(key) {
|
||||||
|
switch (key) {
|
||||||
|
case Qt.Key_Up:
|
||||||
|
if (inSubmenu)
|
||||||
|
submenuSelection = wrapIndex(submenuSelection - 1);
|
||||||
|
else
|
||||||
|
traySelection = wrapIndex(traySelection - 1);
|
||||||
|
normalizeSelection();
|
||||||
|
return true;
|
||||||
|
case Qt.Key_Down:
|
||||||
|
if (inSubmenu)
|
||||||
|
submenuSelection = wrapIndex(submenuSelection + 1);
|
||||||
|
else
|
||||||
|
traySelection = wrapIndex(traySelection + 1);
|
||||||
|
normalizeSelection();
|
||||||
|
return true;
|
||||||
|
case Qt.Key_Z:
|
||||||
|
case Qt.Key_Return:
|
||||||
|
case Qt.Key_Enter:
|
||||||
|
if (inSubmenu)
|
||||||
|
triggerSelectedSubmenuEntry();
|
||||||
|
else
|
||||||
|
openSelectedTraySubmenu();
|
||||||
|
return true;
|
||||||
|
case Qt.Key_X:
|
||||||
|
case Qt.Key_Shift:
|
||||||
|
case Qt.Key_Escape:
|
||||||
|
backAction();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: manager
|
||||||
|
ignoreUnknownSignals: true
|
||||||
|
function onPowerMenuOpened() {
|
||||||
|
root.inSubmenu = false;
|
||||||
|
root.currentMenuHandle = null;
|
||||||
|
root.submenuSourceTrayItem = null;
|
||||||
|
root.menuStack = [];
|
||||||
|
root.submenuPollTicks = 0;
|
||||||
|
submenuPollTimer.stop();
|
||||||
|
submenuData.clear();
|
||||||
|
root.scrollOffset = 0;
|
||||||
|
root.traySelection = 0;
|
||||||
|
root.submenuSelection = 0;
|
||||||
|
root.normalizeSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SystemTray.items
|
||||||
|
ignoreUnknownSignals: true
|
||||||
|
function onCountChanged() {
|
||||||
|
root.normalizeSelection();
|
||||||
|
}
|
||||||
|
function onModelReset() {
|
||||||
|
root.normalizeSelection();
|
||||||
|
}
|
||||||
|
function onRowsInserted() {
|
||||||
|
root.normalizeSelection();
|
||||||
|
}
|
||||||
|
function onRowsRemoved() {
|
||||||
|
root.normalizeSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: opener.children
|
||||||
|
ignoreUnknownSignals: true
|
||||||
|
function onCountChanged() {
|
||||||
|
root.rebuildSubmenuData();
|
||||||
|
}
|
||||||
|
function onModelReset() {
|
||||||
|
root.rebuildSubmenuData();
|
||||||
|
}
|
||||||
|
function onRowsInserted() {
|
||||||
|
root.rebuildSubmenuData();
|
||||||
|
}
|
||||||
|
function onRowsRemoved() {
|
||||||
|
root.rebuildSubmenuData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: inSubmenu ? "OPTIONS" : "POWER"
|
||||||
|
font.family: "8bitoperator JVE"
|
||||||
|
font.pixelSize: 71
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
font.hintingPreference: Font.PreferNoHinting
|
||||||
|
smooth: false
|
||||||
|
antialiasing: false
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: "#ffffff"
|
||||||
|
y: 32
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: trayRepeater
|
||||||
|
model: SystemTray.items
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
width: root.width
|
||||||
|
height: root.lineHeight
|
||||||
|
x: 0
|
||||||
|
y: root.menuTop + (index - root.scrollOffset) * root.lineHeight
|
||||||
|
visible: !root.inSubmenu && index >= root.scrollOffset && index < root.scrollOffset + root.visibleRows
|
||||||
|
|
||||||
|
property var trayItem: modelData
|
||||||
|
|
||||||
|
Text {
|
||||||
|
x: root.textStartX
|
||||||
|
y: 0
|
||||||
|
text: trayItem && trayItem.tooltipTitle ? trayItem.tooltipTitle : (trayItem ? trayItem.title : "")
|
||||||
|
width: root.width - root.textStartX - root.textRightPadding
|
||||||
|
font.family: "8bitoperator JVE"
|
||||||
|
font.pixelSize: 71
|
||||||
|
font.letterSpacing: 1
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
font.hintingPreference: Font.PreferNoHinting
|
||||||
|
smooth: false
|
||||||
|
antialiasing: false
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: root.activeSelection == index ? "#fefe00" : "#ffffff"
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: trayIcon
|
||||||
|
x: 182
|
||||||
|
y: 8 + 14
|
||||||
|
implicitSize: root.iconSize
|
||||||
|
source: trayItem ? trayItem.icon : ""
|
||||||
|
asynchronous: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "../QuickSettings/soul.png"
|
||||||
|
width: 36
|
||||||
|
height: 36
|
||||||
|
x: trayIcon.x
|
||||||
|
y: trayIcon.y
|
||||||
|
opacity: 0.7
|
||||||
|
visible: root.activeSelection == index
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: {
|
||||||
|
root.traySelection = index;
|
||||||
|
root.normalizeSelection();
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
root.traySelection = index;
|
||||||
|
root.normalizeSelection();
|
||||||
|
root.openSelectedTraySubmenu();
|
||||||
|
}
|
||||||
|
onWheel: function (wheel) {
|
||||||
|
if (!trayItem)
|
||||||
|
return;
|
||||||
|
const horizontal = Math.abs(wheel.angleDelta.x) > Math.abs(wheel.angleDelta.y);
|
||||||
|
const delta = horizontal ? wheel.angleDelta.x : wheel.angleDelta.y;
|
||||||
|
trayItem.scroll(delta, horizontal);
|
||||||
|
wheel.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: submenuRepeater
|
||||||
|
model: submenuData
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
property var entry: model.entry
|
||||||
|
width: root.width
|
||||||
|
height: root.lineHeight
|
||||||
|
x: 0
|
||||||
|
y: root.menuTop + (index - root.scrollOffset) * root.lineHeight
|
||||||
|
visible: root.inSubmenu && index >= root.scrollOffset && index < root.scrollOffset + root.visibleRows
|
||||||
|
|
||||||
|
Text {
|
||||||
|
x: root.textStartX
|
||||||
|
y: 0
|
||||||
|
width: root.width - root.textStartX - root.textRightPadding
|
||||||
|
text: {
|
||||||
|
if (!entry)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
let prefix = "";
|
||||||
|
if (entry.buttonType === QsMenuButtonType.CheckBox)
|
||||||
|
prefix = entry.checkState === Qt.Checked ? "[x] " : "[ ] ";
|
||||||
|
else if (entry.buttonType === QsMenuButtonType.RadioButton)
|
||||||
|
prefix = entry.checkState === Qt.Checked ? "(x) " : "( ) ";
|
||||||
|
|
||||||
|
return prefix + String(entry.text || "") + (entry.hasChildren ? " >" : "");
|
||||||
|
}
|
||||||
|
font.family: "8bitoperator JVE"
|
||||||
|
font.pixelSize: 71
|
||||||
|
font.letterSpacing: 1
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
font.hintingPreference: Font.PreferNoHinting
|
||||||
|
smooth: false
|
||||||
|
antialiasing: false
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: root.activeSelection == index ? "#fefe00" : "#ffffff"
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "../QuickSettings/soul.png"
|
||||||
|
width: 36
|
||||||
|
height: 36
|
||||||
|
x: 182
|
||||||
|
y: 8 + 14
|
||||||
|
opacity: 0.7
|
||||||
|
visible: root.activeSelection == index
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: {
|
||||||
|
root.submenuSelection = index;
|
||||||
|
root.normalizeSelection();
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
root.submenuSelection = index;
|
||||||
|
root.normalizeSelection();
|
||||||
|
root.triggerSelectedSubmenuEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WheelHandler {
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
onWheel: function (event) {
|
||||||
|
const count = root.currentCount();
|
||||||
|
if (count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const delta = event.angleDelta.y > 0 ? -1 : 1;
|
||||||
|
if (root.inSubmenu)
|
||||||
|
root.submenuSelection = root.wrapIndex(root.submenuSelection + delta);
|
||||||
|
else
|
||||||
|
root.traySelection = root.wrapIndex(root.traySelection + delta);
|
||||||
|
|
||||||
|
root.normalizeSelection();
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
visible: !root.inSubmenu && trayCount() === 0
|
||||||
|
text: "NO TRAY ITEMS"
|
||||||
|
font.family: "8bitoperator JVE"
|
||||||
|
font.pixelSize: 54
|
||||||
|
font.letterSpacing: 1
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
font.hintingPreference: Font.PreferNoHinting
|
||||||
|
smooth: false
|
||||||
|
antialiasing: false
|
||||||
|
color: "#ffffff"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
y: 302
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
visible: root.inSubmenu && !submenuPollTimer.running && submenuCount() === 0
|
||||||
|
text: "NO OPTIONS"
|
||||||
|
font.family: "8bitoperator JVE"
|
||||||
|
font.pixelSize: 54
|
||||||
|
font.letterSpacing: 1
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
font.hintingPreference: Font.PreferNoHinting
|
||||||
|
smooth: false
|
||||||
|
antialiasing: false
|
||||||
|
color: "#ffffff"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
y: 302
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: normalizeSelection()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user