1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00
Files
DankMaterialShell/Modals/Spotlight/FileSearchResults.qml

247 lines
8.9 KiB
QML

import QtQuick
import QtQuick.Effects
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: resultsContainer
property var fileSearchController: null
function resetScroll() {
filesList.contentY = 0
}
color: "transparent"
clip: true
DankListView {
id: filesList
property int itemHeight: 60
property int itemSpacing: Theme.spacingS
property bool hoverUpdatesSelection: false
property bool keyboardNavigationActive: fileSearchController ? fileSearchController.keyboardNavigationActive : false
signal keyboardNavigationReset
signal itemClicked(int index)
signal itemRightClicked(int index)
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
model: fileSearchController ? fileSearchController.model : null
currentIndex: fileSearchController ? fileSearchController.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: function (index) {
if (fileSearchController) {
const item = fileSearchController.model.get(index)
fileSearchController.openFile(item.filePath)
}
}
onItemRightClicked: function (index) {
if (fileSearchController) {
const item = fileSearchController.model.get(index)
fileSearchController.openFolder(item.filePath)
}
}
onKeyboardNavigationReset: {
if (fileSearchController)
fileSearchController.keyboardNavigationActive = false
}
delegate: Rectangle {
required property int index
required property string filePath
required property string fileName
required property string fileExtension
required property string fileType
required property string dirPath
width: ListView.view.width
height: filesList.itemHeight
radius: Theme.cornerRadius
color: ListView.isCurrentItem ? Theme.primaryPressed : fileMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingL
Item {
width: 40
height: 40
anchors.verticalCenter: parent.verticalCenter
Rectangle {
id: iconBackground
anchors.fill: parent
radius: width / 2
color: Theme.surfaceLight
visible: fileType !== "image"
DankNFIcon {
id: nerdIcon
anchors.centerIn: parent
name: {
const lowerName = fileName.toLowerCase()
if (lowerName.startsWith("dockerfile"))
return "docker"
if (lowerName.startsWith("makefile"))
return "makefile"
if (lowerName.startsWith("license"))
return "license"
if (lowerName.startsWith("readme"))
return "readme"
return fileExtension.toLowerCase()
}
size: Theme.fontSizeXLarge
color: Theme.surfaceText
}
StyledText {
anchors.centerIn: parent
text: fileExtension ? (fileExtension.length > 4 ? fileExtension.substring(0, 4) : fileExtension) : "?"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Bold
visible: !nerdIcon.visible
}
}
Loader {
anchors.fill: parent
active: fileType === "image"
sourceComponent: Image {
anchors.fill: parent
source: "file://" + filePath
fillMode: Image.PreserveAspectCrop
asynchronous: true
cache: false
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskThresholdMin: 0.5
maskSpreadAtMin: 1.0
maskSource: ShaderEffectSource {
sourceItem: Rectangle {
width: 40
height: 40
radius: 20
}
}
}
}
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: parent.width - 40 - Theme.spacingL
spacing: Theme.spacingXS
StyledText {
width: parent.width
text: fileName || ""
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideMiddle
maximumLineCount: 1
}
StyledText {
width: parent.width
text: dirPath || ""
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
elide: Text.ElideMiddle
maximumLineCount: 1
}
}
}
MouseArea {
id: fileMouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
z: 10
onEntered: {
if (filesList.hoverUpdatesSelection && !filesList.keyboardNavigationActive)
filesList.currentIndex = index
}
onPositionChanged: {
filesList.keyboardNavigationReset()
}
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
filesList.itemClicked(index)
} else if (mouse.button === Qt.RightButton) {
filesList.itemRightClicked(index)
}
}
}
}
}
Item {
anchors.fill: parent
visible: !fileSearchController || !fileSearchController.model || fileSearchController.model.count === 0
StyledText {
property string displayText: {
if (!fileSearchController) {
return ""
}
if (!DSearchService.dsearchAvailable) {
return I18n.tr("DankSearch not available")
}
if (fileSearchController.isSearching) {
return I18n.tr("Searching...")
}
if (fileSearchController.searchQuery.length === 0) {
return I18n.tr("Enter a search query")
}
if (!fileSearchController.model || fileSearchController.model.count === 0) {
return I18n.tr("No files found")
}
return ""
}
text: displayText
anchors.centerIn: parent
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
visible: displayText.length > 0
}
}
}