mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 13:32:50 -05:00
launcher: add name, icon, description overrides + hide/unhide options
- convenient helpers without needing to make .desktop overrides fixes #1329
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
This file is more of a quick reference so I know what to account for before next releases.
|
||||
|
||||
# 1.4.0
|
||||
|
||||
- Overhauled system monitor, graphs, styling
|
||||
- dbus API for plugins, KDEConnect
|
||||
- new dank16 algorithm
|
||||
- launcher actions, customize env, args, name, icon
|
||||
|
||||
# 1.2.0
|
||||
|
||||
- Added clipboard and clipboard history integration
|
||||
|
||||
@@ -21,6 +21,7 @@ Singleton {
|
||||
property bool _isReadOnly: false
|
||||
property bool _hasUnsavedChanges: false
|
||||
property var _loadedSessionSnapshot: null
|
||||
readonly property var _hooks: ({})
|
||||
readonly property string _stateUrl: StandardPaths.writableLocation(StandardPaths.GenericStateLocation)
|
||||
readonly property string _stateDir: Paths.strip(_stateUrl)
|
||||
|
||||
@@ -102,6 +103,10 @@ Singleton {
|
||||
property string weatherLocation: "New York, NY"
|
||||
property string weatherCoordinates: "40.7128,-74.0060"
|
||||
|
||||
property var hiddenApps: []
|
||||
property var appOverrides: ({})
|
||||
property bool searchAppActions: true
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!isGreeterMode) {
|
||||
loadSettings();
|
||||
@@ -261,6 +266,10 @@ Singleton {
|
||||
_checkSessionWritable();
|
||||
}
|
||||
|
||||
function set(key, value) {
|
||||
Spec.set(root, key, value, saveSettings, _hooks);
|
||||
}
|
||||
|
||||
function migrateFromUndefinedToV1(settings) {
|
||||
console.info("SessionData: Migrating configuration from undefined to version 1");
|
||||
if (typeof SettingsData !== "undefined") {
|
||||
@@ -906,6 +915,61 @@ Singleton {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function hideApp(appId) {
|
||||
if (!appId)
|
||||
return;
|
||||
const current = [...hiddenApps];
|
||||
if (current.indexOf(appId) === -1) {
|
||||
current.push(appId);
|
||||
hiddenApps = current;
|
||||
saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
function showApp(appId) {
|
||||
if (!appId)
|
||||
return;
|
||||
hiddenApps = hiddenApps.filter(id => id !== appId);
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function isAppHidden(appId) {
|
||||
return appId && hiddenApps.indexOf(appId) !== -1;
|
||||
}
|
||||
|
||||
function setAppOverride(appId, overrides) {
|
||||
if (!appId)
|
||||
return;
|
||||
const newOverrides = Object.assign({}, appOverrides);
|
||||
if (!overrides || Object.keys(overrides).length === 0) {
|
||||
delete newOverrides[appId];
|
||||
} else {
|
||||
newOverrides[appId] = overrides;
|
||||
}
|
||||
appOverrides = newOverrides;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function getAppOverride(appId) {
|
||||
if (!appId)
|
||||
return null;
|
||||
return appOverrides[appId] || null;
|
||||
}
|
||||
|
||||
function clearAppOverride(appId) {
|
||||
if (!appId)
|
||||
return;
|
||||
const newOverrides = Object.assign({}, appOverrides);
|
||||
delete newOverrides[appId];
|
||||
appOverrides = newOverrides;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setSearchAppActions(enabled) {
|
||||
searchAppActions = enabled;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function syncWallpaperForCurrentMode() {
|
||||
if (!perModeWallpaper)
|
||||
return;
|
||||
|
||||
@@ -55,9 +55,23 @@ var SPEC = {
|
||||
enabledGpuPciIds: { def: [] },
|
||||
|
||||
wifiDeviceOverride: { def: "" },
|
||||
weatherHourlyDetailed: { def: true }
|
||||
weatherHourlyDetailed: { def: true },
|
||||
|
||||
hiddenApps: { def: [] },
|
||||
appOverrides: { def: {} },
|
||||
searchAppActions: { def: true }
|
||||
};
|
||||
|
||||
function getValidKeys() {
|
||||
return Object.keys(SPEC).concat(["configVersion"]);
|
||||
}
|
||||
|
||||
function set(root, key, value, saveFn, hooks) {
|
||||
if (!(key in SPEC)) return;
|
||||
root[key] = value;
|
||||
var hookName = SPEC[key].onChange;
|
||||
if (hookName && hooks && hooks[hookName]) {
|
||||
hooks[hookName](root);
|
||||
}
|
||||
saveFn();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Modals.Spotlight
|
||||
@@ -19,6 +20,10 @@ Item {
|
||||
property string searchMode: "apps"
|
||||
property bool usePopupContextMenu: false
|
||||
|
||||
property bool editMode: false
|
||||
property var editingApp: null
|
||||
property string editAppId: ""
|
||||
|
||||
function resetScroll() {
|
||||
if (searchMode === "apps") {
|
||||
resultsView.resetScroll();
|
||||
@@ -43,6 +48,49 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function openEditMode(app) {
|
||||
if (!app)
|
||||
return;
|
||||
editingApp = app;
|
||||
editAppId = app.id || app.execString || app.exec || "";
|
||||
const existing = SessionData.getAppOverride(editAppId);
|
||||
editNameField.text = existing?.name || "";
|
||||
editIconField.text = existing?.icon || "";
|
||||
editCommentField.text = existing?.comment || "";
|
||||
editEnvVarsField.text = existing?.envVars || "";
|
||||
editExtraFlagsField.text = existing?.extraFlags || "";
|
||||
editMode = true;
|
||||
Qt.callLater(() => editNameField.forceActiveFocus());
|
||||
}
|
||||
|
||||
function closeEditMode() {
|
||||
editMode = false;
|
||||
editingApp = null;
|
||||
editAppId = "";
|
||||
Qt.callLater(() => searchField.forceActiveFocus());
|
||||
}
|
||||
|
||||
function saveAppOverride() {
|
||||
const override = {};
|
||||
if (editNameField.text.trim())
|
||||
override.name = editNameField.text.trim();
|
||||
if (editIconField.text.trim())
|
||||
override.icon = editIconField.text.trim();
|
||||
if (editCommentField.text.trim())
|
||||
override.comment = editCommentField.text.trim();
|
||||
if (editEnvVarsField.text.trim())
|
||||
override.envVars = editEnvVarsField.text.trim();
|
||||
if (editExtraFlagsField.text.trim())
|
||||
override.extraFlags = editExtraFlagsField.text.trim();
|
||||
SessionData.setAppOverride(editAppId, override);
|
||||
closeEditMode();
|
||||
}
|
||||
|
||||
function resetAppOverride() {
|
||||
SessionData.clearAppOverride(editAppId);
|
||||
closeEditMode();
|
||||
}
|
||||
|
||||
onSearchModeChanged: {
|
||||
if (searchMode === "files") {
|
||||
appLauncher.keyboardNavigationActive = false;
|
||||
@@ -55,10 +103,16 @@ Item {
|
||||
focus: true
|
||||
clip: false
|
||||
Keys.onPressed: event => {
|
||||
if (editMode) {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
closeEditMode();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
if (parentModal)
|
||||
parentModal.hide();
|
||||
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Down) {
|
||||
if (searchMode === "apps") {
|
||||
@@ -155,7 +209,6 @@ Item {
|
||||
if (searchMode === "apps" && appLauncher.model.count > 0) {
|
||||
const selectedApp = appLauncher.model.get(appLauncher.selectedIndex);
|
||||
const menu = usePopupContextMenu ? popupContextMenu : layerContextMenuLoader.item;
|
||||
|
||||
if (selectedApp && menu && resultsView) {
|
||||
const itemPos = resultsView.getSelectedItemPosition();
|
||||
const contentPos = resultsView.mapToItem(spotlightKeyHandler, itemPos.x, itemPos.y);
|
||||
@@ -168,7 +221,6 @@ Item {
|
||||
|
||||
AppLauncher {
|
||||
id: appLauncher
|
||||
|
||||
viewMode: SettingsData.spotlightModalViewMode
|
||||
gridColumns: SettingsData.appLauncherGridColumns
|
||||
onAppLaunched: () => {
|
||||
@@ -185,7 +237,6 @@ Item {
|
||||
|
||||
FileSearchController {
|
||||
id: fileSearchController
|
||||
|
||||
onFileOpened: () => {
|
||||
if (parentModal)
|
||||
parentModal.hide();
|
||||
@@ -197,7 +248,6 @@ Item {
|
||||
|
||||
SpotlightContextMenuPopup {
|
||||
id: popupContextMenu
|
||||
|
||||
parent: spotlightKeyHandler
|
||||
appLauncher: spotlightKeyHandler.appLauncher
|
||||
parentHandler: spotlightKeyHandler
|
||||
@@ -231,20 +281,37 @@ Item {
|
||||
target: parentModal
|
||||
function onSpotlightOpenChanged() {
|
||||
if (parentModal && !parentModal.spotlightOpen) {
|
||||
if (layerContextMenuLoader.item) {
|
||||
if (layerContextMenuLoader.item)
|
||||
layerContextMenuLoader.item.hide();
|
||||
}
|
||||
popupContextMenu.hide();
|
||||
if (editMode)
|
||||
closeEditMode();
|
||||
}
|
||||
}
|
||||
enabled: parentModal !== null
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: popupContextMenu
|
||||
function onEditAppRequested(app) {
|
||||
spotlightKeyHandler.openEditMode(app);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: layerContextMenuLoader.item
|
||||
function onEditAppRequested(app) {
|
||||
spotlightKeyHandler.openEditMode(app);
|
||||
}
|
||||
enabled: layerContextMenuLoader.item !== null
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
clip: false
|
||||
visible: !editMode
|
||||
|
||||
Item {
|
||||
id: searchRow
|
||||
@@ -275,18 +342,14 @@ Item {
|
||||
ignoreTabKeys: true
|
||||
keyForwardTargets: [spotlightKeyHandler]
|
||||
onTextChanged: {
|
||||
if (searchMode === "apps") {
|
||||
if (searchMode === "apps")
|
||||
appLauncher.searchQuery = text;
|
||||
}
|
||||
}
|
||||
onTextEdited: {
|
||||
updateSearchMode();
|
||||
}
|
||||
onTextEdited: updateSearchMode()
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
if (parentModal)
|
||||
parentModal.hide();
|
||||
|
||||
event.accepted = true;
|
||||
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length > 0) {
|
||||
if (searchMode === "apps") {
|
||||
@@ -334,13 +397,10 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
id: listViewArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
appLauncher.setViewMode("list");
|
||||
}
|
||||
onClicked: appLauncher.setViewMode("list")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,13 +419,10 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
id: gridViewArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
appLauncher.setViewMode("grid");
|
||||
}
|
||||
onClicked: appLauncher.setViewMode("grid")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,7 +436,6 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
id: filenameFilterButton
|
||||
|
||||
width: 36
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
@@ -394,13 +450,10 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
id: filenameFilterArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
fileSearchController.searchField = "filename";
|
||||
}
|
||||
onClicked: fileSearchController.searchField = "filename"
|
||||
onEntered: {
|
||||
filenameTooltipLoader.active = true;
|
||||
Qt.callLater(() => {
|
||||
@@ -413,7 +466,6 @@ Item {
|
||||
onExited: {
|
||||
if (filenameTooltipLoader.item)
|
||||
filenameTooltipLoader.item.hide();
|
||||
|
||||
filenameTooltipLoader.active = false;
|
||||
}
|
||||
}
|
||||
@@ -421,7 +473,6 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
id: contentFilterButton
|
||||
|
||||
width: 36
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
@@ -436,13 +487,10 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
id: contentFilterArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
fileSearchController.searchField = "body";
|
||||
}
|
||||
onClicked: fileSearchController.searchField = "body"
|
||||
onEntered: {
|
||||
contentTooltipLoader.active = true;
|
||||
Qt.callLater(() => {
|
||||
@@ -455,7 +503,6 @@ Item {
|
||||
onExited: {
|
||||
if (contentTooltipLoader.item)
|
||||
contentTooltipLoader.item.hide();
|
||||
|
||||
contentTooltipLoader.active = false;
|
||||
}
|
||||
}
|
||||
@@ -474,13 +521,10 @@ Item {
|
||||
anchors.fill: parent
|
||||
appLauncher: spotlightKeyHandler.appLauncher
|
||||
visible: searchMode === "apps"
|
||||
|
||||
onItemRightClicked: (index, modelData, mouseX, mouseY) => {
|
||||
const menu = usePopupContextMenu ? popupContextMenu : layerContextMenuLoader.item;
|
||||
|
||||
if (menu?.show) {
|
||||
const isPopup = menu.contentItem !== undefined;
|
||||
|
||||
if (isPopup) {
|
||||
const localPos = popupContextMenu.parent.mapFromItem(null, mouseX, mouseY);
|
||||
menu.show(localPos.x, localPos.y, modelData, false);
|
||||
@@ -500,16 +544,320 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
id: editView
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
visible: editMode
|
||||
focus: editMode
|
||||
|
||||
Keys.onPressed: event => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
closeEditMode();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
saveAppOverride();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
case Qt.Key_S:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
saveAppOverride();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
case Qt.Key_R:
|
||||
if ((event.modifiers & Qt.ControlModifier) && SessionData.getAppOverride(editAppId) !== null) {
|
||||
resetAppOverride();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: backButtonArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "arrow_back"
|
||||
size: 20
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: backButtonArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: closeEditMode()
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
width: 40
|
||||
height: 40
|
||||
source: editingApp?.icon ? "image://icon/" + editingApp.icon : "image://icon/application-x-executable"
|
||||
sourceSize.width: 40
|
||||
sourceSize.height: 40
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Edit App")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: editingApp?.name || ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.outlineMedium
|
||||
}
|
||||
|
||||
Flickable {
|
||||
width: parent.width
|
||||
height: parent.height - y - buttonsRow.height - Theme.spacingM
|
||||
contentHeight: editFieldsColumn.height
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
Column {
|
||||
id: editFieldsColumn
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Name")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: editNameField
|
||||
width: parent.width
|
||||
height: 44
|
||||
placeholderText: editingApp?.name || ""
|
||||
keyNavigationTab: editIconField
|
||||
keyNavigationBacktab: editExtraFlagsField
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Icon")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: editIconField
|
||||
width: parent.width
|
||||
height: 44
|
||||
placeholderText: editingApp?.icon || ""
|
||||
keyNavigationTab: editCommentField
|
||||
keyNavigationBacktab: editNameField
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Description")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: editCommentField
|
||||
width: parent.width
|
||||
height: 44
|
||||
placeholderText: editingApp?.comment || ""
|
||||
keyNavigationTab: editEnvVarsField
|
||||
keyNavigationBacktab: editIconField
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Environment Variables")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "KEY=value KEY2=value2"
|
||||
font.pixelSize: Theme.fontSizeSmall - 1
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: editEnvVarsField
|
||||
width: parent.width
|
||||
height: 44
|
||||
placeholderText: "VAR=value"
|
||||
keyNavigationTab: editExtraFlagsField
|
||||
keyNavigationBacktab: editCommentField
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Extra Arguments")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: editExtraFlagsField
|
||||
width: parent.width
|
||||
height: 44
|
||||
placeholderText: "--flag --option=value"
|
||||
keyNavigationTab: editNameField
|
||||
keyNavigationBacktab: editEnvVarsField
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonsRow
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Rectangle {
|
||||
id: resetButton
|
||||
width: 90
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: resetButtonArea.containsMouse ? Theme.surfacePressed : Theme.surfaceVariantAlpha
|
||||
visible: SessionData.getAppOverride(editAppId) !== null
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Reset")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.error
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: resetButtonArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: resetAppOverride()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: cancelButton
|
||||
width: 90
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: cancelButtonArea.containsMouse ? Theme.surfacePressed : Theme.surfaceVariantAlpha
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Cancel")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cancelButtonArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: closeEditMode()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: saveButton
|
||||
width: 90
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: saveButtonArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.9) : Theme.primary
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Save")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.primaryText
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: saveButtonArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: saveAppOverride()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: filenameTooltipLoader
|
||||
|
||||
active: false
|
||||
sourceComponent: DankTooltip {}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: contentTooltipLoader
|
||||
|
||||
active: false
|
||||
sourceComponent: DankTooltip {}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Modals.Spotlight
|
||||
|
||||
@@ -19,6 +18,8 @@ PanelWindow {
|
||||
property real menuPositionX: 0
|
||||
property real menuPositionY: 0
|
||||
|
||||
signal editAppRequested(var app)
|
||||
|
||||
readonly property real shadowBuffer: 5
|
||||
|
||||
screen: parentModal?.effectiveScreen
|
||||
@@ -106,6 +107,7 @@ PanelWindow {
|
||||
}
|
||||
|
||||
onHideRequested: root.hide()
|
||||
onEditAppRequested: app => root.editAppRequested(app)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
||||
@@ -68,6 +68,27 @@ Item {
|
||||
hideRequested();
|
||||
}
|
||||
|
||||
readonly property bool isRegularApp: desktopEntry && !currentApp?.isPlugin && !currentApp?.isCore && !currentApp?.isAction && !currentApp?.isBuiltInLauncher
|
||||
|
||||
signal editAppRequested(var app)
|
||||
|
||||
function hideCurrentApp() {
|
||||
if (!desktopEntry)
|
||||
return;
|
||||
const appId = desktopEntry.id || desktopEntry.execString || "";
|
||||
SessionData.hideApp(appId);
|
||||
if (appLauncher)
|
||||
appLauncher.updateFilteredModel();
|
||||
hideRequested();
|
||||
}
|
||||
|
||||
function editCurrentApp() {
|
||||
if (!desktopEntry)
|
||||
return;
|
||||
editAppRequested(desktopEntry);
|
||||
hideRequested();
|
||||
}
|
||||
|
||||
readonly property var menuItems: {
|
||||
const items = [];
|
||||
|
||||
@@ -103,6 +124,21 @@ Item {
|
||||
action: togglePin
|
||||
});
|
||||
|
||||
if (isRegularApp) {
|
||||
items.push({
|
||||
type: "item",
|
||||
icon: "visibility_off",
|
||||
text: I18n.tr("Hide App"),
|
||||
action: hideCurrentApp
|
||||
});
|
||||
items.push({
|
||||
type: "item",
|
||||
icon: "edit",
|
||||
text: I18n.tr("Edit App"),
|
||||
action: editCurrentApp
|
||||
});
|
||||
}
|
||||
|
||||
if (desktopEntry && desktopEntry.actions) {
|
||||
items.push({
|
||||
type: "separator"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Modals.Spotlight
|
||||
|
||||
@@ -11,6 +10,8 @@ Popup {
|
||||
property var parentHandler: null
|
||||
property var searchField: null
|
||||
|
||||
signal editAppRequested(var app)
|
||||
|
||||
function show(x, y, app, fromKeyboard) {
|
||||
fromKeyboard = fromKeyboard || false;
|
||||
menuContent.currentApp = app;
|
||||
@@ -53,7 +54,7 @@ Popup {
|
||||
if (parentHandler) {
|
||||
parentHandler.enabled = true;
|
||||
}
|
||||
if (searchField) {
|
||||
if (searchField?.visible) {
|
||||
Qt.callLater(() => {
|
||||
searchField.forceActiveFocus();
|
||||
});
|
||||
@@ -84,5 +85,6 @@ Popup {
|
||||
id: menuContent
|
||||
appLauncher: root.appLauncher
|
||||
onHideRequested: root.hide()
|
||||
onEditAppRequested: app => root.editAppRequested(app)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,20 @@ DankModal {
|
||||
});
|
||||
}
|
||||
|
||||
function showWithEditApp(app) {
|
||||
openedFromOverview = false;
|
||||
isClosing = false;
|
||||
resetContent();
|
||||
spotlightOpen = true;
|
||||
open();
|
||||
Qt.callLater(() => {
|
||||
if (spotlightContent?.appLauncher)
|
||||
spotlightContent.appLauncher.ensureInitialized();
|
||||
if (spotlightContent?.openEditMode)
|
||||
spotlightContent.openEditMode(app);
|
||||
});
|
||||
}
|
||||
|
||||
function hide() {
|
||||
openedFromOverview = false;
|
||||
isClosing = true;
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Modals.Spotlight
|
||||
import qs.Modules.AppDrawer
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
DankPopout {
|
||||
@@ -15,6 +11,63 @@ DankPopout {
|
||||
|
||||
property string searchMode: "apps"
|
||||
property alias fileSearch: fileSearchController
|
||||
property bool editMode: false
|
||||
property var editingApp: null
|
||||
property string editAppId: ""
|
||||
|
||||
function openEditMode(app) {
|
||||
if (!app)
|
||||
return;
|
||||
editingApp = app;
|
||||
editAppId = app.id || app.execString || app.exec || "";
|
||||
const existing = SessionData.getAppOverride(editAppId);
|
||||
if (contentLoader.item) {
|
||||
contentLoader.item.searchField.focus = false;
|
||||
contentLoader.item.editNameField.text = existing?.name || "";
|
||||
contentLoader.item.editIconField.text = existing?.icon || "";
|
||||
contentLoader.item.editCommentField.text = existing?.comment || "";
|
||||
contentLoader.item.editEnvVarsField.text = existing?.envVars || "";
|
||||
contentLoader.item.editExtraFlagsField.text = existing?.extraFlags || "";
|
||||
}
|
||||
editMode = true;
|
||||
Qt.callLater(() => {
|
||||
if (contentLoader.item?.editNameField)
|
||||
contentLoader.item.editNameField.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
|
||||
function closeEditMode() {
|
||||
editMode = false;
|
||||
editingApp = null;
|
||||
editAppId = "";
|
||||
Qt.callLater(() => {
|
||||
if (contentLoader.item?.searchField)
|
||||
contentLoader.item.searchField.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
|
||||
function saveAppOverride() {
|
||||
const override = {};
|
||||
if (contentLoader.item) {
|
||||
if (contentLoader.item.editNameField.text.trim())
|
||||
override.name = contentLoader.item.editNameField.text.trim();
|
||||
if (contentLoader.item.editIconField.text.trim())
|
||||
override.icon = contentLoader.item.editIconField.text.trim();
|
||||
if (contentLoader.item.editCommentField.text.trim())
|
||||
override.comment = contentLoader.item.editCommentField.text.trim();
|
||||
if (contentLoader.item.editEnvVarsField.text.trim())
|
||||
override.envVars = contentLoader.item.editEnvVarsField.text.trim();
|
||||
if (contentLoader.item.editExtraFlagsField.text.trim())
|
||||
override.extraFlags = contentLoader.item.editExtraFlagsField.text.trim();
|
||||
}
|
||||
SessionData.setAppOverride(editAppId, override);
|
||||
closeEditMode();
|
||||
}
|
||||
|
||||
function resetAppOverride() {
|
||||
SessionData.clearAppOverride(editAppId);
|
||||
closeEditMode();
|
||||
}
|
||||
|
||||
function updateSearchMode(text) {
|
||||
if (text.startsWith("/")) {
|
||||
@@ -42,16 +95,25 @@ DankPopout {
|
||||
popupHeight: 600
|
||||
triggerWidth: 40
|
||||
positioning: ""
|
||||
contentHandlesKeys: editMode
|
||||
|
||||
onBackgroundClicked: {
|
||||
if (contextMenu.visible) {
|
||||
contextMenu.close();
|
||||
return;
|
||||
}
|
||||
if (editMode) {
|
||||
closeEditMode();
|
||||
return;
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
searchMode = "apps";
|
||||
editMode = false;
|
||||
editingApp = null;
|
||||
editAppId = "";
|
||||
appLauncher.ensureInitialized();
|
||||
appLauncher.searchQuery = "";
|
||||
appLauncher.selectedIndex = 0;
|
||||
@@ -100,8 +162,45 @@ DankPopout {
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
property alias searchField: searchField
|
||||
property alias keyHandler: keyHandler
|
||||
property alias editNameField: editNameField
|
||||
property alias editIconField: editIconField
|
||||
property alias editCommentField: editCommentField
|
||||
property alias editEnvVarsField: editEnvVarsField
|
||||
property alias editExtraFlagsField: editExtraFlagsField
|
||||
|
||||
focus: true
|
||||
color: "transparent"
|
||||
|
||||
Keys.onPressed: function (event) {
|
||||
if (appDrawerPopout.editMode) {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
appDrawerPopout.closeEditMode();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
appDrawerPopout.saveAppOverride();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
case Qt.Key_S:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
appDrawerPopout.saveAppOverride();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
case Qt.Key_R:
|
||||
if ((event.modifiers & Qt.ControlModifier) && SessionData.getAppOverride(appDrawerPopout.editAppId) !== null) {
|
||||
appDrawerPopout.resetAppOverride();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
radius: Theme.cornerRadius
|
||||
antialiasing: true
|
||||
smooth: true
|
||||
@@ -140,7 +239,7 @@ DankPopout {
|
||||
id: keyHandler
|
||||
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
focus: !appDrawerPopout.editMode
|
||||
|
||||
function selectNext() {
|
||||
switch (appDrawerPopout.searchMode) {
|
||||
@@ -172,6 +271,29 @@ DankPopout {
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedItemPosition() {
|
||||
const index = appLauncher.selectedIndex;
|
||||
if (appLauncher.viewMode === "list") {
|
||||
const y = index * (appList.itemHeight + appList.itemSpacing) - appList.contentY;
|
||||
return Qt.point(appList.width / 2, y + appList.itemHeight / 2 + appList.y);
|
||||
}
|
||||
const row = Math.floor(index / appGrid.actualColumns);
|
||||
const col = index % appGrid.actualColumns;
|
||||
const x = col * appGrid.cellWidth + appGrid.cellWidth / 2;
|
||||
const y = row * appGrid.cellHeight - appGrid.contentY + appGrid.cellHeight / 2 + appGrid.y;
|
||||
return Qt.point(x, y);
|
||||
}
|
||||
|
||||
function openContextMenuForSelected() {
|
||||
if (appDrawerPopout.searchMode !== "apps" || appLauncher.model.count === 0)
|
||||
return;
|
||||
const selectedApp = appLauncher.model.get(appLauncher.selectedIndex);
|
||||
if (!selectedApp)
|
||||
return;
|
||||
const pos = getSelectedItemPosition();
|
||||
contextMenu.show(pos.x, pos.y, selectedApp, true);
|
||||
}
|
||||
|
||||
readonly property var keyMappings: {
|
||||
const mappings = {};
|
||||
mappings[Qt.Key_Escape] = () => appDrawerPopout.close();
|
||||
@@ -181,6 +303,8 @@ DankPopout {
|
||||
mappings[Qt.Key_Enter] = () => keyHandler.activateSelected();
|
||||
mappings[Qt.Key_Tab] = () => appDrawerPopout.searchMode === "apps" && appLauncher.viewMode === "grid" ? appLauncher.selectNextInRow() : keyHandler.selectNext();
|
||||
mappings[Qt.Key_Backtab] = () => appDrawerPopout.searchMode === "apps" && appLauncher.viewMode === "grid" ? appLauncher.selectPreviousInRow() : keyHandler.selectPrevious();
|
||||
mappings[Qt.Key_Menu] = () => keyHandler.openContextMenuForSelected();
|
||||
mappings[Qt.Key_F10] = () => keyHandler.openContextMenuForSelected();
|
||||
|
||||
if (appDrawerPopout.searchMode === "apps" && appLauncher.viewMode === "grid") {
|
||||
mappings[Qt.Key_Right] = () => I18n.isRtl ? appLauncher.selectPreviousInRow() : appLauncher.selectNextInRow();
|
||||
@@ -191,6 +315,9 @@ DankPopout {
|
||||
}
|
||||
|
||||
Keys.onPressed: function (event) {
|
||||
if (appDrawerPopout.editMode)
|
||||
return;
|
||||
|
||||
if (keyMappings[event.key]) {
|
||||
keyMappings[event.key]();
|
||||
event.accepted = true;
|
||||
@@ -198,9 +325,8 @@ DankPopout {
|
||||
}
|
||||
|
||||
const hasCtrl = event.modifiers & Qt.ControlModifier;
|
||||
if (!hasCtrl) {
|
||||
if (!hasCtrl)
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case Qt.Key_N:
|
||||
@@ -234,6 +360,7 @@ DankPopout {
|
||||
x: Theme.spacingS
|
||||
y: Theme.spacingS
|
||||
spacing: Theme.spacingS
|
||||
visible: !appDrawerPopout.editMode
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
@@ -487,7 +614,7 @@ DankPopout {
|
||||
appLauncher.launchApp(modelData);
|
||||
}
|
||||
onItemRightClicked: function (index, modelData, mouseX, mouseY) {
|
||||
contextMenu.show(mouseX, mouseY, modelData);
|
||||
contextMenu.show(mouseX, mouseY, modelData, false);
|
||||
}
|
||||
onKeyboardNavigationReset: {
|
||||
appLauncher.keyboardNavigationActive = false;
|
||||
@@ -574,7 +701,7 @@ DankPopout {
|
||||
appLauncher.launchApp(modelData);
|
||||
}
|
||||
onItemRightClicked: function (index, modelData, mouseX, mouseY) {
|
||||
contextMenu.show(mouseX, mouseY, modelData);
|
||||
contextMenu.show(mouseX, mouseY, modelData, false);
|
||||
}
|
||||
onKeyboardNavigationReset: {
|
||||
appLauncher.keyboardNavigationActive = false;
|
||||
@@ -615,6 +742,281 @@ DankPopout {
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: editView
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
visible: appDrawerPopout.editMode
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: backButtonArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "arrow_back"
|
||||
size: 20
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: backButtonArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: appDrawerPopout.closeEditMode()
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
width: 40
|
||||
height: 40
|
||||
source: appDrawerPopout.editingApp?.icon ? "image://icon/" + appDrawerPopout.editingApp.icon : "image://icon/application-x-executable"
|
||||
sourceSize.width: 40
|
||||
sourceSize.height: 40
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Edit App")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: appDrawerPopout.editingApp?.name || ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.outlineMedium
|
||||
}
|
||||
|
||||
Flickable {
|
||||
width: parent.width
|
||||
height: parent.height - y - editButtonsRow.height - Theme.spacingM
|
||||
contentHeight: editFieldsColumn.height
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
Column {
|
||||
id: editFieldsColumn
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Name")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: editNameField
|
||||
width: parent.width
|
||||
height: 44
|
||||
focus: true
|
||||
placeholderText: appDrawerPopout.editingApp?.name || ""
|
||||
keyNavigationTab: editIconField
|
||||
keyNavigationBacktab: editExtraFlagsField
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Icon")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: editIconField
|
||||
width: parent.width
|
||||
height: 44
|
||||
placeholderText: appDrawerPopout.editingApp?.icon || ""
|
||||
keyNavigationTab: editCommentField
|
||||
keyNavigationBacktab: editNameField
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Description")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: editCommentField
|
||||
width: parent.width
|
||||
height: 44
|
||||
placeholderText: appDrawerPopout.editingApp?.comment || ""
|
||||
keyNavigationTab: editEnvVarsField
|
||||
keyNavigationBacktab: editIconField
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Environment Variables")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "KEY=value KEY2=value2"
|
||||
font.pixelSize: Theme.fontSizeSmall - 1
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: editEnvVarsField
|
||||
width: parent.width
|
||||
height: 44
|
||||
placeholderText: "VAR=value"
|
||||
keyNavigationTab: editExtraFlagsField
|
||||
keyNavigationBacktab: editCommentField
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Extra Arguments")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: editExtraFlagsField
|
||||
width: parent.width
|
||||
height: 44
|
||||
placeholderText: "--flag --option=value"
|
||||
keyNavigationTab: editNameField
|
||||
keyNavigationBacktab: editEnvVarsField
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: editButtonsRow
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Rectangle {
|
||||
width: 90
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: resetButtonArea.containsMouse ? Theme.surfacePressed : Theme.surfaceVariantAlpha
|
||||
visible: SessionData.getAppOverride(appDrawerPopout.editAppId) !== null
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Reset")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.error
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: resetButtonArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: appDrawerPopout.resetAppOverride()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 90
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: cancelButtonArea.containsMouse ? Theme.surfacePressed : Theme.surfaceVariantAlpha
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Cancel")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cancelButtonArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: appDrawerPopout.closeEditMode()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 90
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: saveButtonArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.9) : Theme.primary
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Save")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.onPrimary
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: saveButtonArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: appDrawerPopout.saveAppOverride()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
visible: contextMenu.visible
|
||||
@@ -624,338 +1026,21 @@ DankPopout {
|
||||
}
|
||||
}
|
||||
|
||||
Popup {
|
||||
SpotlightContextMenuPopup {
|
||||
id: contextMenu
|
||||
|
||||
property var currentApp: null
|
||||
readonly property var desktopEntry: (currentApp && !currentApp.isPlugin && appLauncher && appLauncher._uniqueApps && currentApp.appIndex >= 0 && currentApp.appIndex < appLauncher._uniqueApps.length) ? appLauncher._uniqueApps[currentApp.appIndex] : null
|
||||
readonly property string appId: desktopEntry ? (desktopEntry.id || desktopEntry.execString || "") : ""
|
||||
readonly property bool isPinned: appId && SessionData.isPinnedApp(appId)
|
||||
parent: contentLoader.item
|
||||
appLauncher: appLauncher
|
||||
parentHandler: contentLoader.item?.keyHandler ?? null
|
||||
searchField: contentLoader.item?.searchField ?? null
|
||||
visible: false
|
||||
z: 1000
|
||||
}
|
||||
|
||||
function show(x, y, app) {
|
||||
currentApp = app;
|
||||
let finalX = x + 4;
|
||||
let finalY = y + 4;
|
||||
|
||||
if (contextMenu.parent) {
|
||||
const parentWidth = contextMenu.parent.width;
|
||||
const parentHeight = contextMenu.parent.height;
|
||||
const menuWidth = contextMenu.width;
|
||||
const menuHeight = contextMenu.height;
|
||||
|
||||
if (finalX + menuWidth > parentWidth) {
|
||||
finalX = Math.max(0, parentWidth - menuWidth);
|
||||
}
|
||||
|
||||
if (finalY + menuHeight > parentHeight) {
|
||||
finalY = Math.max(0, parentHeight - menuHeight);
|
||||
}
|
||||
}
|
||||
|
||||
contextMenu.x = finalX;
|
||||
contextMenu.y = finalY;
|
||||
contextMenu.open();
|
||||
}
|
||||
|
||||
function hide() {
|
||||
contextMenu.close();
|
||||
}
|
||||
|
||||
width: Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2)
|
||||
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
||||
padding: 0
|
||||
closePolicy: Popup.CloseOnPressOutside
|
||||
modal: false
|
||||
dim: false
|
||||
|
||||
background: Rectangle {
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 4
|
||||
anchors.leftMargin: 2
|
||||
anchors.rightMargin: -2
|
||||
anchors.bottomMargin: -4
|
||||
radius: parent.radius
|
||||
color: Qt.rgba(0, 0, 0, 0.15)
|
||||
z: -1
|
||||
}
|
||||
}
|
||||
|
||||
enter: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
exit: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: menuColumn
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
spacing: 1
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 32
|
||||
radius: Theme.cornerRadius
|
||||
color: pinMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: contextMenu.isPinned ? "keep_off" : "push_pin"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.7
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: contextMenu.isPinned ? I18n.tr("Unpin from Dock") : I18n.tr("Pin to Dock")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: pinMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (!contextMenu.desktopEntry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (contextMenu.isPinned) {
|
||||
SessionData.removePinnedApp(contextMenu.appId);
|
||||
} else {
|
||||
SessionData.addPinnedApp(contextMenu.appId);
|
||||
}
|
||||
contextMenu.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
height: 5
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: contextMenu.desktopEntry && contextMenu.desktopEntry.actions ? contextMenu.desktopEntry.actions : []
|
||||
|
||||
Rectangle {
|
||||
width: Math.max(parent.width, actionRow.implicitWidth + Theme.spacingS * 2)
|
||||
height: 32
|
||||
radius: Theme.cornerRadius
|
||||
color: actionMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
Row {
|
||||
id: actionRow
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Theme.iconSize - 2
|
||||
height: Theme.iconSize - 2
|
||||
visible: modelData.icon && modelData.icon !== ""
|
||||
|
||||
IconImage {
|
||||
anchors.fill: parent
|
||||
source: modelData.icon ? Quickshell.iconPath(modelData.icon, true) : ""
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
visible: status === Image.Ready
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: modelData.name || ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: actionMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData && contextMenu.desktopEntry) {
|
||||
SessionService.launchDesktopAction(contextMenu.desktopEntry, modelData);
|
||||
if (contextMenu.currentApp) {
|
||||
appLauncher.appLaunched(contextMenu.currentApp);
|
||||
}
|
||||
}
|
||||
contextMenu.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: contextMenu.desktopEntry && contextMenu.desktopEntry.actions && contextMenu.desktopEntry.actions.length > 0
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
height: 5
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 32
|
||||
radius: Theme.cornerRadius
|
||||
color: launchMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: "launch"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.7
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Launch")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: launchMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (contextMenu.currentApp)
|
||||
appLauncher.launchApp(contextMenu.currentApp);
|
||||
|
||||
contextMenu.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: SessionService.nvidiaCommand
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
height: 5
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: SessionService.nvidiaCommand
|
||||
width: parent.width
|
||||
height: 32
|
||||
radius: Theme.cornerRadius
|
||||
color: nvidiaMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: "memory"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.7
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Launch on dGPU")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: nvidiaMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (contextMenu.desktopEntry) {
|
||||
SessionService.launchDesktopEntry(contextMenu.desktopEntry, true);
|
||||
if (contextMenu.currentApp) {
|
||||
appLauncher.appLaunched(contextMenu.currentApp);
|
||||
}
|
||||
}
|
||||
contextMenu.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: contextMenu
|
||||
function onEditAppRequested(app) {
|
||||
appDrawerPopout.openEditMode(app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,16 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onHiddenAppsChanged() {
|
||||
updateFilteredModel();
|
||||
}
|
||||
function onAppOverridesChanged() {
|
||||
updateFilteredModel();
|
||||
}
|
||||
}
|
||||
|
||||
function updateFilteredModel() {
|
||||
if (suppressUpdatesWhileLaunching) {
|
||||
suppressUpdatesWhileLaunching = false;
|
||||
@@ -124,7 +134,7 @@ Item {
|
||||
emptyTriggerItems = emptyTriggerItems.concat(items);
|
||||
});
|
||||
const coreItems = AppSearchService.getCoreApps("");
|
||||
apps = AppSearchService.applications.concat(emptyTriggerItems).concat(coreItems);
|
||||
apps = AppSearchService.getVisibleApplications().concat(emptyTriggerItems).concat(coreItems);
|
||||
} else {
|
||||
apps = AppSearchService.getAppsInCategory(selectedCategory).slice(0, maxResults);
|
||||
const coreItems = AppSearchService.getCoreApps("").filter(app => app.categories.includes(selectedCategory));
|
||||
@@ -205,6 +215,7 @@ Item {
|
||||
"isPlugin": isPluginItem,
|
||||
"isCore": app.isCore === true,
|
||||
"isBuiltInLauncher": app.isBuiltInLauncher === true,
|
||||
"isAction": app.isAction === true,
|
||||
"appIndex": uniqueApps.length - 1,
|
||||
"pinned": app._pinned === true
|
||||
});
|
||||
@@ -283,6 +294,13 @@ Item {
|
||||
return;
|
||||
}
|
||||
|
||||
if (appData.isAction && actualApp.parentApp && actualApp.actionData) {
|
||||
SessionService.launchDesktopAction(actualApp.parentApp, actualApp.actionData);
|
||||
appLaunched(appData);
|
||||
AppUsageHistoryData.addAppUsage(actualApp.parentApp);
|
||||
return;
|
||||
}
|
||||
|
||||
SessionService.launchDesktopEntry(actualApp);
|
||||
appLaunched(appData);
|
||||
AppUsageHistoryData.addAppUsage(actualApp);
|
||||
|
||||
@@ -462,6 +462,250 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "search"
|
||||
title: I18n.tr("Search Options")
|
||||
settingKey: "searchOptions"
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "searchAppActions"
|
||||
tags: ["launcher", "search", "actions", "shortcuts"]
|
||||
text: I18n.tr("Search App Actions")
|
||||
description: I18n.tr("Include desktop actions (shortcuts) in search results.")
|
||||
checked: SessionData.searchAppActions
|
||||
onToggled: checked => SessionData.setSearchAppActions(checked)
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
id: hiddenAppsCard
|
||||
width: parent.width
|
||||
iconName: "visibility_off"
|
||||
title: I18n.tr("Hidden Apps")
|
||||
settingKey: "hiddenApps"
|
||||
|
||||
property var hiddenAppsModel: {
|
||||
SessionData.hiddenApps;
|
||||
const apps = [];
|
||||
const allApps = AppSearchService.applications || [];
|
||||
for (const hiddenId of SessionData.hiddenApps) {
|
||||
const app = allApps.find(a => (a.id || a.execString || a.exec) === hiddenId);
|
||||
if (app) {
|
||||
apps.push({
|
||||
id: hiddenId,
|
||||
name: app.name || hiddenId,
|
||||
icon: app.icon || "",
|
||||
comment: app.comment || ""
|
||||
});
|
||||
} else {
|
||||
apps.push({
|
||||
id: hiddenId,
|
||||
name: hiddenId,
|
||||
icon: "",
|
||||
comment: ""
|
||||
});
|
||||
}
|
||||
}
|
||||
return apps.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: I18n.tr("Hidden apps won't appear in the launcher. Right-click an app and select 'Hide App' to hide it.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Column {
|
||||
id: hiddenAppsList
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Repeater {
|
||||
model: hiddenAppsCard.hiddenAppsModel
|
||||
|
||||
delegate: Rectangle {
|
||||
width: hiddenAppsList.width
|
||||
height: 48
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.3)
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Image {
|
||||
width: 24
|
||||
height: 24
|
||||
source: modelData.icon ? "image://icon/" + modelData.icon : "image://icon/application-x-executable"
|
||||
sourceSize.width: 24
|
||||
sourceSize.height: 24
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onStatusChanged: {
|
||||
if (status === Image.Error)
|
||||
source = "image://icon/application-x-executable";
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
text: modelData.name
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: modelData.comment || modelData.id
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
visible: text.length > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "visibility"
|
||||
iconSize: 18
|
||||
iconColor: Theme.primary
|
||||
onClicked: SessionData.showApp(modelData.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: I18n.tr("No hidden apps.")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: hiddenAppsCard.hiddenAppsModel.length === 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
id: appOverridesCard
|
||||
width: parent.width
|
||||
iconName: "edit"
|
||||
title: I18n.tr("App Customizations")
|
||||
settingKey: "appOverrides"
|
||||
|
||||
property var overridesModel: {
|
||||
SessionData.appOverrides;
|
||||
const items = [];
|
||||
const allApps = AppSearchService.applications || [];
|
||||
for (const appId in SessionData.appOverrides) {
|
||||
const override = SessionData.appOverrides[appId];
|
||||
const app = allApps.find(a => (a.id || a.execString || a.exec) === appId);
|
||||
items.push({
|
||||
id: appId,
|
||||
name: override.name || app?.name || appId,
|
||||
originalName: app?.name || appId,
|
||||
icon: override.icon || app?.icon || "",
|
||||
hasOverride: true
|
||||
});
|
||||
}
|
||||
return items.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: I18n.tr("Apps with custom display name, icon, or launch options. Right-click an app and select 'Edit App' to customize.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Column {
|
||||
id: overridesList
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Repeater {
|
||||
model: appOverridesCard.overridesModel
|
||||
|
||||
delegate: Rectangle {
|
||||
width: overridesList.width
|
||||
height: 48
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.3)
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Image {
|
||||
width: 24
|
||||
height: 24
|
||||
source: modelData.icon ? "image://icon/" + modelData.icon : "image://icon/application-x-executable"
|
||||
sourceSize.width: 24
|
||||
sourceSize.height: 24
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onStatusChanged: {
|
||||
if (status === Image.Error)
|
||||
source = "image://icon/application-x-executable";
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
text: modelData.name
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: modelData.originalName !== modelData.name ? modelData.originalName : modelData.id
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "delete"
|
||||
iconSize: 18
|
||||
iconColor: Theme.error
|
||||
onClicked: SessionData.clearAppOverride(modelData.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: I18n.tr("No app customizations.")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: appOverridesCard.overridesModel.length === 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
id: recentAppsCard
|
||||
width: parent.width
|
||||
|
||||
@@ -10,6 +10,8 @@ Singleton {
|
||||
|
||||
property var applications: []
|
||||
property var _cachedCategories: null
|
||||
property var _cachedVisibleApps: null
|
||||
property var _hiddenAppsSet: new Set()
|
||||
|
||||
readonly property int maxResults: 10
|
||||
readonly property int frecencySampleSize: 10
|
||||
@@ -40,6 +42,51 @@ Singleton {
|
||||
function refreshApplications() {
|
||||
applications = DesktopEntries.applications.values;
|
||||
_cachedCategories = null;
|
||||
_cachedVisibleApps = null;
|
||||
}
|
||||
|
||||
function _rebuildHiddenSet() {
|
||||
_hiddenAppsSet = new Set(SessionData.hiddenApps || []);
|
||||
_cachedVisibleApps = null;
|
||||
}
|
||||
|
||||
function isAppHidden(app) {
|
||||
if (!app)
|
||||
return false;
|
||||
const appId = app.id || app.execString || app.exec || "";
|
||||
return _hiddenAppsSet.has(appId);
|
||||
}
|
||||
|
||||
function getVisibleApplications() {
|
||||
if (_cachedVisibleApps === null) {
|
||||
_cachedVisibleApps = applications.filter(app => !isAppHidden(app));
|
||||
}
|
||||
return _cachedVisibleApps.map(app => applyAppOverride(app));
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onHiddenAppsChanged() {
|
||||
root._rebuildHiddenSet();
|
||||
}
|
||||
function onAppOverridesChanged() {
|
||||
root._cachedVisibleApps = null;
|
||||
}
|
||||
}
|
||||
|
||||
function applyAppOverride(app) {
|
||||
if (!app)
|
||||
return app;
|
||||
const appId = app.id || app.execString || app.exec || "";
|
||||
const override = SessionData.getAppOverride(appId);
|
||||
if (!override)
|
||||
return app;
|
||||
return Object.assign({}, app, {
|
||||
name: override.name || app.name,
|
||||
icon: override.icon || app.icon,
|
||||
comment: override.comment || app.comment,
|
||||
_override: override
|
||||
});
|
||||
}
|
||||
|
||||
readonly property string dmsLogoPath: Qt.resolvedUrl("../assets/danklogo2.svg")
|
||||
@@ -226,7 +273,10 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: refreshApplications()
|
||||
Component.onCompleted: {
|
||||
_rebuildHiddenSet();
|
||||
refreshApplications();
|
||||
}
|
||||
|
||||
function tokenize(text) {
|
||||
return text.toLowerCase().trim().split(/[\s\-_]+/).filter(w => w.length > 0);
|
||||
@@ -345,17 +395,17 @@ Singleton {
|
||||
}
|
||||
|
||||
function searchApplications(query) {
|
||||
if (!query || query.length === 0) {
|
||||
return applications;
|
||||
}
|
||||
if (!query || query.length === 0)
|
||||
return getVisibleApplications();
|
||||
if (applications.length === 0)
|
||||
return [];
|
||||
|
||||
const queryLower = query.toLowerCase().trim();
|
||||
const scoredApps = [];
|
||||
const results = [];
|
||||
const visibleApps = getVisibleApplications();
|
||||
|
||||
for (const app of applications) {
|
||||
for (const app of visibleApps) {
|
||||
const name = (app.name || "").toLowerCase();
|
||||
const genericName = (app.genericName || "").toLowerCase();
|
||||
const comment = (app.comment || "").toLowerCase();
|
||||
@@ -440,10 +490,58 @@ Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
if (SessionData.searchAppActions) {
|
||||
const actionResults = searchAppActions(queryLower, visibleApps);
|
||||
for (const actionResult of actionResults) {
|
||||
scoredApps.push({
|
||||
app: actionResult.app,
|
||||
score: actionResult.score
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
scoredApps.sort((a, b) => b.score - a.score);
|
||||
return scoredApps.slice(0, maxResults).map(item => item.app);
|
||||
}
|
||||
|
||||
function searchAppActions(query, apps) {
|
||||
const results = [];
|
||||
for (const app of apps) {
|
||||
if (!app.actions || app.actions.length === 0)
|
||||
continue;
|
||||
for (const action of app.actions) {
|
||||
const actionName = (action.name || "").toLowerCase();
|
||||
if (!actionName)
|
||||
continue;
|
||||
|
||||
let score = 0;
|
||||
if (actionName === query) {
|
||||
score = 8000;
|
||||
} else if (actionName.startsWith(query)) {
|
||||
score = 4000;
|
||||
} else if (actionName.includes(query)) {
|
||||
score = 400;
|
||||
}
|
||||
|
||||
if (score > 0) {
|
||||
results.push({
|
||||
app: {
|
||||
name: action.name,
|
||||
icon: action.icon || app.icon,
|
||||
comment: app.name,
|
||||
categories: app.categories || [],
|
||||
isAction: true,
|
||||
parentApp: app,
|
||||
actionData: action
|
||||
},
|
||||
score: score
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function getCategoriesForApp(app) {
|
||||
if (!app?.categories)
|
||||
return [];
|
||||
@@ -525,17 +623,15 @@ Singleton {
|
||||
}
|
||||
|
||||
function getAppsInCategory(category) {
|
||||
if (category === I18n.tr("All")) {
|
||||
return applications;
|
||||
}
|
||||
const visibleApps = getVisibleApplications();
|
||||
if (category === I18n.tr("All"))
|
||||
return visibleApps;
|
||||
|
||||
// Check if it's a plugin category
|
||||
const pluginItems = getPluginItems(category, "");
|
||||
if (pluginItems.length > 0) {
|
||||
if (pluginItems.length > 0)
|
||||
return pluginItems;
|
||||
}
|
||||
|
||||
return applications.filter(app => {
|
||||
return visibleApps.filter(app => {
|
||||
const appCategories = getCategoriesForApp(app);
|
||||
return appCategories.includes(category);
|
||||
});
|
||||
|
||||
@@ -182,17 +182,44 @@ Singleton {
|
||||
return /[;&|<>()$`\\"']/.test(prefix);
|
||||
}
|
||||
|
||||
function parseEnvVars(envVarsStr) {
|
||||
if (!envVarsStr || envVarsStr.trim().length === 0)
|
||||
return {};
|
||||
const envObj = {};
|
||||
const pairs = envVarsStr.trim().split(/\s+/);
|
||||
for (const pair of pairs) {
|
||||
const eqIndex = pair.indexOf("=");
|
||||
if (eqIndex > 0) {
|
||||
const key = pair.substring(0, eqIndex);
|
||||
const value = pair.substring(eqIndex + 1);
|
||||
envObj[key] = value;
|
||||
}
|
||||
}
|
||||
return envObj;
|
||||
}
|
||||
|
||||
function launchDesktopEntry(desktopEntry, useNvidia) {
|
||||
let cmd = desktopEntry.command;
|
||||
if (useNvidia && nvidiaCommand)
|
||||
cmd = [nvidiaCommand].concat(cmd);
|
||||
|
||||
const appId = desktopEntry.id || desktopEntry.execString || desktopEntry.exec || "";
|
||||
const override = SessionData.getAppOverride(appId);
|
||||
|
||||
if (override?.extraFlags) {
|
||||
const extraArgs = override.extraFlags.trim().split(/\s+/).filter(arg => arg.length > 0);
|
||||
cmd = cmd.concat(extraArgs);
|
||||
}
|
||||
|
||||
const userPrefix = SettingsData.launchPrefix?.trim() || "";
|
||||
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
|
||||
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
|
||||
const workDir = desktopEntry.workingDirectory || Quickshell.env("HOME");
|
||||
const cursorEnv = typeof SettingsData.getCursorEnvironment === "function" ? SettingsData.getCursorEnvironment() : {};
|
||||
|
||||
const overrideEnv = override?.envVars ? parseEnvVars(override.envVars) : {};
|
||||
const finalEnv = Object.assign({}, cursorEnv, overrideEnv);
|
||||
|
||||
if (desktopEntry.runInTerminal) {
|
||||
const terminal = Quickshell.env("TERMINAL") || "xterm";
|
||||
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
|
||||
@@ -200,7 +227,7 @@ Singleton {
|
||||
Quickshell.execDetached({
|
||||
command: [terminal, "-e", "sh", "-c", shellCmd],
|
||||
workingDirectory: workDir,
|
||||
environment: cursorEnv
|
||||
environment: finalEnv
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -210,7 +237,7 @@ Singleton {
|
||||
Quickshell.execDetached({
|
||||
command: ["sh", "-c", `${prefix} ${escapedCmd}`],
|
||||
workingDirectory: workDir,
|
||||
environment: cursorEnv
|
||||
environment: finalEnv
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -221,7 +248,7 @@ Singleton {
|
||||
Quickshell.execDetached({
|
||||
command: cmd,
|
||||
workingDirectory: workDir,
|
||||
environment: cursorEnv
|
||||
environment: finalEnv
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ Item {
|
||||
property bool shouldBeVisible: false
|
||||
property var customKeyboardFocus: null
|
||||
property bool backgroundInteractive: true
|
||||
property bool contentHandlesKeys: false
|
||||
|
||||
property real storedBarThickness: Theme.barHeight - 4
|
||||
property real storedBarSpacing: 4
|
||||
@@ -461,8 +462,12 @@ Item {
|
||||
id: focusHelper
|
||||
parent: contentContainer
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
visible: !root.contentHandlesKeys
|
||||
enabled: !root.contentHandlesKeys
|
||||
focus: !root.contentHandlesKeys
|
||||
Keys.onPressed: event => {
|
||||
if (root.contentHandlesKeys)
|
||||
return;
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
close();
|
||||
event.accepted = true;
|
||||
|
||||
Reference in New Issue
Block a user