feat: app launcher
This commit is contained in:
@@ -4,6 +4,7 @@ import Quickshell.Wayland
|
|||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
import "Windows/QuickSettings"
|
import "Windows/QuickSettings"
|
||||||
|
import "Windows/AppLauncher"
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: overlay
|
id: overlay
|
||||||
@@ -70,4 +71,9 @@ PanelWindow {
|
|||||||
id: quickSettingsWindow
|
id: quickSettingsWindow
|
||||||
manager: ShellStateManager
|
manager: ShellStateManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppLauncher {
|
||||||
|
id: appLauncherWindow
|
||||||
|
manager: ShellStateManager
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ QtObject {
|
|||||||
if (!ShellStateManager.shellOpen)
|
if (!ShellStateManager.shellOpen)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var context = ShellStateManager.quickSettingsOpen ? "quickSettings" : "topbar";
|
var context = "topbar";
|
||||||
|
if (ShellStateManager.appLauncherOpen)
|
||||||
|
context = "appLauncher";
|
||||||
|
else if (ShellStateManager.quickSettingsOpen)
|
||||||
|
context = "quickSettings";
|
||||||
var handler = handlers[context];
|
var handler = handlers[context];
|
||||||
if (handler) {
|
if (handler) {
|
||||||
var handled = handler(key);
|
var handled = handler(key);
|
||||||
@@ -39,7 +43,9 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (key === Qt.Key_Escape || key === Qt.Key_Shift || key === Qt.Key_X) {
|
if (key === Qt.Key_Escape || key === Qt.Key_Shift || key === Qt.Key_X) {
|
||||||
if (context === "quickSettings") {
|
if (context === "appLauncher") {
|
||||||
|
ShellStateManager.closeAppLauncher();
|
||||||
|
} else if (context === "quickSettings") {
|
||||||
ShellStateManager.closeQuickSettings();
|
ShellStateManager.closeQuickSettings();
|
||||||
} else if (context === "topbar") {
|
} else if (context === "topbar") {
|
||||||
ShellStateManager.closeShell();
|
ShellStateManager.closeShell();
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: manager
|
id: manager
|
||||||
|
|
||||||
property bool shellOpen: false
|
property bool shellOpen: false
|
||||||
property bool quickSettingsOpen: false
|
property bool quickSettingsOpen: false
|
||||||
|
property bool appLauncherOpen: false
|
||||||
|
property var appUsageCounts: ({})
|
||||||
|
property string appUsagePath: {
|
||||||
|
var homeDir = Quickshell.env("HOME");
|
||||||
|
return homeDir ? homeDir + "/.local/share/deltarunequickshell/apps.json" : "";
|
||||||
|
}
|
||||||
|
property bool appUsageLoaded: false
|
||||||
property var windowRequests: ({})
|
property var windowRequests: ({})
|
||||||
property var quickSettingsPayload: ({})
|
property var quickSettingsPayload: ({})
|
||||||
property var globals: ({})
|
property var globals: ({})
|
||||||
@@ -15,6 +24,8 @@ QtObject {
|
|||||||
signal shellClosed
|
signal shellClosed
|
||||||
signal quickSettingsOpened
|
signal quickSettingsOpened
|
||||||
signal quickSettingsClosed
|
signal quickSettingsClosed
|
||||||
|
signal appLauncherOpened
|
||||||
|
signal appLauncherClosed
|
||||||
signal windowRequested(string name, var payload)
|
signal windowRequested(string name, var payload)
|
||||||
|
|
||||||
function openShell() {
|
function openShell() {
|
||||||
@@ -33,6 +44,9 @@ QtObject {
|
|||||||
if (quickSettingsOpen) {
|
if (quickSettingsOpen) {
|
||||||
closeQuickSettings();
|
closeQuickSettings();
|
||||||
}
|
}
|
||||||
|
if (appLauncherOpen) {
|
||||||
|
closeAppLauncher();
|
||||||
|
}
|
||||||
shellOpen = false;
|
shellOpen = false;
|
||||||
shellClosed();
|
shellClosed();
|
||||||
}
|
}
|
||||||
@@ -55,6 +69,9 @@ QtObject {
|
|||||||
var normalizedPayload = payload || ({});
|
var normalizedPayload = payload || ({});
|
||||||
quickSettingsPayload = normalizedPayload;
|
quickSettingsPayload = normalizedPayload;
|
||||||
console.log("ShellStateManager: openQuickSettings", normalizedPayload);
|
console.log("ShellStateManager: openQuickSettings", normalizedPayload);
|
||||||
|
if (appLauncherOpen) {
|
||||||
|
closeAppLauncher();
|
||||||
|
}
|
||||||
if (!quickSettingsOpen) {
|
if (!quickSettingsOpen) {
|
||||||
quickSettingsOpen = true;
|
quickSettingsOpen = true;
|
||||||
quickSettingsOpened();
|
quickSettingsOpened();
|
||||||
@@ -74,6 +91,72 @@ QtObject {
|
|||||||
quickSettingsOpen ? closeQuickSettings() : openQuickSettings(payload);
|
quickSettingsOpen ? closeQuickSettings() : openQuickSettings(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openAppLauncher(payload) {
|
||||||
|
var normalizedPayload = payload || ({});
|
||||||
|
console.log("ShellStateManager: openAppLauncher", normalizedPayload);
|
||||||
|
if (quickSettingsOpen) {
|
||||||
|
closeQuickSettings();
|
||||||
|
}
|
||||||
|
if (!appLauncherOpen) {
|
||||||
|
appLauncherOpen = true;
|
||||||
|
appLauncherOpened();
|
||||||
|
}
|
||||||
|
requestWindow("appLauncher", normalizedPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAppLauncher() {
|
||||||
|
if (appLauncherOpen) {
|
||||||
|
console.log("ShellStateManager: closeAppLauncher");
|
||||||
|
appLauncherOpen = false;
|
||||||
|
appLauncherClosed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAppLauncher(payload) {
|
||||||
|
appLauncherOpen ? closeAppLauncher() : openAppLauncher(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bumpAppUsage(id) {
|
||||||
|
if (!id)
|
||||||
|
return;
|
||||||
|
var updated = Object.assign({}, appUsageCounts);
|
||||||
|
updated[id] = (updated[id] || 0) + 1;
|
||||||
|
appUsageCounts = updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
function persistAppUsage() {
|
||||||
|
if (!appUsageLoaded)
|
||||||
|
return;
|
||||||
|
if (!appUsageFile || !appUsageAdapter)
|
||||||
|
return;
|
||||||
|
appUsageAdapter.counts = appUsageCounts || ({});
|
||||||
|
appUsageFile.writeAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
onAppUsageCountsChanged: persistAppUsage()
|
||||||
|
|
||||||
|
property FileView appUsageFile: FileView {
|
||||||
|
id: appUsageFile
|
||||||
|
path: manager.appUsagePath
|
||||||
|
watchChanges: true
|
||||||
|
onFileChanged: reload()
|
||||||
|
onLoaded: {
|
||||||
|
if (appUsageAdapter) {
|
||||||
|
manager.appUsageCounts = appUsageAdapter.counts || ({});
|
||||||
|
}
|
||||||
|
manager.appUsageLoaded = true;
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
manager.appUsageLoaded = true;
|
||||||
|
manager.persistAppUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonAdapter {
|
||||||
|
id: appUsageAdapter
|
||||||
|
property var counts: ({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setGlobal(key, value) {
|
function setGlobal(key, value) {
|
||||||
globals[key] = value;
|
globals[key] = value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ Item {
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (iconSources[selectedIndex] === "item.png") {
|
||||||
|
ShellStateManager.openAppLauncher({
|
||||||
|
source: "Topbar"
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -84,7 +90,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
|
showSoul: !topbar.manager.quickSettingsOpen && !topbar.manager.appLauncherOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -96,6 +102,11 @@ Item {
|
|||||||
source: "Topbar"
|
source: "Topbar"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (topbar.iconSources[repeatitem.index] === "item.png") {
|
||||||
|
ShellStateManager.openAppLauncher({
|
||||||
|
source: "Topbar"
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
44
Shell/Windows/AppLauncher/AppLauncher.qml
Normal file
44
Shell/Windows/AppLauncher/AppLauncher.qml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import QtQuick
|
||||||
|
import "../.."
|
||||||
|
import "../../Window" as ShellWindow
|
||||||
|
|
||||||
|
ShellWindow.Window {
|
||||||
|
id: appLauncherWindow
|
||||||
|
property var manager: ShellStateManager
|
||||||
|
|
||||||
|
property var appLauncherApp: null
|
||||||
|
|
||||||
|
width: 1217 + 52
|
||||||
|
height: 767 + 52
|
||||||
|
visible: manager ? manager.appLauncherOpen : false
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: appLauncherLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
asynchronous: true
|
||||||
|
source: "./AppLauncherApp.qml"
|
||||||
|
onLoaded: {
|
||||||
|
appLauncherApp = item;
|
||||||
|
if (appLauncherApp)
|
||||||
|
appLauncherApp.manager = manager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onManagerChanged: {
|
||||||
|
if (appLauncherApp)
|
||||||
|
appLauncherApp.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: appLauncherKeyHandler
|
||||||
|
function handle(event) {
|
||||||
|
if (appLauncherApp && appLauncherApp.handleKey)
|
||||||
|
return appLauncherApp.handleKey(event.key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: ShellInputManager.registerHandler("appLauncher", handle)
|
||||||
|
Component.onDestruction: ShellInputManager.unregisterHandler("appLauncher")
|
||||||
|
}
|
||||||
|
}
|
||||||
287
Shell/Windows/AppLauncher/AppLauncherApp.qml
Normal file
287
Shell/Windows/AppLauncher/AppLauncherApp.qml
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
width: parent ? parent.width : 1280
|
||||||
|
height: parent ? parent.height : 820
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
/* ------------------------------
|
||||||
|
PIXEL CONSTANTS (DO NOT TOUCH)
|
||||||
|
------------------------------ */
|
||||||
|
|
||||||
|
property int menuLeft: 64
|
||||||
|
property int menuTop: 140
|
||||||
|
property int lineHeight: 38 + 40 + 1
|
||||||
|
|
||||||
|
property int nameFontSize: 32
|
||||||
|
property int stateFontSize: 28
|
||||||
|
|
||||||
|
property int stateColumnX: 824
|
||||||
|
|
||||||
|
property int soulOffsetX: -36 - 32
|
||||||
|
property int soulOffsetY: -26
|
||||||
|
|
||||||
|
/* ------------------------------ */
|
||||||
|
|
||||||
|
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 int scrollOffset: 0
|
||||||
|
|
||||||
|
property ShellStateManager manager: null
|
||||||
|
property int activeSelection: 0
|
||||||
|
property bool isSelected: true
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: appEntriesModel
|
||||||
|
}
|
||||||
|
|
||||||
|
function clamp(v, lo, hi) {
|
||||||
|
return Math.max(lo, Math.min(hi, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
function menuLength() {
|
||||||
|
return appEntriesModel.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function menuAt(index) {
|
||||||
|
if (index < 0 || index >= appEntriesModel.count)
|
||||||
|
return null;
|
||||||
|
var row = appEntriesModel.get(index);
|
||||||
|
return row ? row.entry : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapIndex(i) {
|
||||||
|
const length = menuLength();
|
||||||
|
if (length === 0)
|
||||||
|
return 0;
|
||||||
|
return (i + length) % length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampSelection() {
|
||||||
|
const length = menuLength();
|
||||||
|
if (length === 0) {
|
||||||
|
activeSelection = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activeSelection = clamp(activeSelection, 0, length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureVisible() {
|
||||||
|
const length = menuLength();
|
||||||
|
if (length === 0)
|
||||||
|
return;
|
||||||
|
if (activeSelection < scrollOffset)
|
||||||
|
scrollOffset = activeSelection;
|
||||||
|
else if (activeSelection >= scrollOffset + visibleRows)
|
||||||
|
scrollOffset = Math.max(0, activeSelection - visibleRows + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function appName(entry) {
|
||||||
|
if (!entry)
|
||||||
|
return "";
|
||||||
|
if (entry.name && entry.name.length > 0)
|
||||||
|
return entry.name;
|
||||||
|
return entry.id || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function appUsageCount(entry) {
|
||||||
|
if (!entry || !manager || !manager.appUsageCounts)
|
||||||
|
return 0;
|
||||||
|
var key = entry.id || entry.name;
|
||||||
|
return manager.appUsageCounts[key] || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applicationsToArray() {
|
||||||
|
var apps = DesktopEntries.applications;
|
||||||
|
var list = [];
|
||||||
|
if (!apps)
|
||||||
|
return list;
|
||||||
|
if (apps.values && apps.values.length !== undefined) {
|
||||||
|
for (var i = 0; i < apps.values.length; i++) {
|
||||||
|
list.push(apps.values[i]);
|
||||||
|
}
|
||||||
|
} else if (apps.length !== undefined) {
|
||||||
|
for (var j = 0; j < apps.length; j++) {
|
||||||
|
list.push(apps[j]);
|
||||||
|
}
|
||||||
|
} else if (apps.count !== undefined && apps.get) {
|
||||||
|
for (var k = 0; k < apps.count; k++) {
|
||||||
|
list.push(apps.get(k));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebuildAppEntries() {
|
||||||
|
var list = applicationsToArray();
|
||||||
|
console.log("AppLauncher: rebuilding entries", list.length);
|
||||||
|
var filtered = [];
|
||||||
|
for (var i = 0; i < list.length; i++) {
|
||||||
|
var entry = list[i];
|
||||||
|
if (!entry)
|
||||||
|
continue;
|
||||||
|
if (entry.noDisplay)
|
||||||
|
continue;
|
||||||
|
filtered.push(entry);
|
||||||
|
}
|
||||||
|
console.log("AppLauncher: filtered entries", filtered.length);
|
||||||
|
filtered.sort(function (a, b) {
|
||||||
|
var usageA = appUsageCount(a);
|
||||||
|
var usageB = appUsageCount(b);
|
||||||
|
if (usageA !== usageB)
|
||||||
|
return usageB - usageA;
|
||||||
|
var nameA = appName(a);
|
||||||
|
var nameB = appName(b);
|
||||||
|
return nameA.localeCompare(nameB);
|
||||||
|
});
|
||||||
|
appEntriesModel.clear();
|
||||||
|
for (var i = 0; i < filtered.length; i++) {
|
||||||
|
appEntriesModel.append({
|
||||||
|
entry: filtered[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
clampSelection();
|
||||||
|
ensureVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: DesktopEntries
|
||||||
|
function onApplicationsChanged() {
|
||||||
|
rebuildAppEntries();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: manager
|
||||||
|
ignoreUnknownSignals: true
|
||||||
|
function onAppUsageCountsChanged() {
|
||||||
|
rebuildAppEntries();
|
||||||
|
}
|
||||||
|
function onAppLauncherOpened() {
|
||||||
|
activeSelection = 0;
|
||||||
|
isSelected = true;
|
||||||
|
clampSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "APPS"
|
||||||
|
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 {
|
||||||
|
model: appEntriesModel
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
width: root.width
|
||||||
|
height: lineHeight
|
||||||
|
x: 0
|
||||||
|
y: menuTop + (index - root.scrollOffset) * lineHeight
|
||||||
|
visible: index >= root.scrollOffset && index < root.scrollOffset + root.visibleRows
|
||||||
|
|
||||||
|
Text {
|
||||||
|
x: root.textStartX
|
||||||
|
y: 0
|
||||||
|
text: root.appName(model.entry)
|
||||||
|
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 && root.isSelected == true) ? "#fefe00" : "#ffffff"
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: appIcon
|
||||||
|
x: 182
|
||||||
|
y: 8 + 14
|
||||||
|
implicitSize: root.iconSize
|
||||||
|
source: Quickshell.iconPath(model.entry && model.entry.icon ? model.entry.icon : "")
|
||||||
|
asynchronous: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "../QuickSettings/soul.png"
|
||||||
|
width: 36
|
||||||
|
height: 36
|
||||||
|
x: appIcon.x
|
||||||
|
y: appIcon.y
|
||||||
|
opacity: 0.7
|
||||||
|
visible: root.activeSelection == index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------
|
||||||
|
INPUT HANDLING
|
||||||
|
------------------------------ */
|
||||||
|
|
||||||
|
function handleKey(key) {
|
||||||
|
switch (key) {
|
||||||
|
case Qt.Key_Up:
|
||||||
|
activeSelection = wrapIndex(activeSelection - 1);
|
||||||
|
ensureVisible();
|
||||||
|
return true;
|
||||||
|
case Qt.Key_Down:
|
||||||
|
activeSelection = wrapIndex(activeSelection + 1);
|
||||||
|
ensureVisible();
|
||||||
|
return true;
|
||||||
|
case Qt.Key_Z:
|
||||||
|
case Qt.Key_Return:
|
||||||
|
case Qt.Key_Enter:
|
||||||
|
{
|
||||||
|
var entry = menuAt(activeSelection);
|
||||||
|
if (entry) {
|
||||||
|
if (manager && manager.bumpAppUsage) {
|
||||||
|
manager.bumpAppUsage(entry.id || entry.name);
|
||||||
|
}
|
||||||
|
entry.execute();
|
||||||
|
}
|
||||||
|
if (manager && manager.closeShell) {
|
||||||
|
manager.closeShell();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Qt.Key_X:
|
||||||
|
case Qt.Key_Shift:
|
||||||
|
case Qt.Key_Escape:
|
||||||
|
if (manager && manager.closeAppLauncher) {
|
||||||
|
manager.closeAppLauncher();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
activeSelection = 0;
|
||||||
|
isSelected = true;
|
||||||
|
scrollOffset = 0;
|
||||||
|
rebuildAppEntries();
|
||||||
|
Qt.callLater(rebuildAppEntries);
|
||||||
|
ShellInputManager.registerHandler("appLauncher", handleKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
ShellInputManager.unregisterHandler("appLauncher");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user