1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

spotlight: fix clipping and add context menu keyboard navigation (#840)

* spotlight: fix clipping and add context menu keyboard navigation

* prime: also detect nvidia-offload command

* spotlight: fix review nitpicks
This commit is contained in:
Lucas
2025-11-28 21:36:35 -03:00
committed by GitHub
parent 9643de3ca0
commit 4723bffcd2
11 changed files with 641 additions and 371 deletions

View File

@@ -11,8 +11,10 @@ Item {
property alias appLauncher: appLauncher
property alias searchField: searchField
property alias fileSearchController: fileSearchController
property alias resultsView: resultsView
property var parentModal: null
property string searchMode: "apps"
property bool usePopupContextMenu: false
function resetScroll() {
if (searchMode === "apps") {
@@ -146,6 +148,18 @@ Item {
fileSearchController.openSelected();
}
event.accepted = true;
} else if (event.key === Qt.Key_Menu) {
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);
menu.show(contentPos.x, contentPos.y, selectedApp, true);
}
}
event.accepted = true;
}
}
@@ -178,6 +192,52 @@ Item {
}
}
SpotlightContextMenuPopup {
id: popupContextMenu
parent: spotlightKeyHandler
appLauncher: spotlightKeyHandler.appLauncher
parentHandler: spotlightKeyHandler
searchField: spotlightKeyHandler.searchField
visible: false
z: 1000
}
MouseArea {
anchors.fill: parent
visible: usePopupContextMenu && popupContextMenu.visible
hoverEnabled: true
z: 999
onClicked: popupContextMenu.hide()
}
Loader {
id: layerContextMenuLoader
active: !spotlightKeyHandler.usePopupContextMenu
asynchronous: false
sourceComponent: Component {
SpotlightContextMenu {
appLauncher: spotlightKeyHandler.appLauncher
parentHandler: spotlightKeyHandler
parentModal: spotlightKeyHandler.parentModal
}
}
}
Connections {
target: parentModal
function onSpotlightOpenChanged() {
if (parentModal && !parentModal.spotlightOpen) {
if (layerContextMenuLoader.item) {
layerContextMenuLoader.item.hide();
}
popupContextMenu.hide();
}
}
enabled: parentModal !== null
}
Column {
anchors.fill: parent
anchors.margins: Theme.spacingM
@@ -397,8 +457,22 @@ Item {
id: resultsView
anchors.fill: parent
appLauncher: spotlightKeyHandler.appLauncher
contextMenu: contextMenu
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);
} else {
menu.show(mouseX, mouseY, modelData, false);
}
}
}
}
FileSearchResults {
@@ -410,31 +484,6 @@ Item {
}
}
SpotlightContextMenu {
id: contextMenu
appLauncher: spotlightKeyHandler.appLauncher
parentHandler: spotlightKeyHandler
}
MouseArea {
anchors.fill: parent
visible: contextMenu.visible
z: 999
onClicked: () => {
contextMenu.hide();
}
MouseArea {
x: contextMenu.x
y: contextMenu.y
width: contextMenu.width
height: contextMenu.height
onClicked: () => {}
}
}
Loader {
id: filenameTooltipLoader

View File

@@ -1,338 +1,117 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modals.Spotlight
Popup {
id: contextMenu
PanelWindow {
id: root
WlrLayershell.namespace: "dms:spotlight-context-menu"
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
property var currentApp: null
property var appLauncher: null
property var parentHandler: null
readonly property var desktopEntry: (currentApp && !currentApp.isPlugin && appLauncher && appLauncher._uniqueApps && currentApp.appIndex >= 0 && currentApp.appIndex < appLauncher._uniqueApps.length) ? appLauncher._uniqueApps[currentApp.appIndex] : null
property var parentModal: null
property real menuPositionX: 0
property real menuPositionY: 0
readonly property real shadowBuffer: 5
screen: parentModal?.effectiveScreen
function show(x, y, app) {
currentApp = app
contextMenu.x = x + 4
contextMenu.y = y + 4
contextMenu.open()
function show(x, y, app, fromKeyboard) {
fromKeyboard = fromKeyboard || false;
menuContent.currentApp = app;
let screenX = x;
let screenY = y;
if (parentModal) {
if (fromKeyboard) {
screenX = x + parentModal.alignedX;
screenY = y + parentModal.alignedY;
} else {
screenX = x + (parentModal.alignedX - shadowBuffer);
screenY = y + (parentModal.alignedY - shadowBuffer);
}
}
menuPositionX = screenX;
menuPositionY = screenY;
menuContent.selectedMenuIndex = fromKeyboard ? 0 : -1;
menuContent.keyboardNavigation = true;
visible = true;
if (parentHandler) {
parentHandler.enabled = false;
}
Qt.callLater(() => {
menuContent.keyboardHandler.forceActiveFocus();
});
}
function hide() {
contextMenu.close()
if (parentHandler) {
parentHandler.enabled = true;
}
visible = false;
}
width: Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2)
height: menuColumn.implicitHeight + Theme.spacingS * 2
padding: 0
closePolicy: Popup.CloseOnPressOutside
modal: false
dim: false
visible: false
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
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
onVisibleChanged: {
if (!visible && parentHandler) {
parentHandler.enabled = true;
}
}
enter: Transition {
NumberAnimation {
property: "opacity"
from: 0
to: 1
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
SpotlightContextMenuContent {
id: menuContent
x: {
const left = 10;
const right = root.width - width - 10;
const want = menuPositionX;
return Math.max(left, Math.min(right, want));
}
y: {
const top = 10;
const bottom = root.height - height - 10;
const want = menuPositionY;
return Math.max(top, Math.min(bottom, want));
}
appLauncher: root.appLauncher
opacity: root.visible ? 1 : 0
visible: opacity > 0
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
onHideRequested: root.hide()
}
exit: Transition {
NumberAnimation {
property: "opacity"
from: 1
to: 0
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
Column {
id: menuColumn
MouseArea {
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 {
id: pinRow
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: {
if (!desktopEntry)
return "push_pin"
const appId = desktopEntry.id || desktopEntry.execString || ""
return SessionData.isPinnedApp(appId) ? "keep_off" : "push_pin"
}
size: Theme.iconSize - 2
color: Theme.surfaceText
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: {
if (!desktopEntry)
return I18n.tr("Pin to Dock")
const appId = desktopEntry.id || desktopEntry.execString || ""
return SessionData.isPinnedApp(appId) ? 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 (!desktopEntry)
return
const appId = desktopEntry.id || desktopEntry.execString || ""
if (SessionData.isPinnedApp(appId))
SessionData.removePinnedApp(appId)
else
SessionData.addPinnedApp(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: desktopEntry && desktopEntry.actions ? desktopEntry.actions : []
Rectangle {
width: parent.width
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 && desktopEntry) {
SessionService.launchDesktopAction(desktopEntry, modelData)
if (appLauncher && contextMenu.currentApp) {
appLauncher.appLaunched(contextMenu.currentApp)
}
}
contextMenu.hide()
}
}
}
}
Rectangle {
visible: desktopEntry && desktopEntry.actions && 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 {
id: launchRow
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)
appLauncher.launchApp(contextMenu.currentApp)
contextMenu.hide()
}
}
}
Rectangle {
visible: SessionService.hasPrimeRun
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.hasPrimeRun
width: parent.width
height: 32
radius: Theme.cornerRadius
color: primeRunMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
id: primeRunRow
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: primeRunMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
if (desktopEntry) {
SessionService.launchDesktopEntry(desktopEntry, true)
if (appLauncher && contextMenu.currentApp) {
appLauncher.appLaunched(contextMenu.currentApp)
}
}
contextMenu.hide()
}
}
}
z: -1
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: root.hide()
}
}

View File

@@ -0,0 +1,292 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
property var currentApp: null
property var appLauncher: null
property int selectedMenuIndex: 0
property bool keyboardNavigation: false
signal hideRequested()
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 var menuItems: {
const items = [];
const appId = desktopEntry ? (desktopEntry.id || desktopEntry.execString || "") : "";
const isPinned = SessionData.isPinnedApp(appId);
items.push({
type: "item",
icon: isPinned ? "keep_off" : "push_pin",
text: isPinned ? I18n.tr("Unpin from Dock") : I18n.tr("Pin to Dock"),
action: togglePin
});
if (desktopEntry && desktopEntry.actions) {
items.push({
type: "separator"
});
for (let i = 0; i < desktopEntry.actions.length; i++) {
const act = desktopEntry.actions[i];
items.push({
type: "item",
text: act.name || "",
action: () => launchAction(act)
});
}
}
items.push({
type: "separator",
hidden: !desktopEntry || !desktopEntry.actions || desktopEntry.actions.length === 0
});
items.push({
type: "item",
icon: "launch",
text: I18n.tr("Launch"),
action: launchCurrentApp
});
if (SessionService.nvidiaCommand) {
items.push({
type: "separator"
});
items.push({
type: "item",
icon: "memory",
text: I18n.tr("Launch on dGPU"),
action: launchWithNvidia
});
}
return items;
}
readonly property int visibleItemCount: {
let count = 0;
for (let i = 0; i < menuItems.length; i++) {
if (menuItems[i].type === "item" && !menuItems[i].hidden) {
count++;
}
}
return count;
}
function selectNext() {
if (visibleItemCount > 0) {
selectedMenuIndex = (selectedMenuIndex + 1) % visibleItemCount;
}
}
function selectPrevious() {
if (visibleItemCount > 0) {
selectedMenuIndex = (selectedMenuIndex - 1 + visibleItemCount) % visibleItemCount;
}
}
function togglePin() {
if (!desktopEntry)
return;
const appId = desktopEntry.id || desktopEntry.execString || "";
if (SessionData.isPinnedApp(appId))
SessionData.removePinnedApp(appId);
else
SessionData.addPinnedApp(appId);
hideRequested();
}
function launchCurrentApp() {
if (currentApp && appLauncher)
appLauncher.launchApp(currentApp);
hideRequested();
}
function launchWithNvidia() {
if (desktopEntry) {
SessionService.launchDesktopEntry(desktopEntry, true);
if (appLauncher && currentApp) {
appLauncher.appLaunched(currentApp);
}
}
hideRequested();
}
function launchAction(action) {
if (desktopEntry) {
SessionService.launchDesktopAction(desktopEntry, action);
if (appLauncher && currentApp) {
appLauncher.appLaunched(currentApp);
}
}
hideRequested();
}
function activateSelected() {
let itemIndex = 0;
for (let i = 0; i < menuItems.length; i++) {
if (menuItems[i].type === "item" && !menuItems[i].hidden) {
if (itemIndex === selectedMenuIndex) {
menuItems[i].action();
return;
}
itemIndex++;
}
}
}
property alias keyboardHandler: keyboardHandler
implicitWidth: Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2)
implicitHeight: menuColumn.implicitHeight + Theme.spacingS * 2
width: implicitWidth
height: implicitHeight
Rectangle {
id: menuContainer
anchors.fill: parent
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
radius: Theme.cornerRadius
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
}
Item {
id: keyboardHandler
anchors.fill: parent
focus: keyboardNavigation
Keys.onPressed: event => {
if (event.key === Qt.Key_Down) {
selectNext();
event.accepted = true;
} else if (event.key === Qt.Key_Up) {
selectPrevious();
event.accepted = true;
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
activateSelected();
event.accepted = true;
} else if (event.key === Qt.Key_Escape) {
hideRequested();
event.accepted = true;
}
}
Column {
id: menuColumn
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: 1
Repeater {
model: menuItems
Item {
width: parent.width
height: modelData.type === "separator" ? 5 : 32
visible: !modelData.hidden
property int itemIndex: {
let count = 0;
for (let i = 0; i < index; i++) {
if (menuItems[i].type === "item" && !menuItems[i].hidden) {
count++;
}
}
return count;
}
Rectangle {
visible: modelData.type === "separator"
width: parent.width - Theme.spacingS * 2
height: parent.height
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: modelData.type === "item"
width: parent.width
height: parent.height
radius: Theme.cornerRadius
color: {
if (keyboardNavigation && selectedMenuIndex === itemIndex) {
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2);
}
return mouseArea.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.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
Item {
width: Theme.iconSize - 2
height: Theme.iconSize - 2
anchors.verticalCenter: parent.verticalCenter
DankIcon {
visible: modelData.icon !== undefined && modelData.icon !== ""
name: modelData.icon || ""
size: Theme.iconSize - 2
color: Theme.surfaceText
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
}
StyledText {
text: modelData.text || ""
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight
width: parent.width - (Theme.iconSize - 2) - Theme.spacingS
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: {
keyboardNavigation = false;
selectedMenuIndex = itemIndex;
}
onClicked: modelData.action
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,88 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Modals.Spotlight
Popup {
id: root
property var appLauncher: null
property var parentHandler: null
property var searchField: null
function show(x, y, app, fromKeyboard) {
fromKeyboard = fromKeyboard || false;
menuContent.currentApp = app;
root.x = x + 4;
root.y = y + 4;
menuContent.selectedMenuIndex = fromKeyboard ? 0 : -1;
menuContent.keyboardNavigation = true;
if (parentHandler) {
parentHandler.enabled = false;
}
open();
}
onOpened: {
Qt.callLater(() => {
menuContent.keyboardHandler.forceActiveFocus();
});
}
function hide() {
if (parentHandler) {
parentHandler.enabled = true;
}
close();
}
width: menuContent.implicitWidth
height: menuContent.implicitHeight
padding: 0
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
modal: true
dim: false
background: Item {}
onClosed: {
if (parentHandler) {
parentHandler.enabled = true;
}
if (searchField) {
Qt.callLater(() => {
searchField.forceActiveFocus();
});
}
}
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
}
}
contentItem: SpotlightContextMenuContent {
id: menuContent
appLauncher: root.appLauncher
onHideRequested: root.hide()
}
}

View File

@@ -8,7 +8,8 @@ Rectangle {
id: resultsContainer
property var appLauncher: null
property var contextMenu: null
signal itemRightClicked(int index, var modelData, real mouseX, real mouseY)
function resetScroll() {
resultsList.contentY = 0
@@ -17,6 +18,24 @@ Rectangle {
}
}
function getSelectedItemPosition() {
if (!appLauncher) return { x: 0, y: 0 };
const selectedIndex = appLauncher.selectedIndex;
if (appLauncher.viewMode === "list") {
const itemY = selectedIndex * (resultsList.itemHeight + resultsList.itemSpacing) - resultsList.contentY;
return { x: resultsList.width / 2, y: itemY + resultsList.itemHeight / 2 };
} else if (gridLoader.item) {
const grid = gridLoader.item;
const row = Math.floor(selectedIndex / grid.actualColumns);
const col = selectedIndex % grid.actualColumns;
const itemX = col * grid.cellWidth + grid.leftMargin + grid.cellWidth / 2;
const itemY = row * grid.cellHeight - grid.contentY + grid.cellHeight / 2;
return { x: itemX, y: itemY };
}
return { x: 0, y: 0 };
}
radius: Theme.cornerRadius
color: "transparent"
clip: true
@@ -67,9 +86,8 @@ Rectangle {
appLauncher.launchApp(modelData)
}
onItemRightClicked: (index, modelData, mouseX, mouseY) => {
if (contextMenu)
contextMenu.show(mouseX, mouseY, modelData)
}
resultsContainer.itemRightClicked(index, modelData, mouseX, mouseY);
}
onKeyboardNavigationReset: () => {
if (appLauncher)
appLauncher.keyboardNavigationActive = false
@@ -87,8 +105,7 @@ Rectangle {
iconUnicodeScale: 0.8
onItemClicked: (idx, modelData) => resultsList.itemClicked(idx, modelData)
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
const modalPos = resultsContainer.parent.mapFromItem(null, mouseX, mouseY)
resultsList.itemRightClicked(idx, modelData, modalPos.x, modalPos.y)
resultsList.itemRightClicked(idx, modelData, mouseX, mouseY)
}
onKeyboardNavigationReset: resultsList.keyboardNavigationReset
}
@@ -103,6 +120,14 @@ Rectangle {
anchors.margins: Theme.spacingS
visible: appLauncher && appLauncher.viewMode === "grid"
active: appLauncher && appLauncher.viewMode === "grid"
asynchronous: false
onLoaded: {
if (item) {
item.appLauncher = Qt.binding(() => resultsContainer.appLauncher);
}
}
onWidthChanged: {
if (visible && Math.abs(width - _lastWidth) > 1) {
_lastWidth = width
@@ -116,6 +141,8 @@ Rectangle {
DankGridView {
id: resultsGrid
property var appLauncher: null
property int currentIndex: appLauncher ? appLauncher.selectedIndex : -1
property int columns: appLauncher ? appLauncher.gridColumns : 4
property bool adaptiveColumns: false
@@ -167,9 +194,8 @@ Rectangle {
appLauncher.launchApp(modelData)
}
onItemRightClicked: (index, modelData, mouseX, mouseY) => {
if (contextMenu)
contextMenu.show(mouseX, mouseY, modelData)
}
resultsContainer.itemRightClicked(index, modelData, mouseX, mouseY);
}
onKeyboardNavigationReset: () => {
if (appLauncher)
appLauncher.keyboardNavigationActive = false
@@ -188,8 +214,7 @@ Rectangle {
currentIndex: resultsGrid.currentIndex
onItemClicked: (idx, modelData) => resultsGrid.itemClicked(idx, modelData)
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
const modalPos = resultsContainer.parent.mapFromItem(null, mouseX, mouseY)
resultsGrid.itemRightClicked(idx, modelData, modalPos.x, modalPos.y)
resultsGrid.itemRightClicked(idx, modelData, mouseX, mouseY)
}
onKeyboardNavigationReset: resultsGrid.keyboardNavigationReset
}

View File

@@ -749,7 +749,7 @@ DankPopout {
}
Rectangle {
visible: SessionService.hasPrimeRun
visible: SessionService.nvidiaCommand
width: parent.width - Theme.spacingS * 2
height: 5
anchors.horizontalCenter: parent.horizontalCenter
@@ -764,11 +764,11 @@ DankPopout {
}
Rectangle {
visible: SessionService.hasPrimeRun
visible: SessionService.nvidiaCommand
width: parent.width
height: 32
radius: Theme.cornerRadius
color: primeRunMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
color: nvidiaMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
anchors.left: parent.left
@@ -794,7 +794,7 @@ DankPopout {
}
MouseArea {
id: primeRunMouseArea
id: nvidiaMouseArea
anchors.fill: parent
hoverEnabled: true

View File

@@ -356,7 +356,7 @@ PanelWindow {
if (!root.desktopEntry?.actions || root.desktopEntry.actions.length === 0) {
return false
}
return !root.hidePin || (!root.isDmsWindow && root.desktopEntry && SessionService.hasPrimeRun)
return !root.hidePin || (!root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand)
}
width: parent.width
height: 1
@@ -405,10 +405,10 @@ PanelWindow {
Rectangle {
visible: {
const hasPrimeRun = !root.isDmsWindow && root.desktopEntry && SessionService.hasPrimeRun
const hasNvidia = !root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand
const hasWindow = root.appData && (root.appData.type === "window" || (root.appData.type === "grouped" && root.appData.windowCount > 0))
const hasPinOption = !root.hidePin
const hasContentAbove = hasPinOption || hasPrimeRun
const hasContentAbove = hasPinOption || hasNvidia
return hasContentAbove && hasWindow
}
width: parent.width
@@ -417,11 +417,11 @@ PanelWindow {
}
Rectangle {
visible: !root.isDmsWindow && root.desktopEntry && SessionService.hasPrimeRun
visible: !root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand
width: parent.width
height: 28
radius: Theme.cornerRadius
color: primeRunArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
color: nvidiaArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
StyledText {
anchors.left: parent.left
@@ -438,7 +438,7 @@ PanelWindow {
}
MouseArea {
id: primeRunArea
id: nvidiaArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor

View File

@@ -235,6 +235,7 @@ Scope {
id: spotlightContent
anchors.fill: parent
anchors.margins: 0
usePopupContextMenu: true
property var fakeParentModal: QtObject {
property bool spotlightOpen: spotlightContainer.visible

View File

@@ -18,7 +18,7 @@ Singleton {
property bool inhibitorAvailable: true
property bool idleInhibited: false
property string inhibitReason: "Keep system awake"
property bool hasPrimeRun: false
property string nvidiaCommand: ""
readonly property bool nativeInhibitorAvailable: {
try {
@@ -109,7 +109,23 @@ Singleton {
command: ["which", "prime-run"]
onExited: function (exitCode) {
hasPrimeRun = (exitCode === 0);
if (exitCode === 0) {
nvidiaCommand = "prime-run"
} else {
detectNvidiaOffloadProcess.running = true
}
}
}
Process {
id: detectNvidiaOffloadProcess
running: false
command: ["which", "nvidia-offload"]
onExited: function (exitCode) {
if (exitCode === 0) {
nvidiaCommand = "nvidia-offload"
}
}
}
@@ -145,10 +161,10 @@ Singleton {
return /[;&|<>()$`\\"']/.test(prefix);
}
function launchDesktopEntry(desktopEntry, usePrimeRun) {
function launchDesktopEntry(desktopEntry, useNvidia) {
let cmd = desktopEntry.command;
if (usePrimeRun && hasPrimeRun) {
cmd = ["prime-run"].concat(cmd);
if (useNvidia && nvidiaCommand) {
cmd = [nvidiaCommand].concat(cmd);
}
const userPrefix = SettingsData.launchPrefix?.trim() || "";
@@ -176,10 +192,10 @@ Singleton {
}
}
function launchDesktopAction(desktopEntry, action, usePrimeRun) {
function launchDesktopAction(desktopEntry, action, useNvidia) {
let cmd = action.command;
if (usePrimeRun && hasPrimeRun) {
cmd = ["prime-run"].concat(cmd);
if (useNvidia && nvidiaCommand) {
cmd = [nvidiaCommand].concat(cmd);
}
const userPrefix = SettingsData.launchPrefix?.trim() || "";

View File

@@ -95,10 +95,20 @@ Rectangle {
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
root.itemClicked(root.index, root.model)
} else if (mouse.button === Qt.RightButton && !root.isPlugin) {
}
}
onPressAndHold: mouse => {
if (!root.isPlugin) {
const globalPos = mapToItem(null, mouse.x, mouse.y)
root.itemRightClicked(root.index, root.model, globalPos.x, globalPos.y)
}
}
onPressed: mouse => {
if (mouse.button === Qt.RightButton && !root.isPlugin) {
const globalPos = mapToItem(null, mouse.x, mouse.y)
root.itemRightClicked(root.index, root.model, globalPos.x, globalPos.y)
mouse.accepted = true
}
}
}
}

View File

@@ -105,10 +105,20 @@ Rectangle {
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
root.itemClicked(root.index, root.model)
} else if (mouse.button === Qt.RightButton && !root.isPlugin) {
}
}
onPressAndHold: mouse => {
if (!root.isPlugin) {
const globalPos = mapToItem(null, mouse.x, mouse.y)
root.itemRightClicked(root.index, root.model, globalPos.x, globalPos.y)
}
}
onPressed: mouse => {
if (mouse.button === Qt.RightButton && !root.isPlugin) {
const globalPos = mapToItem(null, mouse.x, mouse.y)
root.itemRightClicked(root.index, root.model, globalPos.x, globalPos.y)
mouse.accepted = true
}
}
}
}