1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquickabstractcolorpicker_p_p.h" |
5 | |
6 | #include "qquickcolordialogutils_p.h" |
7 | |
8 | #include <QtQuickTemplates2/private/qquickcontrol_p_p.h> |
9 | #include <QtQuickTemplates2/private/qquickdeferredexecute_p_p.h> |
10 | |
11 | #include <qpa/qplatformintegration.h> |
12 | #include <private/qguiapplication_p.h> |
13 | |
14 | QQuickAbstractColorPickerPrivate::QQuickAbstractColorPickerPrivate() = default; |
15 | |
16 | bool QQuickAbstractColorPickerPrivate::handlePress(const QPointF &point, ulong timestamp) |
17 | { |
18 | Q_Q(QQuickAbstractColorPicker); |
19 | QQuickControlPrivate::handlePress(point, timestamp); |
20 | m_pressPoint = point; |
21 | q->setPressed(true); |
22 | q->updateColor(pos: point); |
23 | return true; |
24 | } |
25 | |
26 | bool QQuickAbstractColorPickerPrivate::handleMove(const QPointF &point, ulong timestamp) |
27 | { |
28 | Q_Q(QQuickAbstractColorPicker); |
29 | QQuickControlPrivate::handleMove(point, timestamp); |
30 | if (point != m_pressPoint) |
31 | q->updateColor(pos: point); |
32 | return true; |
33 | } |
34 | |
35 | bool QQuickAbstractColorPickerPrivate::handleRelease(const QPointF &point, ulong timestamp) |
36 | { |
37 | Q_Q(QQuickAbstractColorPicker); |
38 | QQuickControlPrivate::handleRelease(point, timestamp); |
39 | m_pressPoint = QPointF(); |
40 | q->setKeepMouseGrab(false); |
41 | q->setKeepTouchGrab(false); |
42 | q->setPressed(false); |
43 | q->updateColor(pos: point); |
44 | return true; |
45 | } |
46 | |
47 | void QQuickAbstractColorPickerPrivate::handleUngrab() |
48 | { |
49 | Q_Q(QQuickAbstractColorPicker); |
50 | QQuickControlPrivate::handleUngrab(); |
51 | m_pressPoint = QPointF(); |
52 | q->setPressed(false); |
53 | } |
54 | |
55 | void QQuickAbstractColorPickerPrivate::cancelHandle() |
56 | { |
57 | Q_Q(QQuickAbstractColorPicker); |
58 | quickCancelDeferred(object: q, property: handleName()); |
59 | } |
60 | |
61 | void QQuickAbstractColorPickerPrivate::executeHandle(bool complete) |
62 | { |
63 | Q_Q(QQuickAbstractColorPicker); |
64 | if (m_handle.wasExecuted()) |
65 | return; |
66 | |
67 | if (!m_handle || complete) |
68 | quickBeginDeferred(object: q, property: handleName(), delegate&: m_handle); |
69 | if (complete) |
70 | quickCompleteDeferred(object: q, property: handleName(), delegate&: m_handle); |
71 | } |
72 | |
73 | void QQuickAbstractColorPickerPrivate::itemImplicitWidthChanged(QQuickItem *item) |
74 | { |
75 | Q_Q(QQuickAbstractColorPicker); |
76 | QQuickControlPrivate::itemImplicitWidthChanged(item); |
77 | if (item == m_handle) |
78 | emit q->implicitHandleWidthChanged(); |
79 | } |
80 | |
81 | void QQuickAbstractColorPickerPrivate::itemImplicitHeightChanged(QQuickItem *item) |
82 | { |
83 | Q_Q(QQuickAbstractColorPicker); |
84 | QQuickControlPrivate::itemImplicitHeightChanged(item); |
85 | if (item == m_handle) |
86 | emit q->implicitHandleHeightChanged(); |
87 | } |
88 | |
89 | QQuickAbstractColorPicker::QQuickAbstractColorPicker(QQuickAbstractColorPickerPrivate &dd, |
90 | QQuickItem *parent) |
91 | : QQuickControl(dd, parent) |
92 | { |
93 | setActiveFocusOnTab(true); |
94 | setAcceptedMouseButtons(Qt::LeftButton); |
95 | } |
96 | |
97 | QColor QQuickAbstractColorPicker::color() const |
98 | { |
99 | Q_D(const QQuickAbstractColorPicker); |
100 | return d->m_hsl ? QColor::fromHslF(h: d->m_hsva.h, s: d->m_hsva.s, l: d->m_hsva.l, a: d->m_hsva.a) |
101 | : QColor::fromHsvF(h: d->m_hsva.h, s: d->m_hsva.s, v: d->m_hsva.v, a: d->m_hsva.a); |
102 | } |
103 | |
104 | void QQuickAbstractColorPicker::setColor(const QColor &c) |
105 | { |
106 | Q_D(QQuickAbstractColorPicker); |
107 | // QColor represents a theoretical color, rather than simply an rgba value. |
108 | // Therefore, two QColor objects can be different, |
109 | // and yet translate to the same rgba value. |
110 | // Since the color picker can reuse the same rgba value for multiple pixels, |
111 | // we should not return early if the rgba() values are equal, |
112 | // but only if the QColor objects are exactly the same. |
113 | |
114 | if (color() == c) |
115 | return; |
116 | |
117 | // When called from QQuickColorDialogImpl, it might not have the same spec as the current color |
118 | // picker. |
119 | if (d->m_hsl && c.spec() == QColor::Spec::Hsv) { |
120 | const auto sl = getSaturationAndLightness(saturation: c.hsvSaturationF(), value: c.valueF()); |
121 | d->m_hsva.h = qBound(min: .0, val: c.hsvHueF(), max: 1.0); |
122 | d->m_hsva.s = qBound(min: .0, val: sl.first, max: 1.0); |
123 | d->m_hsva.l = qBound(min: .0, val: sl.second, max: 1.0); |
124 | } else if (!d->m_hsl && c.spec() == QColor::Spec::Hsl) { |
125 | const auto sv = getSaturationAndValue(saturation: c.hslSaturationF(), lightness: c.lightnessF()); |
126 | d->m_hsva.h = qBound(min: .0, val: c.hslHueF(), max: 1.0); |
127 | d->m_hsva.s = qBound(min: .0, val: sv.first, max: 1.0); |
128 | d->m_hsva.v = qBound(min: .0, val: sv.second, max: 1.0); |
129 | } else { |
130 | d->m_hsva.h = qBound(min: .0, val: d->m_hsl ? c.hslHueF() : c.hsvHueF(), max: 1.0); |
131 | d->m_hsva.s = qBound(min: .0, val: d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF(), max: 1.0); |
132 | d->m_hsva.v = qBound(min: .0, val: d->m_hsl ? c.lightnessF() : c.valueF(), max: 1.0); |
133 | } |
134 | |
135 | d->m_hsva.a = qBound(min: .0, val: c.alphaF(), max: 1.0); |
136 | |
137 | emit colorChanged(color: color()); |
138 | } |
139 | |
140 | qreal QQuickAbstractColorPicker::alpha() const |
141 | { |
142 | Q_D(const QQuickAbstractColorPicker); |
143 | return d->m_hsva.a; |
144 | } |
145 | |
146 | void QQuickAbstractColorPicker::setAlpha(qreal alpha) |
147 | { |
148 | Q_D(QQuickAbstractColorPicker); |
149 | |
150 | if (!qt_is_finite(d: alpha)) |
151 | return; |
152 | |
153 | alpha = qBound(min: .0, val: alpha, max: 1.0); |
154 | |
155 | if (qFuzzyCompare(p1: d->m_hsva.a, p2: alpha)) |
156 | return; |
157 | |
158 | d->m_hsva.a = alpha; |
159 | |
160 | emit colorChanged(color: color()); |
161 | } |
162 | |
163 | qreal QQuickAbstractColorPicker::hue() const |
164 | { |
165 | Q_D(const QQuickAbstractColorPicker); |
166 | return d->m_hsva.h; |
167 | } |
168 | void QQuickAbstractColorPicker::setHue(qreal hue) |
169 | { |
170 | Q_D(QQuickAbstractColorPicker); |
171 | |
172 | if (!qt_is_finite(d: hue)) |
173 | return; |
174 | |
175 | d->m_hsva.h = hue; |
176 | |
177 | emit colorChanged(color: color()); |
178 | } |
179 | |
180 | qreal QQuickAbstractColorPicker::saturation() const |
181 | { |
182 | Q_D(const QQuickAbstractColorPicker); |
183 | return d->m_hsva.s; |
184 | } |
185 | |
186 | void QQuickAbstractColorPicker::setSaturation(qreal saturation) |
187 | { |
188 | Q_D(QQuickAbstractColorPicker); |
189 | if (!qt_is_finite(d: saturation)) |
190 | return; |
191 | |
192 | d->m_hsva.s = saturation; |
193 | |
194 | emit colorChanged(color: color()); |
195 | } |
196 | qreal QQuickAbstractColorPicker::value() const |
197 | { |
198 | Q_D(const QQuickAbstractColorPicker); |
199 | return d->m_hsl ? getSaturationAndValue(saturation: d->m_hsva.s, lightness: d->m_hsva.l).second : d->m_hsva.v; |
200 | } |
201 | void QQuickAbstractColorPicker::setValue(qreal value) |
202 | { |
203 | Q_D(QQuickAbstractColorPicker); |
204 | if (!qt_is_finite(d: value)) |
205 | return; |
206 | |
207 | const auto sv = d->m_hsl ? getSaturationAndValue(saturation: d->m_hsva.s, lightness: d->m_hsva.l) |
208 | : std::pair<qreal, qreal>(d->m_hsva.s, value); |
209 | d->m_hsva.s = sv.first; |
210 | d->m_hsva.v = sv.second; |
211 | |
212 | emit colorChanged(color: color()); |
213 | } |
214 | |
215 | qreal QQuickAbstractColorPicker::lightness() const |
216 | { |
217 | Q_D(const QQuickAbstractColorPicker); |
218 | return d->m_hsl ? d->m_hsva.l : getSaturationAndLightness(saturation: d->m_hsva.s, value: d->m_hsva.v).second; |
219 | } |
220 | void QQuickAbstractColorPicker::setLightness(qreal lightness) |
221 | { |
222 | Q_D(QQuickAbstractColorPicker); |
223 | if (!qt_is_finite(d: lightness)) |
224 | return; |
225 | |
226 | const auto sl = !d->m_hsl ? getSaturationAndLightness(saturation: d->m_hsva.s, value: d->m_hsva.v) |
227 | : std::pair<qreal, qreal>(d->m_hsva.s, lightness); |
228 | d->m_hsva.s = sl.first; |
229 | d->m_hsva.l = sl.second; |
230 | |
231 | emit colorChanged(color: color()); |
232 | } |
233 | |
234 | /*! |
235 | \internal |
236 | |
237 | This property holds whether the slider is pressed. |
238 | */ |
239 | bool QQuickAbstractColorPicker::isPressed() const |
240 | { |
241 | Q_D(const QQuickAbstractColorPicker); |
242 | return d->m_pressed; |
243 | } |
244 | |
245 | void QQuickAbstractColorPicker::setPressed(bool pressed) |
246 | { |
247 | Q_D(QQuickAbstractColorPicker); |
248 | if (pressed == d->m_pressed) |
249 | return; |
250 | |
251 | d->m_pressed = pressed; |
252 | emit pressedChanged(); |
253 | } |
254 | |
255 | /*! |
256 | \internal |
257 | |
258 | This property holds the handle item. |
259 | */ |
260 | QQuickItem *QQuickAbstractColorPicker::handle() const |
261 | { |
262 | QQuickAbstractColorPickerPrivate *d = const_cast<QQuickAbstractColorPickerPrivate *>(d_func()); |
263 | if (!d->m_handle) |
264 | d->executeHandle(); |
265 | return d->m_handle; |
266 | } |
267 | |
268 | void QQuickAbstractColorPicker::setHandle(QQuickItem *handle) |
269 | { |
270 | Q_D(QQuickAbstractColorPicker); |
271 | if (handle == d->m_handle) |
272 | return; |
273 | |
274 | if (!d->m_handle.isExecuting()) |
275 | d->cancelHandle(); |
276 | |
277 | const qreal oldImplicitHandleWidth = implicitHandleWidth(); |
278 | const qreal oldImplicitHandleHeight = implicitHandleHeight(); |
279 | |
280 | d->removeImplicitSizeListener(item: d->m_handle); |
281 | QQuickControlPrivate::hideOldItem(item: d->m_handle); |
282 | d->m_handle = handle; |
283 | |
284 | if (handle) { |
285 | if (!handle->parentItem()) |
286 | handle->setParentItem(this); |
287 | d->addImplicitSizeListener(item: handle); |
288 | } |
289 | |
290 | if (!qFuzzyCompare(p1: oldImplicitHandleWidth, p2: implicitHandleWidth())) |
291 | emit implicitHandleWidthChanged(); |
292 | if (!qFuzzyCompare(p1: oldImplicitHandleHeight, p2: implicitHandleHeight())) |
293 | emit implicitHandleHeightChanged(); |
294 | if (!d->m_handle.isExecuting()) |
295 | emit handleChanged(); |
296 | } |
297 | |
298 | /*! |
299 | \internal |
300 | \readonly |
301 | |
302 | This property holds the implicit handle width. |
303 | |
304 | The value is equal to \c {handle ? handle.implicitWidth : 0}. |
305 | |
306 | This is typically used, together with \l {Control::}{implicitContentWidth} and |
307 | \l {Control::}{implicitBackgroundWidth}, to calculate the \l {Item::}{implicitWidth}. |
308 | |
309 | \sa implicitHandleHeight |
310 | */ |
311 | qreal QQuickAbstractColorPicker::implicitHandleWidth() const |
312 | { |
313 | Q_D(const QQuickAbstractColorPicker); |
314 | if (!d->m_handle) |
315 | return 0; |
316 | return d->m_handle->implicitWidth(); |
317 | } |
318 | |
319 | /*! |
320 | \internal |
321 | \readonly |
322 | |
323 | This property holds the implicit handle height. |
324 | |
325 | The value is equal to \c {handle ? handle.implicitHeight : 0}. |
326 | |
327 | This is typically used, together with \l {Control::}{implicitContentHeight} and |
328 | \l {Control::}{implicitBackgroundHeight}, to calculate the \l {Item::}{implicitHeight}. |
329 | |
330 | \sa implicitHandleWidth |
331 | */ |
332 | qreal QQuickAbstractColorPicker::implicitHandleHeight() const |
333 | { |
334 | Q_D(const QQuickAbstractColorPicker); |
335 | if (!d->m_handle) |
336 | return 0; |
337 | return d->m_handle->implicitHeight(); |
338 | } |
339 | |
340 | void QQuickAbstractColorPicker::componentComplete() |
341 | { |
342 | Q_D(QQuickAbstractColorPicker); |
343 | d->executeHandle(complete: true); |
344 | QQuickControl::componentComplete(); |
345 | } |
346 | |
347 | void QQuickAbstractColorPicker::updateColor(const QPointF &pos) |
348 | { |
349 | QColor c = colorAt(pos); |
350 | c.setAlphaF(alpha()); |
351 | setColor(c); |
352 | |
353 | emit colorPicked(color: c); |
354 | } |
355 | |