CustomButtonWidget: add icon picker to improve usability
This commit is contained in:
parent
ee44920fa4
commit
61cf7ab843
2 changed files with 182 additions and 6 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Window
|
||||||
import qs.Commons
|
import qs.Commons
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
@ -9,7 +10,6 @@ ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
// Properties to receive data from parent
|
|
||||||
property var widgetData: null
|
property var widgetData: null
|
||||||
property var widgetMetadata: null
|
property var widgetMetadata: null
|
||||||
|
|
||||||
|
|
@ -22,16 +22,190 @@ ColumnLayout {
|
||||||
return settings
|
return settings
|
||||||
}
|
}
|
||||||
|
|
||||||
// Icon setting
|
|
||||||
NTextInput {
|
NTextInput {
|
||||||
id: iconInput
|
id: iconInput
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
label: "Icon Name"
|
label: "Icon Name"
|
||||||
description: "Choose a name from the Material Icon set."
|
description: "Pick from Bootstrap Icons or type a name."
|
||||||
placeholderText: "Enter icon name (e.g., favorite, home, settings)"
|
placeholderText: "Enter icon name (e.g., speedometer2, gear, house)"
|
||||||
text: widgetData?.icon || widgetMetadata.icon
|
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 {
|
NTextInput {
|
||||||
id: leftClickExecInput
|
id: leftClickExecInput
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
||||||
|
|
@ -192,8 +192,10 @@ Singleton {
|
||||||
}
|
}
|
||||||
|
|
||||||
windowsList.push({
|
windowsList.push({
|
||||||
"id": (toplevel.address !== undefined && toplevel.address !== null) ? String(toplevel.address) : "",
|
"id": (toplevel.address !== undefined
|
||||||
"title": (toplevel.title !== undefined && toplevel.title !== null) ? String(toplevel.title) : "",
|
&& toplevel.address !== null) ? String(toplevel.address) : "",
|
||||||
|
"title": (toplevel.title !== undefined && toplevel.title !== null) ? String(
|
||||||
|
toplevel.title) : "",
|
||||||
"appId": (appId !== undefined && appId !== null) ? String(appId) : "",
|
"appId": (appId !== undefined && appId !== null) ? String(appId) : "",
|
||||||
"workspaceId": toplevel.workspace?.id || null,
|
"workspaceId": toplevel.workspace?.id || null,
|
||||||
"isFocused": toplevel.activated === true
|
"isFocused": toplevel.activated === true
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue