14 changed files with 372 additions and 11 deletions
@ -0,0 +1,321 @@ |
|||||
|
import QtQuick 2.10 |
||||
|
import QtQuick.Controls 2.3 |
||||
|
import QtQuick.Controls.Material 2.3 |
||||
|
import QtQuick.Layouts 1.3 |
||||
|
import QtGraphicalEffects 1.0 |
||||
|
import de.skycoder42.QtMvvm.Quick 1.1 |
||||
|
|
||||
|
GridLayout { |
||||
|
id: _colorPicker |
||||
|
columns: 2 |
||||
|
rowSpacing: 16 |
||||
|
|
||||
|
ColorHelper { |
||||
|
id: helper |
||||
|
} |
||||
|
|
||||
|
property bool alpha: false |
||||
|
property color color |
||||
|
onColorChanged: { |
||||
|
if(!_p.changing){ |
||||
|
var hsvColor = _p.rgbToHsv(color); |
||||
|
_p.changing = true; |
||||
|
hueSlider.value = hsvColor.h; |
||||
|
saturationSlider.value = 1 - hsvColor.s; |
||||
|
valueSlider.value = hsvColor.v; |
||||
|
_colorEdit.setColor(color); |
||||
|
_p.changing = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
QtObject { |
||||
|
id: _p |
||||
|
|
||||
|
property bool changing: false |
||||
|
readonly property color color: Qt.hsva(hueSlider.position, 1 - saturationSlider.position, valueSlider.position) |
||||
|
readonly property color hueColor: Qt.hsva(hueSlider.position, 1, 1); |
||||
|
|
||||
|
onColorChanged: { |
||||
|
if(!_p.changing) { |
||||
|
_p.changing = true; |
||||
|
_colorPicker.color = _p.color |
||||
|
_colorEdit.setColor(color); |
||||
|
_p.changing = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function rgbToHsv(rgbColor) { |
||||
|
var rr, gg, bb, |
||||
|
r = rgbColor.r, |
||||
|
g = rgbColor.g, |
||||
|
b = rgbColor.b, |
||||
|
h, s, |
||||
|
v = Math.max(r, g, b), |
||||
|
diff = v - Math.min(r, g, b), |
||||
|
diffc = function(c){ |
||||
|
return (v - c) / 6 / diff + 1 / 2; |
||||
|
}; |
||||
|
|
||||
|
if (diff == 0) { |
||||
|
h = s = 0; |
||||
|
} else { |
||||
|
s = diff / v; |
||||
|
rr = diffc(r); |
||||
|
gg = diffc(g); |
||||
|
bb = diffc(b); |
||||
|
|
||||
|
if (r === v) { |
||||
|
h = bb - gg; |
||||
|
} else if (g === v) { |
||||
|
h = (1 / 3) + rr - bb; |
||||
|
} else if (b === v) { |
||||
|
h = (2 / 3) + gg - rr; |
||||
|
} |
||||
|
|
||||
|
if (h < 0) { |
||||
|
h += 1; |
||||
|
} else if (h > 1) { |
||||
|
h -= 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
h: h, |
||||
|
s: s, |
||||
|
v: v |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Rectangle { |
||||
|
id: colorPreview |
||||
|
color: _colorPicker.color |
||||
|
Layout.rowSpan: 3 |
||||
|
Layout.preferredWidth: hueSlider.height + saturationSlider.height + valueSlider.height + 2 * _colorPicker.rowSpacing |
||||
|
Layout.maximumWidth: _colorPicker.width/3 |
||||
|
Layout.fillHeight: true |
||||
|
|
||||
|
border.color: helper.text |
||||
|
border.width: 1 |
||||
|
} |
||||
|
|
||||
|
Slider { |
||||
|
id: hueSlider |
||||
|
Layout.fillWidth: true |
||||
|
Material.accent: "#FF0000" |
||||
|
from: 0 |
||||
|
to: 1 |
||||
|
|
||||
|
readonly property real handleWidth: 25 |
||||
|
readonly property real handleHeight: ((handleWidth-1)/2)*3 + 1 |
||||
|
leftPadding: (handleWidth-1)/2 + 1 |
||||
|
rightPadding: (handleWidth-1)/2 + 1 |
||||
|
|
||||
|
background: LinearGradient { |
||||
|
x: hueSlider.leftPadding |
||||
|
y: 0 |
||||
|
implicitWidth: 200 |
||||
|
implicitHeight: hueSlider.handleHeight |
||||
|
width: hueSlider.availableWidth |
||||
|
height: implicitHeight |
||||
|
start: Qt.point(0, 0) |
||||
|
end: Qt.point(width, 0) |
||||
|
|
||||
|
Rectangle { |
||||
|
anchors.fill: parent |
||||
|
color: "transparent" |
||||
|
border.color: helper.text |
||||
|
} |
||||
|
|
||||
|
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) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
handle: Item { |
||||
|
x: 1 + hueSlider.visualPosition * (hueSlider.availableWidth - 1) |
||||
|
width: hueSlider.handleWidth |
||||
|
height: hueSlider.handleHeight |
||||
|
|
||||
|
Rectangle { |
||||
|
anchors.horizontalCenter: parent.horizontalCenter |
||||
|
anchors.top: parent.top |
||||
|
anchors.bottom: circle.top |
||||
|
width: 1 |
||||
|
color: helper.text |
||||
|
} |
||||
|
|
||||
|
Rectangle { |
||||
|
id: circle |
||||
|
anchors.centerIn: parent |
||||
|
implicitWidth: parent.width |
||||
|
implicitHeight: parent.width |
||||
|
radius: parent.width / 2 |
||||
|
color: Qt.hsva(hueSlider.position, 1, 1) |
||||
|
border.color: helper.text |
||||
|
} |
||||
|
|
||||
|
Rectangle { |
||||
|
anchors.horizontalCenter: parent.horizontalCenter |
||||
|
anchors.top: circle.bottom |
||||
|
anchors.bottom: parent.bottom |
||||
|
width: 1 |
||||
|
color: helper.text |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Slider { |
||||
|
id: saturationSlider |
||||
|
Layout.fillWidth: true |
||||
|
from: 0 |
||||
|
to: 1 |
||||
|
|
||||
|
readonly property real handleSize: 24 |
||||
|
leftPadding: handleSize/2 + 1 |
||||
|
rightPadding: handleSize/2 + 1 |
||||
|
|
||||
|
background: Item { |
||||
|
x: saturationSlider.leftPadding |
||||
|
y: saturationSlider.topPadding + (saturationSlider.availableHeight - height) / 2 |
||||
|
implicitWidth: 200 |
||||
|
implicitHeight: 6 |
||||
|
width: saturationSlider.availableWidth |
||||
|
height: implicitHeight |
||||
|
|
||||
|
Rectangle { |
||||
|
rotation: -90 |
||||
|
anchors.centerIn: parent |
||||
|
width: parent.height |
||||
|
height: parent.width |
||||
|
radius: 3 |
||||
|
border.color: helper.text |
||||
|
|
||||
|
gradient: Gradient { |
||||
|
GradientStop { |
||||
|
position: 0.000 |
||||
|
color: Qt.hsva(hueSlider.position, 1, 1) |
||||
|
} |
||||
|
GradientStop { |
||||
|
position: 1.000 |
||||
|
color: Qt.hsva(hueSlider.position, 0, 1) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
handle: Rectangle { |
||||
|
x: 1 + saturationSlider.visualPosition * saturationSlider.availableWidth |
||||
|
y: saturationSlider.topPadding + (saturationSlider.availableHeight - height)/2 |
||||
|
width: saturationSlider.handleSize |
||||
|
height: saturationSlider.handleSize |
||||
|
radius: saturationSlider.handleSize/2 |
||||
|
color: Qt.hsva(hueSlider.position, 1 - saturationSlider.position, 1) |
||||
|
border.color: helper.text |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Slider { |
||||
|
id: valueSlider |
||||
|
Layout.fillWidth: true |
||||
|
from: 0 |
||||
|
to: 1 |
||||
|
|
||||
|
readonly property real handleSize: 24 |
||||
|
leftPadding: handleSize/2 + 1 |
||||
|
rightPadding: handleSize/2 + 1 |
||||
|
|
||||
|
background: Item { |
||||
|
x: valueSlider.leftPadding |
||||
|
y: valueSlider.topPadding + (valueSlider.availableHeight - height) / 2 |
||||
|
implicitWidth: 200 |
||||
|
implicitHeight: 6 |
||||
|
width: valueSlider.availableWidth |
||||
|
height: implicitHeight |
||||
|
|
||||
|
Rectangle { |
||||
|
rotation: -90 |
||||
|
anchors.centerIn: parent |
||||
|
width: parent.height |
||||
|
height: parent.width |
||||
|
radius: 3 |
||||
|
border.color: helper.text |
||||
|
|
||||
|
gradient: Gradient { |
||||
|
GradientStop { |
||||
|
position: 0.000 |
||||
|
color: Qt.hsva(hueSlider.position, 1, 0) |
||||
|
} |
||||
|
GradientStop { |
||||
|
position: 1.000 |
||||
|
color: Qt.hsva(hueSlider.position, 1, 1) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
handle: Rectangle { |
||||
|
x: 1 + valueSlider.visualPosition * valueSlider.availableWidth |
||||
|
y: valueSlider.topPadding + (valueSlider.availableHeight - height)/2 |
||||
|
width: valueSlider.handleSize |
||||
|
height: valueSlider.handleSize |
||||
|
radius: valueSlider.handleSize/2 |
||||
|
color: Qt.hsva(hueSlider.position, 1, valueSlider.position) |
||||
|
border.color: helper.text |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
TextField { |
||||
|
id: _colorEdit |
||||
|
Layout.columnSpan: 2 |
||||
|
Layout.fillWidth: true |
||||
|
selectByMouse: true |
||||
|
|
||||
|
validator: RegExpValidator { |
||||
|
regExp: _colorPicker.alpha ? /^#(?:[0-9a-f]{4}){1,2}$/ : /^#(?:[0-9a-f]{3}){1,2}$/ |
||||
|
} |
||||
|
|
||||
|
property bool _skipSet: false |
||||
|
function setColor(color) { |
||||
|
if(_skipSet) |
||||
|
_skipSet = false; |
||||
|
else |
||||
|
text = color; |
||||
|
} |
||||
|
|
||||
|
text: _colorPicker.color |
||||
|
onTextEdited: { |
||||
|
if(_colorEdit.acceptableInput) { |
||||
|
_skipSet = true; |
||||
|
_colorPicker.color = text; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
import QtQuick 2.10 |
||||
|
import de.skycoder42.QtMvvm.Quick 1.1 as QtMvvm |
||||
|
|
||||
|
QtMvvm.MsgDelegate { |
||||
|
id: _colorDelegate |
||||
|
|
||||
|
editDialogType: "QColor" |
||||
|
indicatorComponent: Rectangle { |
||||
|
id: colorCircle |
||||
|
width: 24 |
||||
|
height: 24 |
||||
|
radius: height / 2 |
||||
|
color: inputValue |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
import de.skycoder42.QtMvvm.Quick 1.1 as QtMvvm |
||||
|
|
||||
|
QtMvvm.ColorEdit { |
||||
|
id: _edit |
||||
|
property alias inputValue: _edit.color |
||||
|
} |
Loading…
Reference in new issue