1 | // Copyright (C) 2016 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 "qtgradientstopsmodel_p.h" |
5 | |
6 | #include <QtGui/QColor> |
7 | #include <QtCore/QHash> |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | class QtGradientStopPrivate |
12 | { |
13 | public: |
14 | qreal m_position = 0; |
15 | QColor m_color = Qt::white; |
16 | QtGradientStopsModel *m_model = nullptr; |
17 | }; |
18 | |
19 | qreal QtGradientStop::position() const |
20 | { |
21 | return d_ptr->m_position; |
22 | } |
23 | |
24 | QColor QtGradientStop::color() const |
25 | { |
26 | return d_ptr->m_color; |
27 | } |
28 | |
29 | QtGradientStopsModel *QtGradientStop::gradientModel() const |
30 | { |
31 | return d_ptr->m_model; |
32 | } |
33 | |
34 | void QtGradientStop::setColor(QColor color) |
35 | { |
36 | d_ptr->m_color = color; |
37 | } |
38 | |
39 | void QtGradientStop::setPosition(qreal position) |
40 | { |
41 | d_ptr->m_position = position; |
42 | } |
43 | |
44 | QtGradientStop::QtGradientStop(QtGradientStopsModel *model) |
45 | : d_ptr(new QtGradientStopPrivate()) |
46 | { |
47 | d_ptr->m_model = model; |
48 | } |
49 | |
50 | QtGradientStop::~QtGradientStop() |
51 | { |
52 | } |
53 | |
54 | class QtGradientStopsModelPrivate |
55 | { |
56 | QtGradientStopsModel *q_ptr = nullptr; |
57 | Q_DECLARE_PUBLIC(QtGradientStopsModel) |
58 | public: |
59 | QMap<qreal, QtGradientStop *> m_posToStop; |
60 | QHash<QtGradientStop *, qreal> m_stopToPos; |
61 | QHash<QtGradientStop *, bool> m_selection; |
62 | QtGradientStop *m_current = nullptr; |
63 | }; |
64 | |
65 | |
66 | |
67 | QtGradientStopsModel::QtGradientStopsModel(QObject *parent) |
68 | : QObject(parent), d_ptr(new QtGradientStopsModelPrivate) |
69 | { |
70 | d_ptr->q_ptr = this; |
71 | } |
72 | |
73 | QtGradientStopsModel::~QtGradientStopsModel() |
74 | { |
75 | clear(); |
76 | } |
77 | |
78 | QtGradientStopsModel::PositionStopMap QtGradientStopsModel::stops() const |
79 | { |
80 | return d_ptr->m_posToStop; |
81 | } |
82 | |
83 | QtGradientStop *QtGradientStopsModel::at(qreal pos) const |
84 | { |
85 | if (d_ptr->m_posToStop.contains(key: pos)) |
86 | return d_ptr->m_posToStop[pos]; |
87 | return 0; |
88 | } |
89 | |
90 | QColor QtGradientStopsModel::color(qreal pos) const |
91 | { |
92 | PositionStopMap gradStops = stops(); |
93 | if (gradStops.isEmpty()) |
94 | return QColor::fromRgbF(r: pos, g: pos, b: pos, a: 1.0); |
95 | if (gradStops.contains(key: pos)) |
96 | return gradStops[pos]->color(); |
97 | |
98 | gradStops[pos] = 0; |
99 | auto itStop = gradStops.constFind(key: pos); |
100 | if (itStop == gradStops.constBegin()) { |
101 | ++itStop; |
102 | return itStop.value()->color(); |
103 | } |
104 | if (itStop == --gradStops.constEnd()) { |
105 | --itStop; |
106 | return itStop.value()->color(); |
107 | } |
108 | auto itPrev = itStop; |
109 | auto itNext = itStop; |
110 | --itPrev; |
111 | ++itNext; |
112 | |
113 | double prevX = itPrev.key(); |
114 | double nextX = itNext.key(); |
115 | |
116 | double coefX = (pos - prevX) / (nextX - prevX); |
117 | QColor prevCol = itPrev.value()->color(); |
118 | QColor nextCol = itNext.value()->color(); |
119 | |
120 | QColor newColor; |
121 | newColor.setRgbF(r: (nextCol.redF() - prevCol.redF() ) * coefX + prevCol.redF(), |
122 | g: (nextCol.greenF() - prevCol.greenF()) * coefX + prevCol.greenF(), |
123 | b: (nextCol.blueF() - prevCol.blueF() ) * coefX + prevCol.blueF(), |
124 | a: (nextCol.alphaF() - prevCol.alphaF()) * coefX + prevCol.alphaF()); |
125 | return newColor; |
126 | } |
127 | |
128 | QList<QtGradientStop *> QtGradientStopsModel::selectedStops() const |
129 | { |
130 | return d_ptr->m_selection.keys(); |
131 | } |
132 | |
133 | QtGradientStop *QtGradientStopsModel::currentStop() const |
134 | { |
135 | return d_ptr->m_current; |
136 | } |
137 | |
138 | bool QtGradientStopsModel::isSelected(QtGradientStop *stop) const |
139 | { |
140 | if (d_ptr->m_selection.contains(key: stop)) |
141 | return true; |
142 | return false; |
143 | } |
144 | |
145 | QtGradientStop *QtGradientStopsModel::addStop(qreal pos, QColor color) |
146 | { |
147 | qreal newPos = pos; |
148 | if (pos < 0.0) |
149 | newPos = 0.0; |
150 | if (pos > 1.0) |
151 | newPos = 1.0; |
152 | if (d_ptr->m_posToStop.contains(key: newPos)) |
153 | return 0; |
154 | QtGradientStop *stop = new QtGradientStop(); |
155 | stop->setPosition(newPos); |
156 | stop->setColor(color); |
157 | |
158 | d_ptr->m_posToStop[newPos] = stop; |
159 | d_ptr->m_stopToPos[stop] = newPos; |
160 | |
161 | emit stopAdded(stop); |
162 | |
163 | return stop; |
164 | } |
165 | |
166 | void QtGradientStopsModel::removeStop(QtGradientStop *stop) |
167 | { |
168 | if (!d_ptr->m_stopToPos.contains(key: stop)) |
169 | return; |
170 | if (currentStop() == stop) |
171 | setCurrentStop(0); |
172 | selectStop(stop, select: false); |
173 | |
174 | emit stopRemoved(stop); |
175 | |
176 | qreal pos = d_ptr->m_stopToPos[stop]; |
177 | d_ptr->m_stopToPos.remove(key: stop); |
178 | d_ptr->m_posToStop.remove(key: pos); |
179 | delete stop; |
180 | } |
181 | |
182 | void QtGradientStopsModel::moveStop(QtGradientStop *stop, qreal newPos) |
183 | { |
184 | if (!d_ptr->m_stopToPos.contains(key: stop)) |
185 | return; |
186 | if (d_ptr->m_posToStop.contains(key: newPos)) |
187 | return; |
188 | |
189 | if (newPos > 1.0) |
190 | newPos = 1.0; |
191 | else if (newPos < 0.0) |
192 | newPos = 0.0; |
193 | |
194 | emit stopMoved(stop, newPos); |
195 | |
196 | const qreal oldPos = stop->position(); |
197 | stop->setPosition(newPos); |
198 | d_ptr->m_stopToPos[stop] = newPos; |
199 | d_ptr->m_posToStop.remove(key: oldPos); |
200 | d_ptr->m_posToStop[newPos] = stop; |
201 | } |
202 | |
203 | void QtGradientStopsModel::swapStops(QtGradientStop *stop1, QtGradientStop *stop2) |
204 | { |
205 | if (stop1 == stop2) |
206 | return; |
207 | if (!d_ptr->m_stopToPos.contains(key: stop1)) |
208 | return; |
209 | if (!d_ptr->m_stopToPos.contains(key: stop2)) |
210 | return; |
211 | |
212 | emit stopsSwapped(stop1, stop2); |
213 | |
214 | const qreal pos1 = stop1->position(); |
215 | const qreal pos2 = stop2->position(); |
216 | stop1->setPosition(pos2); |
217 | stop2->setPosition(pos1); |
218 | d_ptr->m_stopToPos[stop1] = pos2; |
219 | d_ptr->m_stopToPos[stop2] = pos1; |
220 | d_ptr->m_posToStop[pos1] = stop2; |
221 | d_ptr->m_posToStop[pos2] = stop1; |
222 | } |
223 | |
224 | void QtGradientStopsModel::changeStop(QtGradientStop *stop, QColor newColor) |
225 | { |
226 | if (!d_ptr->m_stopToPos.contains(key: stop)) |
227 | return; |
228 | if (stop->color() == newColor) |
229 | return; |
230 | |
231 | emit stopChanged(stop, newColor); |
232 | |
233 | stop->setColor(newColor); |
234 | } |
235 | |
236 | void QtGradientStopsModel::selectStop(QtGradientStop *stop, bool select) |
237 | { |
238 | if (!d_ptr->m_stopToPos.contains(key: stop)) |
239 | return; |
240 | bool selected = d_ptr->m_selection.contains(key: stop); |
241 | if (select == selected) |
242 | return; |
243 | |
244 | emit stopSelected(stop, selected: select); |
245 | |
246 | if (select) |
247 | d_ptr->m_selection[stop] = true; |
248 | else |
249 | d_ptr->m_selection.remove(key: stop); |
250 | } |
251 | |
252 | void QtGradientStopsModel::setCurrentStop(QtGradientStop *stop) |
253 | { |
254 | if (stop && !d_ptr->m_stopToPos.contains(key: stop)) |
255 | return; |
256 | if (stop == currentStop()) |
257 | return; |
258 | |
259 | emit currentStopChanged(stop); |
260 | |
261 | d_ptr->m_current = stop; |
262 | } |
263 | |
264 | QtGradientStop *QtGradientStopsModel::firstSelected() const |
265 | { |
266 | PositionStopMap stopList = stops(); |
267 | auto itStop = stopList.cbegin(); |
268 | while (itStop != stopList.constEnd()) { |
269 | QtGradientStop *stop = itStop.value(); |
270 | if (isSelected(stop)) |
271 | return stop; |
272 | ++itStop; |
273 | }; |
274 | return 0; |
275 | } |
276 | |
277 | QtGradientStop *QtGradientStopsModel::lastSelected() const |
278 | { |
279 | PositionStopMap stopList = stops(); |
280 | auto itStop = stopList.cend(); |
281 | while (itStop != stopList.constBegin()) { |
282 | --itStop; |
283 | |
284 | QtGradientStop *stop = itStop.value(); |
285 | if (isSelected(stop)) |
286 | return stop; |
287 | }; |
288 | return 0; |
289 | } |
290 | |
291 | QtGradientStopsModel *QtGradientStopsModel::clone() const |
292 | { |
293 | QtGradientStopsModel *model = new QtGradientStopsModel(); |
294 | |
295 | QMap<qreal, QtGradientStop *> stopsToClone = stops(); |
296 | for (auto it = stopsToClone.cbegin(), end = stopsToClone.cend(); it != end; ++it) |
297 | model->addStop(pos: it.key(), color: it.value()->color()); |
298 | // clone selection and current also |
299 | return model; |
300 | } |
301 | |
302 | void QtGradientStopsModel::moveStops(double newPosition) |
303 | { |
304 | QtGradientStop *current = currentStop(); |
305 | if (!current) |
306 | return; |
307 | |
308 | double newPos = newPosition; |
309 | |
310 | if (newPos > 1) |
311 | newPos = 1; |
312 | else if (newPos < 0) |
313 | newPos = 0; |
314 | |
315 | if (newPos == current->position()) |
316 | return; |
317 | |
318 | double offset = newPos - current->position(); |
319 | |
320 | QtGradientStop *first = firstSelected(); |
321 | QtGradientStop *last = lastSelected(); |
322 | |
323 | if (first && last) { // multiselection |
324 | double maxOffset = 1.0 - last->position(); |
325 | double minOffset = -first->position(); |
326 | |
327 | if (offset > maxOffset) |
328 | offset = maxOffset; |
329 | else if (offset < minOffset) |
330 | offset = minOffset; |
331 | |
332 | } |
333 | |
334 | if (offset == 0) |
335 | return; |
336 | |
337 | bool forward = (offset > 0) ? false : true; |
338 | |
339 | PositionStopMap stopList; |
340 | |
341 | const auto selected = selectedStops(); |
342 | for (QtGradientStop *stop : selected) |
343 | stopList[stop->position()] = stop; |
344 | stopList[current->position()] = current; |
345 | |
346 | auto itStop = forward ? stopList.cbegin() : stopList.cend(); |
347 | while (itStop != (forward ? stopList.constEnd() : stopList.constBegin())) { |
348 | if (!forward) |
349 | --itStop; |
350 | QtGradientStop *stop = itStop.value(); |
351 | double pos = stop->position() + offset; |
352 | if (pos > 1) |
353 | pos = 1; |
354 | if (pos < 0) |
355 | pos = 0; |
356 | |
357 | if (current == stop) |
358 | pos = newPos; |
359 | |
360 | QtGradientStop *oldStop = at(pos); |
361 | if (oldStop && !stopList.values().contains(t: oldStop)) |
362 | removeStop(stop: oldStop); |
363 | moveStop(stop, newPos: pos); |
364 | |
365 | if (forward) |
366 | ++itStop; |
367 | } |
368 | } |
369 | |
370 | void QtGradientStopsModel::clear() |
371 | { |
372 | const auto stopsList = stops().values(); |
373 | for (QtGradientStop *stop : stopsList) |
374 | removeStop(stop); |
375 | } |
376 | |
377 | void QtGradientStopsModel::clearSelection() |
378 | { |
379 | const auto stopsList = selectedStops(); |
380 | for (QtGradientStop *stop : stopsList) |
381 | selectStop(stop, select: false); |
382 | } |
383 | |
384 | void QtGradientStopsModel::flipAll() |
385 | { |
386 | QMap<qreal, QtGradientStop *> stopsMap = stops(); |
387 | QHash<QtGradientStop *, bool> swappedList; |
388 | for (auto itStop = stopsMap.keyValueEnd(), begin = stopsMap.keyValueBegin(); itStop != begin;) { |
389 | --itStop; |
390 | QtGradientStop *stop = (*itStop).second; |
391 | if (swappedList.contains(key: stop)) |
392 | continue; |
393 | const double newPos = 1.0 - (*itStop).first; |
394 | if (stopsMap.contains(key: newPos)) { |
395 | QtGradientStop *swapped = stopsMap.value(key: newPos); |
396 | swappedList[swapped] = true; |
397 | swapStops(stop1: stop, stop2: swapped); |
398 | } else { |
399 | moveStop(stop, newPos); |
400 | } |
401 | } |
402 | } |
403 | |
404 | void QtGradientStopsModel::selectAll() |
405 | { |
406 | const auto stopsMap = stops(); |
407 | for (auto it = stopsMap.cbegin(), end = stopsMap.cend(); it != end; ++it) |
408 | selectStop(stop: it.value(), select: true); |
409 | } |
410 | |
411 | void QtGradientStopsModel::deleteStops() |
412 | { |
413 | const auto selected = selectedStops(); |
414 | for (QtGradientStop *stop : selected) |
415 | removeStop(stop); |
416 | QtGradientStop *current = currentStop(); |
417 | if (current) |
418 | removeStop(stop: current); |
419 | } |
420 | |
421 | QT_END_NAMESPACE |
422 |
Definitions
- QtGradientStopPrivate
- position
- color
- gradientModel
- setColor
- setPosition
- QtGradientStop
- ~QtGradientStop
- QtGradientStopsModelPrivate
- QtGradientStopsModel
- ~QtGradientStopsModel
- stops
- at
- color
- selectedStops
- currentStop
- isSelected
- addStop
- removeStop
- moveStop
- swapStops
- changeStop
- selectStop
- setCurrentStop
- firstSelected
- lastSelected
- clone
- moveStops
- clear
- clearSelection
- flipAll
- selectAll
Start learning QML with our Intro Training
Find out more