Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_spin_box_unit_manager.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2017 Laurent Valentin Jospin <laurent.valentin@famillejospin.ch>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include "KoUnit.h"
10#include <klocalizedstring.h>
11
12#include <QtMath>
13
14static QString percentStr()
15{
16 static QString str = i18n("Percent (%)");
17 return str;
18}
19
21
23{
24 if (builder == nullptr) {
25 return new KisSpinBoxUnitManager(parent);
26 }
27
28 return builder->buildUnitManager(parent);
29}
30
32{
33 if (builder != nullptr) {
34 delete builder; //The factory took over the lifecycle of the builder, so it delete it when replaced.
35 }
36
37 builder = pBuilder;
38}
39
41{
42 if (builder != nullptr) {
43 delete builder; //The factory took over the lifecycle of the builder, so it delete it when replaced.
44 }
45
46 builder = nullptr;
47}
48
49const QStringList KisSpinBoxUnitManager::referenceUnitSymbols = {"pt", "px", "°", "frame"};
50
51const QStringList KisSpinBoxUnitManager::documentRelativeLengthUnitSymbols = {"px", "vw", "vh"}; //px are relative to the resolution, vw and vh to the width and height.
52const QStringList KisSpinBoxUnitManager::documentRelativeTimeUnitSymbols = {"s", "%"}; //secondes are relative to the framerate, % to the sequence length.
53
54class Q_DECL_HIDDEN KisSpinBoxUnitManager::Private
55{
56public:
58 QString pUnitSymbol = "pt",
59 double pConv = 1.0):
60 dim(pDim),
61 unitSymbol(pUnitSymbol),
62 conversionFactor(pConv)
63 {
64
65 }
66
68
69 QString unitSymbol;
70 mutable double conversionFactor;
71 bool conversionFactorIsFixed {true}; //tell if it's possible to trust the conversion factor stored or if it's needed to recompute it.
72 mutable double conversionConstant {0};
73 bool conversionConstantIsFixed {true}; //tell if it's possible to trust the conversion constant stored or if it's needed to recompute it.
74
76
78 mutable bool unitListCached {false};
79
81 mutable bool unitListWithNameCached {false};
82
83 //it's possible to store a reference for the % unit, for length.
84 bool hasHundredPercent {false};
85 qreal hundredPercent {0};
86
87 bool canAccessDocument {false};
88
90};
91
92KisSpinBoxUnitManager::KisSpinBoxUnitManager(QObject *parent) : QAbstractListModel(parent)
93{
94 d = new Private();
95
97}
102
104{
105 return d->dim;
106}
107
109{
110 return referenceUnitSymbols[d->dim];
111}
112
114{
115 return d->unitSymbol;
116}
117
119{
121 return list.indexOf(d->unitSymbol);
122}
123
125
126 switch (d->dim) {
127
128 case LENGTH:
129 if (d->unitSymbol == "px") {
130 return 0;
131 } else {
132 return 2;
133 }
134 case IMLENGTH:
135 if (d->unitSymbol == "px") {
136 return 0;
137 } else {
138 return 2;
139 }
140 default: //by default return 2.
141 break;
142 }
143
144 return 2;
145
146}
147
149
150 QStringList list;
151
152 if (withName) {
153 if (d->unitListWithNameCached) {
154 return d->unitListWithName;
155 }
156 } else {
157 if (d->unitListCached) {
158 return d->unitList;
159 }
160 }
161
162 switch (d->dim) {
163
164 case LENGTH:
165
166 for (int i = 0; i < KoUnit::TypeCount; i++) {
167
168 if (KoUnit::Type(i) == KoUnit::Pixel) {
169 continue; //skip pixel, which is a document relative unit, in the base class.
170 }
171
172 if (withName) {
174 } else {
175 list << KoUnit(KoUnit::Type(i)).symbol();
176 }
177 }
178
179 if (hasPercent(LENGTH)) {
180
181 if (withName) {
182 list << percentStr();
183 } else {
184 list << "%";
185 }
186
187 }
188
189 if (d->canAccessDocument) {
190 // ad document relative units
191 if (withName) {
192 list << KoUnit::unitDescription(KoUnit::Pixel) << i18n("percent of view width (vw)") << i18n("percent of view height (vh)");
193 } else {
195 }
196 }
197
198 break;
199
200 case IMLENGTH:
201
202 if (withName) {
204 } else {
205 list << "px";
206 }
207
208 if (hasPercent(IMLENGTH)) {
209
210 if (withName) {
211 list << percentStr();
212 } else {
213 list << "%";
214 }
215
216 }
217
218 if (d->canAccessDocument) {
219 // ad document relative units
220 if (withName) {
221 list << i18n("percent of view width (vw)") << i18n("percent of view height (vh)");
222 } else {
223 list << "vw" << "vh";
224 }
225 }
226 break;
227
228 case ANGLE:
229
230 if (withName) {
231 list << i18n("degrees (°)") << i18n("radians (rad)") << i18n("gons (gon)") << i18n("percent of circle (%)");
232 } else {
233 list << "°" << "rad" << "gon" << "%";
234 }
235 break;
236
237 case TIME:
238
239 if (withName) {
240 list << i18n("frames (f)");
241 } else {
242 list << "f";
243 }
244
245 if (d->canAccessDocument) {
246 if (withName) {
247 list << i18n("seconds (s)") << i18n("percent of animation (%)");
248 } else {
250 }
251 }
252
253 break;
254
255 }
256
257 if (withName) {
258 d->unitListWithName = list;
259 d->unitListWithNameCached = true;
260 } else {
261 d->unitList = list;
262 d->unitListCached = true;
263 }
264
265 return list;
266
267}
268
269qreal KisSpinBoxUnitManager::getConversionConstant(int dim, QString symbol) const
270{
271 Q_UNUSED(dim);
272 Q_UNUSED(symbol);
273
274 return 0; // all units managed here are transform via a linear function, so this will always be 0 in this class.
275}
276
277qreal KisSpinBoxUnitManager::getReferenceValue(double apparentValue) const
278{
279 if (!d->conversionFactorIsFixed) {
281 }
282
283 if(!d->conversionConstantIsFixed) {
285 }
286
287 qreal v = (apparentValue - d->conversionConstant)/d->conversionFactor;
288
289 if (d->constrains &= REFISINT) {
290 v = qFloor(v);
291 }
292
293 return v;
294
295}
296
297int KisSpinBoxUnitManager::rowCount(const QModelIndex &parent) const {
298 if (parent == QModelIndex()) {
299 return getsUnitSymbolList().size();
300 }
301 return 0;
302}
303
304QVariant KisSpinBoxUnitManager::data(const QModelIndex &index, int role) const {
305
306 if (role == Qt::DisplayRole) {
307
308 return getsUnitSymbolList(false).at(index.row());
309
310 } else if (role == Qt::ToolTipRole) {
311
312 return getsUnitSymbolList(true).at(index.row());
313
314 }
315
316 return QVariant();
317}
318
319qreal KisSpinBoxUnitManager::getApparentValue(double refValue) const
320{
321 if (!d->conversionFactorIsFixed) {
323 }
324
325 if(!d->conversionConstantIsFixed) {
327 }
328
329 qreal v = refValue*d->conversionFactor + d->conversionConstant;
330
331 if (d->constrains &= VALISINT) {
332 v = qFloor(v);
333 }
334
335 return v;
336}
337
338qreal KisSpinBoxUnitManager::getConversionFactor(int dim, QString symbol) const
339{
340
341 qreal factor = -1;
342
343 switch (dim) {
344
345 case LENGTH:
346 do {
347 if (symbol == "px") {
348 break;
349 }
350
351 bool ok;
352 KoUnit unit = KoUnit::fromSymbol(symbol, &ok);
353 if (! ok) {
354 break;
355 }
356 factor = unit.toUserValuePrecise(1.0); // use the precise function
357 } while (0) ;
358 break;
359
360 case IMLENGTH:
361 if (symbol == "px") {
362 factor = 1;
363 }
364 break;
365
366 case ANGLE:
367 if (symbol == "°") {
368 factor = 1.0;
369 break;
370 }
371 if (symbol == "rad") {
372 factor = acos(-1)/90.0;
373 break;
374 }
375 if (symbol == "gon") {
376 factor = 10.0/9.0;
377 break;
378 }
379 if (symbol == "%") {
380 factor = 2.5/9.0; //(25% of circle is 90°)
381 break;
382 }
383 break;
384
385 case TIME:
386
387 if (symbol != "f") { //we have only frames for the moment.
388 break;
389 }
390 factor = 1.0;
391 break;
392
393 default:
394 break;
395 }
396
397 return factor;
398}
399
400
402{
403 if (dimension == d->dim) {
404 return;
405 }
406
407 d->dim = dimension;
408 d->unitSymbol = referenceUnitSymbols[d->dim]; //Active dim is reference dim when just changed.
409 d->conversionFactor = 1.0;
410
412
413}
414
416{
417
418 QString symbol = pSymbol.trimmed();
419
420 if (symbol == d->unitSymbol) {
421 return;
422 }
423
425
426 QString newSymb = "";
427
428 switch (d->dim) {
429
430 case ANGLE:
431 if (symbol.toLower() == "deg") {
432 newSymb = "°";
433 break;
434 }
435 goto default_identifier; //always do default after handling possible special cases.
436
438 default:
440 if (list.contains(symbol, Qt::CaseInsensitive)) {
441 for (QString str : list) {
442 if (str.toLower() == symbol.toLower()) {
443 newSymb = str; //official symbol may contain capitals letters, so better take the official version.
444 break;
445 }
446 }
447 break;
448 }
449
450 }
451
452 if(newSymb.isEmpty()) {
453 return; //abort if it was impossible to locate the correct symbol.
454 }
455
456 if (d->canAccessDocument) {
457 //manage document relative units.
458
460
461 switch (d->dim) {
462
463 case LENGTH:
466
467 case IMLENGTH:
468 speUnits << "vw" << "vh";
470
471 case TIME:
474
476 default:
477
478 if (speUnits.isEmpty()) {
479 d->conversionFactorIsFixed = true;
480 break;
481 }
482
483 if (speUnits.contains(newSymb)) {
484 d->conversionFactorIsFixed = false;
485 break;
486 }
487
488 d->conversionFactorIsFixed = true;
489 break;
490 }
491
492 if (d->dim == TIME) {
493 if (newSymb == "%") {
494 d->conversionConstantIsFixed = false;
495 }
496 } else {
497 d->conversionConstantIsFixed = true;
498 }
499
500 }
501
503 qreal oldConversionFact = d->conversionFactor;
504
505 d->conversionFactor = conversionFact;
507
508 d->unitSymbol = newSymb;
510
511}
512
514
515 if (index >= 0 && index < rowCount()) {
517 }
518
519}
520
521
523
524 if (d->connectedUnitManagers.indexOf(other) >= 0) {
525 return;
526 }
527
528 if (other->getUnitDimensionType() == getUnitDimensionType()) { //sync only unitmanager of the same type.
529 if (other->getsUnitSymbolList() == getsUnitSymbolList()) { //and if we have identical units available.
530
531 connect(this, SIGNAL(unitChanged(int)), other, SLOT(selectApparentUnitFromIndex(int))); //sync units.
532 connect(other, SIGNAL(unitChanged(int)), this, SLOT(selectApparentUnitFromIndex(int))); //sync units.
533
534 d->connectedUnitManagers.append(other);
535
536 }
537 }
538
539}
540
542
543 int id = d->connectedUnitManagers.indexOf(other);
544
545 if (id < 0) {
546 return;
547 }
548
549 disconnect(this, SIGNAL(unitChanged(int)), other, SLOT(selectApparentUnitFromIndex(int))); //unsync units.
550 disconnect(other, SIGNAL(unitChanged(int)), this, SLOT(selectApparentUnitFromIndex(int))); //unsync units.
551
552 d->connectedUnitManagers.removeAt(id);
553
554}
555
557 int id = getsUnitSymbolList().indexOf(symbol);
558
559 if (id >= 0) {
561 }
562}
563
564bool KisSpinBoxUnitManager::hasPercent(int unitDim) const {
565
566 if (unitDim == IMLENGTH || unitDim == LENGTH) {
567 return false;
568 }
569
570 if (unitDim == TIME) {
571 return d->canAccessDocument;
572 }
573
574 if (unitDim == ANGLE) {
575 return true; //percent is fixed when considering angles.
576 }
577
578 return false;
579}
580
582{
583 if (d->conversionFactorIsFixed) {
584 return;
585 }
586
587 qreal oldConversionFactor = d->conversionFactor;
588
589 d->conversionFactor = getConversionFactor(d->dim, d->unitSymbol);
590
591 if (oldConversionFactor != d->conversionFactor) {
593 }
594}
595
597{
598 if (d->conversionConstantIsFixed) {
599 return;
600 }
601
602 qreal oldConversionConstant = d->conversionConstant;
603
604 d->conversionConstant = getConversionConstant(d->dim, d->unitSymbol);
605
606 if (oldConversionConstant != d->conversionConstant) {
608 }
609}
610
612{
613 d->canAccessDocument = true;
614}
qreal v
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
The KisSpinBoxUnitManagerBuilder class is the base class, used in the strategy pattern of KisSpinBoxU...
virtual KisSpinBoxUnitManager * buildUnitManager(QObject *parent)=0
static KisSpinBoxUnitManager * buildDefaultUnitManager(QObject *parent)
static KisSpinBoxUnitManagerBuilder * builder
static void setDefaultUnitManagerBuilder(KisSpinBoxUnitManagerBuilder *pBuilder)
set a builder the factory can use. The factory should take on the lifecycle of the builder,...
The KisSpinBoxUnitManager class is an abstract interface for the unitspinboxes classes to manage diff...
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
void unitDimensionChanged(int dimCode)
KisSpinBoxUnitManager(QObject *parent=0)
virtual qreal getConversionConstant(int dim, QString symbol) const
some units conversions are done via an affine transform, not just a linear transform....
void setApparentUnitFromSymbol(QString pSymbol)
static const QStringList documentRelativeLengthUnitSymbols
void syncWithOtherUnitManager(KisSpinBoxUnitManager *other)
void newUnitSymbolToUnitIndex(QString symbol)
convert a unitChanged signal with a QString to one with an index.
virtual qreal getConversionFactor(int dim, QString symbol) const
gets the conversion factor of a managed unit, or -1 in case of error. This method is the one that nee...
int getApparentUnitRecommendedDecimals() const
get a hint of how many decimals the spinbox needs to display.
void setUnitDimension(UnitDimension dimension)
KisSpinBoxUnitManager::UnitDimension dim
Private(KisSpinBoxUnitManager::UnitDimension pDim=KisSpinBoxUnitManager::LENGTH, QString pUnitSymbol="pt", double pConv=1.0)
void conversionFactorChanged(qreal newConversionFactor, qreal oldConversionFactor) const
void grantDocumentRelativeUnits()
calling this method gives access to document relative units. Only subclasses that manage those units ...
void conversionConstantChanged(qreal newConversionFactor, qreal oldConversionFactor) const
qreal getApparentValue(double refValue) const
static const QStringList referenceUnitSymbols
this list holds the symbols of the reference unit per dimension. The index is equal to the value in U...
qreal getReferenceValue(double apparentValue) const
static const QStringList documentRelativeTimeUnitSymbols
void clearSyncWithOtherUnitManager(KisSpinBoxUnitManager *other)
QVector< KisSpinBoxUnitManager * > connectedUnitManagers
virtual bool hasPercent(int unitDim) const
indicate if the unit manager has some kind of way of using a percent unit, used by the main class to ...
int getApparentUnitId() const
get the position of the apparent unit in the list of units. It is useful if we want to build a model ...
int rowCount(const QModelIndex &parent=QModelIndex()) const override
virtual QStringList getsUnitSymbolList(bool withName=false) const
void unitChanged(QString symbol)
static QString unitDescription(KoUnit::Type type)
Get the description string of the given unit.
Definition KoUnit.cpp:32
qreal toUserValuePrecise(const qreal ptValue) const
Definition KoUnit.cpp:161
static KoUnit fromSymbol(const QString &symbol, bool *ok=0)
Definition KoUnit.cpp:271
QString symbol() const
Get the symbol string of the unit.
Definition KoUnit.cpp:347
@ Pixel
Definition KoUnit.h:82
@ TypeCount
Definition KoUnit.h:83
static QString percentStr()