QmlDesigner: Fix ColorEditor

* Change the look of the picker cross
* Cleanup custom RGB, HSL, HSV and Alpha value storage and handling
* Fix issue of alpha not updating when selecting gradient stops
* Fix initial color not shown correctly
* Fix ComboBox warning about parameter injection
* Add checkerboard image to GradientLine
* Remove GradientPopupIndicator
* Add signals to RealSpinBox in order to react to user interaction
* Cleanup code
* Change how porperty editor value emits value if old and new color are
  equal

Task-number: QDS-4755
Change-Id: I1a8095664fc8ed53c52659ac20557c03b89704c9
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Henning Gruendl
2021-08-20 17:37:17 +02:00
committed by Henning Gründl
parent 74bfae6a4a
commit 04c36419f7
12 changed files with 366 additions and 288 deletions

View File

@@ -189,8 +189,20 @@ SecondColumnLayout {
colorEditor.originalColor = colorEditor.color
}
onValueChanged: colorEditor.color = colorEditor.value
onBackendValueChanged: colorEditor.color = colorEditor.value
Connections {
id: backendConnection
target: colorEditor
function onValueChanged() {
if (isNotInGradientMode())
colorEditor.color = colorEditor.value
}
function onBackendValueChanged() {
if (isNotInGradientMode())
colorEditor.color = colorEditor.value
}
}
Timer {
id: colorEditorTimer
@@ -198,15 +210,18 @@ SecondColumnLayout {
interval: 100
running: false
onTriggered: {
backendConnection.enabled = false
if (colorEditor.backendValue !== undefined) {
if (isVector3D) {
if (colorEditor.isVector3D)
colorEditor.backendValue.value = Qt.vector3d(colorEditor.color.r,
colorEditor.color.g,
colorEditor.color.b)
} else {
else
colorEditor.backendValue.value = colorEditor.color
}
}
backendConnection.enabled = true
}
}
@@ -223,8 +238,6 @@ SecondColumnLayout {
if (isNotInGradientMode())
colorEditorTimer.restart() // Delay setting the color to keep ui responsive
colorPalette.selectedColor = colorEditor.color
}
Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth }
@@ -419,6 +432,7 @@ SecondColumnLayout {
tooltip: qsTr("Transparent")
onClicked: {
colorPicker.alpha = 0
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
@@ -503,8 +517,10 @@ SecondColumnLayout {
visible: !isNotInGradientMode()
onCurrentColorChanged: {
if (colorEditor.supportGradient && gradientLine.hasGradient)
if (colorEditor.supportGradient && gradientLine.hasGradient) {
colorEditor.color = gradientLine.currentColor
colorPicker.color = colorEditor.color
}
}
onHasGradientChanged: {
@@ -515,10 +531,9 @@ SecondColumnLayout {
}
onSelectedNodeChanged: {
if (colorEditor.supportGradient && gradientLine.hasGradient) {
if (colorEditor.supportGradient && gradientLine.hasGradient)
colorEditor.originalColor = gradientLine.currentColor
}
}
onInvalidated: colorEditor.updateThumbnail()
@@ -546,13 +561,15 @@ SecondColumnLayout {
function onSelectionChanged() {
if (colorEditor.supportGradient && gradientLine.hasGradient) {
colorEditor.color = gradientLine.currentColor
gradientLine.currentColor = color
gradientLine.currentColor = colorEditor.color
hexTextField.text = colorEditor.color
popupHexTextField.text = colorEditor.color
}
gradientLine.isInValidState = true
colorEditor.originalColor = colorEditor.color
colorPalette.selectedColor = colorEditor.color
colorPicker.color = colorEditor.color
colorEditor.createModel()
colorEditor.determineActiveColorMode()
@@ -563,44 +580,32 @@ SecondColumnLayout {
ColorPicker {
id: colorPicker
property color boundColor: colorEditor.color
width: parent.width
sliderMargins: 4
// Prevent the binding to be deleted by assignment
onBoundColorChanged: colorPicker.color = colorPicker.boundColor
onUpdateColor: {
colorEditor.color = colorPicker.color
if (contextMenu.opened)
contextMenu.close()
}
onRightMouseButtonClicked: contextMenu.popup(colorPicker)
onColorInvalidated: {
switch (colorPicker.mode) {
case ColorPicker.Mode.HSLA:
hslHueSpinBox.value = colorPicker.hue
hslSaturationSpinBox.value = colorPicker.saturationHSL
hslLightnessSpinBox.value = colorPicker.lightness
hslAlphaSpinBox.value = colorPicker.alpha
break
case ColorPicker.Mode.RGBA:
redSpinBox.value = (colorPicker.color.r * 255)
greenSpinBox.value = (colorPicker.color.g * 255)
blueSpinBox.value = (colorPicker.color.b * 255)
redSpinBox.value = (colorPicker.red * 255)
greenSpinBox.value = (colorPicker.green * 255)
blueSpinBox.value = (colorPicker.blue * 255)
rgbAlphaSpinBox.value = (colorPicker.alpha * 255)
break
case ColorPicker.Mode.HSVA:
default:
hsvHueSpinBox.value = colorPicker.hue
hsvSaturationSpinBox.value = colorPicker.saturationHSV
hsvValueSpinBox.value = colorPicker.value
hsvAlphaSpinBox.value = colorPicker.alpha
break
}
}
}
@@ -790,7 +795,6 @@ SecondColumnLayout {
RowLayout {
id: rgbaRow
visible: colorPicker.mode === ColorPicker.Mode.RGBA
Layout.fillWidth: true
spacing: StudioTheme.Values.controlGap
@@ -798,7 +802,6 @@ SecondColumnLayout {
DoubleSpinBox {
id: redSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
stepSize: 1
minimumValue: 0
maximumValue: 255
@@ -806,17 +809,19 @@ SecondColumnLayout {
onValueModified: {
var tmp = redSpinBox.value / 255.0
if (colorPicker.color.r !== tmp && !colorPicker.block) {
colorPicker.color.r = tmp
if (colorPicker.red !== tmp && !colorPicker.block) {
colorPicker.red = tmp
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
DoubleSpinBox {
id: greenSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
stepSize: 1
minimumValue: 0
maximumValue: 255
@@ -824,17 +829,19 @@ SecondColumnLayout {
onValueModified: {
var tmp = greenSpinBox.value / 255.0
if (colorPicker.color.g !== tmp && !colorPicker.block) {
colorPicker.color.g = tmp
if (colorPicker.green !== tmp && !colorPicker.block) {
colorPicker.green = tmp
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
DoubleSpinBox {
id: blueSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
stepSize: 1
minimumValue: 0
maximumValue: 255
@@ -842,17 +849,19 @@ SecondColumnLayout {
onValueModified: {
var tmp = blueSpinBox.value / 255.0
if (colorPicker.color.b !== tmp && !colorPicker.block) {
colorPicker.color.b = tmp
if (colorPicker.blue !== tmp && !colorPicker.block) {
colorPicker.blue = tmp
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
DoubleSpinBox {
id: rgbAlphaSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
stepSize: 1
minimumValue: 0
maximumValue: 255
@@ -862,15 +871,17 @@ SecondColumnLayout {
var tmp = rgbAlphaSpinBox.value / 255.0
if (colorPicker.alpha !== tmp && !colorPicker.block) {
colorPicker.alpha = tmp
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
}
RowLayout {
id: hslaRow
visible: colorPicker.mode === ColorPicker.Mode.HSLA
Layout.fillWidth: true
spacing: StudioTheme.Values.controlGap
@@ -882,9 +893,12 @@ SecondColumnLayout {
if (colorPicker.hue !== hslHueSpinBox.value
&& !colorPicker.block) {
colorPicker.hue = hslHueSpinBox.value
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
DoubleSpinBox {
@@ -894,9 +908,12 @@ SecondColumnLayout {
if (colorPicker.saturationHSL !== hslSaturationSpinBox.value
&& !colorPicker.block) {
colorPicker.saturationHSL = hslSaturationSpinBox.value
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
DoubleSpinBox {
@@ -906,6 +923,7 @@ SecondColumnLayout {
if (colorPicker.lightness !== hslLightnessSpinBox.value
&& !colorPicker.block) {
colorPicker.lightness = hslLightnessSpinBox.value
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
@@ -918,15 +936,17 @@ SecondColumnLayout {
if (colorPicker.alpha !== hslAlphaSpinBox.value
&& !colorPicker.block) {
colorPicker.alpha = hslAlphaSpinBox.value
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
}
RowLayout {
id: hsvaRow
visible: colorPicker.mode === ColorPicker.Mode.HSVA
Layout.fillWidth: true
spacing: StudioTheme.Values.controlGap
@@ -938,9 +958,12 @@ SecondColumnLayout {
if (colorPicker.hue !== hsvHueSpinBox.value
&& !colorPicker.block) {
colorPicker.hue = hsvHueSpinBox.value
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
DoubleSpinBox {
@@ -950,9 +973,12 @@ SecondColumnLayout {
if (colorPicker.saturationHSV !== hsvSaturationSpinBox.value
&& !colorPicker.block) {
colorPicker.saturationHSV = hsvSaturationSpinBox.value
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
DoubleSpinBox {
@@ -962,9 +988,12 @@ SecondColumnLayout {
if (colorPicker.value !== hsvValueSpinBox.value
&& !colorPicker.block) {
colorPicker.value = hsvValueSpinBox.value
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
DoubleSpinBox {
@@ -974,9 +1003,12 @@ SecondColumnLayout {
if (colorPicker.alpha !== hsvAlphaSpinBox.value
&& !colorPicker.block) {
colorPicker.alpha = hsvAlphaSpinBox.value
colorPicker.invalidateColor()
colorPicker.updateColor()
}
}
onDragStarted: colorEditorTimer.stop()
onIndicatorPressed: colorEditorTimer.stop()
}
}
}
@@ -986,7 +1018,6 @@ SecondColumnLayout {
caption: qsTr("Palette")
anchors.left: parent.left
anchors.right: parent.right
leftPadding: 10
rightPadding: 10
bottomPadding: 5
@@ -994,8 +1025,14 @@ SecondColumnLayout {
ColorPalette {
id: colorPalette
enableSingletonConnection: cePopup.opened
onSelectedColorChanged: colorEditor.color = colorPalette.selectedColor
onDialogColorChanged: colorEditor.color = colorPalette.selectedColor
onSelectedColorChanged: {
colorPicker.color = colorPalette.selectedColor
colorEditor.color = colorPalette.selectedColor
}
onDialogColorChanged: {
colorPicker.color = colorPalette.selectedColor
colorEditor.color = colorPalette.selectedColor
}
}
}
@@ -1005,7 +1042,6 @@ SecondColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
visible: !colorEditor.isNotInGradientMode()
leftPadding: 10
rightPadding: 10
@@ -1039,12 +1075,10 @@ SecondColumnLayout {
Column {
id: defaultGradientControls
spacing: 10
visible: colorEditor.hasLinearGradient() && !colorEditor.shapeGradients
RowLayout {
id: defaultGradientOrientation
Layout.fillWidth: true
spacing: 0
@@ -1055,9 +1089,9 @@ SecondColumnLayout {
width: implicitWidth
model: [{ value: Gradient.Vertical, text: qsTr("Vertical") },
{ value: Gradient.Horizontal, text: qsTr("Horizontal") }]
textRole: "text"
valueRole: "value"
onActivated: {
gradientLine.model.setGradientOrientation(gradientOrientation.currentValue)
colorEditor.updateThumbnail()
@@ -1090,7 +1124,6 @@ SecondColumnLayout {
Column {
id: linearGradientControls
spacing: 10
visible: colorEditor.hasLinearGradient() && colorEditor.shapeGradients
ControlsRow {
@@ -1126,7 +1159,6 @@ SecondColumnLayout {
Column {
id: radialGradientControls
spacing: 10
visible: colorEditor.hasRadialGradient()
ControlsRow {
@@ -1170,7 +1202,6 @@ SecondColumnLayout {
Column {
id: concialGradientControls
spacing: 10
visible: colorEditor.hasConicalGradient()
ControlsRow {

View File

@@ -36,18 +36,20 @@ Column {
}
property int mode: ColorPicker.Mode.HSVA
property color color
property color color: "#303091"
property real red: 0
property real green: 0
property real blue: 0
property real hue: 0
property real saturationHSL: 0
property real saturationHSV: 0
property real lightness: 0
property real value: 0
property real saturationHSL: 0.5
property real saturationHSV: 0.5
property real lightness: 0.5
property real value: 0.5
property real alpha: 1
property bool achromatic: false
property int sliderMargins: 6
property bool block: false
@@ -57,81 +59,90 @@ Column {
spacing: 10
onModeChanged: {
onColorChanged: {
if (root.block)
return
switch (root.mode) {
case ColorPicker.Mode.RGBA:
root.color = Qt.rgba(root.color.r, root.color.g, root.color.b, root.alpha)
root.red = root.color.r
root.green = root.color.g
root.blue = root.color.b
root.alpha = root.color.a
break
case ColorPicker.Mode.HSLA:
root.color = Qt.hsla(root.hue, root.saturationHSL, root.lightness, root.alpha)
if (root.color.hslHue !== -1)
root.hue = root.color.hslHue
root.saturationHSL = root.color.hslSaturation
root.lightness = root.color.hslLightness
root.alpha = root.color.a
break
case ColorPicker.Mode.HSVA:
default:
root.color = Qt.hsva(root.hue, root.saturationHSV, root.value, root.alpha)
if (root.color.hsvHue !== -1)
root.hue = root.color.hsvHue
root.saturationHSV = root.color.hsvSaturation
root.value = root.color.hsvValue
root.alpha = root.color.a
break
}
gradientOverlay.requestPaint()
root.invalidateColor()
}
onHueChanged: {
if (root.mode === ColorPicker.Mode.HSLA)
root.color.hslHue = root.hue
else
root.color.hsvHue = root.hue
}
onSaturationHSLChanged: {
root.color.hslSaturation = root.saturationHSL
invalidateColor()
}
onSaturationHSVChanged: {
root.color.hsvSaturation = root.saturationHSV
}
onLightnessChanged: {
root.color.hslLightness = root.lightness
}
onValueChanged: {
root.color.hsvValue = root.value
}
onAlphaChanged: invalidateColor()
onColorChanged: invalidateColor()
function invalidateColor() {
if (root.block)
return
root.block = true
if (root.color.hsvSaturation > 0.0
&& root.color.hsvValue > 0.0
&& root.color.hsvHue !== -1.0)
root.hue = root.color.hsvHue
switch (root.mode) {
case ColorPicker.Mode.RGBA:
root.color = Qt.rgba(root.red, root.green, root.blue, root.alpha)
// Set HSVA and HSLA
if (root.color.hsvHue !== -1)
root.hue = root.color.hsvHue // doesn't matter if hsvHue or hslHue
if (root.color.hslSaturation > 0.0
&& root.color.hslLightness > 0.0
&& root.color.hslHue !== -1.0)
root.hue = root.color.hslHue
if (root.color.hslLightness !== 0.0 && root.color.hslLightness !== 1.0 && !root.achromatic)
if (root.color.hslLightness !== 0.0 && root.color.hslLightness !== 1.0)
root.saturationHSL = root.color.hslSaturation
if (root.color.hsvValue !== 0.0 && root.color.hsvValue !== 1.0 && !root.achromatic)
if (root.color.hsvValue !== 0.0)
root.saturationHSV = root.color.hsvSaturation
root.lightness = root.color.hslLightness
root.value = root.color.hsvValue
if (root.color.hslLightness === 0.0 || root.color.hslLightness === 1.0
|| root.color.hsvValue === 0.0 || root.color.hsvValue === 1.0
|| root.color.hsvHue === -1.0 || root.color.hslHue === -1.0)
root.achromatic = true
else
root.achromatic = false
if (root.mode === ColorPicker.Mode.HSLA)
break
case ColorPicker.Mode.HSLA:
root.color = Qt.hsla(root.hue, root.saturationHSL, root.lightness, root.alpha)
else
// Set RGBA and HSVA
root.red = root.color.r
root.green = root.color.g
root.blue = root.color.b
if (root.color.hsvValue !== 0.0)
root.saturationHSV = root.color.hsvSaturation
root.value = root.color.hsvValue
break
case ColorPicker.Mode.HSVA:
default:
root.color = Qt.hsva(root.hue, root.saturationHSV, root.value, root.alpha)
// Set RGBA and HSLA
root.red = root.color.r
root.green = root.color.g
root.blue = root.color.b
if (root.color.hslLightness !== 0.0 && root.color.hslLightness !== 1.0)
root.saturationHSL = root.color.hslSaturation
root.lightness = root.color.hslLightness
break
}
luminanceSlider.value = (1.0 - root.value)
hueSlider.value = root.hue
@@ -142,21 +153,21 @@ Column {
root.block = false
}
function drawHSVA(ctx) {
for (var row = 0; row < gradientOverlay.height; row++) {
var gradient = ctx.createLinearGradient(0, 0, gradientOverlay.width, 0)
var v = Math.abs(row - gradientOverlay.height) / gradientOverlay.height
function drawHSVA(ctx, width, height, hue) {
for (var row = 0; row < height; row++) {
var gradient = ctx.createLinearGradient(0, 0, width, 0)
var v = Math.abs(row - height) / height
gradient.addColorStop(0, Qt.hsva(root.hue, 0, v, 1))
gradient.addColorStop(1, Qt.hsva(root.hue, 1, v, 1))
gradient.addColorStop(0, Qt.hsva(hue, 0, v, 1))
gradient.addColorStop(1, Qt.hsva(hue, 1, v, 1))
ctx.fillStyle = gradient
ctx.fillRect(0, row, gradientOverlay.width, 1)
ctx.fillRect(0, row, width, 1)
}
}
function drawRGBA(ctx) {
var gradient = ctx.createLinearGradient(0, 0, gradientOverlay.width, 0)
function drawRGBA(ctx, width, height) {
var gradient = ctx.createLinearGradient(0, 0, width, 0)
gradient.addColorStop(0.000, Qt.rgba(1, 0, 0, 1))
gradient.addColorStop(0.167, Qt.rgba(1, 1, 0, 1))
gradient.addColorStop(0.333, Qt.rgba(0, 1, 0, 1))
@@ -166,26 +177,26 @@ Column {
gradient.addColorStop(1.000, Qt.rgba(1, 0, 0, 1))
ctx.fillStyle = gradient
ctx.fillRect(0, 0, gradientOverlay.width, gradientOverlay.height)
ctx.fillRect(0, 0, width, height)
gradient = ctx.createLinearGradient(0, 0, 0, gradientOverlay.height)
gradient = ctx.createLinearGradient(0, 0, 0, height)
gradient.addColorStop(0.000, Qt.rgba(0, 0, 0, 0))
gradient.addColorStop(1.000, Qt.rgba(1, 1, 1, 1))
ctx.fillStyle = gradient
ctx.fillRect(0, 0, gradientOverlay.width, gradientOverlay.height)
ctx.fillRect(0, 0, width, height)
}
function drawHSLA(ctx) {
for (var row = 0; row < gradientOverlay.height; row++) {
var gradient = ctx.createLinearGradient(0, 0, gradientOverlay.width, 0)
var l = Math.abs(row - gradientOverlay.height) / gradientOverlay.height
function drawHSLA(ctx, width, height, hue) {
for (var row = 0; row < height; row++) {
var gradient = ctx.createLinearGradient(0, 0, width, 0)
var l = Math.abs(row - height) / height
gradient.addColorStop(0, Qt.hsla(root.hue, 0, l, 1))
gradient.addColorStop(1, Qt.hsla(root.hue, 1, l, 1))
gradient.addColorStop(0, Qt.hsla(hue, 0, l, 1))
gradient.addColorStop(1, Qt.hsla(hue, 1, l, 1))
ctx.fillStyle = gradient
ctx.fillRect(0, row, gradientOverlay.width, 1)
ctx.fillRect(0, row, width, 1)
}
}
@@ -211,11 +222,12 @@ Column {
id: gradientOverlay
anchors.fill: parent
opacity: root.color.a
opacity: root.alpha
Connections {
target: root
function onHueChanged() { gradientOverlay.requestPaint() }
function onModeChanged() { gradientOverlay.requestPaint() }
}
onPaint: {
@@ -225,14 +237,14 @@ Column {
switch (root.mode) {
case ColorPicker.Mode.RGBA:
root.drawRGBA(ctx)
root.drawRGBA(ctx, gradientOverlay.width, gradientOverlay.height)
break
case ColorPicker.Mode.HSLA:
root.drawHSLA(ctx)
root.drawHSLA(ctx, gradientOverlay.width, gradientOverlay.height, root.hue)
break
case ColorPicker.Mode.HSVA:
default:
root.drawHSVA(ctx)
root.drawHSVA(ctx, gradientOverlay.width, gradientOverlay.height, root.hue)
break
}
@@ -244,9 +256,14 @@ Column {
id: pickerCross
property color strokeStyle: "lightGray"
property string loadImageUrl: "images/checkers.png"
property int radius: 10
Component.onCompleted: pickerCross.loadImage(pickerCross.loadImageUrl)
onImageLoaded: pickerCross.requestPaint()
opacity: 0.8
anchors.fill: parent
anchors.margins: -pickerCross.radius
antialiasing: true
Connections {
@@ -261,35 +278,63 @@ Column {
ctx.save()
ctx.clearRect(0, 0, pickerCross.width, pickerCross.height)
var yy, xx = 0
var normX, normY = 0
switch (root.mode) {
case ColorPicker.Mode.RGBA:
yy = pickerCross.height - root.saturationHSV * pickerCross.height
xx = root.hue * pickerCross.width
normX = root.hue
normY = 1.0 - root.saturationHSV
break
case ColorPicker.Mode.HSLA:
yy = pickerCross.height - root.lightness * pickerCross.height
xx = root.saturationHSL * pickerCross.width
normX = root.saturationHSL
normY = 1.0 - root.lightness
break
case ColorPicker.Mode.HSVA:
default:
yy = pickerCross.height - root.value * pickerCross.height
xx = root.saturationHSV * pickerCross.width
normX = root.saturationHSV
normY = 1.0 - root.value
break
}
ctx.strokeStyle = pickerCross.strokeStyle
ctx.lineWidth = 1
var width = pickerCross.width - pickerCross.radius * 2
var height = pickerCross.height - pickerCross.radius * 2
var x = normX * width
var y = normY * height
var centerX = pickerCross.radius + x
var centerY = pickerCross.radius + y
// Draw checkerboard
if (isImageLoaded(pickerCross.loadImageUrl)) {
ctx.beginPath()
ctx.moveTo(0, yy)
ctx.lineTo(pickerCross.width, yy)
ctx.arc(centerX, centerY, pickerCross.radius, 0, 2 * Math.PI)
ctx.clip()
var pattern = context.createPattern(pickerCross.loadImageUrl, 'repeat')
context.fillStyle = pattern
ctx.fillRect(x, y, pickerCross.radius * 2, pickerCross.radius * 2)
}
// Draw current color
if (root.mode === ColorPicker.Mode.RGBA)
ctx.fillStyle = Qt.hsva(root.hue, root.saturationHSV, 1, root.alpha)
else
ctx.fillStyle = root.color
ctx.fillRect(x, y, pickerCross.radius * 2, pickerCross.radius * 2)
// Draw black and white circle
ctx.lineWidth = 2
ctx.strokeStyle = "black"
ctx.beginPath()
ctx.arc(centerX, centerY, pickerCross.radius, 0, 2 * Math.PI)
ctx.stroke()
ctx.strokeStyle = "white"
ctx.beginPath()
ctx.moveTo(xx, 0)
ctx.lineTo(xx, pickerCross.height)
ctx.arc(centerX, centerY, pickerCross.radius - 2, 0, 2 * Math.PI)
ctx.stroke()
ctx.restore()
@@ -298,31 +343,48 @@ Column {
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.margins: -pickerCross.radius
preventStealing: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPositionChanged: function(mouse) {
if (mouseArea.pressed && mouse.buttons === Qt.LeftButton) {
var xx = Math.max(0, Math.min(mouse.x, parent.width))
var yy = Math.max(0, Math.min(mouse.y, parent.height))
// Generate color values from mouse position
// Clip/limit to margin
var x = Math.max(0, Math.min(mouse.x - pickerCross.radius, parent.width))
var y = Math.max(0, Math.min(mouse.y - pickerCross.radius, parent.height))
var normX = x / parent.width
var normY = y / parent.height
switch (root.mode) {
case ColorPicker.Mode.RGBA:
root.saturationHSV = 1.0 - yy / parent.height
root.hue = xx / parent.width
var tmpColor = Qt.hsva(normX, // hue
1.0 - normY, // saturation
root.value,
root.alpha)
root.hue = normX
root.saturationHSV = 1.0 - normY
root.red = tmpColor.r
root.green = tmpColor.g
root.blue = tmpColor.b
break
case ColorPicker.Mode.HSLA:
root.saturationHSL = xx / parent.width
root.lightness = 1.0 - yy / parent.height
root.saturationHSL = normX
root.lightness = 1.0 - normY
break
case ColorPicker.Mode.HSVA:
default:
root.saturationHSV = xx / parent.width
root.value = 1.0 - yy / parent.height
root.saturationHSV = normX
root.value = 1.0 - normY
break
}
root.invalidateColor()
}
}
onPressed: function(mouse) {
@@ -345,9 +407,11 @@ Column {
id: hueSlider
visible: root.mode !== ColorPicker.Mode.RGBA
width: parent.width
onValueChanged: {
onMoved: {
if (root.hue !== hueSlider.value)
root.hue = hueSlider.value
root.invalidateColor()
}
onClicked: root.updateColor()
}
@@ -356,10 +420,21 @@ Column {
id: luminanceSlider
visible: root.mode === ColorPicker.Mode.RGBA
width: parent.width
color: Qt.hsva(root.hue, root.color.hsvSaturation, 1, 1)
onValueChanged: {
if (root.value !== luminanceSlider.value)
color: Qt.hsva(root.hue, root.saturationHSV, root.value, root.alpha)
onMoved: {
if (root.value !== (1.0 - luminanceSlider.value))
root.value = (1.0 - luminanceSlider.value)
var tmpColor = Qt.hsva(root.hue,
root.saturationHSV,
root.value,
root.alpha)
root.red = tmpColor.r
root.green = tmpColor.g
root.blue = tmpColor.b
root.invalidateColor()
}
onClicked: root.updateColor()
}
@@ -367,10 +442,12 @@ Column {
OpacitySlider {
id: opacitySlider
width: parent.width
color: Qt.rgba(root.color.r, root.color.g, root.color.b, 1)
onValueChanged: {
if (root.alpha !== opacitySlider.value)
color: root.color
onMoved: {
if (root.alpha !== (1.0 - opacitySlider.value))
root.alpha = (1.0 - opacitySlider.value)
root.invalidateColor()
}
onClicked: root.updateColor()
}

View File

@@ -39,6 +39,8 @@ Item {
property alias hover: spinBox.hover
signal valueModified
signal dragStarted
signal indicatorPressed
width: 90
implicitHeight: spinBox.height
@@ -48,9 +50,13 @@ Item {
StudioControls.RealSpinBox {
id: spinBox
onDragStarted: hideCursor()
onDragStarted: {
hideCursor()
wrapper.dragStarted()
}
onDragEnded: restoreCursor()
onDragging: holdCursorInPlace()
onIndicatorPressed: wrapper.indicatorPressed()
property bool hasSlider: spinBox.sliderIndicatorVisible

View File

@@ -96,13 +96,12 @@ Item {
height: 80
width: parent.width
property int effectiveWidth: width - 10
property int effectiveWidth: colorLine.width - 10
property int selectedIndex: 0
function select(index) {
for (var i = 0; i < repeater.model.count; i++) {
for (var i = 0; i < repeater.model.count; i++)
repeater.itemAt(i).item.highlighted = false
}
if (repeater.model.count < index + 1)
return
@@ -113,11 +112,12 @@ Item {
gradientModel.lock()
root.currentColor = repeater.itemAt(index).item.color
gradientModel.unlock()
root.selectedNodeChanged()
}
function invalidate() {
var gradientString = "import QtQuick 2.15; Gradient {"
var gradientString = "import QtQuick 2.15; Gradient { orientation: Gradient.Horizontal;"
for (var i = 0; i < gradientModel.count; i++) {
gradientString += "GradientStop {}"
@@ -209,21 +209,21 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
Image {
id: checkerboard
anchors.fill: parent
source: "images/checkers.png"
fillMode: Image.Tile
}
Rectangle {
id: gradientRectangle
smooth: true
x: 0
y: 16
radius: 1
border.color: "#555555"
border.width: 1
width: parent.height
height: parent.width
anchors.fill: parent
border.color: StudioTheme.Values.themeControlOutline
border.width: StudioTheme.Values.border
gradient: Gradient {
id: gradient
}
transformOrigin: Item.TopLeft
rotation: 270
}
}
}
@@ -270,6 +270,13 @@ Item {
onXChanged: gradientStopHandle.refreshToolTip(gradientStopHandle.toolTipVisible)
Image {
width: 10
height: 10
source: "images/checkers.png"
fillMode: Image.Tile
}
Rectangle {
id: rectangle
width: 10
@@ -277,7 +284,6 @@ Item {
color: "red"
border.color: "gray"
border.width: 1
radius: 1
}
Canvas {

View File

@@ -1,50 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** 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 General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** 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-3.0.html.
**
****************************************************************************/
import QtQuick 2.15
import QtQuick.Layouts 1.15
Image {
id: root
signal clicked
visible: colorEditor.shapeGradients && parent.checked
width: 8
height: 4
source: "image://icons/down-arrow"
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.bottom
anchors.topMargin: 2
opacity: popupRegion.containsMouse ? 1 : 0.8
ToolTipArea {
id: popupRegion
onClicked: root.clicked()
hoverEnabled: true
anchors.fill: parent
anchors.margins: -2
tooltip: qsTr("Edit Gradient Properties")
}
}

View File

@@ -36,13 +36,14 @@ Item {
property bool integer: false
signal clicked
signal moved
height: StudioTheme.Values.hueSliderHeight
function updatePos() {
if (root.maximum > root.minimum) {
var pos = (track.width - handle.width) * (root.value - root.minimum) / (root.maximum - root.minimum)
return Math.min(Math.max(pos, 0), track.width - handle.width)
var pos = track.width * (root.value - root.minimum) / (root.maximum - root.minimum)
return Math.min(Math.max(pos, 0), track.width)
} else {
return 0
}
@@ -50,7 +51,6 @@ Item {
Item {
id: track
width: parent.width
height: parent.height
@@ -74,21 +74,20 @@ Item {
Rectangle {
id: handle
width: StudioTheme.Values.hueSliderHandleWidth
height: track.height - 4
height: track.height + 4
anchors.verticalCenter: parent.verticalCenter
smooth: true
color: "transparent"
radius: 2
border.color: "black"
border.width: 1
x: root.updatePos()
x: root.updatePos() - handle.width * 0.5
y: 2
z: 1
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: "transparent"
color: Qt.hsva(value, 1, 1, 1)
radius: 1
border.color: "white"
border.width: 1
@@ -98,21 +97,23 @@ Item {
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.margins: -StudioTheme.Values.hueSliderHandleWidth * 0.5
preventStealing: true
function calculateValue() {
var handleX = Math.max(0, Math.min(mouseArea.mouseX, mouseArea.width))
var realValue = (root.maximum - root.minimum) * handleX / mouseArea.width + root.minimum
var halfHandle = StudioTheme.Values.hueSliderHandleWidth * 0.5
var handleX = Math.max(0, Math.min(mouseArea.mouseX - halfHandle, parent.width))
var realValue = (root.maximum - root.minimum) * handleX / parent.width + root.minimum
root.value = root.integer ? Math.round(realValue) : realValue
root.moved()
}
onPressed: calculateValue()
onPressed: mouseArea.calculateValue()
onReleased: root.clicked()
onPositionChanged: {
if (pressed)
calculateValue()
if (mouseArea.pressed)
mouseArea.calculateValue()
}
}
}
}

View File

@@ -37,13 +37,14 @@ Item {
property color color
signal clicked
signal moved
height: StudioTheme.Values.hueSliderHeight
function updatePos() {
if (root.maximum > root.minimum) {
var pos = (track.width - handle.width) * (root.value - root.minimum) / (root.maximum - root.minimum)
return Math.min(Math.max(pos, 0), track.width - handle.width)
var pos = track.width * (root.value - root.minimum) / (root.maximum - root.minimum)
return Math.min(Math.max(pos, 0), track.width)
} else {
return 0
}
@@ -51,17 +52,9 @@ Item {
Item {
id: track
width: parent.width
height: parent.height
Image {
id: checkerboard
anchors.fill: parent
source: "images/checkers.png"
fillMode: Image.Tile
}
Rectangle {
anchors.fill: parent
border.color: StudioTheme.Values.themeControlOutline
@@ -69,7 +62,7 @@ Item {
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.000; color: root.color }
GradientStop { position: 0.000; color: Qt.hsva(root.color.hsvHue, root.color.hsvSaturation, 1, 1) }
GradientStop { position: 1.000; color: "black" }
}
}
@@ -77,21 +70,20 @@ Item {
Rectangle {
id: handle
width: StudioTheme.Values.hueSliderHandleWidth
height: track.height - 4
height: track.height + 4
anchors.verticalCenter: parent.verticalCenter
smooth: true
color: "transparent"
radius: 2
border.color: "black"
border.width: 1
x: root.updatePos()
x: root.updatePos() - handle.width * 0.5
y: 2
z: 1
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: "transparent"
color: Qt.hsva(root.color.hsvHue, root.color.hsvSaturation, root.color.hsvValue, 1)
radius: 1
border.color: "white"
border.width: 1
@@ -101,19 +93,22 @@ Item {
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.margins: -StudioTheme.Values.hueSliderHandleWidth * 0.5
preventStealing: true
function calculateValue() {
var handleX = Math.max(0, Math.min(mouseArea.mouseX, mouseArea.width))
var realValue = (root.maximum - root.minimum) * handleX / mouseArea.width + root.minimum
var halfHandle = StudioTheme.Values.hueSliderHandleWidth * 0.5
var handleX = Math.max(0, Math.min(mouseArea.mouseX - halfHandle, parent.width))
var realValue = (root.maximum - root.minimum) * handleX / parent.width + root.minimum
root.value = root.integer ? Math.round(realValue) : realValue
root.moved()
}
onPressed: calculateValue()
onPressed: mouseArea.calculateValue()
onReleased: root.clicked()
onPositionChanged: {
if (pressed)
calculateValue()
if (mouseArea.pressed)
mouseArea.calculateValue()
}
}

View File

@@ -37,13 +37,14 @@ Item {
property color color
signal clicked
signal moved
height: StudioTheme.Values.hueSliderHeight
function updatePos() {
if (root.maximum > root.minimum) {
var pos = (track.width - handle.width) * (root.value - root.minimum) / (root.maximum - root.minimum)
return Math.min(Math.max(pos, 0), track.width - handle.width)
var pos = track.width * (root.value - root.minimum) / (root.maximum - root.minimum)
return Math.min(Math.max(pos, 0), track.width)
} else {
return 0
}
@@ -51,12 +52,10 @@ Item {
Item {
id: track
width: parent.width
height: parent.height
Image {
id: checkerboard
anchors.fill: parent
source: "images/checkers.png"
fillMode: Image.Tile
@@ -69,7 +68,7 @@ Item {
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.000; color: root.color }
GradientStop { position: 0.000; color: Qt.rgba(root.color.r, root.color.g, root.color.b, 1) }
GradientStop { position: 1.000; color: "transparent" }
}
}
@@ -77,21 +76,27 @@ Item {
Rectangle {
id: handle
width: StudioTheme.Values.hueSliderHandleWidth
height: track.height - 4
height: track.height + 4
anchors.verticalCenter: parent.verticalCenter
smooth: true
color: "transparent"
radius: 2
border.color: "black"
border.width: 1
x: root.updatePos()
x: root.updatePos() - handle.width * 0.5
y: 2
z: 1
Image {
anchors.fill: handleInside
source: "images/checkers.png"
fillMode: Image.Tile
}
Rectangle {
id: handleInside
anchors.fill: parent
anchors.margins: 1
color: "transparent"
color: root.color
radius: 1
border.color: "white"
border.width: 1
@@ -101,19 +106,22 @@ Item {
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.margins: -StudioTheme.Values.hueSliderHandleWidth * 0.5
preventStealing: true
function calculateValue() {
var handleX = Math.max(0, Math.min(mouseArea.mouseX, mouseArea.width))
var realValue = (root.maximum - root.minimum) * handleX / mouseArea.width + root.minimum
var halfHandle = StudioTheme.Values.hueSliderHandleWidth * 0.5
var handleX = Math.max(0, Math.min(mouseArea.mouseX - halfHandle, parent.width))
var realValue = (root.maximum - root.minimum) * handleX / parent.width + root.minimum
root.value = root.integer ? Math.round(realValue) : realValue
root.moved()
}
onPressed: calculateValue()
onPressed: mouseArea.calculateValue()
onReleased: root.clicked()
onPositionChanged: {
if (pressed)
calculateValue()
if (mouseArea.pressed)
mouseArea.calculateValue()
}
}

View File

@@ -31,7 +31,6 @@ FontExtrasSection 2.0 FontExtrasSection.qml
FontSection 2.0 FontSection.qml
FontStyleButtons 2.0 FontStyleButtons.qml
GradientLine 2.0 GradientLine.qml
GradientPopupIndicator 2.0 GradientPopupIndicator.qml
GradientPresetList 2.0 GradientPresetList.qml
GradientPresetTabContent 2.0 GradientPresetTabContent.qml
GradientPropertySpinBox 2.0 GradientPropertySpinBox.qml

View File

@@ -121,7 +121,7 @@ T.ComboBox {
ComboBox.ActivatedReason.Other)
}
onActivated: {
onActivated: function(index) {
myTimer.activatedIndex = index
myTimer.restart()
}

View File

@@ -81,6 +81,7 @@ T.SpinBox {
signal dragStarted
signal dragEnded
signal dragging
signal indicatorPressed
// Use custom wheel handling due to bugs
property bool __wheelEnabled: false
@@ -124,6 +125,7 @@ T.SpinBox {
myControl: mySpinBox
iconFlip: -1
visible: mySpinBox.spinBoxIndicatorVisible
onRealPressed: mySpinBox.indicatorPressed()
onRealReleased: mySpinBox.realIncrease()
onRealPressAndHold: mySpinBox.realIncrease()
x: actionIndicator.width + StudioTheme.Values.border
@@ -139,6 +141,7 @@ T.SpinBox {
id: spinBoxIndicatorDown
myControl: mySpinBox
visible: mySpinBox.spinBoxIndicatorVisible
onRealPressed: mySpinBox.indicatorPressed()
onRealReleased: mySpinBox.realDecrease()
onRealPressAndHold: mySpinBox.realDecrease()
x: actionIndicator.width + StudioTheme.Values.border

View File

@@ -155,15 +155,17 @@ void PropertyEditorValue::setValueWithEmit(const QVariant &value)
void PropertyEditorValue::setValue(const QVariant &value)
{
const bool colorsEqual = cleverColorCompare(value, m_value);
if (!compareVariants(m_value, value) &&
!cleverDoubleCompare(value, m_value) &&
!cleverColorCompare(value, m_value))
!colorsEqual)
m_value = value;
fixAmbigousColorNames(modelNode(), name(), &m_value);
fixUrl(modelNode(), name(), &m_value);
if (m_value.isValid())
if (m_value.isValid() && !colorsEqual)
emit valueChangedQml();
emit isExplicitChanged();