/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Quick Dialogs module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.4 import QtQuick.Controls 1.2 import QtQuick.Controls.Private 1.0 import QtQuick.Dialogs 1.0 import QtQuick.Window 2.1 import "qml" AbstractColorDialog { id: root property bool __valueSet: true // guard to prevent binding loops function __setControlsFromColor() { __valueSet = false hueSlider.value = root.currentHue saturationSlider.value = root.currentSaturation lightnessSlider.value = root.currentLightness alphaSlider.value = root.currentAlpha crosshairs.x = root.currentLightness * paletteMap.width crosshairs.y = (1.0 - root.currentSaturation) * paletteMap.height __valueSet = true } onCurrentColorChanged: __setControlsFromColor() Rectangle { id: content implicitHeight: Math.min(root.__maximumDimension, Screen.pixelDensity * (usePaletteMap ? 120 : 50)) implicitWidth: Math.min(root.__maximumDimension, usePaletteMap ? Screen.pixelDensity * (usePaletteMap ? 100 : 50) : implicitHeight * 1.5) color: palette.window focus: root.visible property real bottomMinHeight: sliders.height + buttonRow.height + outerSpacing * 3 property real spacing: 8 property real outerSpacing: 12 property bool usePaletteMap: true Keys.onPressed: { event.accepted = true switch (event.key) { case Qt.Key_Return: case Qt.Key_Select: accept() break case Qt.Key_Escape: case Qt.Key_Back: reject() break case Qt.Key_C: if (event.modifiers & Qt.ControlModifier) colorField.copyAll() break case Qt.Key_V: if (event.modifiers & Qt.ControlModifier) { colorField.paste() root.currentColor = colorField.text } break default: // do nothing event.accepted = false break } } SystemPalette { id: palette } Item { id: paletteFrame visible: content.usePaletteMap anchors { top: parent.top left: parent.left right: parent.right margins: content.outerSpacing } height: Math.min(content.height - content.bottomMinHeight, content.width - content.outerSpacing * 2) Image { id: paletteMap x: (parent.width - width) / 2 width: height onWidthChanged: root.__setControlsFromColor() height: parent.height source: "images/checkers.png" fillMode: Image.Tile // note we smoothscale the shader from a smaller version to improve performance ShaderEffect { id: map width: 64 height: 64 opacity: alphaSlider.value scale: paletteMap.width / width; layer.enabled: true layer.smooth: true anchors.centerIn: parent property real hue: hueSlider.value fragmentShader: content.OpenGLInfo.profile === OpenGLInfo.CoreProfile ? "#version 150 in vec2 qt_TexCoord0; uniform float qt_Opacity; uniform float hue; out vec4 fragColor; float hueToIntensity(float v1, float v2, float h) { h = fract(h); if (h < 1.0 / 6.0) return v1 + (v2 - v1) * 6.0 * h; else if (h < 1.0 / 2.0) return v2; else if (h < 2.0 / 3.0) return v1 + (v2 - v1) * 6.0 * (2.0 / 3.0 - h); return v1; } vec3 HSLtoRGB(vec3 color) { float h = color.x; float l = color.z; float s = color.y; if (s < 1.0 / 256.0) return vec3(l, l, l); float v1; float v2; if (l < 0.5) v2 = l * (1.0 + s); else v2 = (l + s) - (s * l); v1 = 2.0 * l - v2; float d = 1.0 / 3.0; float r = hueToIntensity(v1, v2, h + d); float g = hueToIntensity(v1, v2, h); float b = hueToIntensity(v1, v2, h - d); return vec3(r, g, b); } void main() { vec4 c = vec4(1.0); c.rgb = HSLtoRGB(vec3(hue, 1.0 - qt_TexCoord0.t, qt_TexCoord0.s)); fragColor = c * qt_Opacity; } " : " varying mediump vec2 qt_TexCoord0; uniform highp float qt_Opacity; uniform highp float hue; highp float hueToIntensity(highp float v1, highp float v2, highp float h) { h = fract(h); if (h < 1.0 / 6.0) return v1 + (v2 - v1) * 6.0 * h; else if (h < 1.0 / 2.0) return v2; else if (h < 2.0 / 3.0) return v1 + (v2 - v1) * 6.0 * (2.0 / 3.0 - h); return v1; } highp vec3 HSLtoRGB(highp vec3 color) { highp float h = color.x; highp float l = color.z; highp float s = color.y; if (s < 1.0 / 256.0) return vec3(l, l, l); highp float v1; highp float v2; if (l < 0.5) v2 = l * (1.0 + s); else v2 = (l + s) - (s * l); v1 = 2.0 * l - v2; highp float d = 1.0 / 3.0; highp float r = hueToIntensity(v1, v2, h + d); highp float g = hueToIntensity(v1, v2, h); highp float b = hueToIntensity(v1, v2, h - d); return vec3(r, g, b); } void main() { lowp vec4 c = vec4(1.0); c.rgb = HSLtoRGB(vec3(hue, 1.0 - qt_TexCoord0.t, qt_TexCoord0.s)); gl_FragColor = c * qt_Opacity; } " } MouseArea { id: mapMouseArea anchors.fill: parent onPositionChanged: { if (pressed && containsMouse) { var xx = Math.max(0, Math.min(mouse.x, parent.width)) var yy = Math.max(0, Math.min(mouse.y, parent.height)) saturationSlider.value = 1.0 - yy / parent.height lightnessSlider.value = xx / parent.width // TODO if we constrain the movement here, can avoid the containsMouse test crosshairs.x = mouse.x - crosshairs.radius crosshairs.y = mouse.y - crosshairs.radius } } onPressed: positionChanged(mouse) } Image { id: crosshairs property int radius: width / 2 // truncated to int source: "images/crosshairs.png" } BorderImage { anchors.fill: parent anchors.margins: -1 anchors.leftMargin: -2 source: "images/sunken_frame.png" border.left: 8 border.right: 8 border.top: 8 border.bottom: 8 } } } Column { id: sliders anchors { top: paletteFrame.bottom left: parent.left right: parent.right margins: content.outerSpacing } ColorSlider { id: hueSlider value: 0.5 onValueChanged: if (__valueSet) root.currentColor = Qt.hsla(hueSlider.value, saturationSlider.value, lightnessSlider.value, alphaSlider.value) text: qsTr("Hue") trackDelegate: Rectangle { rotation: -90 transformOrigin: Item.TopLeft gradient: Gradient { GradientStop {position: 0.000; color: Qt.rgba(1, 0, 0, 1)} GradientStop {position: 0.167; color: Qt.rgba(1, 1, 0, 1)} GradientStop {position: 0.333; color: Qt.rgba(0, 1, 0, 1)} GradientStop {position: 0.500; color: Qt.rgba(0, 1, 1, 1)} GradientStop {position: 0.667; color: Qt.rgba(0, 0, 1, 1)} GradientStop {position: 0.833; color: Qt.rgba(1, 0, 1, 1)} GradientStop {position: 1.000; color: Qt.rgba(1, 0, 0, 1)} } } } ColorSlider { id: saturationSlider visible: !content.usePaletteMap value: 0.5 onValueChanged: if (__valueSet) root.currentColor = Qt.hsla(hueSlider.value, saturationSlider.value, lightnessSlider.value, alphaSlider.value) text: qsTr("Saturation") trackDelegate: Rectangle { rotation: -90 transformOrigin: Item.TopLeft gradient: Gradient { GradientStop { position: 0; color: Qt.hsla(hueSlider.value, 0.0, lightnessSlider.value, 1.0) } GradientStop { position: 1; color: Qt.hsla(hueSlider.value, 1.0, lightnessSlider.value, 1.0) } } } } ColorSlider { id: lightnessSlider visible: !content.usePaletteMap value: 0.5 onValueChanged: if (__valueSet) root.currentColor = Qt.hsla(hueSlider.value, saturationSlider.value, lightnessSlider.value, alphaSlider.value) text: qsTr("Luminosity") trackDelegate: Rectangle { rotation: -90 transformOrigin: Item.TopLeft gradient: Gradient { GradientStop { position: 0; color: "black" } GradientStop { position: 0.5; color: Qt.hsla(hueSlider.value, saturationSlider.value, 0.5, 1.0) } GradientStop { position: 1; color: "white" } } } } ColorSlider { id: alphaSlider minimum: 0.0 maximum: 1.0 value: 1.0 onValueChanged: if (__valueSet) root.currentColor = Qt.hsla(hueSlider.value, saturationSlider.value, lightnessSlider.value, alphaSlider.value) text: qsTr("Alpha") visible: root.showAlphaChannel trackDelegate: Item { rotation: -90 transformOrigin: Item.TopLeft Image { anchors {fill: parent} source: "images/checkers.png" fillMode: Image.TileVertically } Rectangle { anchors.fill: parent gradient: Gradient { GradientStop { position: 0; color: "transparent" } GradientStop { position: 1; color: Qt.hsla(hueSlider.value, saturationSlider.value, lightnessSlider.value, 1.0) } } } } } } Item { id: buttonRow height: Math.max(buttonsOnly.height, copyIcon.height) width: parent.width anchors { left: parent.left right: parent.right bottom: content.bottom margins: content.outerSpacing } Row { spacing: content.spacing height: visible ? parent.height : 0 visible: !Settings.isMobile TextField { id: colorField text: root.currentColor.toString() anchors.verticalCenter: parent.verticalCenter onAccepted: root.currentColor = text Component.onCompleted: width = implicitWidth + 10 } Image { id: copyIcon anchors.verticalCenter: parent.verticalCenter source: "images/copy.png" MouseArea { anchors.fill: parent onClicked: colorField.copyAll() } } } Row { id: buttonsOnly spacing: content.spacing anchors.right: parent.right Button { id: cancelButton text: qsTr("Cancel") onClicked: root.reject() } Button { id: okButton text: qsTr("OK") onClicked: root.accept() } } } } }