diff --git a/Bin/dev/BluetoothConnectionScript.sh b/Bin/bluetoot-connect.sh similarity index 91% rename from Bin/dev/BluetoothConnectionScript.sh rename to Bin/bluetoot-connect.sh index d74a7b73c..f2af270d0 100755 --- a/Bin/dev/BluetoothConnectionScript.sh +++ b/Bin/bluetoot-connect.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash -# BluetoothConnectionScript.sh +# bluetooth-connect.sh # Pairs, trusts, and attempts to connect to a Bluetooth device using bluetoothctl. -# Usage: BluetoothConnectionScript.sh +# Usage: bluetooth-connect.sh set -euo pipefail diff --git a/Modules/Bar/Widgets/Bluetooth.qml b/Modules/Bar/Widgets/Bluetooth.qml index 2e48977d1..0a1c1521c 100644 --- a/Modules/Bar/Widgets/Bluetooth.qml +++ b/Modules/Bar/Widgets/Bluetooth.qml @@ -71,11 +71,7 @@ Item { screen: root.screen density: Settings.data.bar.density oppositeDirection: BarService.getPillDirection(root) - icon: !BluetoothService.enabled - ? "bluetooth-off" - : ((BluetoothService.connectedDevices && BluetoothService.connectedDevices.length > 0) - ? "bluetooth-connected" - : "bluetooth") + icon: !BluetoothService.enabled ? "bluetooth-off" : ((BluetoothService.connectedDevices && BluetoothService.connectedDevices.length > 0) ? "bluetooth-connected" : "bluetooth") text: { if (BluetoothService.connectedDevices && BluetoothService.connectedDevices.length > 0) { const firstDevice = BluetoothService.connectedDevices[0]; @@ -94,7 +90,8 @@ Item { forceClose: isBarVertical || root.displayMode === "alwaysHide" || text === "" onClicked: { var p = PanelService.getPanel("bluetoothPanel", screen); - if (p) p.toggle(this); + if (p) + p.toggle(this); } onRightClicked: { var popupMenuWindow = PanelService.getPopupMenuWindow(screen); diff --git a/Modules/Bar/Widgets/MediaMini.qml b/Modules/Bar/Widgets/MediaMini.qml index e3c423b3a..9dedd565c 100644 --- a/Modules/Bar/Widgets/MediaMini.qml +++ b/Modules/Bar/Widgets/MediaMini.qml @@ -216,8 +216,7 @@ Item { if (!isNaN(idx)) { MediaService.switchToPlayer(idx); } - } - else if (action === "widget-settings") { + } else if (action === "widget-settings") { BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings); } } diff --git a/Modules/Panels/Bluetooth/BluetoothDevicesList.qml b/Modules/Panels/Bluetooth/BluetoothDevicesList.qml index 3447b50ea..351e91084 100644 --- a/Modules/Panels/Bluetooth/BluetoothDevicesList.qml +++ b/Modules/Panels/Bluetooth/BluetoothDevicesList.qml @@ -134,10 +134,14 @@ NBox { NText { text: { const k = BluetoothService.getStatusKey(modelData); - if (k === "pairing") return I18n.tr("bluetooth.panel.pairing"); - if (k === "blocked") return I18n.tr("bluetooth.panel.blocked"); - if (k === "connecting") return I18n.tr("bluetooth.panel.connecting"); - if (k === "disconnecting") return I18n.tr("bluetooth.panel.disconnecting"); + if (k === "pairing") + return I18n.tr("bluetooth.panel.pairing"); + if (k === "blocked") + return I18n.tr("bluetooth.panel.blocked"); + if (k === "connecting") + return I18n.tr("bluetooth.panel.connecting"); + if (k === "disconnecting") + return I18n.tr("bluetooth.panel.disconnecting"); return ""; } visible: text !== "" diff --git a/Modules/Panels/Bluetooth/BluetoothPanel.qml b/Modules/Panels/Bluetooth/BluetoothPanel.qml index 275c003c2..aa3b8d316 100644 --- a/Modules/Panels/Bluetooth/BluetoothPanel.qml +++ b/Modules/Panels/Bluetooth/BluetoothPanel.qml @@ -273,8 +273,8 @@ SmartPanel { return false; var availableCount = BluetoothService.adapter.devices.values.filter(dev => { - return dev && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0); - }).length; + return dev && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0); + }).length; return (availableCount === 0); } Layout.fillWidth: true @@ -327,8 +327,8 @@ SmartPanel { } var availableCount = BluetoothService.adapter.devices.values.filter(dev => { - return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0); - }).length; + return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0); + }).length; return (availableCount === 0); } diff --git a/Modules/Panels/ControlCenter/Widgets/Bluetooth.qml b/Modules/Panels/ControlCenter/Widgets/Bluetooth.qml index b11055069..16c35e1ca 100644 --- a/Modules/Panels/ControlCenter/Widgets/Bluetooth.qml +++ b/Modules/Panels/ControlCenter/Widgets/Bluetooth.qml @@ -8,15 +8,12 @@ import qs.Widgets NIconButtonHot { property ShellScreen screen - icon: !BluetoothService.enabled - ? "bluetooth-off" - : ((BluetoothService.connectedDevices && BluetoothService.connectedDevices.length > 0) - ? "bluetooth-connected" - : "bluetooth") + icon: !BluetoothService.enabled ? "bluetooth-off" : ((BluetoothService.connectedDevices && BluetoothService.connectedDevices.length > 0) ? "bluetooth-connected" : "bluetooth") tooltipText: I18n.tr("quickSettings.bluetooth.tooltip.action") onClicked: { var p = PanelService.getPanel("bluetoothPanel", screen); - if (p) p.toggle(this); + if (p) + p.toggle(this); } onRightClicked: BluetoothService.setBluetoothEnabled(!BluetoothService.enabled) } diff --git a/Modules/Panels/Settings/Tabs/NetworkTab.qml b/Modules/Panels/Settings/Tabs/NetworkTab.qml index bf4aa0316..15185c714 100644 --- a/Modules/Panels/Settings/Tabs/NetworkTab.qml +++ b/Modules/Panels/Settings/Tabs/NetworkTab.qml @@ -41,11 +41,7 @@ ColumnLayout { description: I18n.tr("settings.network.bluetooth.rssi-polling.description") checked: Settings.data && Settings.data.network && Settings.data.network.bluetoothRssiPollingEnabled enabled: BluetoothService.enabled - onToggled: function(checked) { - if (Settings.data && Settings.data.network) { - Settings.data.network.bluetoothRssiPollingEnabled = checked; - } - } + onToggled: checked => Settings.data.network.bluetoothRssiPollingEnabled = checked } NDivider { diff --git a/Modules/Panels/Settings/Tabs/PluginsTab.qml b/Modules/Panels/Settings/Tabs/PluginsTab.qml index b4511ab8e..ed730bb2c 100644 --- a/Modules/Panels/Settings/Tabs/PluginsTab.qml +++ b/Modules/Panels/Settings/Tabs/PluginsTab.qml @@ -287,13 +287,13 @@ ColumnLayout { NToggle { checked: modelData.enabled baseSize: Style.baseWidgetSize * 0.7 - onToggled: function (checked) { - if (checked) { - PluginService.enablePlugin(modelData.compositeKey); - } else { - PluginService.disablePlugin(modelData.compositeKey); - } - } + onToggled: checked => { + if (checked) { + PluginService.enablePlugin(modelData.compositeKey); + } else { + PluginService.disablePlugin(modelData.compositeKey); + } + } } } } @@ -382,11 +382,11 @@ ColumnLayout { NToggle { checked: modelData.enabled !== false // Default to true if not set baseSize: Style.baseWidgetSize * 0.7 - onToggled: function (checked) { - PluginRegistry.setSourceEnabled(modelData.url, checked); - PluginService.refreshAvailablePlugins(); - ToastService.showNotice(I18n.tr("settings.plugins.title"), I18n.tr("settings.plugins.refresh.refreshing")); - } + onToggled: checked => { + PluginRegistry.setSourceEnabled(modelData.url, checked); + PluginService.refreshAvailablePlugins(); + ToastService.showNotice(I18n.tr("settings.plugins.title"), I18n.tr("settings.plugins.refresh.refreshing")); + } } } } diff --git a/Modules/Panels/Settings/Tabs/SessionMenu/SessionMenuTab.qml b/Modules/Panels/Settings/Tabs/SessionMenu/SessionMenuTab.qml index 8481e170f..1c05d06be 100644 --- a/Modules/Panels/Settings/Tabs/SessionMenu/SessionMenuTab.qml +++ b/Modules/Panels/Settings/Tabs/SessionMenu/SessionMenuTab.qml @@ -493,11 +493,9 @@ ColumnLayout { NToggle { checked: modelData.countdownEnabled !== undefined ? modelData.countdownEnabled : true - onToggled: function (checked) { - root.updateEntry(delegateItem.index, { - "countdownEnabled": checked - }); - } + onToggled: checked => root.updateEntry(delegateItem.index, { + "countdownEnabled": checked + }) } } diff --git a/Modules/Panels/SetupWizard/SetupCustomizeStep.qml b/Modules/Panels/SetupWizard/SetupCustomizeStep.qml index c8648ab22..5d408adbf 100644 --- a/Modules/Panels/SetupWizard/SetupCustomizeStep.qml +++ b/Modules/Panels/SetupWizard/SetupCustomizeStep.qml @@ -418,9 +418,7 @@ ColumnLayout { } NToggle { checked: Settings.data.bar.floating - onToggled: function (checked) { - Settings.data.bar.floating = checked; - } + onToggled: checked => Settings.data.bar.floating = checked } } @@ -534,9 +532,7 @@ ColumnLayout { } NToggle { checked: Settings.data.general.enableShadows - onToggled: function (checked) { - Settings.data.general.enableShadows = checked; - } + onToggled: checked => Settings.data.general.enableShadows = checked } } diff --git a/Services/Networking/BluetoothRssi.qml b/Services/Networking/BluetoothRssi.qml index 52665963c..cc2212707 100644 --- a/Services/Networking/BluetoothRssi.qml +++ b/Services/Networking/BluetoothRssi.qml @@ -23,7 +23,9 @@ QtObject { property Process rssiProcess: Process { id: proc running: false - stdout: StdioCollector { id: out } + stdout: StdioCollector { + id: out + } onExited: function (exitCode, exitStatus) { try { var text = out.text || ""; @@ -35,8 +37,7 @@ QtObject { root.version++; } } - } catch (e) { - } finally { + } catch (e) {} finally { root._currentAddr = ""; } } @@ -63,7 +64,9 @@ QtObject { return; // avoid overlap root._currentAddr = addr; proc.command = ["sh", "-c", `bluetoothctl info "${addr}"`]; - try { proc.running = true; } catch (e) {} + try { + proc.running = true; + } catch (e) {} } } } diff --git a/Services/Networking/BluetoothService.qml b/Services/Networking/BluetoothService.qml index 9614ada55..59847dd9c 100644 --- a/Services/Networking/BluetoothService.qml +++ b/Services/Networking/BluetoothService.qml @@ -1,14 +1,14 @@ pragma Singleton +import QtQml import QtQuick -import QtQml import Quickshell import Quickshell.Bluetooth import Quickshell.Io +import "../../Helpers/BluetoothUtils.js" as BluetoothUtils +import "." import qs.Commons import qs.Services.UI -import "." -import "../../Helpers/BluetoothUtils.js" as BluetoothUtils QtObject { id: root @@ -20,7 +20,7 @@ QtObject { property bool airplaneModeToggled: false readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter - + // Power/blocked state property bool enabled: false // driven by bluetoothctl property bool ctlPowered: false @@ -123,7 +123,8 @@ QtObject { manualScanTimer.interval = durationMs; manualScanTimer.restart(); } else { - if (manualScanTimer.running) manualScanTimer.stop(); + if (manualScanTimer.running) + manualScanTimer.stop(); } requestCtlPoll(ctlPollSoonMs); } @@ -133,7 +134,7 @@ QtObject { if (!adapter) return; setScanActive(!root.scanningActive, scanAutoStopMs); - } + } // Auto-stop manual discovery after a short window property Timer manualScanTimer: Timer { @@ -183,7 +184,9 @@ QtObject { property Process ctlShowProcess: Process { id: ctlProc running: false - stdout: StdioCollector { id: ctlStdout } + stdout: StdioCollector { + id: ctlStdout + } onExited: function (exitCode, exitStatus) { try { var text = ctlStdout.text || ""; @@ -341,14 +344,17 @@ QtObject { var p = getSignalPercent(device); if (p === null) return I18n.tr("bluetooth.panel.signal-text.unknown"); - if (p >= 80) return I18n.tr("bluetooth.panel.signal-text.excellent"); - if (p >= 60) return I18n.tr("bluetooth.panel.signal-text.good"); - if (p >= 40) return I18n.tr("bluetooth.panel.signal-text.fair"); - if (p >= 20) return I18n.tr("bluetooth.panel.signal-text.poor"); + if (p >= 80) + return I18n.tr("bluetooth.panel.signal-text.excellent"); + if (p >= 60) + return I18n.tr("bluetooth.panel.signal-text.good"); + if (p >= 40) + return I18n.tr("bluetooth.panel.signal-text.fair"); + if (p >= 20) + return I18n.tr("bluetooth.panel.signal-text.poor"); return I18n.tr("bluetooth.panel.signal-text.very-poor"); } - // Numeric helpers for UI rendering function getSignalPercent(device) { // Establish binding dependency so UI updates when RSSI cache changes @@ -356,7 +362,6 @@ QtObject { return BluetoothUtils.signalPercent(device, rssi.cache, _v); } - function getBatteryPercent(device) { return BluetoothUtils.batteryPercent(device); } @@ -428,7 +433,7 @@ QtObject { _pauseDiscoveryFor(totalPauseMs); // Prefer external dev script for pairing/connecting; executed detached - const scriptPath = Quickshell.shellDir + "/Bin/dev/BluetoothConnectionScript.sh"; + const scriptPath = Quickshell.shellDir + "/Bin/bluetooth-connect.sh"; // Use bash explicitly to avoid relying on executable bit in all environments btExec(["bash", scriptPath, String(addr), String(pairWait), String(attempts), String(intervalSec)]); } diff --git a/Widgets/NColorPickerDialog.qml b/Widgets/NColorPickerDialog.qml index 2d8f467b9..ad7cbe105 100644 --- a/Widgets/NColorPickerDialog.qml +++ b/Widgets/NColorPickerDialog.qml @@ -64,704 +64,704 @@ Popup { RowLayout { Layout.fillWidth: true - RowLayout { - spacing: Style.marginS + RowLayout { + spacing: Style.marginS - NIcon { - icon: "color-picker" - pointSize: Style.fontSizeXXL - color: Color.mPrimary - } + NIcon { + icon: "color-picker" + pointSize: Style.fontSizeXXL + color: Color.mPrimary + } - NText { - text: I18n.tr("widgets.color-picker.title") - pointSize: Style.fontSizeXL - font.weight: Style.fontWeightBold - color: Color.mPrimary - } + NText { + text: I18n.tr("widgets.color-picker.title") + pointSize: Style.fontSizeXL + font.weight: Style.fontWeightBold + color: Color.mPrimary + } + } + + Item { + Layout.fillWidth: true + } + + NIconButton { + icon: "close" + onClicked: root.close() + } + } + + // Color preview section + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 80 + radius: Style.iRadiusS + color: root.selectedColor + border.color: Color.mOutline + border.width: Style.borderS + + ColumnLayout { + spacing: 0 + anchors.fill: parent + + Item { + Layout.fillHeight: true + } + + NText { + text: root.selectedColor.toString().toUpperCase() + family: Settings.data.ui.fontFixed + pointSize: Style.fontSizeL + font.weight: Style.fontWeightMedium + color: root.selectedColor.r + root.selectedColor.g + root.selectedColor.b > 1.5 ? Color.black : Color.white + Layout.alignment: Qt.AlignHCenter + } + + NText { + text: "RGB(" + Math.round(root.selectedColor.r * 255) + ", " + Math.round(root.selectedColor.g * 255) + ", " + Math.round(root.selectedColor.b * 255) + ")" + family: Settings.data.ui.fontFixed + pointSize: Style.fontSizeM + color: root.selectedColor.r + root.selectedColor.g + root.selectedColor.b > 1.5 ? Color.black : Color.white + Layout.alignment: Qt.AlignHCenter } Item { - Layout.fillWidth: true - } - - NIconButton { - icon: "close" - onClicked: root.close() + Layout.fillHeight: true } } + } - // Color preview section - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 80 - radius: Style.iRadiusS - color: root.selectedColor - border.color: Color.mOutline - border.width: Style.borderS + // Main Box + NBox { + Layout.fillWidth: true + Layout.preferredHeight: controlsOutterColumn.implicitHeight + Style.marginL * 2 - ColumnLayout { - spacing: 0 - anchors.fill: parent - - Item { - Layout.fillHeight: true - } - - NText { - text: root.selectedColor.toString().toUpperCase() - family: Settings.data.ui.fontFixed - pointSize: Style.fontSizeL - font.weight: Style.fontWeightMedium - color: root.selectedColor.r + root.selectedColor.g + root.selectedColor.b > 1.5 ? Color.black : Color.white - Layout.alignment: Qt.AlignHCenter - } - - NText { - text: "RGB(" + Math.round(root.selectedColor.r * 255) + ", " + Math.round(root.selectedColor.g * 255) + ", " + Math.round(root.selectedColor.b * 255) + ")" - family: Settings.data.ui.fontFixed - pointSize: Style.fontSizeM - color: root.selectedColor.r + root.selectedColor.g + root.selectedColor.b > 1.5 ? Color.black : Color.white - Layout.alignment: Qt.AlignHCenter - } - - Item { - Layout.fillHeight: true - } - } + ButtonGroup { + id: colorValues } - // Main Box - NBox { - Layout.fillWidth: true - Layout.preferredHeight: controlsOutterColumn.implicitHeight + Style.marginL * 2 + ColumnLayout { + id: controlsOutterColumn + anchors.fill: parent + anchors.margins: Style.marginL + spacing: Style.marginM - ButtonGroup { - id: colorValues - } + RowLayout { + spacing: Style.marginL // Ensure nice gap between Left and Right groups - ColumnLayout { - id: controlsOutterColumn - anchors.fill: parent - anchors.margins: Style.marginL - spacing: Style.marginM + // SpinBoxes Column + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignTop + Layout.preferredWidth: 3 - RowLayout { - spacing: Style.marginL // Ensure nice gap between Left and Right groups - - // SpinBoxes Column - ColumnLayout { + // --- RED --- + RowLayout { Layout.fillWidth: true - Layout.fillHeight: true - Layout.alignment: Qt.AlignTop - Layout.preferredWidth: 3 + spacing: Style.marginM - // --- RED --- - RowLayout { - Layout.fillWidth: true - spacing: Style.marginM - - NRadioButton { - ButtonGroup.group: colorValues - text: "R" - font.weight: Style.fontWeightMedium - checked: true - onClicked: root.editMode = NColorPickerDialog.EditMode.R - Layout.fillWidth: false - } - - NSpinBox { - id: redSpinBox - from: 0 - to: 255 - - onValueChanged: { - if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.r * 255)) { - root.selectedColor = Qt.rgba(value / 255, root.selectedColor.g, root.selectedColor.b, 1); - } - } - Binding { - target: redSpinBox - property: "value" - value: Math.round(root.selectedColor.r * 255) - } - } + NRadioButton { + ButtonGroup.group: colorValues + text: "R" + font.weight: Style.fontWeightMedium + checked: true + onClicked: root.editMode = NColorPickerDialog.EditMode.R + Layout.fillWidth: false } - // --- GREEN --- - RowLayout { - Layout.fillWidth: true - spacing: Style.marginM + NSpinBox { + id: redSpinBox + from: 0 + to: 255 - NRadioButton { - ButtonGroup.group: colorValues - text: "G" - font.weight: Style.fontWeightMedium - onClicked: root.editMode = NColorPickerDialog.EditMode.G - Layout.fillWidth: false - } - - NSpinBox { - id: greenSpinBox - from: 0 - to: 255 - - onValueChanged: { - if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.g * 255)) { - root.selectedColor = Qt.rgba(root.selectedColor.r, value / 255, root.selectedColor.b, 1); - } - } - Binding { - target: greenSpinBox - property: "value" - value: Math.round(root.selectedColor.g * 255) + onValueChanged: { + if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.r * 255)) { + root.selectedColor = Qt.rgba(value / 255, root.selectedColor.g, root.selectedColor.b, 1); } } - } - - // --- BLUE --- - RowLayout { - Layout.fillWidth: true - spacing: Style.marginM - - NRadioButton { - ButtonGroup.group: colorValues - text: "B" - font.weight: Style.fontWeightMedium - onClicked: root.editMode = NColorPickerDialog.EditMode.B - Layout.fillWidth: false - } - - NSpinBox { - id: blueSpinBox - from: 0 - to: 255 - - onValueChanged: { - if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.b * 255)) { - root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, value / 255, 1); - } - } - Binding { - target: blueSpinBox - property: "value" - value: Math.round(root.selectedColor.b * 255) - } - } - } - - // Spacer - Item { - Layout.fillHeight: true - Layout.fillWidth: true - } - - // --- HUE --- - RowLayout { - Layout.fillWidth: true - spacing: Style.marginM - - NRadioButton { - ButtonGroup.group: colorValues - text: "H" - font.weight: Style.fontWeightMedium - checked: true - onClicked: root.editMode = NColorPickerDialog.EditMode.H - Layout.fillWidth: false - } - - NSpinBox { - id: hueSpinBox - from: 0 - to: 360 - - onValueChanged: { - if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.displayHue * 360)) { - var newHue = value / 360; - root.selectedColor = Qt.hsva(newHue, root.selectedColor.hsvSaturation, root.selectedColor.hsvValue, 1); - root.stableHue = newHue; - } - } - - Binding { - target: hueSpinBox - property: "value" - value: Math.round(root.displayHue * 360) - } - } - } - - // --- SATURATION --- - RowLayout { - Layout.fillWidth: true - spacing: Style.marginM - - NRadioButton { - ButtonGroup.group: colorValues - text: "S" - font.weight: Style.fontWeightMedium - onClicked: root.editMode = NColorPickerDialog.EditMode.S - Layout.fillWidth: false - } - - NSpinBox { - id: satSpinBox - from: 0 - to: 100 - - onValueChanged: { - if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.hsvSaturation * 100)) { - root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, value / 100, root.selectedColor.hsvValue, 1); - } - } - Binding { - target: satSpinBox - property: "value" - value: Math.round(root.selectedColor.hsvSaturation * 100) - } - } - } - - // --- VALUE --- - RowLayout { - spacing: Style.marginM - - NRadioButton { - ButtonGroup.group: colorValues - text: "V" - font.weight: Style.fontWeightMedium - onClicked: root.editMode = NColorPickerDialog.EditMode.V - Layout.fillWidth: false - } - - NSpinBox { - id: valSpinBox - from: 0 - to: 100 - - onValueChanged: { - if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.hsvValue * 100)) { - root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, root.selectedColor.hsvSaturation, value / 100, 1); - } - } - Binding { - target: valSpinBox - property: "value" - value: Math.round(root.selectedColor.hsvValue * 100) - } - } - } - - // Spacer - Item { - Layout.fillHeight: true - Layout.fillWidth: true - } - - // Hex input - RowLayout { - id: hexInput - Layout.fillWidth: true - spacing: Style.marginM - - NLabel { - label: "Hex:" - Layout.alignment: Qt.AlignVCenter - } - - NTextInput { - text: root.selectedColor.toString().toUpperCase() - fontFamily: Settings.data.ui.fontFixed - Layout.fillWidth: true - Layout.minimumWidth: implicitWidth - onEditingFinished: { - if (/^#[0-9A-F]{6}$/i.test(text)) { - root.selectedColor = text; - } - } + Binding { + target: redSpinBox + property: "value" + value: Math.round(root.selectedColor.r * 255) } } } + // --- GREEN --- RowLayout { Layout.fillWidth: true + spacing: Style.marginM + + NRadioButton { + ButtonGroup.group: colorValues + text: "G" + font.weight: Style.fontWeightMedium + onClicked: root.editMode = NColorPickerDialog.EditMode.G + Layout.fillWidth: false + } + + NSpinBox { + id: greenSpinBox + from: 0 + to: 255 + + onValueChanged: { + if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.g * 255)) { + root.selectedColor = Qt.rgba(root.selectedColor.r, value / 255, root.selectedColor.b, 1); + } + } + Binding { + target: greenSpinBox + property: "value" + value: Math.round(root.selectedColor.g * 255) + } + } + } + + // --- BLUE --- + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM + + NRadioButton { + ButtonGroup.group: colorValues + text: "B" + font.weight: Style.fontWeightMedium + onClicked: root.editMode = NColorPickerDialog.EditMode.B + Layout.fillWidth: false + } + + NSpinBox { + id: blueSpinBox + from: 0 + to: 255 + + onValueChanged: { + if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.b * 255)) { + root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, value / 255, 1); + } + } + Binding { + target: blueSpinBox + property: "value" + value: Math.round(root.selectedColor.b * 255) + } + } + } + + // Spacer + Item { Layout.fillHeight: true - Layout.preferredWidth: 5 - Layout.alignment: Qt.AlignTop - spacing: Style.marginS + Layout.fillWidth: true + } - Layout.topMargin: Math.round(2 * Style.uiScaleRatio) //Shim to try and line up colorField with SpinBoxes + // --- HUE --- + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM - // --- SLIDER --- - NColorSlider { - id: selectedSlider - Layout.fillHeight: true - rainbowMode: root.editMode === "h" - topColor: { - if (rainbowMode) - return "transparent"; - switch (root.editMode) { - case NColorPickerDialog.EditMode.R: - return "#FF0000"; - case NColorPickerDialog.EditMode.G: - return "#00FF00"; - case NColorPickerDialog.EditMode.B: - return "#0000FF"; - default: - return "#FFFFFF"; + NRadioButton { + ButtonGroup.group: colorValues + text: "H" + font.weight: Style.fontWeightMedium + checked: true + onClicked: root.editMode = NColorPickerDialog.EditMode.H + Layout.fillWidth: false + } + + NSpinBox { + id: hueSpinBox + from: 0 + to: 360 + + onValueChanged: { + if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.displayHue * 360)) { + var newHue = value / 360; + root.selectedColor = Qt.hsva(newHue, root.selectedColor.hsvSaturation, root.selectedColor.hsvValue, 1); + root.stableHue = newHue; } } Binding { - target: selectedSlider + target: hueSpinBox property: "value" - when: !selectedSlider.pressed // Only update from model when NOT dragging - value: { - switch (root.editMode) { - case NColorPickerDialog.EditMode.R: - return root.selectedColor.r; - case NColorPickerDialog.EditMode.G: - return root.selectedColor.g; - case NColorPickerDialog.EditMode.B: - return root.selectedColor.b; - case NColorPickerDialog.EditMode.H: - return root.displayHue; - case NColorPickerDialog.EditMode.S: - return root.selectedColor.hsvSaturation; - case NColorPickerDialog.EditMode.V: - return root.selectedColor.hsvValue; - default: - return 0; - } - } - } - - onMoved: { - var v = value; - switch (root.editMode) { - case NColorPickerDialog.EditMode.R: - root.selectedColor = Qt.rgba(v, root.selectedColor.g, root.selectedColor.b, 1); - break; - case NColorPickerDialog.EditMode.G: - root.selectedColor = Qt.rgba(root.selectedColor.r, v, root.selectedColor.b, 1); - break; - case NColorPickerDialog.EditMode.B: - root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, v, 1); - break; - case NColorPickerDialog.EditMode.H: - root.selectedColor = Qt.hsva(v, root.selectedColor.hsvSaturation, root.selectedColor.hsvValue, 1); - root.stableHue = v; - break; - case NColorPickerDialog.EditMode.S: - root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, v, root.selectedColor.hsvValue, 1); - break; - case NColorPickerDialog.EditMode.V: - root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, root.selectedColor.hsvSaturation, v, 1); - break; - } + value: Math.round(root.displayHue * 360) } } + } - // Color Field - Rectangle { - id: colorField + // --- SATURATION --- + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM + + NRadioButton { + ButtonGroup.group: colorValues + text: "S" + font.weight: Style.fontWeightMedium + onClicked: root.editMode = NColorPickerDialog.EditMode.S + Layout.fillWidth: false + } + + NSpinBox { + id: satSpinBox + from: 0 + to: 100 + + onValueChanged: { + if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.hsvSaturation * 100)) { + root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, value / 100, root.selectedColor.hsvValue, 1); + } + } + Binding { + target: satSpinBox + property: "value" + value: Math.round(root.selectedColor.hsvSaturation * 100) + } + } + } + + // --- VALUE --- + RowLayout { + spacing: Style.marginM + + NRadioButton { + ButtonGroup.group: colorValues + text: "V" + font.weight: Style.fontWeightMedium + onClicked: root.editMode = NColorPickerDialog.EditMode.V + Layout.fillWidth: false + } + + NSpinBox { + id: valSpinBox + from: 0 + to: 100 + + onValueChanged: { + if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.hsvValue * 100)) { + root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, root.selectedColor.hsvSaturation, value / 100, 1); + } + } + Binding { + target: valSpinBox + property: "value" + value: Math.round(root.selectedColor.hsvValue * 100) + } + } + } + + // Spacer + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + // Hex input + RowLayout { + id: hexInput + Layout.fillWidth: true + spacing: Style.marginM + + NLabel { + label: "Hex:" + Layout.alignment: Qt.AlignVCenter + } + + NTextInput { + text: root.selectedColor.toString().toUpperCase() + fontFamily: Settings.data.ui.fontFixed Layout.fillWidth: true - - Layout.preferredHeight: width - Layout.alignment: Qt.AlignTop - - radius: 0 - border.color: Color.mOutline - border.width: Style.borderS - clip: true - - ShaderEffect { - anchors.fill: parent - anchors.margins: 1 // Avoid drawing over the border - - fragmentShader: "../Shaders/qsb/color_picker.frag.qsb" - - // Pass which radio is selected - readonly property real fixedVal: { - switch (root.editMode) { - case NColorPickerDialog.EditMode.R: - return root.selectedColor.r; - case NColorPickerDialog.EditMode.G: - return root.selectedColor.g; - case NColorPickerDialog.EditMode.B: - return root.selectedColor.b; - case NColorPickerDialog.EditMode.H: - return root.displayHue; - case NColorPickerDialog.EditMode.S: - return root.selectedColor.hsvSaturation; - case NColorPickerDialog.EditMode.V: - return root.selectedColor.hsvValue; - default: - return 0; - } + Layout.minimumWidth: implicitWidth + onEditingFinished: { + if (/^#[0-9A-F]{6}$/i.test(text)) { + root.selectedColor = text; } - - // Send as one vector because GPUs are so damn picky - property vector4d params: Qt.vector4d(1.0, fixedVal, root.editMode, 0) - } - - MouseArea { - id: fieldMouse - anchors.fill: parent - cursorShape: Qt.CrossCursor - - // Update color when clicking or dragging - function updateColor(mouse) { - // Normalize X and Y (0.0 to 1.0) - var xVal = Math.max(0, Math.min(1, mouse.x / width)); - var yVal = Math.max(0, Math.min(1, 1.0 - (mouse.y / height))); // Flip Y (0 at bottom) - - // Get the current fixed value (from the slider) - var fixed = 0.0; - - switch (root.editMode) { - case NColorPickerDialog.EditMode.R: - fixed = root.selectedColor.r; - root.selectedColor = Qt.rgba(fixed, xVal, yVal, 1); - break; - case NColorPickerDialog.EditMode.G: - fixed = root.selectedColor.g; - root.selectedColor = Qt.rgba(xVal, fixed, yVal, 1); - break; - case NColorPickerDialog.EditMode.B: - fixed = root.selectedColor.b; - root.selectedColor = Qt.rgba(xVal, yVal, fixed, 1); - break; - case NColorPickerDialog.EditMode.H: - // Use stableHue to prevent flipping to -1 - fixed = root.displayHue; - root.selectedColor = Qt.hsva(fixed, xVal, yVal, 1); - root.stableHue = fixed; - break; - case NColorPickerDialog.EditMode.S: - fixed = root.selectedColor.hsvSaturation; - root.selectedColor = Qt.hsva(xVal, fixed, yVal, 1); - // If we dragged Hue (xVal), update stableHue - root.stableHue = yVal; - break; - case NColorPickerDialog.EditMode.V: - fixed = root.selectedColor.hsvValue; - root.selectedColor = Qt.hsva(xVal, yVal, fixed, 1); - // If we dragged Hue (xVal), update stableHue - root.stableHue = xVal; - break; - } - } - onPressed: mouse => updateColor(mouse) - onPositionChanged: mouse => updateColor(mouse) - } - - // Color Indicator - Rectangle { - width: 10 - height: 10 - radius: Math.min(Style.iRadiusXS, width / 2) - color: "transparent" - border.color: root.selectedColor.hsvValue < 0.5 ? "white" : "black" - border.width: 1 - - // Find position based on the current color - readonly property point selectedPos: { - switch (root.editMode) { - case NColorPickerDialog.EditMode.R: - return Qt.point(root.selectedColor.g, root.selectedColor.b); - case NColorPickerDialog.EditMode.G: - return Qt.point(root.selectedColor.r, root.selectedColor.b); - case NColorPickerDialog.EditMode.B: - return Qt.point(root.selectedColor.r, root.selectedColor.g); - case NColorPickerDialog.EditMode.H: - return Qt.point(root.selectedColor.hsvSaturation, root.selectedColor.hsvValue); - case NColorPickerDialog.EditMode.S: - return Qt.point(root.displayHue, root.selectedColor.hsvValue); - case NColorPickerDialog.EditMode.V: - return Qt.point(root.displayHue, root.selectedColor.hsvSaturation); - default: - return Qt.point(0, 0); - } - } - - // Convert values to pixel position - x: (selectedPos.x * parent.width) - width / 2 - y: ((1.0 - selectedPos.y) * parent.height) - height / 2 - } - - // Redraw the border in case Color Indicator is near the edge - Rectangle { - anchors.fill: parent - color: "transparent" - border.color: Color.mOutline - border.width: Style.borderS - antialiasing: false } } } } - NLabel { - label: I18n.tr("widgets.color-picker.palette.label") + RowLayout { Layout.fillWidth: true - } + Layout.fillHeight: true + Layout.preferredWidth: 5 + Layout.alignment: Qt.AlignTop + spacing: Style.marginS - NScrollView { - Layout.fillWidth: true - Layout.preferredHeight: Math.min(paletteGrid.implicitHeight, 200) - verticalPolicy: paletteGrid.implicitHeight > 200 ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff - horizontalPolicy: ScrollBar.AlwaysOff + Layout.topMargin: Math.round(2 * Style.uiScaleRatio) //Shim to try and line up colorField with SpinBoxes - GridLayout { - id: paletteGrid - width: parent.availableWidth - columns: 17 - columnSpacing: 6 - rowSpacing: 6 - - NLabel { - Layout.columnSpan: 17 - Layout.fillWidth: true - description: I18n.tr("widgets.color-picker.palette.theme-colors") + // --- SLIDER --- + NColorSlider { + id: selectedSlider + Layout.fillHeight: true + rainbowMode: root.editMode === "h" + topColor: { + if (rainbowMode) + return "transparent"; + switch (root.editMode) { + case NColorPickerDialog.EditMode.R: + return "#FF0000"; + case NColorPickerDialog.EditMode.G: + return "#00FF00"; + case NColorPickerDialog.EditMode.B: + return "#0000FF"; + default: + return "#FFFFFF"; + } } - Repeater { - model: [ - { - name: "mPrimary", - color: Color.mPrimary - }, - { - name: "mSecondary", - color: Color.mSecondary - }, - { - name: "mTertiary", - color: Color.mTertiary - }, - { - name: "mError", - color: Color.mError - }, - { - name: "mSurface", - color: Color.mSurface - }, - { - name: "mSurfaceVariant", - color: Color.mSurfaceVariant - }, - { - name: "mOutline", - color: Color.mOutline - } - ] - - Rectangle { - width: 24 - height: 24 - radius: Style.iRadiusXXS - color: modelData.color - border.color: root.selectedColor.toString() === modelData.color.toString() ? Color.mPrimary : Color.mOutline - border.width: Math.max(1, root.selectedColor.toString() === modelData.color.toString() ? Style.borderM : Style.borderS) - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - - onEntered: { - TooltipService.show(parent, modelData.name + "\n" + parent.color.toString().toUpperCase()); - } - onExited: { - TooltipService.hide(); - } - onClicked: { - root.selectedColor = modelData.color; - TooltipService.hide(); - } + Binding { + target: selectedSlider + property: "value" + when: !selectedSlider.pressed // Only update from model when NOT dragging + value: { + switch (root.editMode) { + case NColorPickerDialog.EditMode.R: + return root.selectedColor.r; + case NColorPickerDialog.EditMode.G: + return root.selectedColor.g; + case NColorPickerDialog.EditMode.B: + return root.selectedColor.b; + case NColorPickerDialog.EditMode.H: + return root.displayHue; + case NColorPickerDialog.EditMode.S: + return root.selectedColor.hsvSaturation; + case NColorPickerDialog.EditMode.V: + return root.selectedColor.hsvValue; + default: + return 0; } } } - NDivider { - Layout.columnSpan: 17 - Layout.fillWidth: true - Layout.topMargin: Style.marginXS - Layout.bottomMargin: 0 + onMoved: { + var v = value; + switch (root.editMode) { + case NColorPickerDialog.EditMode.R: + root.selectedColor = Qt.rgba(v, root.selectedColor.g, root.selectedColor.b, 1); + break; + case NColorPickerDialog.EditMode.G: + root.selectedColor = Qt.rgba(root.selectedColor.r, v, root.selectedColor.b, 1); + break; + case NColorPickerDialog.EditMode.B: + root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, v, 1); + break; + case NColorPickerDialog.EditMode.H: + root.selectedColor = Qt.hsva(v, root.selectedColor.hsvSaturation, root.selectedColor.hsvValue, 1); + root.stableHue = v; + break; + case NColorPickerDialog.EditMode.S: + root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, v, root.selectedColor.hsvValue, 1); + break; + case NColorPickerDialog.EditMode.V: + root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, root.selectedColor.hsvSaturation, v, 1); + break; + } } + } - NLabel { - Layout.columnSpan: 17 - Layout.fillWidth: true - description: I18n.tr("widgets.color-picker.palette.description") - } + // Color Field + Rectangle { + id: colorField + Layout.fillWidth: true - Repeater { - model: ColorList.colors + Layout.preferredHeight: width + Layout.alignment: Qt.AlignTop - Rectangle { - width: 24 - height: 24 - radius: Math.min(Style.iRadiusXS, width / 2) - color: modelData.color - border.color: root.selectedColor.toString() === modelData.color.toString() ? Color.mPrimary : Color.mOutline - border.width: root.selectedColor.toString() === modelData.color.toString() ? 2 : 1 + radius: 0 + border.color: Color.mOutline + border.width: Style.borderS + clip: true - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true + ShaderEffect { + anchors.fill: parent + anchors.margins: 1 // Avoid drawing over the border - onEntered: { - TooltipService.show(parent, modelData.name + "\n" + parent.color.toString().toUpperCase(), "auto"); - } - onExited: { - TooltipService.hide(); - } - onClicked: { - root.selectedColor = modelData.color; - TooltipService.hide(); - } + fragmentShader: "../Shaders/qsb/color_picker.frag.qsb" + + // Pass which radio is selected + readonly property real fixedVal: { + switch (root.editMode) { + case NColorPickerDialog.EditMode.R: + return root.selectedColor.r; + case NColorPickerDialog.EditMode.G: + return root.selectedColor.g; + case NColorPickerDialog.EditMode.B: + return root.selectedColor.b; + case NColorPickerDialog.EditMode.H: + return root.displayHue; + case NColorPickerDialog.EditMode.S: + return root.selectedColor.hsvSaturation; + case NColorPickerDialog.EditMode.V: + return root.selectedColor.hsvValue; + default: + return 0; } } + + // Send as one vector because GPUs are so damn picky + property vector4d params: Qt.vector4d(1.0, fixedVal, root.editMode, 0) + } + + MouseArea { + id: fieldMouse + anchors.fill: parent + cursorShape: Qt.CrossCursor + + // Update color when clicking or dragging + function updateColor(mouse) { + // Normalize X and Y (0.0 to 1.0) + var xVal = Math.max(0, Math.min(1, mouse.x / width)); + var yVal = Math.max(0, Math.min(1, 1.0 - (mouse.y / height))); // Flip Y (0 at bottom) + + // Get the current fixed value (from the slider) + var fixed = 0.0; + + switch (root.editMode) { + case NColorPickerDialog.EditMode.R: + fixed = root.selectedColor.r; + root.selectedColor = Qt.rgba(fixed, xVal, yVal, 1); + break; + case NColorPickerDialog.EditMode.G: + fixed = root.selectedColor.g; + root.selectedColor = Qt.rgba(xVal, fixed, yVal, 1); + break; + case NColorPickerDialog.EditMode.B: + fixed = root.selectedColor.b; + root.selectedColor = Qt.rgba(xVal, yVal, fixed, 1); + break; + case NColorPickerDialog.EditMode.H: + // Use stableHue to prevent flipping to -1 + fixed = root.displayHue; + root.selectedColor = Qt.hsva(fixed, xVal, yVal, 1); + root.stableHue = fixed; + break; + case NColorPickerDialog.EditMode.S: + fixed = root.selectedColor.hsvSaturation; + root.selectedColor = Qt.hsva(xVal, fixed, yVal, 1); + // If we dragged Hue (xVal), update stableHue + root.stableHue = yVal; + break; + case NColorPickerDialog.EditMode.V: + fixed = root.selectedColor.hsvValue; + root.selectedColor = Qt.hsva(xVal, yVal, fixed, 1); + // If we dragged Hue (xVal), update stableHue + root.stableHue = xVal; + break; + } + } + onPressed: mouse => updateColor(mouse) + onPositionChanged: mouse => updateColor(mouse) + } + + // Color Indicator + Rectangle { + width: 10 + height: 10 + radius: Math.min(Style.iRadiusXS, width / 2) + color: "transparent" + border.color: root.selectedColor.hsvValue < 0.5 ? "white" : "black" + border.width: 1 + + // Find position based on the current color + readonly property point selectedPos: { + switch (root.editMode) { + case NColorPickerDialog.EditMode.R: + return Qt.point(root.selectedColor.g, root.selectedColor.b); + case NColorPickerDialog.EditMode.G: + return Qt.point(root.selectedColor.r, root.selectedColor.b); + case NColorPickerDialog.EditMode.B: + return Qt.point(root.selectedColor.r, root.selectedColor.g); + case NColorPickerDialog.EditMode.H: + return Qt.point(root.selectedColor.hsvSaturation, root.selectedColor.hsvValue); + case NColorPickerDialog.EditMode.S: + return Qt.point(root.displayHue, root.selectedColor.hsvValue); + case NColorPickerDialog.EditMode.V: + return Qt.point(root.displayHue, root.selectedColor.hsvSaturation); + default: + return Qt.point(0, 0); + } + } + + // Convert values to pixel position + x: (selectedPos.x * parent.width) - width / 2 + y: ((1.0 - selectedPos.y) * parent.height) - height / 2 + } + + // Redraw the border in case Color Indicator is near the edge + Rectangle { + anchors.fill: parent + color: "transparent" + border.color: Color.mOutline + border.width: Style.borderS + antialiasing: false } } } } - } - RowLayout { - Layout.fillWidth: true - Layout.topMargin: 1 - Layout.bottomMargin: 1 - spacing: 10 - - Item { + NLabel { + label: I18n.tr("widgets.color-picker.palette.label") Layout.fillWidth: true } - NButton { - id: cancelButton - text: I18n.tr("widgets.color-picker.cancel") - outlined: cancelButton.hovered ? false : true - onClicked: { - root.close(); - } - } + NScrollView { + Layout.fillWidth: true + Layout.preferredHeight: Math.min(paletteGrid.implicitHeight, 200) + verticalPolicy: paletteGrid.implicitHeight > 200 ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff + horizontalPolicy: ScrollBar.AlwaysOff - NButton { - text: I18n.tr("widgets.color-picker.apply") - icon: "check" - onClicked: { - root.colorSelected(root.selectedColor); - // Delay close to prevent click propagation to elements behind the dialog - Qt.callLater(() => { - root.close(); - }); + GridLayout { + id: paletteGrid + width: parent.availableWidth + columns: 17 + columnSpacing: 6 + rowSpacing: 6 + + NLabel { + Layout.columnSpan: 17 + Layout.fillWidth: true + description: I18n.tr("widgets.color-picker.palette.theme-colors") + } + + Repeater { + model: [ + { + name: "mPrimary", + color: Color.mPrimary + }, + { + name: "mSecondary", + color: Color.mSecondary + }, + { + name: "mTertiary", + color: Color.mTertiary + }, + { + name: "mError", + color: Color.mError + }, + { + name: "mSurface", + color: Color.mSurface + }, + { + name: "mSurfaceVariant", + color: Color.mSurfaceVariant + }, + { + name: "mOutline", + color: Color.mOutline + } + ] + + Rectangle { + width: 24 + height: 24 + radius: Style.iRadiusXXS + color: modelData.color + border.color: root.selectedColor.toString() === modelData.color.toString() ? Color.mPrimary : Color.mOutline + border.width: Math.max(1, root.selectedColor.toString() === modelData.color.toString() ? Style.borderM : Style.borderS) + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + + onEntered: { + TooltipService.show(parent, modelData.name + "\n" + parent.color.toString().toUpperCase()); + } + onExited: { + TooltipService.hide(); + } + onClicked: { + root.selectedColor = modelData.color; + TooltipService.hide(); + } + } + } + } + + NDivider { + Layout.columnSpan: 17 + Layout.fillWidth: true + Layout.topMargin: Style.marginXS + Layout.bottomMargin: 0 + } + + NLabel { + Layout.columnSpan: 17 + Layout.fillWidth: true + description: I18n.tr("widgets.color-picker.palette.description") + } + + Repeater { + model: ColorList.colors + + Rectangle { + width: 24 + height: 24 + radius: Math.min(Style.iRadiusXS, width / 2) + color: modelData.color + border.color: root.selectedColor.toString() === modelData.color.toString() ? Color.mPrimary : Color.mOutline + border.width: root.selectedColor.toString() === modelData.color.toString() ? 2 : 1 + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + + onEntered: { + TooltipService.show(parent, modelData.name + "\n" + parent.color.toString().toUpperCase(), "auto"); + } + onExited: { + TooltipService.hide(); + } + onClicked: { + root.selectedColor = modelData.color; + TooltipService.hide(); + } + } + } + } } } } + } + + RowLayout { + Layout.fillWidth: true + Layout.topMargin: 1 + Layout.bottomMargin: 1 + spacing: 10 + + Item { + Layout.fillWidth: true + } + + NButton { + id: cancelButton + text: I18n.tr("widgets.color-picker.cancel") + outlined: cancelButton.hovered ? false : true + onClicked: { + root.close(); + } + } + + NButton { + text: I18n.tr("widgets.color-picker.apply") + icon: "check" + onClicked: { + root.colorSelected(root.selectedColor); + // Delay close to prevent click propagation to elements behind the dialog + Qt.callLater(() => { + root.close(); + }); + } + } + } } }