From 93168ee073c9a6a32884d0798068edff6c79b1c3 Mon Sep 17 00:00:00 2001 From: bbedward Date: Tue, 7 Jul 2026 21:15:51 -0400 Subject: [PATCH] qs: launcher optimizations and weather tab optimizations --- .../Modals/DankLauncherV2/AppEditView.qml | 324 +++++++++++ .../Modals/DankLauncherV2/Controller.qml | 101 ++-- .../Modals/DankLauncherV2/LauncherContent.qml | 320 +---------- .../Modals/DankLauncherV2/ResultsList.qml | 161 +++--- quickshell/Modules/DankDash/WeatherTab.qml | 508 +++++++++--------- 5 files changed, 741 insertions(+), 673 deletions(-) create mode 100644 quickshell/Modals/DankLauncherV2/AppEditView.qml diff --git a/quickshell/Modals/DankLauncherV2/AppEditView.qml b/quickshell/Modals/DankLauncherV2/AppEditView.qml new file mode 100644 index 000000000..716debfb0 --- /dev/null +++ b/quickshell/Modals/DankLauncherV2/AppEditView.qml @@ -0,0 +1,324 @@ +import QtQuick +import qs.Common +import qs.Services +import qs.Widgets + +FocusScope { + id: root + + property var editingApp: null + property string editAppId: "" + + signal closeRequested + + function loadOverride() { + var existing = SessionData.getAppOverride(editAppId); + editNameField.text = existing?.name || ""; + editIconField.text = existing?.icon || ""; + editCommentField.text = existing?.comment || ""; + editEnvVarsField.text = existing?.envVars || ""; + editExtraFlagsField.text = existing?.extraFlags || ""; + Qt.callLater(() => editNameField.forceActiveFocus()); + } + + function saveAppOverride() { + var 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); + closeRequested(); + } + + function resetAppOverride() { + SessionData.clearAppOverride(editAppId); + closeRequested(); + } + + Keys.onPressed: event => { + if (event.key === Qt.Key_Escape) { + closeRequested(); + event.accepted = true; + } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + if (event.modifiers & Qt.ControlModifier) { + saveAppOverride(); + event.accepted = true; + } + } else if (event.key === Qt.Key_S && event.modifiers & Qt.ControlModifier) { + saveAppOverride(); + event.accepted = true; + } + } + + 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 : Theme.withAlpha(Theme.surfaceHover, 0) + + DankIcon { + anchors.centerIn: parent + name: "arrow_back" + size: 20 + color: Theme.surfaceText + } + + MouseArea { + id: backButtonArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: root.closeRequested() + } + } + + Image { + width: 40 + height: 40 + source: Paths.resolveIconUrl(root.editingApp?.icon || "application-x-executable") + sourceSize.width: 40 + sourceSize.height: 40 + fillMode: Image.PreserveAspectFit + anchors.verticalCenter: parent.verticalCenter + } + + Column { + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingXXS + + StyledText { + text: I18n.tr("Edit App") + font.pixelSize: Theme.fontSizeLarge + color: Theme.surfaceText + font.weight: Font.Medium + } + + StyledText { + text: root.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: Theme.spacingXS + + StyledText { + text: I18n.tr("Name") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + font.weight: Font.Medium + } + + DankTextField { + id: editNameField + width: parent.width + placeholderText: root.editingApp?.name || "" + keyNavigationTab: editIconField + keyNavigationBacktab: editExtraFlagsField + } + } + + Column { + width: parent.width + spacing: Theme.spacingXS + + StyledText { + text: I18n.tr("Icon") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + font.weight: Font.Medium + } + + DankTextField { + id: editIconField + width: parent.width + placeholderText: root.editingApp?.icon || "" + keyNavigationTab: editCommentField + keyNavigationBacktab: editNameField + } + } + + Column { + width: parent.width + spacing: Theme.spacingXS + + StyledText { + text: I18n.tr("Description") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + font.weight: Font.Medium + } + + DankTextField { + id: editCommentField + width: parent.width + placeholderText: root.editingApp?.comment || "" + keyNavigationTab: editEnvVarsField + keyNavigationBacktab: editIconField + } + } + + Column { + width: parent.width + spacing: Theme.spacingXS + + 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 + placeholderText: "VAR=value" + keyNavigationTab: editExtraFlagsField + keyNavigationBacktab: editCommentField + } + } + + Column { + width: parent.width + spacing: Theme.spacingXS + + StyledText { + text: I18n.tr("Extra Arguments") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + font.weight: Font.Medium + } + + DankTextField { + id: editExtraFlagsField + width: parent.width + 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(root.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: root.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: root.closeRequested() + } + } + + Rectangle { + id: saveButton + width: 90 + height: 40 + radius: Theme.cornerRadius + color: saveButtonArea.containsMouse ? Theme.withAlpha(Theme.primary, 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: root.saveAppOverride() + } + } + } + } +} diff --git a/quickshell/Modals/DankLauncherV2/Controller.qml b/quickshell/Modals/DankLauncherV2/Controller.qml index ef668ea13..503df53c8 100644 --- a/quickshell/Modals/DankLauncherV2/Controller.qml +++ b/quickshell/Modals/DankLauncherV2/Controller.qml @@ -930,23 +930,10 @@ Item { searchCompleted(); return; } else if (!searchQuery) { - var emptyTriggerOrdered = getEmptyTriggerPluginsOrdered(); - for (var i = 0; i < emptyTriggerOrdered.length; i++) { - var plugin = emptyTriggerOrdered[i]; - if (plugin.isBuiltIn) { - var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery); - for (var j = 0; j < blItems.length; j++) - allItems.push(transformBuiltInSearchItem(blItems[j], plugin.id)); - } else { - var pItems = getPluginItems(plugin.id, searchQuery); - for (var j = 0; j < pItems.length; j++) - allItems.push(pItems[j]); - } - } - - var browseItems = getPluginBrowseItems(); - for (var i = 0; i < browseItems.length; i++) - allItems.push(browseItems[i]); + _pluginPhasePending = true; + _phase1Items = allItems.slice(); + _pluginPhaseForceFirst = shouldResetSelection; + pluginPhaseTimer.restart(); } } @@ -977,11 +964,6 @@ Item { flatModel = Scorer.flattenSections(newSections); sections = newSections; - if (!AppSearchService.isCacheValid() && !searchQuery && searchMode === "all" && !pluginFilter) { - AppSearchService.setCachedDefaultSections(sections, flatModel); - _saveDiskCache(sections); - } - selectedFlatIndex = restoreSelection(flatModel); updateSelectedItem(); @@ -991,7 +973,9 @@ Item { function _performPluginPhase() { _pluginPhasePending = false; - if (!searchQuery || searchQuery.length < 2 || searchMode !== "all") + if (searchMode !== "all") + return; + if (searchQuery && searchQuery.length < 2) return; var currentVersion = _searchVersion; @@ -999,27 +983,49 @@ Item { var allItems = _phase1Items; _phase1Items = []; - var allPluginsOrdered = getAllVisiblePluginsOrdered(); - var maxPerPlugin = 10; - for (var i = 0; i < allPluginsOrdered.length; i++) { - if (currentVersion !== _searchVersion) - return; - var plugin = allPluginsOrdered[i]; - if (plugin.isBuiltIn && (plugin.id === "dms_settings_search" || plugin.id === "dms_clipboard_search")) - continue; - if (plugin.isBuiltIn) { - var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery); - var blLimit = Math.min(blItems.length, maxPerPlugin); - for (var j = 0; j < blLimit; j++) { - var item = transformBuiltInSearchItem(blItems[j], plugin.id); - item._preScored = 900 - j; - allItems.push(item); + if (!searchQuery) { + var emptyTriggerOrdered = getEmptyTriggerPluginsOrdered(); + for (var i = 0; i < emptyTriggerOrdered.length; i++) { + if (currentVersion !== _searchVersion) + return; + var plugin = emptyTriggerOrdered[i]; + if (plugin.isBuiltIn) { + var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery); + for (var j = 0; j < blItems.length; j++) + allItems.push(transformBuiltInSearchItem(blItems[j], plugin.id)); + } else { + var pItems = getPluginItems(plugin.id, searchQuery); + for (var j = 0; j < pItems.length; j++) + allItems.push(pItems[j]); } - } else { - var pItems = getPluginItems(plugin.id, searchQuery, maxPerPlugin); - for (var j = 0; j < pItems.length; j++) { - pItems[j]._preScored = 900 - j; - allItems.push(pItems[j]); + } + + var browseItems = getPluginBrowseItems(); + for (var i = 0; i < browseItems.length; i++) + allItems.push(browseItems[i]); + } else { + var allPluginsOrdered = getAllVisiblePluginsOrdered(); + var maxPerPlugin = 10; + for (var i = 0; i < allPluginsOrdered.length; i++) { + if (currentVersion !== _searchVersion) + return; + var plugin = allPluginsOrdered[i]; + if (plugin.isBuiltIn && (plugin.id === "dms_settings_search" || plugin.id === "dms_clipboard_search")) + continue; + if (plugin.isBuiltIn) { + var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery); + var blLimit = Math.min(blItems.length, maxPerPlugin); + for (var j = 0; j < blLimit; j++) { + var item = transformBuiltInSearchItem(blItems[j], plugin.id); + item._preScored = 900 - j; + allItems.push(item); + } + } else { + var pItems = getPluginItems(plugin.id, searchQuery, maxPerPlugin); + for (var j = 0; j < pItems.length; j++) { + pItems[j]._preScored = 900 - j; + allItems.push(pItems[j]); + } } } } @@ -1029,7 +1035,8 @@ Item { var dynamicDefs = buildDynamicSectionDefs(allItems); var scoredItems = Scorer.scoreItems(allItems, searchQuery, getFrecencyForItem); - var newSections = Scorer.groupBySection(scoredItems, dynamicDefs, false, 50); + var sortAlpha = !searchQuery && SettingsData.sortAppsAlphabetically; + var newSections = Scorer.groupBySection(scoredItems, dynamicDefs, sortAlpha, searchQuery ? 50 : 500); if (currentVersion !== _searchVersion) return; @@ -1043,6 +1050,12 @@ Item { _applyHighlights(newSections, searchQuery); flatModel = Scorer.flattenSections(newSections); sections = newSections; + + if (!AppSearchService.isCacheValid() && !searchQuery && !pluginFilter) { + AppSearchService.setCachedDefaultSections(sections, flatModel); + _saveDiskCache(sections); + } + selectedFlatIndex = restoreSelection(flatModel); updateSelectedItem(); isSearching = false; diff --git a/quickshell/Modals/DankLauncherV2/LauncherContent.qml b/quickshell/Modals/DankLauncherV2/LauncherContent.qml index 9f1135a1e..d6aa71208 100644 --- a/quickshell/Modals/DankLauncherV2/LauncherContent.qml +++ b/quickshell/Modals/DankLauncherV2/LauncherContent.qml @@ -54,14 +54,7 @@ FocusScope { return; editingApp = app; editAppId = app.id || app.execString || app.exec || ""; - var 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() { @@ -71,27 +64,6 @@ FocusScope { Qt.callLater(() => searchField.forceActiveFocus()); } - function saveAppOverride() { - var 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(); - } - function showContextMenu(item, x, y, fromKeyboard) { if (!item) return; @@ -817,291 +789,21 @@ FocusScope { } } - FocusScope { - id: editView + Loader { + id: editLoader anchors.fill: parent anchors.margins: Theme.spacingM - visible: editMode - focus: editMode + active: root.editMode + visible: active + focus: root.editMode - Keys.onPressed: event => { - if (event.key === Qt.Key_Escape) { - closeEditMode(); - event.accepted = true; - } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { - if (event.modifiers & Qt.ControlModifier) { - saveAppOverride(); - event.accepted = true; - } - } else if (event.key === Qt.Key_S && event.modifiers & Qt.ControlModifier) { - saveAppOverride(); - event.accepted = true; - } + sourceComponent: AppEditView { + focus: true + editingApp: root.editingApp + editAppId: root.editAppId + onCloseRequested: root.closeEditMode() } - 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 : Theme.withAlpha(Theme.surfaceHover, 0) - - 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: Paths.resolveIconUrl(editingApp?.icon || "application-x-executable") - sourceSize.width: 40 - sourceSize.height: 40 - fillMode: Image.PreserveAspectFit - anchors.verticalCenter: parent.verticalCenter - } - - Column { - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingXXS - - 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: Theme.spacingXS - - StyledText { - text: I18n.tr("Name") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - font.weight: Font.Medium - } - - DankTextField { - id: editNameField - width: parent.width - placeholderText: editingApp?.name || "" - keyNavigationTab: editIconField - keyNavigationBacktab: editExtraFlagsField - } - } - - Column { - width: parent.width - spacing: Theme.spacingXS - - StyledText { - text: I18n.tr("Icon") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - font.weight: Font.Medium - } - - DankTextField { - id: editIconField - width: parent.width - placeholderText: editingApp?.icon || "" - keyNavigationTab: editCommentField - keyNavigationBacktab: editNameField - } - } - - Column { - width: parent.width - spacing: Theme.spacingXS - - StyledText { - text: I18n.tr("Description") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - font.weight: Font.Medium - } - - DankTextField { - id: editCommentField - width: parent.width - placeholderText: editingApp?.comment || "" - keyNavigationTab: editEnvVarsField - keyNavigationBacktab: editIconField - } - } - - Column { - width: parent.width - spacing: Theme.spacingXS - - 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 - placeholderText: "VAR=value" - keyNavigationTab: editExtraFlagsField - keyNavigationBacktab: editCommentField - } - } - - Column { - width: parent.width - spacing: Theme.spacingXS - - StyledText { - text: I18n.tr("Extra Arguments") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - font.weight: Font.Medium - } - - DankTextField { - id: editExtraFlagsField - width: parent.width - 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 ? Theme.withAlpha(Theme.primary, 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() - } - } - } - } + onLoaded: item.loadOverride() } } diff --git a/quickshell/Modals/DankLauncherV2/ResultsList.qml b/quickshell/Modals/DankLauncherV2/ResultsList.qml index 7bc096124..2f1cb0090 100644 --- a/quickshell/Modals/DankLauncherV2/ResultsList.qml +++ b/quickshell/Modals/DankLauncherV2/ResultsList.qml @@ -241,107 +241,124 @@ Item { required property var modelData required property int index + readonly property string rowType: modelData?.type ?? "" + width: mainListView.width height: modelData?.height ?? 52 - SectionHeader { + Loader { 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"; + active: delegateRoot.rowType === "header" + visible: active + sourceComponent: SectionHeader { + 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 + transientSurfaceTracker: root.transientSurfaceTracker } - 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 - transientSurfaceTracker: root.transientSurfaceTracker } - ResultItem { + Loader { anchors.fill: parent anchors.topMargin: 2 anchors.bottomMargin: 2 - 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 + active: delegateRoot.rowType === "list_item" + visible: active + sourceComponent: ResultItem { + item: delegateRoot.modelData?.item ?? null + isSelected: (delegateRoot.modelData?.flatIndex ?? -1) === root.controller?.selectedFlatIndex + controller: root.controller + flatIndex: delegateRoot.modelData?.flatIndex ?? -1 - onClicked: { - if (root.controller && delegateRoot.modelData?.item) { - root.controller.executeItem(delegateRoot.modelData.item); + 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); + onRightClicked: (mouseX, mouseY) => { + root.itemRightClicked(delegateRoot.modelData?.flatIndex ?? -1, delegateRoot.modelData?.item ?? null, mouseX, mouseY); + } } } - Row { - id: gridRowContent + Loader { anchors.fill: parent - visible: delegateRoot.modelData?.type === "grid_row" + active: delegateRoot.rowType === "grid_row" + visible: active + sourceComponent: Row { + Repeater { + model: delegateRoot.modelData?.items ?? [] - Repeater { - model: delegateRoot.modelData?.type === "grid_row" ? (delegateRoot.modelData?.items ?? []) : [] + Item { + id: gridCellDelegate + required property var modelData + required property int index - Item { - id: gridCellDelegate - required property var modelData - required property int index + readonly property bool isTile: delegateRoot.modelData?.viewMode === "tile" + readonly property real cellWidth: isTile ? Math.floor(delegateRoot.width / 3) : Math.floor(delegateRoot.width / (delegateRoot.modelData?.cols ?? root.gridColumns)) - 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 - width: cellWidth - height: delegateRoot.height + Loader { + width: parent.width - 4 + height: parent.height - 4 + anchors.centerIn: parent + sourceComponent: gridCellDelegate.isTile ? tileCellComponent : gridCellComponent - 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 + Component { + id: gridCellComponent - onClicked: { - if (root.controller && gridCellDelegate.modelData?.item) { - root.controller.executeItem(gridCellDelegate.modelData.item); + GridItem { + 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); + } + } } - } - onRightClicked: (mouseX, mouseY) => { - root.itemRightClicked(gridCellDelegate.modelData?.flatIndex ?? -1, gridCellDelegate.modelData?.item ?? null, mouseX, mouseY); - } - } + Component { + id: tileCellComponent - 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 + TileItem { + 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); + 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); + } + } } } - - onRightClicked: (mouseX, mouseY) => { - root.itemRightClicked(gridCellDelegate.modelData?.flatIndex ?? -1, gridCellDelegate.modelData?.item ?? null, mouseX, mouseY); - } } } } diff --git a/quickshell/Modules/DankDash/WeatherTab.qml b/quickshell/Modules/DankDash/WeatherTab.qml index 6b673e446..437ed1556 100644 --- a/quickshell/Modules/DankDash/WeatherTab.qml +++ b/quickshell/Modules/DankDash/WeatherTab.qml @@ -550,276 +550,288 @@ Item { color: "transparent" - Rectangle { - anchors.fill: parent - opacity: skyBox.backgroundOpacity - - gradient: Gradient { - GradientStop { - position: 0.0 - color: Theme.withAlpha(skyBox.blackColor, 0.0) - } - GradientStop { - position: 0.05 - color: skyBox.topColor - } - GradientStop { - position: 0.3 - color: skyBox.topColor - } - GradientStop { - position: 0.5 - color: skyBox.topColor - } - GradientStop { - position: 0.501 - color: skyBox.blackColor - } - GradientStop { - position: 0.9 - color: skyBox.blackColor - } - GradientStop { - position: 1.0 - color: Theme.withAlpha(skyBox.blackColor, 0.0) - } - } - } - property var currentDate: dateStepper.currentDate property var hMargin: 0 property var vMargin: Theme.spacingM property var effectiveHeight: skyBox.height - 2 * vMargin property var effectiveWidth: skyBox.width - 2 * hMargin - StyledText { - text: parent.sunTime?.period ?? "" - font.pixelSize: Theme.fontSizeSmall - color: Theme.withAlpha(Theme.surfaceText, 0.7) - x: 0 - y: 0 - } - - Shape { - id: skyShape - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - height: parent.height / 2 - opacity: skyBox.backgroundOpacity - - ShapePath { - strokeColor: "transparent" - fillGradient: RadialGradient { - centerX: skyBox.hMargin + sun.x + sun.width / 2 - centerY: skyBox.vMargin + sun.y + 30 - centerRadius: { - const a = Math.abs((skyBox.sunTime?.dayPercent ?? 0) - 0.5); - const out = 200 * (0.5 - a * a); - return out; - } - focalX: skyBox.hMargin + sun.x + sun.width / 2 - focalY: skyBox.vMargin + sun.y - GradientStop { - position: 0 - color: skyBox.sunColor - } - GradientStop { - position: 0.3 - color: Theme.blendAlpha(skyBox.sunColor, 0.5) - } - GradientStop { - position: 1 - color: "transparent" - } - } - PathLine { - x: 0 - y: 0 - } - PathLine { - x: skyShape.width - y: 0 - } - PathLine { - x: skyShape.width - y: skyShape.height - } - PathLine { - x: 0 - y: skyShape.height - } - } - - ShapePath { - strokeColor: "transparent" - fillGradient: RadialGradient { - centerX: sun.x - centerY: sun.y - centerRadius: 500 - focalX: centerX - focalY: centerY + 0.99 * (centerRadius - focalRadius) - focalRadius: 10 - GradientStop { - position: 0 - color: skyBox.sunColor - } - GradientStop { - position: 0.45 - color: skyBox.sunColor - } - GradientStop { - position: 0.55 - color: "transparent" - } - GradientStop { - position: 1 - color: "transparent" - } - } - PathLine { - x: 0 - y: 0 - } - PathLine { - x: skyShape.width - y: 0 - } - PathLine { - x: skyShape.width - y: skyShape.height - } - PathLine { - x: 0 - y: skyShape.height - } - } - } - - Canvas { - id: ecliptic - anchors.fill: parent - property var points: WeatherService.getEcliptic(dateStepper.currentDate) - - function getX(index) { - return points[index].h * skyBox.effectiveWidth + skyBox.hMargin; - } - function getY(index) { - return points[index].v * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 + skyBox.vMargin; - } - - onPointsChanged: requestPaint() - - onPaint: { - var ctx = getContext("2d"); - ctx.clearRect(0, 0, width, height); - if (!points || points.length === 0) - return; - ctx.beginPath(); - ctx.moveTo(getX(0), getY(0)); - for (var i = 1; i < points.length; i++) { - ctx.lineTo(getX(i), getY(i)); - } - ctx.strokeStyle = Theme.withAlpha(Theme.outline, 0.2); - ctx.stroke(); - } - } - property real latitude: WeatherService.getLocation()?.latitude ?? 0 property real sunDeclination: WeatherService.getSunDeclination(dateStepper.currentDate) readonly property bool solarNoonIsSouth: latitude > sunDeclination - StyledText { - id: middle - text: skyBox.solarNoonIsSouth ? "S" : "N" - font.pixelSize: Theme.fontSizeSmall - color: Theme.primary - x: skyBox.width / 2 - middle.width / 2 - y: skyBox.height / 2 - middle.height / 2 + Loader { + anchors.fill: parent + asynchronous: true + sourceComponent: skyContentComponent + opacity: status === Loader.Ready ? 1 : 0 + + Behavior on opacity { + NumberAnimation { + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } + } } + } - StyledText { - id: left - text: skyBox.solarNoonIsSouth ? "E" : "W" - font.pixelSize: Theme.fontSizeSmall - color: Theme.primary - x: skyBox.width / 4 - left.width / 2 - y: skyBox.height / 2 - left.height / 2 - } + Component { + id: skyContentComponent - StyledText { - id: right - text: skyBox.solarNoonIsSouth ? "W" : "E" - font.pixelSize: Theme.fontSizeSmall - color: Theme.primary - x: 3 * skyBox.width / 4 - right.width / 2 - y: skyBox.height / 2 - right.height / 2 - } + Item { + Rectangle { + anchors.fill: parent + opacity: skyBox.backgroundOpacity - Rectangle { - height: 1 - anchors.leftMargin: Theme.spacingS - anchors.rightMargin: Theme.spacingS - anchors.left: right.right - anchors.right: skyBox.right - anchors.verticalCenter: middle.verticalCenter - color: Theme.outline - } + gradient: Gradient { + GradientStop { + position: 0.0 + color: Theme.withAlpha(skyBox.blackColor, 0.0) + } + GradientStop { + position: 0.05 + color: skyBox.topColor + } + GradientStop { + position: 0.3 + color: skyBox.topColor + } + GradientStop { + position: 0.5 + color: skyBox.topColor + } + GradientStop { + position: 0.501 + color: skyBox.blackColor + } + GradientStop { + position: 0.9 + color: skyBox.blackColor + } + GradientStop { + position: 1.0 + color: Theme.withAlpha(skyBox.blackColor, 0.0) + } + } + } - Rectangle { - height: 1 - anchors.leftMargin: Theme.spacingS - anchors.rightMargin: Theme.spacingS - anchors.left: middle.right - anchors.right: right.left - anchors.verticalCenter: middle.verticalCenter - color: Theme.outline - } + StyledText { + text: skyBox.sunTime?.period ?? "" + font.pixelSize: Theme.fontSizeSmall + color: Theme.withAlpha(Theme.surfaceText, 0.7) + x: 0 + y: 0 + } - Rectangle { - height: 1 - anchors.leftMargin: Theme.spacingS - anchors.rightMargin: Theme.spacingS - anchors.left: left.right - anchors.right: middle.left - anchors.verticalCenter: middle.verticalCenter - color: Theme.outline - } + Shape { + id: skyShape + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + height: parent.height / 2 + opacity: skyBox.backgroundOpacity - Rectangle { - height: 1 - anchors.leftMargin: Theme.spacingS - anchors.rightMargin: Theme.spacingS - anchors.left: skyBox.left - anchors.right: left.left - anchors.verticalCenter: middle.verticalCenter - color: Theme.outline - } + ShapePath { + strokeColor: "transparent" + fillGradient: RadialGradient { + centerX: skyBox.hMargin + sun.x + sun.width / 2 + centerY: skyBox.vMargin + sun.y + 30 + centerRadius: { + const a = Math.abs((skyBox.sunTime?.dayPercent ?? 0) - 0.5); + const out = 200 * (0.5 - a * a); + return out; + } + focalX: skyBox.hMargin + sun.x + sun.width / 2 + focalY: skyBox.vMargin + sun.y + GradientStop { + position: 0 + color: skyBox.sunColor + } + GradientStop { + position: 0.3 + color: Theme.blendAlpha(skyBox.sunColor, 0.5) + } + GradientStop { + position: 1 + color: "transparent" + } + } + PathLine { + x: 0 + y: 0 + } + PathLine { + x: skyShape.width + y: 0 + } + PathLine { + x: skyShape.width + y: skyShape.height + } + PathLine { + x: 0 + y: skyShape.height + } + } - DankNFIcon { - id: moonPhase - name: WeatherService.getMoonPhase(skyBox.currentDate) || "" - size: Theme.fontSizeXLarge - color: Theme.withAlpha(Theme.surfaceText, 0.7) - rotation: (WeatherService.getMoonAngle(skyBox.currentDate) || 0) / Math.PI * 180 - visible: !!pos + ShapePath { + strokeColor: "transparent" + fillGradient: RadialGradient { + centerX: sun.x + centerY: sun.y + centerRadius: 500 + focalX: centerX + focalY: centerY + 0.99 * (centerRadius - focalRadius) + focalRadius: 10 + GradientStop { + position: 0 + color: skyBox.sunColor + } + GradientStop { + position: 0.45 + color: skyBox.sunColor + } + GradientStop { + position: 0.55 + color: "transparent" + } + GradientStop { + position: 1 + color: "transparent" + } + } + PathLine { + x: 0 + y: 0 + } + PathLine { + x: skyShape.width + y: 0 + } + PathLine { + x: skyShape.width + y: skyShape.height + } + PathLine { + x: 0 + y: skyShape.height + } + } + } - property var pos: WeatherService.getSkyArcPosition(skyBox.currentDate, false) - x: (pos?.h ?? 0) * skyBox.effectiveWidth - (moonPhase.width / 2) + skyBox.hMargin - y: (pos?.v ?? 0) * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 - (moonPhase.height / 2) + skyBox.vMargin - } + Shape { + anchors.fill: parent - DankIcon { - id: sun - name: "light_mode" - size: Theme.fontSizeXLarge - color: Theme.primary - visible: !!pos + ShapePath { + strokeColor: Theme.withAlpha(Theme.outline, 0.2) + strokeWidth: 1 + fillColor: "transparent" - property var pos: WeatherService.getSkyArcPosition(skyBox.currentDate, true) - x: (pos?.h ?? 0) * skyBox.effectiveWidth - (sun.width / 2) + skyBox.hMargin - y: (pos?.v ?? 0) * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 - (sun.height / 2) + skyBox.vMargin + PathPolyline { + path: { + const points = WeatherService.getEcliptic(dateStepper.currentDate) ?? []; + const out = []; + for (let i = 0; i < points.length; i++) { + out.push(Qt.point(points[i].h * skyBox.effectiveWidth + skyBox.hMargin, points[i].v * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 + skyBox.vMargin)); + } + return out; + } + } + } + } + + StyledText { + id: middle + text: skyBox.solarNoonIsSouth ? "S" : "N" + font.pixelSize: Theme.fontSizeSmall + color: Theme.primary + x: skyBox.width / 2 - middle.width / 2 + y: skyBox.height / 2 - middle.height / 2 + } + + StyledText { + id: left + text: skyBox.solarNoonIsSouth ? "E" : "W" + font.pixelSize: Theme.fontSizeSmall + color: Theme.primary + x: skyBox.width / 4 - left.width / 2 + y: skyBox.height / 2 - left.height / 2 + } + + StyledText { + id: right + text: skyBox.solarNoonIsSouth ? "W" : "E" + font.pixelSize: Theme.fontSizeSmall + color: Theme.primary + x: 3 * skyBox.width / 4 - right.width / 2 + y: skyBox.height / 2 - right.height / 2 + } + + Rectangle { + height: 1 + anchors.leftMargin: Theme.spacingS + anchors.rightMargin: Theme.spacingS + anchors.left: right.right + anchors.right: parent.right + anchors.verticalCenter: middle.verticalCenter + color: Theme.outline + } + + Rectangle { + height: 1 + anchors.leftMargin: Theme.spacingS + anchors.rightMargin: Theme.spacingS + anchors.left: middle.right + anchors.right: right.left + anchors.verticalCenter: middle.verticalCenter + color: Theme.outline + } + + Rectangle { + height: 1 + anchors.leftMargin: Theme.spacingS + anchors.rightMargin: Theme.spacingS + anchors.left: left.right + anchors.right: middle.left + anchors.verticalCenter: middle.verticalCenter + color: Theme.outline + } + + Rectangle { + height: 1 + anchors.leftMargin: Theme.spacingS + anchors.rightMargin: Theme.spacingS + anchors.left: parent.left + anchors.right: left.left + anchors.verticalCenter: middle.verticalCenter + color: Theme.outline + } + + DankNFIcon { + id: moonPhase + name: WeatherService.getMoonPhase(skyBox.currentDate) || "" + size: Theme.fontSizeXLarge + color: Theme.withAlpha(Theme.surfaceText, 0.7) + rotation: (WeatherService.getMoonAngle(skyBox.currentDate) || 0) / Math.PI * 180 + visible: !!pos + + property var pos: WeatherService.getSkyArcPosition(skyBox.currentDate, false) + x: (pos?.h ?? 0) * skyBox.effectiveWidth - (moonPhase.width / 2) + skyBox.hMargin + y: (pos?.v ?? 0) * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 - (moonPhase.height / 2) + skyBox.vMargin + } + + DankIcon { + id: sun + name: "light_mode" + size: Theme.fontSizeXLarge + color: Theme.primary + visible: !!pos + + property var pos: WeatherService.getSkyArcPosition(skyBox.currentDate, true) + x: (pos?.h ?? 0) * skyBox.effectiveWidth - (sun.width / 2) + skyBox.hMargin + y: (pos?.v ?? 0) * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 - (sun.height / 2) + skyBox.vMargin + } } } }