From 993f14a31fb22c2413d99f77dfbc735437f9dad7 Mon Sep 17 00:00:00 2001 From: bbedward Date: Tue, 9 Dec 2025 13:40:51 -0500 Subject: [PATCH] widgets: make dank icon picker a popup --- quickshell/Widgets/DankIconPicker.qml | 331 ++++++++++++-------------- 1 file changed, 148 insertions(+), 183 deletions(-) diff --git a/quickshell/Widgets/DankIconPicker.qml b/quickshell/Widgets/DankIconPicker.qml index 48660643..5d0386dc 100644 --- a/quickshell/Widgets/DankIconPicker.qml +++ b/quickshell/Widgets/DankIconPicker.qml @@ -1,8 +1,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Effects -import Quickshell -import Quickshell.Wayland import qs.Common import qs.Widgets @@ -10,7 +8,7 @@ Rectangle { id: root property string currentIcon: "" - property string iconType: "icon" // "icon" or "text" + property string iconType: "icon" signal iconSelected(string iconName, string iconType) @@ -18,40 +16,73 @@ Rectangle { height: 32 radius: Theme.cornerRadius color: Theme.surfaceContainer - border.color: dropdownLoader.active ? Theme.primary : Theme.outline + border.color: iconPopup.visible ? Theme.primary : Theme.outline border.width: 1 - property var iconCategories: [{ + property var iconCategories: [ + { "name": I18n.tr("Numbers"), "icons": ["looks_one", "looks_two", "looks_3", "looks_4", "looks_5", "looks_6", "filter_1", "filter_2", "filter_3", "filter_4", "filter_5", "filter_6", "filter_7", "filter_8", "filter_9", "filter_9_plus", "plus_one", "exposure_plus_1", "exposure_plus_2"] - }, { + }, + { "name": I18n.tr("Workspace"), "icons": ["work", "laptop", "desktop_windows", "folder", "view_module", "dashboard", "apps", "grid_view"] - }, { + }, + { "name": I18n.tr("Development"), "icons": ["code", "terminal", "bug_report", "build", "engineering", "integration_instructions", "data_object", "schema", "api", "webhook"] - }, { + }, + { "name": I18n.tr("Communication"), "icons": ["chat", "mail", "forum", "message", "video_call", "call", "contacts", "group", "notifications", "campaign"] - }, { + }, + { "name": I18n.tr("Media"), "icons": ["music_note", "headphones", "mic", "videocam", "photo", "movie", "library_music", "album", "radio", "volume_up"] - }, { + }, + { "name": I18n.tr("System"), "icons": ["memory", "storage", "developer_board", "monitor", "keyboard", "mouse", "battery_std", "wifi", "bluetooth", "security", "settings"] - }, { + }, + { "name": I18n.tr("Navigation"), "icons": ["home", "arrow_forward", "arrow_back", "expand_more", "expand_less", "menu", "close", "search", "filter_list", "sort"] - }, { + }, + { "name": I18n.tr("Actions"), "icons": ["add", "remove", "edit", "delete", "save", "download", "upload", "share", "content_copy", "content_paste", "content_cut", "undo", "redo"] - }, { + }, + { "name": I18n.tr("Status"), "icons": ["check", "error", "warning", "info", "done", "pending", "schedule", "update", "sync", "offline_bolt"] - }, { + }, + { "name": I18n.tr("Fun"), "icons": ["celebration", "cake", "star", "favorite", "pets", "sports_esports", "local_fire_department", "bolt", "auto_awesome", "diamond"] - }] + } + ] + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (iconPopup.visible) { + iconPopup.close(); + return; + } + const pos = root.mapToItem(Overlay.overlay, 0, 0); + const popupHeight = 500; + const overlayHeight = Overlay.overlay?.height ?? 800; + iconPopup.x = pos.x; + if (pos.y + root.height + popupHeight + 4 > overlayHeight) { + iconPopup.y = pos.y - popupHeight - 4; + } else { + iconPopup.y = pos.y + root.height + 4; + } + iconPopup.open(); + } + } Row { anchors.left: parent.left @@ -73,18 +104,11 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter width: 160 elide: Text.ElideRight - - MouseArea { - anchors.fill: parent - onClicked: { - dropdownLoader.active = !dropdownLoader.active - } - } } } DankIcon { - name: dropdownLoader.active ? "expand_less" : "expand_more" + name: iconPopup.visible ? "expand_less" : "expand_more" size: 16 color: Theme.outline anchors.right: parent.right @@ -92,185 +116,126 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter } - Loader { - id: dropdownLoader - active: false - asynchronous: true + Popup { + id: iconPopup - sourceComponent: PanelWindow { - id: dropdownPopup + parent: Overlay.overlay + width: 320 + height: Math.min(500, dropdownContent.implicitHeight + 32) + padding: 0 + modal: true + dim: false + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - visible: true - implicitWidth: 320 - implicitHeight: Math.min(500, dropdownContent.implicitHeight + 32) + background: Rectangle { color: "transparent" - WlrLayershell.layer: WlrLayershell.Overlay - WlrLayershell.exclusiveZone: -1 + } - anchors { - top: true - left: true - right: true - bottom: true - } + contentItem: Rectangle { + color: Theme.surface + radius: Theme.cornerRadius - // Top area - above popup - MouseArea { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - height: popupContainer.y - onClicked: { - dropdownLoader.active = false - } - } - - // Bottom area - below popup - MouseArea { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: popupContainer.bottom - anchors.bottom: parent.bottom - onClicked: { - dropdownLoader.active = false - } - } - - // Left area - left of popup - MouseArea { - anchors.left: parent.left - anchors.top: popupContainer.top - anchors.bottom: popupContainer.bottom - width: popupContainer.x - onClicked: { - dropdownLoader.active = false - } - } - - // Right area - right of popup - MouseArea { - anchors.right: parent.right - anchors.top: popupContainer.top - anchors.bottom: popupContainer.bottom - anchors.left: popupContainer.right - onClicked: { - dropdownLoader.active = false - } + layer.enabled: true + layer.effect: MultiEffect { + shadowEnabled: true + shadowColor: Theme.shadowStrong + shadowBlur: 0.8 + shadowHorizontalOffset: 0 + shadowVerticalOffset: 4 } Rectangle { - id: popupContainer - width: 320 - height: Math.min(500, dropdownContent.implicitHeight + 32) - x: Math.max(16, Math.min(root.mapToItem(null, 0, 0).x, parent.width - width - 16)) - y: Math.max(16, Math.min(root.mapToItem(null, 0, root.height + 4).y, parent.height - height - 16)) - radius: Theme.cornerRadius - color: Theme.surface + width: 24 + height: 24 + radius: 12 + color: closeMouseArea.containsMouse ? Theme.errorHover : "transparent" + anchors.top: parent.top + anchors.right: parent.right + anchors.topMargin: Theme.spacingS + anchors.rightMargin: Theme.spacingS + z: 1 - layer.enabled: true - layer.effect: MultiEffect { - shadowEnabled: true - shadowColor: Theme.shadowStrong - shadowBlur: 0.8 - shadowHorizontalOffset: 0 - shadowVerticalOffset: 4 + DankIcon { + name: "close" + size: 16 + color: closeMouseArea.containsMouse ? Theme.error : Theme.outline + anchors.centerIn: parent } - // Close button - Rectangle { - width: 24 - height: 24 - radius: 12 - color: closeMouseArea.containsMouse ? Theme.errorHover : "transparent" - anchors.top: parent.top - anchors.right: parent.right - anchors.topMargin: Theme.spacingS - anchors.rightMargin: Theme.spacingS - z: 1 - - DankIcon { - name: "close" - size: 16 - color: closeMouseArea.containsMouse ? Theme.error : Theme.outline - anchors.centerIn: parent - } - - MouseArea { - id: closeMouseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - dropdownLoader.active = false - } - } - } - - DankFlickable { + MouseArea { + id: closeMouseArea anchors.fill: parent - anchors.margins: Theme.spacingS - contentHeight: dropdownContent.height - clip: true - pressDelay: 0 + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: iconPopup.close() + } + } - Column { - id: dropdownContent - width: parent.width - spacing: Theme.spacingM + DankFlickable { + anchors.fill: parent + anchors.margins: Theme.spacingS + contentHeight: dropdownContent.height + clip: true + pressDelay: 0 - // Icon categories - Repeater { - model: root.iconCategories + Column { + id: dropdownContent + width: parent.width + spacing: Theme.spacingM - Column { + Repeater { + model: root.iconCategories + + Column { + required property var modelData + width: parent.width + spacing: Theme.spacingS + + StyledText { + text: modelData.name + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceText + } + + Flow { width: parent.width - spacing: Theme.spacingS + spacing: 4 - StyledText { - text: modelData.name - font.pixelSize: Theme.fontSizeSmall - font.weight: Font.Medium - color: Theme.surfaceText - } + Repeater { + model: modelData.icons - Flow { - width: parent.width - spacing: 4 + Rectangle { + required property string modelData + width: 36 + height: 36 + radius: Theme.cornerRadius + color: iconMouseArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0) + border.color: root.currentIcon === modelData ? Theme.primary : Theme.withAlpha(Theme.primary, 0) + border.width: 2 - Repeater { - model: modelData.icons + DankIcon { + name: parent.modelData + size: 20 + color: root.currentIcon === parent.modelData ? Theme.primary : Theme.surfaceText + anchors.centerIn: parent + } - Rectangle { - width: 36 - height: 36 - radius: Theme.cornerRadius - color: iconMouseArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0) - border.color: root.currentIcon === modelData ? Theme.primary : Theme.withAlpha(Theme.primary, 0) - border.width: 2 - - DankIcon { - name: modelData - size: 20 - color: root.currentIcon === modelData ? Theme.primary : Theme.surfaceText - anchors.centerIn: parent + MouseArea { + id: iconMouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + root.iconSelected(parent.modelData, "icon"); + iconPopup.close(); } + } - MouseArea { - id: iconMouseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - root.iconSelected(modelData, "icon") - dropdownLoader.active = false - } - } - - Behavior on color { - ColorAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } + Behavior on color { + ColorAnimation { + duration: Theme.shortDuration + easing.type: Theme.standardEasing } } } @@ -284,8 +249,8 @@ Rectangle { } function setIcon(iconName, type) { - root.iconType = type - root.iconType = "icon" - root.currentIcon = iconName + root.iconType = type; + root.iconType = "icon"; + root.currentIcon = iconName; } }