From 5cb9935f2f548eabb9ee4666653070363abece27 Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Fri, 19 Sep 2025 16:37:38 -0400 Subject: [PATCH] SidePanelToggle: now allows to pick any icon from the font. --- Modules/Bar/Widgets/SidePanelToggle.qml | 3 +- .../WidgetSettings/CustomButtonSettings.qml | 160 +--------------- .../SidePanelToggleSettings.qml | 32 ++++ Services/BarWidgetRegistry.qml | 3 +- Widgets/NIconPicker.qml | 178 ++++++++++++++++++ 5 files changed, 218 insertions(+), 158 deletions(-) create mode 100644 Widgets/NIconPicker.qml diff --git a/Modules/Bar/Widgets/SidePanelToggle.qml b/Modules/Bar/Widgets/SidePanelToggle.qml index fc5fe300..037de875 100644 --- a/Modules/Bar/Widgets/SidePanelToggle.qml +++ b/Modules/Bar/Widgets/SidePanelToggle.qml @@ -29,9 +29,10 @@ NIconButton { return {} } + readonly property string customIcon: widgetSettings.icon || widgetMetadata.icon readonly property bool useDistroLogo: (widgetSettings.useDistroLogo !== undefined) ? widgetSettings.useDistroLogo : widgetMetadata.useDistroLogo - icon: useDistroLogo ? "" : "noctalia" + icon: useDistroLogo ? "" : customIcon tooltipText: "Open side panel" baseSize: Style.capsuleHeight compact: (Settings.data.bar.density === "compact") diff --git a/Modules/SettingsPanel/Bar/WidgetSettings/CustomButtonSettings.qml b/Modules/SettingsPanel/Bar/WidgetSettings/CustomButtonSettings.qml index 9ad83426..a4dd8cec 100644 --- a/Modules/SettingsPanel/Bar/WidgetSettings/CustomButtonSettings.qml +++ b/Modules/SettingsPanel/Bar/WidgetSettings/CustomButtonSettings.qml @@ -47,163 +47,11 @@ ColumnLayout { } } - Popup { + NIconPicker { id: iconPicker - modal: true - width: { - var w = Math.round(Math.max(screen.width * 0.35, 900) * scaling) - w = Math.min(w, screen.width - Style.marginL * 2) - return w - } - height: { - 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 - } - anchors.centerIn: Overlay.overlay - padding: Style.marginXL * scaling - - property string query: "" - property string selectedIcon: "" - property var allIcons: Object.keys(Icons.icons) - property var filteredIcons: allIcons.filter(function (name) { - return query === "" || name.toLowerCase().indexOf(query.toLowerCase()) !== -1 - }) - readonly property int columns: 6 - 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: "close" - 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 - NScrollView { - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - horizontalPolicy: ScrollBar.AlwaysOff - verticalPolicy: ScrollBar.AlwaysOn - - 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: 42 * 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" - enabled: iconPicker.selectedIcon !== "" - onClicked: { - iconInput.text = iconPicker.selectedIcon - iconPicker.close() - } - } - } + initialIcon: iconInput.text + onIconSelected: function (iconName) { + iconInput.text = iconName } } diff --git a/Modules/SettingsPanel/Bar/WidgetSettings/SidePanelToggleSettings.qml b/Modules/SettingsPanel/Bar/WidgetSettings/SidePanelToggleSettings.qml index abb2b7ec..ef87e177 100644 --- a/Modules/SettingsPanel/Bar/WidgetSettings/SidePanelToggleSettings.qml +++ b/Modules/SettingsPanel/Bar/WidgetSettings/SidePanelToggleSettings.qml @@ -14,10 +14,12 @@ ColumnLayout { property var widgetMetadata: null // Local state + property string valueIcon: widgetData.icon !== undefined ? widgetData.icon : widgetMetadata.icon property bool valueUseDistroLogo: widgetData.useDistroLogo !== undefined ? widgetData.useDistroLogo : widgetMetadata.useDistroLogo function saveSettings() { var settings = Object.assign({}, widgetData || {}) + settings.icon = valueIcon settings.useDistroLogo = valueUseDistroLogo return settings } @@ -27,4 +29,34 @@ ColumnLayout { checked: valueUseDistroLogo onToggled: checked => valueUseDistroLogo = checked } + + RowLayout { + spacing: Style.marginM * scaling + + NLabel { + label: "Icon" + description: "Select an icon from the library." + } + + NIcon { + Layout.alignment: Qt.AlignVCenter + icon: valueIcon + font.pointSize: Style.fontSizeXL * scaling + visible: valueIcon !== "" + } + + NButton { + enabled: !valueUseDistroLogo + text: "Browse" + onClicked: iconPicker.open() + } + } + + NIconPicker { + id: iconPicker + initialIcon: valueIcon + onIconSelected: function (iconName) { + valueIcon = iconName + } + } } diff --git a/Services/BarWidgetRegistry.qml b/Services/BarWidgetRegistry.qml index 304b018b..9783bb4a 100644 --- a/Services/BarWidgetRegistry.qml +++ b/Services/BarWidgetRegistry.qml @@ -99,7 +99,8 @@ Singleton { }, "SidePanelToggle": { "allowUserSettings": true, - "useDistroLogo": false + "useDistroLogo": false, + "icon": "noctalia" }, "Volume": { "allowUserSettings": true, diff --git a/Widgets/NIconPicker.qml b/Widgets/NIconPicker.qml new file mode 100644 index 00000000..d5515f28 --- /dev/null +++ b/Widgets/NIconPicker.qml @@ -0,0 +1,178 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Window +import qs.Commons +import qs.Widgets +import qs.Services + +Popup { + id: root + modal: true + + property string selectedIcon: "" + property string initialIcon: "" + + signal iconSelected(string iconName) + + width: { + var w = Math.round(Math.max(screen.width * 0.35, 900) * scaling) + w = Math.min(w, screen.width - Style.marginL * 2) + return w + } + height: { + 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 + } + anchors.centerIn: Overlay.overlay + padding: Style.marginXL * scaling + + property string query: "" + property var allIcons: Object.keys(Icons.icons) + property var filteredIcons: allIcons.filter(function (name) { + return query === "" || name.toLowerCase().indexOf(query.toLowerCase()) !== -1 + }) + readonly property int columns: 6 + readonly property int cellW: Math.floor(grid.width / columns) + readonly property int cellH: Math.round(cellW * 0.7 + 36 * scaling) + + onOpened: { + selectedIcon = initialIcon + searchInput.forceActiveFocus() + } + + 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: "close" + onClicked: root.close() + } + } + + NDivider { + Layout.fillWidth: true + } + + RowLayout { + Layout.fillWidth: true + spacing: Style.marginS * scaling + NTextInput { + id: searchInput + Layout.fillWidth: true + label: "Search" + placeholderText: "Search (e.g., arrow, battery, cloud)" + text: root.query + onTextChanged: root.query = text.trim().toLowerCase() + } + } + + // Icon grid + NScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + horizontalPolicy: ScrollBar.AlwaysOff + verticalPolicy: ScrollBar.AlwaysOn + + GridView { + id: grid + anchors.fill: parent + anchors.margins: Style.marginM * scaling + cellWidth: root.cellW + cellHeight: root.cellH + model: root.filteredIcons + delegate: Rectangle { + width: grid.cellWidth + height: grid.cellHeight + radius: Style.radiusS * scaling + clip: true + color: (root.selectedIcon === modelData) ? Qt.alpha(Color.mPrimary, 0.15) : "transparent" + border.color: (root.selectedIcon === modelData) ? Color.mPrimary : Qt.rgba(0, 0, 0, 0) + border.width: (root.selectedIcon === modelData) ? Style.borderS * scaling : 0 + + MouseArea { + anchors.fill: parent + onClicked: root.selectedIcon = modelData + onDoubleClicked: { + root.selectedIcon = modelData + root.iconSelected(root.selectedIcon) + root.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: 42 * 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: root.close() + } + NButton { + text: "Apply" + icon: "check" + enabled: root.selectedIcon !== "" + onClicked: { + root.iconSelected(root.selectedIcon) + root.close() + } + } + } + } +}