summaryrefslogtreecommitdiff
path: root/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/ResultsList.qml
diff options
context:
space:
mode:
authornippy <you@example.com>2026-04-18 13:49:56 +0200
committernippy <you@example.com>2026-04-18 13:49:56 +0200
commit5d94c0a7d44a2255b81815a52a7056a94a39842d (patch)
tree759bdea9645c6a62f9e1e4c001f7d81cccd120d2 /raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/ResultsList.qml
parente79cdf210b267f21a186255ce1a4d50029439d54 (diff)
downloadRaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.tar.gz
RaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.zip
update Raveos themes
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/ResultsList.qml')
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/ResultsList.qml498
1 files changed, 498 insertions, 0 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/ResultsList.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/ResultsList.qml
new file mode 100644
index 0000000..53a2d45
--- /dev/null
+++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/ResultsList.qml
@@ -0,0 +1,498 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import Quickshell
+import qs.Common
+import qs.Services
+import qs.Widgets
+
+Item {
+ id: root
+
+ property var controller: null
+ property int gridColumns: controller?.gridColumns ?? 4
+ property var _visualRows: []
+ property var _flatIndexToRowMap: ({})
+ property var _cumulativeHeights: []
+
+ signal itemRightClicked(int index, var item, real mouseX, real mouseY)
+
+ function _rebuildVisualModel() {
+ var sections = root.controller?.sections ?? [];
+ var rows = [];
+ var indexMap = {};
+ var cumHeights = [];
+ var cumY = 0;
+
+ for (var s = 0; s < sections.length; s++) {
+ var section = sections[s];
+ var sectionId = section.id;
+
+ cumHeights.push(cumY);
+ rows.push({
+ _rowId: "h_" + sectionId,
+ type: "header",
+ section: section,
+ sectionId: sectionId,
+ height: 32
+ });
+ cumY += 32;
+
+ if (section.collapsed)
+ continue;
+
+ var versionTrigger = root.controller?.viewModeVersion ?? 0;
+ void (versionTrigger);
+ var mode = root.controller?.getSectionViewMode(sectionId) ?? "list";
+ var items = section.items ?? [];
+ var flatStartIndex = section.flatStartIndex ?? 0;
+
+ if (mode === "list") {
+ for (var i = 0; i < items.length; i++) {
+ var flatIdx = flatStartIndex + i;
+ indexMap[flatIdx] = rows.length;
+ cumHeights.push(cumY);
+ rows.push({
+ _rowId: items[i].id,
+ type: "list_item",
+ item: items[i],
+ flatIndex: flatIdx,
+ sectionId: sectionId,
+ height: 52
+ });
+ cumY += 52;
+ }
+ } else {
+ var cols = root.controller?.getGridColumns(sectionId) ?? root.gridColumns;
+ var cellWidth = mode === "tile" ? Math.floor(root.width / 3) : Math.floor(root.width / root.gridColumns);
+ var cellHeight = mode === "tile" ? cellWidth * 0.75 : cellWidth + 24;
+ var numRows = Math.ceil(items.length / cols);
+
+ for (var r = 0; r < numRows; r++) {
+ var rowItems = [];
+ for (var c = 0; c < cols; c++) {
+ var idx = r * cols + c;
+ if (idx >= items.length)
+ break;
+ var fi = flatStartIndex + idx;
+ indexMap[fi] = rows.length;
+ rowItems.push({
+ item: items[idx],
+ flatIndex: fi
+ });
+ }
+ cumHeights.push(cumY);
+ rows.push({
+ _rowId: "gr_" + sectionId + "_" + r,
+ type: "grid_row",
+ items: rowItems,
+ sectionId: sectionId,
+ viewMode: mode,
+ cols: cols,
+ height: cellHeight
+ });
+ cumY += cellHeight;
+ }
+ }
+ }
+
+ root._flatIndexToRowMap = indexMap;
+ root._cumulativeHeights = cumHeights;
+ root._visualRows = rows;
+ }
+
+ onGridColumnsChanged: Qt.callLater(_rebuildVisualModel)
+ onWidthChanged: Qt.callLater(_rebuildVisualModel)
+
+ Connections {
+ target: root.controller
+ function onSectionsChanged() {
+ Qt.callLater(root._rebuildVisualModel);
+ }
+ function onViewModeVersionChanged() {
+ Qt.callLater(root._rebuildVisualModel);
+ }
+ function onSearchModeChanged() {
+ root._visualRows = [];
+ root._cumulativeHeights = [];
+ root._flatIndexToRowMap = {};
+ }
+ }
+
+ function resetScroll() {
+ mainListView.contentY = mainListView.originY;
+ }
+
+ function ensureVisible(index) {
+ if (index < 0 || !controller?.flatModel || index >= controller.flatModel.length)
+ return;
+ var entry = controller.flatModel[index];
+ if (!entry || entry.isHeader)
+ return;
+ var rowIndex = _flatIndexToRowMap[index];
+ if (rowIndex === undefined)
+ return;
+
+ mainListView.positionViewAtIndex(rowIndex, ListView.Contain);
+
+ if (stickyHeader.visible && rowIndex < _cumulativeHeights.length) {
+ var rowY = _cumulativeHeights[rowIndex];
+ var scrollY = mainListView.contentY - mainListView.originY;
+ if (rowY < scrollY + stickyHeader.height) {
+ mainListView.contentY = Math.max(mainListView.originY, rowY - stickyHeader.height + mainListView.originY);
+ }
+ }
+ }
+
+ function getSelectedItemPosition() {
+ var fallback = mapToItem(null, width / 2, height / 2);
+ if (!controller?.flatModel || controller.selectedFlatIndex < 0)
+ return fallback;
+
+ var entry = controller.flatModel[controller.selectedFlatIndex];
+ if (!entry || entry.isHeader)
+ return fallback;
+
+ var rowIndex = _flatIndexToRowMap[controller.selectedFlatIndex];
+ if (rowIndex === undefined)
+ return fallback;
+
+ var rowY = (rowIndex < _cumulativeHeights.length) ? _cumulativeHeights[rowIndex] : 0;
+ var row = _visualRows[rowIndex];
+ if (!row)
+ return fallback;
+
+ var itemX = width / 2;
+ var itemH = row.height;
+
+ if (row.type === "grid_row") {
+ var rowItems = row.items;
+ for (var i = 0; i < rowItems.length; i++) {
+ if (rowItems[i].flatIndex === controller.selectedFlatIndex) {
+ var cellWidth = row.viewMode === "tile" ? Math.floor(width / 3) : Math.floor(width / row.cols);
+ itemX = i * cellWidth + cellWidth / 2;
+ break;
+ }
+ }
+ }
+
+ var visualY = rowY - mainListView.contentY + mainListView.originY + itemH / 2;
+ var clampedY = Math.max(40, Math.min(height - 40, visualY));
+ return mapToItem(null, itemX, clampedY);
+ }
+
+ Connections {
+ target: root.controller
+ function onSelectedFlatIndexChanged() {
+ if (root.controller?.keyboardNavigationActive) {
+ Qt.callLater(() => root.ensureVisible(root.controller.selectedFlatIndex));
+ }
+ }
+ }
+
+ DankListView {
+ id: mainListView
+ anchors.fill: parent
+ clip: true
+ scrollBarTopMargin: (root.controller?.sections?.length > 0) ? 32 : 0
+
+ model: ScriptModel {
+ values: root._visualRows
+ objectProp: "_rowId"
+ }
+
+ add: null
+ remove: null
+ displaced: null
+ move: null
+
+ delegate: Item {
+ id: delegateRoot
+ required property var modelData
+ required property int index
+
+ width: mainListView.width
+ height: modelData?.height ?? 52
+
+ SectionHeader {
+ anchors.fill: parent
+ visible: delegateRoot.modelData?.type === "header"
+ section: delegateRoot.modelData?.section ?? null
+ controller: root.controller
+ viewMode: {
+ var vt = root.controller?.viewModeVersion ?? 0;
+ void (vt);
+ return root.controller?.getSectionViewMode(delegateRoot.modelData?.sectionId ?? "") ?? "list";
+ }
+ canChangeViewMode: {
+ var vt = root.controller?.viewModeVersion ?? 0;
+ void (vt);
+ return root.controller?.canChangeSectionViewMode(delegateRoot.modelData?.sectionId ?? "") ?? false;
+ }
+ canCollapse: root.controller?.canCollapseSection(delegateRoot.modelData?.sectionId ?? "") ?? false
+ }
+
+ ResultItem {
+ anchors.fill: parent
+ visible: delegateRoot.modelData?.type === "list_item"
+ item: delegateRoot.modelData?.type === "list_item" ? (delegateRoot.modelData?.item ?? null) : null
+ isSelected: delegateRoot.modelData?.type === "list_item" && (delegateRoot.modelData?.flatIndex ?? -1) === root.controller?.selectedFlatIndex
+ controller: root.controller
+ flatIndex: delegateRoot.modelData?.type === "list_item" ? (delegateRoot.modelData?.flatIndex ?? -1) : -1
+
+ onClicked: {
+ if (root.controller && delegateRoot.modelData?.item) {
+ root.controller.executeItem(delegateRoot.modelData.item);
+ }
+ }
+
+ onRightClicked: (mouseX, mouseY) => {
+ root.itemRightClicked(delegateRoot.modelData?.flatIndex ?? -1, delegateRoot.modelData?.item ?? null, mouseX, mouseY);
+ }
+ }
+
+ Row {
+ id: gridRowContent
+ anchors.fill: parent
+ visible: delegateRoot.modelData?.type === "grid_row"
+
+ Repeater {
+ model: delegateRoot.modelData?.type === "grid_row" ? (delegateRoot.modelData?.items ?? []) : []
+
+ Item {
+ id: gridCellDelegate
+ required property var modelData
+ required property int index
+
+ readonly property real cellWidth: delegateRoot.modelData?.viewMode === "tile" ? Math.floor(delegateRoot.width / 3) : Math.floor(delegateRoot.width / (delegateRoot.modelData?.cols ?? root.gridColumns))
+
+ width: cellWidth
+ height: delegateRoot.height
+
+ GridItem {
+ width: parent.width - 4
+ height: parent.height - 4
+ anchors.centerIn: parent
+ visible: delegateRoot.modelData?.viewMode === "grid"
+ item: gridCellDelegate.modelData?.item ?? null
+ isSelected: (gridCellDelegate.modelData?.flatIndex ?? -1) === root.controller?.selectedFlatIndex
+ controller: root.controller
+ flatIndex: gridCellDelegate.modelData?.flatIndex ?? -1
+
+ onClicked: {
+ if (root.controller && gridCellDelegate.modelData?.item) {
+ root.controller.executeItem(gridCellDelegate.modelData.item);
+ }
+ }
+
+ onRightClicked: (mouseX, mouseY) => {
+ root.itemRightClicked(gridCellDelegate.modelData?.flatIndex ?? -1, gridCellDelegate.modelData?.item ?? null, mouseX, mouseY);
+ }
+ }
+
+ TileItem {
+ width: parent.width - 4
+ height: parent.height - 4
+ anchors.centerIn: parent
+ visible: delegateRoot.modelData?.viewMode === "tile"
+ item: gridCellDelegate.modelData?.item ?? null
+ isSelected: (gridCellDelegate.modelData?.flatIndex ?? -1) === root.controller?.selectedFlatIndex
+ controller: root.controller
+ flatIndex: gridCellDelegate.modelData?.flatIndex ?? -1
+
+ onClicked: {
+ if (root.controller && gridCellDelegate.modelData?.item) {
+ root.controller.executeItem(gridCellDelegate.modelData.item);
+ }
+ }
+
+ onRightClicked: (mouseX, mouseY) => {
+ root.itemRightClicked(gridCellDelegate.modelData?.flatIndex ?? -1, gridCellDelegate.modelData?.item ?? null, mouseX, mouseY);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: bottomShadow
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ height: 24
+ z: 100
+ visible: {
+ if (BlurService.enabled)
+ return false;
+ if (mainListView.contentHeight <= mainListView.height)
+ return false;
+ var atBottom = mainListView.contentY >= mainListView.contentHeight - mainListView.height + mainListView.originY - 5;
+ if (atBottom)
+ return false;
+
+ var flatModel = root.controller?.flatModel;
+ if (!flatModel || flatModel.length === 0)
+ return false;
+ var lastItemIdx = -1;
+ for (var i = flatModel.length - 1; i >= 0; i--) {
+ if (!flatModel[i].isHeader) {
+ lastItemIdx = i;
+ break;
+ }
+ }
+ if (lastItemIdx >= 0 && root.controller?.selectedFlatIndex === lastItemIdx)
+ return false;
+ return true;
+ }
+ gradient: Gradient {
+ GradientStop {
+ position: 0.0
+ color: "transparent"
+ }
+ GradientStop {
+ position: 1.0
+ color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
+ }
+ }
+ }
+
+ Rectangle {
+ id: stickyHeader
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ height: 32
+ z: 101
+ color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
+ visible: stickyHeaderSection !== null
+
+ readonly property int versionTrigger: root.controller?.viewModeVersion ?? 0
+
+ readonly property var stickyHeaderSection: {
+ var scrollY = mainListView.contentY - mainListView.originY;
+ if (scrollY <= 0)
+ return null;
+
+ var rows = root._visualRows;
+ var heights = root._cumulativeHeights;
+ if (rows.length === 0 || heights.length === 0)
+ return null;
+
+ var lo = 0;
+ var hi = rows.length - 1;
+ while (lo < hi) {
+ var mid = (lo + hi + 1) >> 1;
+ if (mid < heights.length && heights[mid] <= scrollY)
+ lo = mid;
+ else
+ hi = mid - 1;
+ }
+
+ for (var i = lo; i >= 0; i--) {
+ if (rows[i].type === "header")
+ return rows[i].section;
+ }
+ return null;
+ }
+
+ SectionHeader {
+ width: parent.width
+ section: stickyHeader.stickyHeaderSection
+ controller: root.controller
+ viewMode: {
+ void (stickyHeader.versionTrigger);
+ return root.controller?.getSectionViewMode(stickyHeader.stickyHeaderSection?.id) ?? "list";
+ }
+ canChangeViewMode: {
+ void (stickyHeader.versionTrigger);
+ return root.controller?.canChangeSectionViewMode(stickyHeader.stickyHeaderSection?.id) ?? false;
+ }
+ canCollapse: {
+ void (stickyHeader.versionTrigger);
+ return root.controller?.canCollapseSection(stickyHeader.stickyHeaderSection?.id) ?? false;
+ }
+ isSticky: true
+ }
+ }
+
+ Item {
+ anchors.centerIn: parent
+ visible: (!root.controller?.sections || root.controller.sections.length === 0) && !root.controller?.isFileSearching
+ width: emptyColumn.implicitWidth
+ height: emptyColumn.implicitHeight
+
+ Column {
+ id: emptyColumn
+ spacing: Theme.spacingM
+
+ DankIcon {
+ anchors.horizontalCenter: parent.horizontalCenter
+ name: getEmptyIcon()
+ size: 48
+ color: Theme.outlineButton
+
+ function getEmptyIcon() {
+ var mode = root.controller?.searchMode ?? "all";
+ switch (mode) {
+ case "files":
+ var fileType = root.controller?.fileSearchType ?? "all";
+ switch (fileType) {
+ case "dir":
+ return "folder_open";
+ case "file":
+ return "insert_drive_file";
+ default:
+ return "folder_open";
+ }
+ case "plugins":
+ return "extension";
+ case "apps":
+ return "apps";
+ default:
+ return "search_off";
+ }
+ }
+ }
+
+ StyledText {
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: getEmptyText()
+ font.pixelSize: Theme.fontSizeMedium
+ color: Theme.surfaceVariantText
+ horizontalAlignment: Text.AlignHCenter
+
+ function getEmptyText() {
+ var mode = root.controller?.searchMode ?? "all";
+ var hasQuery = root.controller?.searchQuery?.length > 0;
+
+ switch (mode) {
+ case "files":
+ if (!DSearchService.dsearchAvailable)
+ return I18n.tr("File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch");
+ if (!hasQuery)
+ return I18n.tr("Type to search files");
+ if (root.controller.searchQuery.length < 2)
+ return I18n.tr("Type at least 2 characters");
+ var fileType = root.controller?.fileSearchType ?? "all";
+ switch (fileType) {
+ case "dir":
+ return I18n.tr("No folders found");
+ case "file":
+ return I18n.tr("No files found");
+ default:
+ return I18n.tr("No results found");
+ }
+ case "plugins":
+ return hasQuery ? I18n.tr("No plugin results") : I18n.tr("Browse or search plugins");
+ case "apps":
+ return I18n.tr("No apps found");
+ default:
+ return I18n.tr("No results found");
+ }
+ }
+ }
+ }
+ }
+}