1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-29 16:02:51 -05:00

widgets: make dank icon picker a popup

This commit is contained in:
bbedward
2025-12-09 13:40:51 -05:00
parent 566d617508
commit 993f14a31f

View File

@@ -1,8 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Effects import QtQuick.Effects
import Quickshell
import Quickshell.Wayland
import qs.Common import qs.Common
import qs.Widgets import qs.Widgets
@@ -10,7 +8,7 @@ Rectangle {
id: root id: root
property string currentIcon: "" property string currentIcon: ""
property string iconType: "icon" // "icon" or "text" property string iconType: "icon"
signal iconSelected(string iconName, string iconType) signal iconSelected(string iconName, string iconType)
@@ -18,40 +16,73 @@ Rectangle {
height: 32 height: 32
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainer color: Theme.surfaceContainer
border.color: dropdownLoader.active ? Theme.primary : Theme.outline border.color: iconPopup.visible ? Theme.primary : Theme.outline
border.width: 1 border.width: 1
property var iconCategories: [{ property var iconCategories: [
{
"name": I18n.tr("Numbers"), "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"] "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"), "name": I18n.tr("Workspace"),
"icons": ["work", "laptop", "desktop_windows", "folder", "view_module", "dashboard", "apps", "grid_view"] "icons": ["work", "laptop", "desktop_windows", "folder", "view_module", "dashboard", "apps", "grid_view"]
}, { },
{
"name": I18n.tr("Development"), "name": I18n.tr("Development"),
"icons": ["code", "terminal", "bug_report", "build", "engineering", "integration_instructions", "data_object", "schema", "api", "webhook"] "icons": ["code", "terminal", "bug_report", "build", "engineering", "integration_instructions", "data_object", "schema", "api", "webhook"]
}, { },
{
"name": I18n.tr("Communication"), "name": I18n.tr("Communication"),
"icons": ["chat", "mail", "forum", "message", "video_call", "call", "contacts", "group", "notifications", "campaign"] "icons": ["chat", "mail", "forum", "message", "video_call", "call", "contacts", "group", "notifications", "campaign"]
}, { },
{
"name": I18n.tr("Media"), "name": I18n.tr("Media"),
"icons": ["music_note", "headphones", "mic", "videocam", "photo", "movie", "library_music", "album", "radio", "volume_up"] "icons": ["music_note", "headphones", "mic", "videocam", "photo", "movie", "library_music", "album", "radio", "volume_up"]
}, { },
{
"name": I18n.tr("System"), "name": I18n.tr("System"),
"icons": ["memory", "storage", "developer_board", "monitor", "keyboard", "mouse", "battery_std", "wifi", "bluetooth", "security", "settings"] "icons": ["memory", "storage", "developer_board", "monitor", "keyboard", "mouse", "battery_std", "wifi", "bluetooth", "security", "settings"]
}, { },
{
"name": I18n.tr("Navigation"), "name": I18n.tr("Navigation"),
"icons": ["home", "arrow_forward", "arrow_back", "expand_more", "expand_less", "menu", "close", "search", "filter_list", "sort"] "icons": ["home", "arrow_forward", "arrow_back", "expand_more", "expand_less", "menu", "close", "search", "filter_list", "sort"]
}, { },
{
"name": I18n.tr("Actions"), "name": I18n.tr("Actions"),
"icons": ["add", "remove", "edit", "delete", "save", "download", "upload", "share", "content_copy", "content_paste", "content_cut", "undo", "redo"] "icons": ["add", "remove", "edit", "delete", "save", "download", "upload", "share", "content_copy", "content_paste", "content_cut", "undo", "redo"]
}, { },
{
"name": I18n.tr("Status"), "name": I18n.tr("Status"),
"icons": ["check", "error", "warning", "info", "done", "pending", "schedule", "update", "sync", "offline_bolt"] "icons": ["check", "error", "warning", "info", "done", "pending", "schedule", "update", "sync", "offline_bolt"]
}, { },
{
"name": I18n.tr("Fun"), "name": I18n.tr("Fun"),
"icons": ["celebration", "cake", "star", "favorite", "pets", "sports_esports", "local_fire_department", "bolt", "auto_awesome", "diamond"] "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 { Row {
anchors.left: parent.left anchors.left: parent.left
@@ -73,18 +104,11 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 160 width: 160
elide: Text.ElideRight elide: Text.ElideRight
MouseArea {
anchors.fill: parent
onClicked: {
dropdownLoader.active = !dropdownLoader.active
}
}
} }
} }
DankIcon { DankIcon {
name: dropdownLoader.active ? "expand_less" : "expand_more" name: iconPopup.visible ? "expand_less" : "expand_more"
size: 16 size: 16
color: Theme.outline color: Theme.outline
anchors.right: parent.right anchors.right: parent.right
@@ -92,185 +116,126 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
Loader { Popup {
id: dropdownLoader id: iconPopup
active: false
asynchronous: true
sourceComponent: PanelWindow { parent: Overlay.overlay
id: dropdownPopup width: 320
height: Math.min(500, dropdownContent.implicitHeight + 32)
padding: 0
modal: true
dim: false
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
visible: true background: Rectangle {
implicitWidth: 320
implicitHeight: Math.min(500, dropdownContent.implicitHeight + 32)
color: "transparent" color: "transparent"
WlrLayershell.layer: WlrLayershell.Overlay }
WlrLayershell.exclusiveZone: -1
anchors { contentItem: Rectangle {
top: true color: Theme.surface
left: true radius: Theme.cornerRadius
right: true
bottom: true
}
// Top area - above popup layer.enabled: true
MouseArea { layer.effect: MultiEffect {
anchors.left: parent.left shadowEnabled: true
anchors.right: parent.right shadowColor: Theme.shadowStrong
anchors.top: parent.top shadowBlur: 0.8
height: popupContainer.y shadowHorizontalOffset: 0
onClicked: { shadowVerticalOffset: 4
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
}
} }
Rectangle { Rectangle {
id: popupContainer width: 24
width: 320 height: 24
height: Math.min(500, dropdownContent.implicitHeight + 32) radius: 12
x: Math.max(16, Math.min(root.mapToItem(null, 0, 0).x, parent.width - width - 16)) color: closeMouseArea.containsMouse ? Theme.errorHover : "transparent"
y: Math.max(16, Math.min(root.mapToItem(null, 0, root.height + 4).y, parent.height - height - 16)) anchors.top: parent.top
radius: Theme.cornerRadius anchors.right: parent.right
color: Theme.surface anchors.topMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
z: 1
layer.enabled: true DankIcon {
layer.effect: MultiEffect { name: "close"
shadowEnabled: true size: 16
shadowColor: Theme.shadowStrong color: closeMouseArea.containsMouse ? Theme.error : Theme.outline
shadowBlur: 0.8 anchors.centerIn: parent
shadowHorizontalOffset: 0
shadowVerticalOffset: 4
} }
// Close button MouseArea {
Rectangle { id: closeMouseArea
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 {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingS hoverEnabled: true
contentHeight: dropdownContent.height cursorShape: Qt.PointingHandCursor
clip: true onClicked: iconPopup.close()
pressDelay: 0 }
}
Column { DankFlickable {
id: dropdownContent anchors.fill: parent
width: parent.width anchors.margins: Theme.spacingS
spacing: Theme.spacingM contentHeight: dropdownContent.height
clip: true
pressDelay: 0
// Icon categories Column {
Repeater { id: dropdownContent
model: root.iconCategories 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 width: parent.width
spacing: Theme.spacingS spacing: 4
StyledText { Repeater {
text: modelData.name model: modelData.icons
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
}
Flow { Rectangle {
width: parent.width required property string modelData
spacing: 4 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 { DankIcon {
model: modelData.icons name: parent.modelData
size: 20
color: root.currentIcon === parent.modelData ? Theme.primary : Theme.surfaceText
anchors.centerIn: parent
}
Rectangle { MouseArea {
width: 36 id: iconMouseArea
height: 36 anchors.fill: parent
radius: Theme.cornerRadius hoverEnabled: true
color: iconMouseArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0) cursorShape: Qt.PointingHandCursor
border.color: root.currentIcon === modelData ? Theme.primary : Theme.withAlpha(Theme.primary, 0) onClicked: {
border.width: 2 root.iconSelected(parent.modelData, "icon");
iconPopup.close();
DankIcon {
name: modelData
size: 20
color: root.currentIcon === modelData ? Theme.primary : Theme.surfaceText
anchors.centerIn: parent
} }
}
MouseArea { Behavior on color {
id: iconMouseArea ColorAnimation {
anchors.fill: parent duration: Theme.shortDuration
hoverEnabled: true easing.type: Theme.standardEasing
cursorShape: Qt.PointingHandCursor
onClicked: {
root.iconSelected(modelData, "icon")
dropdownLoader.active = false
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
} }
} }
} }
@@ -284,8 +249,8 @@ Rectangle {
} }
function setIcon(iconName, type) { function setIcon(iconName, type) {
root.iconType = type root.iconType = type;
root.iconType = "icon" root.iconType = "icon";
root.currentIcon = iconName root.currentIcon = iconName;
} }
} }