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