CustomButtonWidget: add icon picker to improve usability

This commit is contained in:
Ly-sec 2025-09-09 15:12:46 +02:00
parent ee44920fa4
commit 61cf7ab843
2 changed files with 182 additions and 6 deletions

View file

@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
import qs.Commons
import qs.Widgets
import qs.Services
@ -9,7 +10,6 @@ ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
@ -22,16 +22,190 @@ ColumnLayout {
return settings
}
// Icon setting
NTextInput {
id: iconInput
Layout.fillWidth: true
label: "Icon Name"
description: "Choose a name from the Material Icon set."
placeholderText: "Enter icon name (e.g., favorite, home, settings)"
description: "Pick from Bootstrap Icons or type a name."
placeholderText: "Enter icon name (e.g., speedometer2, gear, house)"
text: widgetData?.icon || widgetMetadata.icon
}
RowLayout {
spacing: Style.marginS * scaling
Layout.alignment: Qt.AlignLeft
NIcon {
Layout.alignment: Qt.AlignVCenter
icon: iconInput.text
visible: iconInput.text !== ""
}
NButton {
text: "Browse"
onClicked: iconPicker.open()
}
}
Popup {
id: iconPicker
modal: true
property real panelWidth: {
var w = Math.round(Math.max(Screen.width * 0.35, 900) * scaling)
w = Math.min(w, Screen.width - Style.marginL * 2)
return w
}
property real panelHeight: {
var h = Math.round(Math.max(Screen.height * 0.65, 700) * scaling)
h = Math.min(h, Screen.height - Style.barHeight * scaling - Style.marginL * 2)
return h
}
width: panelWidth
height: panelHeight
anchors.centerIn: Overlay.overlay
padding: Style.marginXL * scaling
property string query: ""
property string selectedIcon: ""
property var allIcons: Object.keys(Bootstrap.icons)
property var filteredIcons: allIcons.filter(function (name) {
return query === "" || name.toLowerCase().indexOf(query.toLowerCase()) !== -1
})
readonly property int tileBase: Math.round(112 * scaling)
readonly property int columns: Math.max(3, Math.floor(grid.width / (tileBase + Style.marginS * 2)))
readonly property int cellW: Math.floor(grid.width / columns)
readonly property int cellH: Math.round(cellW * 0.7 + 36 * scaling)
background: Rectangle {
color: Color.mSurface
radius: Style.radiusL * scaling
border.color: Color.mPrimary
border.width: Style.borderM * scaling
}
ColumnLayout {
anchors.fill: parent
spacing: Style.marginM * scaling
// Title row
RowLayout {
Layout.fillWidth: true
NText {
text: "Icon Picker"
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
Layout.fillWidth: true
}
NIconButton {
icon: "x-lg"
onClicked: iconPicker.close()
}
}
NDivider {
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS * scaling
NTextInput {
Layout.fillWidth: true
label: "Search"
placeholderText: "Search (e.g., arrow, battery, cloud)"
text: iconPicker.query
onTextChanged: iconPicker.query = text.trim().toLowerCase()
}
}
// Icon grid
ScrollView {
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
GridView {
id: grid
anchors.fill: parent
anchors.margins: Style.marginM * scaling
cellWidth: iconPicker.cellW
cellHeight: iconPicker.cellH
model: iconPicker.filteredIcons
delegate: Rectangle {
width: grid.cellWidth
height: grid.cellHeight
radius: Style.radiusS * scaling
clip: true
color: (iconPicker.selectedIcon === modelData) ? Qt.alpha(Color.mPrimary, 0.15) : "transparent"
border.color: (iconPicker.selectedIcon === modelData) ? Color.mPrimary : Qt.rgba(0, 0, 0, 0)
border.width: (iconPicker.selectedIcon === modelData) ? Style.borderS * scaling : 0
MouseArea {
anchors.fill: parent
onClicked: iconPicker.selectedIcon = modelData
onDoubleClicked: {
iconPicker.selectedIcon = modelData
iconInput.text = iconPicker.selectedIcon
iconPicker.close()
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginS * scaling
spacing: Style.marginS * scaling
Item {
Layout.fillHeight: true
Layout.preferredHeight: 4 * scaling
}
NIcon {
Layout.alignment: Qt.AlignHCenter
icon: modelData
font.pointSize: Style.fontSizeXXXL * scaling
}
NText {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.topMargin: Style.marginXS * scaling
elide: Text.ElideRight
wrapMode: Text.NoWrap
maximumLineCount: 1
horizontalAlignment: Text.AlignHCenter
color: Color.mOnSurfaceVariant
font.pointSize: Style.fontSizeXS * scaling
text: modelData
}
Item {
Layout.fillHeight: true
}
}
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM * scaling
Item {
Layout.fillWidth: true
}
NButton {
text: "Cancel"
outlined: true
onClicked: iconPicker.close()
}
NButton {
text: "Apply"
icon: "check-lg"
enabled: iconPicker.selectedIcon !== ""
onClicked: {
iconInput.text = iconPicker.selectedIcon
iconPicker.close()
}
}
}
}
}
NTextInput {
id: leftClickExecInput
Layout.fillWidth: true

View file

@ -192,8 +192,10 @@ Singleton {
}
windowsList.push({
"id": (toplevel.address !== undefined && toplevel.address !== null) ? String(toplevel.address) : "",
"title": (toplevel.title !== undefined && toplevel.title !== null) ? String(toplevel.title) : "",
"id": (toplevel.address !== undefined
&& toplevel.address !== null) ? String(toplevel.address) : "",
"title": (toplevel.title !== undefined && toplevel.title !== null) ? String(
toplevel.title) : "",
"appId": (appId !== undefined && appId !== null) ? String(appId) : "",
"workspaceId": toplevel.workspace?.id || null,
"isFocused": toplevel.activated === true