1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00
Files
DankMaterialShell/quickshell/Modals/Spotlight/SpotlightResults.qml
Lucas 4723bffcd2 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
2025-11-28 19:36:35 -05:00

225 lines
9.0 KiB
QML

import QtQuick
import Quickshell
import Quickshell.Widgets
import qs.Common
import qs.Widgets
Rectangle {
id: resultsContainer
property var appLauncher: null
signal itemRightClicked(int index, var modelData, real mouseX, real mouseY)
function resetScroll() {
resultsList.contentY = 0
if (gridLoader.item) {
gridLoader.item.contentY = 0
}
}
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
DankListView {
id: resultsList
property int itemHeight: 60
property int iconSize: 40
property bool showDescription: true
property int itemSpacing: Theme.spacingS
property bool hoverUpdatesSelection: false
property bool keyboardNavigationActive: appLauncher ? appLauncher.keyboardNavigationActive : false
signal keyboardNavigationReset
signal itemClicked(int index, var modelData)
signal itemRightClicked(int index, var modelData, real mouseX, real mouseY)
function ensureVisible(index) {
if (index < 0 || index >= count)
return
const itemY = index * (itemHeight + itemSpacing)
const itemBottom = itemY + itemHeight
if (itemY < contentY)
contentY = itemY
else if (itemBottom > contentY + height)
contentY = itemBottom - height
}
anchors.fill: parent
anchors.margins: Theme.spacingS
visible: appLauncher && appLauncher.viewMode === "list"
model: appLauncher ? appLauncher.model : null
currentIndex: appLauncher ? appLauncher.selectedIndex : -1
clip: true
spacing: itemSpacing
focus: true
interactive: true
cacheBuffer: Math.max(0, Math.min(height * 2, 1000))
reuseItems: true
onCurrentIndexChanged: {
if (keyboardNavigationActive)
ensureVisible(currentIndex)
}
onItemClicked: (index, modelData) => {
if (appLauncher)
appLauncher.launchApp(modelData)
}
onItemRightClicked: (index, modelData, mouseX, mouseY) => {
resultsContainer.itemRightClicked(index, modelData, mouseX, mouseY);
}
onKeyboardNavigationReset: () => {
if (appLauncher)
appLauncher.keyboardNavigationActive = false
}
delegate: AppLauncherListDelegate {
listView: resultsList
itemHeight: resultsList.itemHeight
iconSize: resultsList.iconSize
showDescription: resultsList.showDescription
hoverUpdatesSelection: resultsList.hoverUpdatesSelection
keyboardNavigationActive: resultsList.keyboardNavigationActive
isCurrentItem: ListView.isCurrentItem
iconMaterialSizeAdjustment: 0
iconUnicodeScale: 0.8
onItemClicked: (idx, modelData) => resultsList.itemClicked(idx, modelData)
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
resultsList.itemRightClicked(idx, modelData, mouseX, mouseY)
}
onKeyboardNavigationReset: resultsList.keyboardNavigationReset
}
}
Loader {
id: gridLoader
property real _lastWidth: 0
anchors.fill: parent
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
active = false
Qt.callLater(() => {
active = true
})
}
}
sourceComponent: Component {
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
property int minCellWidth: 120
property int maxCellWidth: 160
property int cellPadding: 8
property real iconSizeRatio: 0.55
property int maxIconSize: 48
property int minIconSize: 32
property bool hoverUpdatesSelection: false
property bool keyboardNavigationActive: appLauncher ? appLauncher.keyboardNavigationActive : false
property int baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : (width - Theme.spacingS * 2) / columns
property int baseCellHeight: baseCellWidth + 20
property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns
property int remainingSpace: width - (actualColumns * cellWidth)
signal keyboardNavigationReset
signal itemClicked(int index, var modelData)
signal itemRightClicked(int index, var modelData, real mouseX, real mouseY)
function ensureVisible(index) {
if (index < 0 || index >= count)
return
const itemY = Math.floor(index / actualColumns) * cellHeight
const itemBottom = itemY + cellHeight
if (itemY < contentY)
contentY = itemY
else if (itemBottom > contentY + height)
contentY = itemBottom - height
}
model: appLauncher ? appLauncher.model : null
clip: true
cellWidth: baseCellWidth
cellHeight: baseCellHeight
leftMargin: Math.max(Theme.spacingS, remainingSpace / 2)
rightMargin: leftMargin
focus: true
interactive: true
cacheBuffer: Math.max(0, Math.min(height * 2, 1000))
reuseItems: true
onCurrentIndexChanged: {
if (keyboardNavigationActive)
ensureVisible(currentIndex)
}
onItemClicked: (index, modelData) => {
if (appLauncher)
appLauncher.launchApp(modelData)
}
onItemRightClicked: (index, modelData, mouseX, mouseY) => {
resultsContainer.itemRightClicked(index, modelData, mouseX, mouseY);
}
onKeyboardNavigationReset: () => {
if (appLauncher)
appLauncher.keyboardNavigationActive = false
}
delegate: AppLauncherGridDelegate {
gridView: resultsGrid
cellWidth: resultsGrid.cellWidth
cellHeight: resultsGrid.cellHeight
cellPadding: resultsGrid.cellPadding
minIconSize: resultsGrid.minIconSize
maxIconSize: resultsGrid.maxIconSize
iconSizeRatio: resultsGrid.iconSizeRatio
hoverUpdatesSelection: resultsGrid.hoverUpdatesSelection
keyboardNavigationActive: resultsGrid.keyboardNavigationActive
currentIndex: resultsGrid.currentIndex
onItemClicked: (idx, modelData) => resultsGrid.itemClicked(idx, modelData)
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
resultsGrid.itemRightClicked(idx, modelData, mouseX, mouseY)
}
onKeyboardNavigationReset: resultsGrid.keyboardNavigationReset
}
}
}
}
}