Files
noctalia-shell/Widgets/NColorPickerDialog.qml
T
2025-11-17 11:42:32 -05:00

408 lines
12 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import "../Helpers/ColorsConvert.js" as ColorsConvert
Popup {
id: root
property color selectedColor: Color.black
property real currentHue: 0
property real currentSaturation: 0
signal colorSelected(color color)
width: 580
height: {
const h = scrollView.implicitHeight + padding * 2;
Math.min(h, screen?.height - Style.barHeight - Style.marginL * 2);
}
padding: Style.marginXL
// Center popup in parent
x: (parent.width - width) * 0.5
y: (parent.height - height) * 0.5
modal: true
background: Rectangle {
color: Color.mSurface
radius: Style.radiusS
border.color: Color.mPrimary
border.width: Style.borderM
}
contentItem: NScrollView {
id: scrollView
width: parent.width
verticalPolicy: ScrollBar.AlwaysOff
horizontalPolicy: ScrollBar.AlwaysOff
ColumnLayout {
width: scrollView.availableWidth
spacing: Style.marginL
// Header
RowLayout {
Layout.fillWidth: true
RowLayout {
spacing: Style.marginS
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
}
}
Item {
Layout.fillWidth: true
}
NIconButton {
icon: "close"
onClicked: root.close()
}
}
// Color preview section
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 80
radius: Style.radiusS
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: Font.Bold
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
}
}
}
// Hex input
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM
NLabel {
label: I18n.tr("widgets.color-picker.hex.label")
description: I18n.tr("widgets.color-picker.hex.description")
Layout.fillWidth: true
}
NTextInput {
text: root.selectedColor.toString().toUpperCase()
fontFamily: Settings.data.ui.fontFixed
Layout.fillWidth: true
onEditingFinished: {
if (/^#[0-9A-F]{6}$/i.test(text)) {
root.selectedColor = text;
}
}
}
}
// RGB sliders section
NBox {
Layout.fillWidth: true
Layout.preferredHeight: slidersSection.implicitHeight + Style.marginL * 2
ColumnLayout {
id: slidersSection
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginM
NLabel {
label: I18n.tr("widgets.color-picker.rgb.label")
description: I18n.tr("widgets.color-picker.rgb.description")
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM
NText {
text: "R"
font.weight: Font.Bold
Layout.preferredWidth: 20
}
NValueSlider {
id: redSlider
Layout.fillWidth: true
from: 0
to: 255
value: Math.round(root.selectedColor.r * 255)
onMoved: value => {
root.selectedColor = Qt.rgba(value / 255, root.selectedColor.g, root.selectedColor.b, 1);
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
root.currentHue = hsv.h;
root.currentSaturation = hsv.s;
}
text: Math.round(value)
}
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM
NText {
text: "G"
font.weight: Font.Bold
Layout.preferredWidth: 20
}
NValueSlider {
id: greenSlider
Layout.fillWidth: true
from: 0
to: 255
value: Math.round(root.selectedColor.g * 255)
onMoved: value => {
root.selectedColor = Qt.rgba(root.selectedColor.r, value / 255, root.selectedColor.b, 1);
// Update stored hue and saturation when RGB changes
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
root.currentHue = hsv.h;
root.currentSaturation = hsv.s;
}
text: Math.round(value)
}
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM
NText {
text: "B"
font.weight: Font.Bold
Layout.preferredWidth: 20
}
NValueSlider {
id: blueSlider
Layout.fillWidth: true
from: 0
to: 255
value: Math.round(root.selectedColor.b * 255)
onMoved: value => {
root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, value / 255, 1);
// Update stored hue and saturation when RGB changes
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
root.currentHue = hsv.h;
root.currentSaturation = hsv.s;
}
text: Math.round(value)
}
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM
NText {
text: I18n.tr("widgets.color-picker.brightness")
font.weight: Font.Bold
Layout.preferredWidth: 80
}
NValueSlider {
id: brightnessSlider
Layout.fillWidth: true
from: 0
to: 100
value: {
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
return hsv.v;
}
onMoved: value => {
var hue = root.currentHue;
var saturation = root.currentSaturation;
if (hue === 0 && saturation === 0) {
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
hue = hsv.h;
saturation = hsv.s;
root.currentHue = hue;
root.currentSaturation = saturation;
}
var rgb = ColorsConvert.hsvToRgb(hue, saturation, value);
root.selectedColor = Qt.rgba(rgb.r / 255, rgb.g / 255, rgb.b / 255, 1);
}
text: Math.round(brightnessSlider.value) + "%"
}
}
}
}
NBox {
Layout.fillWidth: true
Layout.preferredHeight: themePalette.implicitHeight + Style.marginL * 2
ColumnLayout {
id: themePalette
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginS
NLabel {
label: I18n.tr("widgets.color-picker.theme-colors.label")
description: I18n.tr("widgets.color-picker.theme-colors.description")
Layout.fillWidth: true
}
Flow {
spacing: 6
Layout.fillWidth: true
flow: Flow.LeftToRight
Repeater {
model: [Color.mPrimary, Color.mSecondary, Color.mTertiary, Color.mError, Color.mSurface, Color.mSurfaceVariant, Color.mOutline, Color.white, Color.black]
Rectangle {
width: 24
height: 24
radius: 4
color: modelData
border.color: root.selectedColor === modelData ? Color.mPrimary : Color.mOutline
border.width: root.selectedColor === modelData ? 2 : 1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
root.selectedColor = modelData;
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
root.currentHue = hsv.h;
root.currentSaturation = hsv.s;
}
}
}
}
}
}
}
NBox {
Layout.fillWidth: true
Layout.preferredHeight: genericPalette.implicitHeight + Style.marginL * 2
ColumnLayout {
id: genericPalette
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginS
NLabel {
label: I18n.tr("widgets.color-picker.palette.label")
description: I18n.tr("widgets.color-picker.palette.description")
Layout.fillWidth: true
}
Flow {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 6
flow: Flow.LeftToRight
Repeater {
model: ["#F44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", "#2196F3", "#03A9F4", "#00BCD4", "#009688", "#4CAF50", "#8BC34A", "#CDDC39", "#FFEB3B", "#FFC107", "#FF9800", "#FF5722", "#795548", "#9E9E9E", "#E74C3C", "#E67E22", "#F1C40F", "#2ECC71", "#1ABC9C", "#3498DB", "#2980B9", "#9B59B6", "#34495E", "#2C3E50", "#95A5A6", "#7F8C8D",
Color.white, Color.black]
Rectangle {
width: 24
height: 24
radius: Style.radiusXXS
color: modelData
border.color: root.selectedColor === modelData ? Color.mPrimary : Color.mOutline
border.width: Math.max(1, root.selectedColor === modelData ? Style.borderM : Style.borderS)
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
root.selectedColor = modelData;
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
root.currentHue = hsv.h;
root.currentSaturation = hsv.s;
}
}
}
}
}
}
}
RowLayout {
Layout.fillWidth: true
Layout.topMargin: 20
Layout.bottomMargin: 20
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);
root.close();
}
}
}
}
}
}