Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • tool
toolproperties.h
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17
18#ifndef TOOLPROPERTIES_H
19#define TOOLPROPERTIES_H
20
21#include <QHash>
22#include <QSettings>
23#include <QDebug>
24#include <QVector>
25#include <QPair>
26
27#include "pencildef.h"
28
29struct PropertyInfo
30{
31 enum ValueType {
32 INTEGER,
33 REAL,
34 BOOL,
35 INVALID
36 };
37
38 PropertyInfo() {
39 mValueType = INVALID;
40 mBaseValue = -1;
41 }
42 PropertyInfo(int min, int max, int defaultValue)
43 : mValueType(INTEGER) {
44 mMinValue = min;
45 mMaxValue = max;
46 mDefaultValue = defaultValue;
47 setBaseValue(defaultValue);
48 }
49 PropertyInfo(qreal min, qreal max, qreal defaultValue)
50 : mValueType(REAL) {
51 mMinValue = min;
52 mMaxValue = max;
53 mDefaultValue = defaultValue;
54 setBaseValue(defaultValue);
55 }
56
57 PropertyInfo(bool base, bool defaultValue)
58 : mValueType(BOOL) {
59 mMinValue = false;
60 mMaxValue = true;
61 mDefaultValue = defaultValue;
62 setBaseValue(base);
63 }
64
65 PropertyInfo(bool base) : PropertyInfo(base, base) {}
66 PropertyInfo(int base) : PropertyInfo(base, base, base) {}
67 PropertyInfo(qreal base) : PropertyInfo(base, base, base) {}
68
69 void setBaseValue(int newValue) {
70 Q_ASSERT(mValueType == INTEGER);
71 mBaseValue = qBound(mMinValue.toInt(), newValue, mMaxValue.toInt());
72 }
73
74 void setBaseValue(qreal newValue) {
75 Q_ASSERT(mValueType == REAL);
76 mBaseValue = qBound(mMinValue.toReal(), newValue, mMaxValue.toReal());
77 }
78
79 void setBaseValue(bool newValue) {
80 Q_ASSERT(mValueType == BOOL);
81 mBaseValue = newValue;
82 }
83
84 QVariant toVariant() const {
85 switch (mValueType) {
86 case INTEGER:
87 return mBaseValue;
88 case REAL:
89 return mBaseValue;
90 case BOOL:
91 return mBaseValue;
92 case INVALID:
93 return "INVALID";
94 }
95 }
96
97 int intValue() const {
98 if (mValueType != INTEGER) {
99 Q_ASSERT(false);
100 return -1;
101 }
102
103 return mBaseValue.toInt();
104 }
105
106 qreal realValue() const {
107 if (mValueType != REAL) {
108 Q_ASSERT(false);
109 return -1.0;
110 }
111
112 return mBaseValue.toReal();
113 }
114
115 bool boolValue() const {
116 if (mValueType != BOOL) {
117 Q_ASSERT(false);
118 return false;
119 }
120 return mBaseValue.toBool();
121 }
122
123 qreal minReal() const {
124 if (mValueType != REAL) {
125 return -1.0;
126 }
127 return mMinValue.toReal();
128 }
129
130 qreal maxReal() const {
131 if (mValueType != REAL) {
132 return -1.0;
133 }
134
135 return mMaxValue.toReal();
136 }
137
138 int minInt() const {
139 if (mValueType != INTEGER) {
140 return -1;
141 }
142 return mMinValue.toInt();
143 }
144
145 int maxInt() const {
146 if (mValueType != INTEGER) {
147 return -1;
148 }
149
150 return mMaxValue.toInt();
151 }
152
153 void resetBaseValue() {
154 switch (mValueType) {
155 case INTEGER:
156 mBaseValue = mDefaultValue;
157 break;
158 case REAL:
159 mBaseValue = mDefaultValue;
160 break;
161 case BOOL:
162 mBaseValue = mDefaultValue;
163 break;
164 case INVALID:
165 break;
166 }
167 }
168
170 int defaultInt() {
171 if (mValueType != INTEGER) {
172 return -1;
173 }
174 return mDefaultValue.toInt();
175 }
176
178 qreal defaultReal() {
179 if (mValueType != REAL) {
180 return -1.0;
181 }
182 return mDefaultValue.toReal();
183 }
184
186 bool defaultBool() {
187 if (mValueType != BOOL) {
188 return false;
189 }
190 return mDefaultValue.toBool();
191 }
192
193 ValueType type() const { return mValueType; }
194
195private:
196 ValueType mValueType;
197 QVariant mBaseValue;
198 QVariant mMinValue;
199 QVariant mMaxValue;
200 QVariant mDefaultValue;
201};
202
203struct ToolProperties
204{
205 enum Version {
206 NOT_SET = 0,
207 VERSION_1 = 1,
208 VERSION_2,
209 VERSION_3
210 };
211
212 ~ToolProperties() {}
213
214 /* Inserts properties into the ToolSetting model for loading and saving */
215 void insertProperties(const QHash<int, PropertyInfo>& properties) {
216 mProps.insert(properties);
217 }
218
219 /* Loads properties for the given tool from the input QSetting
220
221 If an existing value is found in QSettings then that will be the BaseValue, otherwise
222 it'll use use the default value.
223
224 @param toolIdentifier The identifier for the tool. This is later used to look up settings, so make sure it's consistent
225 @param settings The QSettings instance that contains the settings to load properties from
226 */
227 void loadFrom(const QString& toolIdentifier, QSettings& settings) {
228 mIdentifier = toolIdentifier;
229 settings.beginGroup(mIdentifier);
230
231 Q_ASSERT(mProps.count() > 0);
232 for (auto it = mProps.begin(); it != mProps.end(); ++it) {
233
234 PropertyInfo& info = it.value();
235 const QString& settingName = identifier(it.key());
236 loadProperty(settingName, info, settings);
237 }
238 settings.endGroup();
239 }
240
241 /* Store all tool property changes into the input QSetting instance */
242 void storeTo(QSettings& settings) {
243 settings.setValue(mVersionKey, mVersion);
244 settings.beginGroup(mIdentifier);
245
246 for (auto it = mProps.begin(); it != mProps.end(); ++it) {
247
248 QString propertyId = identifier(it.key());
249 if (it.value().type() == PropertyInfo::INVALID) {
250 Q_ASSERT_X(false, __func__, "Wrong state, expected a value type INTEGER|REAL|BOOL but got INVALID. You've probably misconfigured the property. "
251 "Ensure the property has been setup correctly and try again.");
252 continue;
253 }
254 settings.setValue(propertyId, it.value().toVariant());
255 }
256 settings.endGroup();
257 settings.sync();
258 }
259
260 /* Sets the BaseValue for the given property
261
262 @param rawType The type that identifies with the value, eg. StrokeSettings::WIDTH_VALUE
263 @param info A PropertyInfo struct which can either be INTEGER|REAL|BOOL
264 */
265 void setBaseValue(int rawType, const PropertyInfo& info)
266 {
267 if (!isValidType(rawType)) { return; }
268
269 switch (info.type())
270 {
271 case PropertyInfo::INTEGER:
272 mProps[rawType].setBaseValue(info.intValue());
273 break;
274 case PropertyInfo::REAL:
275 mProps[rawType].setBaseValue(info.realValue());
276 break;
277 case PropertyInfo::BOOL:
278 mProps[rawType].setBaseValue(info.boolValue());
279 break;
280 case PropertyInfo::INVALID:
281 Q_ASSERT_X(false, __func__, "Expected value but got INVALID. Make sure the property has been setup properly before trying to set its base value.");
282 break;
283 }
284 }
285
286 void setVersion(Version version) { mVersion = version; }
287
288 PropertyInfo getInfo(int rawPropertyType) const
289 {
290 return mProps[rawPropertyType];
291 }
292
293 void restoreProperties() {
294 for (auto it = mProps.begin(); it != mProps.end(); ++it) {
295 it.value().resetBaseValue();
296 }
297 }
298
299 /* Checks whether keys referred to in settings needs to be migrated from the input version */
300 bool requireMigration(QSettings& settings, ToolProperties::Version version) {
301
302 if (hasLegacySettings(settings) && !settings.contains(mVersionKey)) {
303 // Let's assume we're dealing with an existing user
304 return true;
305 }
306
307 int migrationNumber = static_cast<int>(version);
308
309 return settings.contains(mVersionKey) && migrationNumber < mVersion && migrationNumber > settings.value(mVersionKey).toInt();
310 }
311
312 bool hasLegacySettings(QSettings& settings) const {
313 // Crude check for existing settings...
314 return settings.contains("brushWidth") || settings.contains("pencilWidth") || settings.contains("penWidth");
315 }
316
317 bool isValidType(int rawType) const {
318 QString rangeCheck;
319 for (const auto& r : mTypeRanges) {
320
321 #ifdef QT_DEBUG
322 rangeCheck += QString("[%1...%2]").arg(r.first).arg(r.second);
323 #endif
324 if (rawType >= r.first && rawType < r.second) {
325 return true;
326 }
327 }
328 qWarning() << __FUNCTION__ << ":" << QString("Expected a valid type in range of %1 but instead got: %2. Make sure the input value matches one of the ranges of the tool setting").arg(rangeCheck).arg(rawType);
329 return false;
330 }
331
332 void insertIdentifiers(const QHash<int, QString>& identifiers) {
333 this->mIdentifiers.insert(identifiers);
334 }
335
336 QString identifier(int rawKey) const {
337 auto it = mIdentifiers.find(rawKey);
338 Q_ASSERT_X(it != mIdentifiers.end(),
339 "ToolSettings::identifier",
340 QString("No identifier matching the key %1 found").arg(rawKey).toUtf8().constData());
341 return it.value();
342 }
343
344 void setRanges(const QVector<QPair<int, int>>& ranges) { mTypeRanges = ranges; }
345 void addRange(QPair<int, int> range) { mTypeRanges.append(range); }
346 const QVector<QPair<int, int>>& typeRanges() const { return mTypeRanges; }
347
348private:
349
350 void loadProperty(const QString& settingName, PropertyInfo& info, const QSettings& settings) {
351 switch (info.type()) {
352 case PropertyInfo::INTEGER: {
353 QVariant value = settings.value(settingName, info.defaultInt());
354 info.setBaseValue(value.toInt());
355 break;
356 }
357 case PropertyInfo::REAL: {
358 QVariant value = settings.value(settingName, info.defaultReal());
359 info.setBaseValue(value.toReal());
360 break;
361 }
362 case PropertyInfo::BOOL: {
363 QVariant value = settings.value(settingName, info.defaultBool());
364 info.setBaseValue(value.toBool());
365 break;
366 }
367 case PropertyInfo::INVALID: {
368 Q_ASSERT_X(false, __func__, "Wrong state, expected a value type but got INVALID. You've probably misconfigured the property. "
369 "Ensure the property has been setup correctly and try again.");
370 break;
371 }
372 }
373 }
374
375 // The list of ranges that are valid for the given tool. ToolProperties can inherit its parents cases as well
376 // eg. PolyLineTool uses both StrokeSettings range as well as it's own
377 QVector<QPair<int, int>> mTypeRanges;
378 QHash<int, QString> mIdentifiers;
379 QHash<int, PropertyInfo> mProps;
380
381 QString mIdentifier = "undefined";
382 Version mVersion = VERSION_1;
383 QString mVersionKey = "ToolProperties_Version";
384};
385
386struct ToolPropertiesBase {
387
388 virtual ~ToolPropertiesBase() {}
389
390 virtual ToolProperties& toolProperties() = 0;
391 virtual PropertyInfo getInfo(int rawPropertyType) const = 0;
392};
393
394struct StrokeToolProperties: public ToolPropertiesBase
395{
396
397 enum Type {
398 START = 100,
399 WIDTH_VALUE = START,
400
401 FEATHER_VALUE = 101,
402 STABILIZATION_VALUE = 102,
403 PRESSURE_ENABLED = 103,
404 INVISIBILITY_ENABLED = 104,
405 FEATHER_ENABLED = 105,
406 ANTI_ALIASING_ENABLED = 106,
407 FILLCONTOUR_ENABLED = 107,
408
409 END = 199,
410 };
411
412 StrokeToolProperties() {
413 mToolProperties.setRanges({ { START, END } });
414
415 mToolProperties.insertIdentifiers({
416 { WIDTH_VALUE, "Width" },
417 { FEATHER_VALUE, "Feather" },
418 { FEATHER_ENABLED, "FeatherEnabled" },
419 { STABILIZATION_VALUE, "LineStabilization" },
420 { PRESSURE_ENABLED, "PressureEnabled" },
421 { INVISIBILITY_ENABLED, "InvisibilityEnabled" },
422 { ANTI_ALIASING_ENABLED, "AntiAliasingEnabled"},
423 { FILLCONTOUR_ENABLED, "FillContourEnabled" }
424 });
425 }
426
427 ToolProperties& toolProperties() override { return mToolProperties; }
428
429 void addRange(const QPair<int, int> range) {
430 mToolProperties.addRange(range);
431 }
432
433 PropertyInfo getInfo(int rawPropertyType) const override {
434 return mToolProperties.getInfo(rawPropertyType);
435 }
436
437 qreal width() const { return getInfo(WIDTH_VALUE).realValue(); }
438 qreal feather() const { return getInfo(FEATHER_VALUE).realValue(); }
439 int stabilizerLevel() const { return getInfo(STABILIZATION_VALUE).intValue(); }
440 bool pressureEnabled() const { return getInfo(PRESSURE_ENABLED).boolValue(); }
441 bool invisibilityEnabled() const { return getInfo(INVISIBILITY_ENABLED).boolValue(); }
442 bool featherEnabled() const { return getInfo(FEATHER_ENABLED).boolValue(); }
443 bool AntiAliasingEnabled() const { return getInfo(ANTI_ALIASING_ENABLED).boolValue(); }
444 bool fillContourEnabled() const { return getInfo(FILLCONTOUR_ENABLED).boolValue(); }
445
446private:
447 ToolProperties mToolProperties;
448};
449
453struct PolylineToolProperties: public ToolPropertiesBase
454{
455 enum Type {
456 START = 200,
457
458 CLOSEDPATH_ENABLED = START,
459 BEZIERPATH_ENABLED = 201,
460
461 END = 299,
462 };
463
464 PolylineToolProperties() {
465 toolProperties().addRange({START, END});
466
467 toolProperties().insertIdentifiers({
468 { CLOSEDPATH_ENABLED, "ClosedPathEnabled"},
469 { BEZIERPATH_ENABLED, "BezierPathEnabled" }
470 });
471 }
472
473 ToolProperties& toolProperties() override { return mStrokeToolProperties.toolProperties(); }
474 const StrokeToolProperties& strokeToolProperties() const { return mStrokeToolProperties; }
475
476 PropertyInfo getInfo(int rawPropertyType) const override {
477 return mStrokeToolProperties.getInfo(rawPropertyType);
478 }
479
480 qreal width() const { return getInfo(StrokeToolProperties::WIDTH_VALUE).realValue(); }
481 bool closedPathEnabled() const { return getInfo(CLOSEDPATH_ENABLED).boolValue(); }
482 bool bezierPathEnabled() const { return getInfo(BEZIERPATH_ENABLED).boolValue(); }
483 bool AntiAliasingEnabled() const { return getInfo(StrokeToolProperties::ANTI_ALIASING_ENABLED).boolValue(); }
484
485private:
486 StrokeToolProperties mStrokeToolProperties;
487};
488
489struct BucketToolProperties: public ToolPropertiesBase
490{
491 enum Type {
492 START = 300,
493 FILLTHICKNESS_VALUE = START,
494
495 COLORTOLERANCE_VALUE = 301,
496 FILLEXPAND_VALUE = 302,
497 FILLLAYERREFERENCEMODE_VALUE = 303,
498 FILLMODE_VALUE = 304,
499 COLORTOLERANCE_ENABLED = 305,
500 FILLEXPAND_ENABLED = 306,
501
502 END = 399,
503 };
504
505 BucketToolProperties() {
506 mToolProperties.setRanges({ { START, END } });
507
508 mToolProperties.insertIdentifiers({
509 { FILLTHICKNESS_VALUE, "FillThickness"},
510 { COLORTOLERANCE_VALUE, "ColorTolerance"},
511 { COLORTOLERANCE_ENABLED, "ColorToleranceEnabled"},
512 { FILLEXPAND_VALUE, "FillExpand"},
513 { FILLEXPAND_ENABLED, "FillExpandEnabled"},
514 { COLORTOLERANCE_ENABLED, "ColorToleranceEnabled"},
515 { FILLLAYERREFERENCEMODE_VALUE, "FillReferenceMode"},
516 { FILLMODE_VALUE, "FillMode"}
517 });
518 }
519
520 ToolProperties& toolProperties() override { return mToolProperties; }
521
522 PropertyInfo getInfo(int rawPropertyType) const override {
523 return mToolProperties.getInfo(rawPropertyType);
524 }
525
526 qreal fillThickness() const { return getInfo(FILLTHICKNESS_VALUE).realValue(); }
527 int tolerance() const { return getInfo(COLORTOLERANCE_VALUE).intValue(); }
528 int fillExpandAmount() const { return getInfo(FILLEXPAND_VALUE).intValue(); }
529 int fillReferenceMode() const { return getInfo(FILLLAYERREFERENCEMODE_VALUE).intValue(); }
530 int fillMode() const { return getInfo(FILLMODE_VALUE).intValue(); }
531 bool colorToleranceEnabled() const { return getInfo(COLORTOLERANCE_ENABLED).boolValue(); }
532 bool fillExpandEnabled() const { return getInfo(FILLEXPAND_ENABLED).boolValue(); }
533
534private:
535 ToolProperties mToolProperties;
536};
537
538struct CameraToolProperties: public ToolPropertiesBase
539{
540 enum Type {
541 START = 400,
542 SHOWPATH_ENABLED = START,
543
544 PATH_DOTCOLOR_TYPE = 401,
545
546 END = 499,
547 };
548
549 CameraToolProperties() {
550 mToolProperties.setRanges({ { START, END }});
551
552 mToolProperties.insertIdentifiers({
553 { SHOWPATH_ENABLED, "ShowPathEnabled"},
554 { PATH_DOTCOLOR_TYPE, "PathDotColorType"},
555 });
556 }
557
558 ToolProperties& toolProperties() override { return mToolProperties; }
559
560 PropertyInfo getInfo(int rawPropertyType) const override {
561 return mToolProperties.getInfo(rawPropertyType);
562 }
563
564 bool showPathEnabled() const { return getInfo(SHOWPATH_ENABLED).boolValue(); }
565 DotColorType dotColorType() const { return static_cast<DotColorType>(getInfo(PATH_DOTCOLOR_TYPE).intValue()); }
566
567private:
568 ToolProperties mToolProperties;
569};
570
571// Used by both select and move tool
572struct TransformToolProperties: public ToolPropertiesBase
573{
574 enum Type {
575 START = 500,
576 SHOWSELECTIONINFO_ENABLED = START,
577 ANTI_ALIASING_ENABLED = 501,
578 END = 599,
579 };
580
581 TransformToolProperties() {
582 mProperties.setRanges({ { START, END } });
583
584 mProperties.insertIdentifiers({
585 { SHOWSELECTIONINFO_ENABLED, "ShowSelectionInfoEnabled" },
586 { ANTI_ALIASING_ENABLED, "AntiAliasingEnabled" }
587 });
588 }
589
590 ToolProperties& toolProperties() override { return mProperties; }
591
592 PropertyInfo getInfo(int rawPropertyType) const override {
593 return mProperties.getInfo(rawPropertyType);
594 }
595
596 bool showSelectionInfoEnabled() const { return getInfo(SHOWSELECTIONINFO_ENABLED).boolValue(); }
597 bool antiAliasingEnabled() const { return getInfo(ANTI_ALIASING_ENABLED).boolValue(); }
598
599private:
600 ToolProperties mProperties;
601};
602
603#endif // TOOLPROPERTIES_H
QHash
QHash::begin
QHash::iterator begin()
QHash::count
int count(const Key &key) const const
QHash::end
QHash::iterator end()
QHash::find
QHash::iterator find(const Key &key)
QHash::insert
QHash::iterator insert(const Key &key, const T &value)
QHash::value
const T value(const Key &key) const const
QPair
QSettings
QSettings::beginGroup
void beginGroup(const QString &prefix)
QSettings::contains
bool contains(const QString &key) const const
QSettings::endGroup
void endGroup()
QSettings::setValue
void setValue(const QString &key, const QVariant &value)
QSettings::sync
void sync()
QSettings::value
QVariant value(const QString &key, const QVariant &defaultValue) const const
QString
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QVariant
QVariant::toBool
bool toBool() const const
QVariant::toInt
int toInt(bool *ok) const const
QVariant::toReal
qreal toReal(bool *ok) const const
QVector
QVector::append
void append(const T &value)
BucketToolProperties
Definition: toolproperties.h:490
CameraToolProperties
Definition: toolproperties.h:539
PolylineToolProperties
This struct is an example of how we can share properties among tools rather than duplicating logic,...
Definition: toolproperties.h:454
PropertyInfo
Definition: toolproperties.h:30
PropertyInfo::defaultReal
qreal defaultReal()
Returns the default value as an real, otherwise -1.0 if it hasn't been specified or the type doesn't ...
Definition: toolproperties.h:178
PropertyInfo::defaultInt
int defaultInt()
Returns the default value as an real, otherwise -1 if it hasn't been specified or the type doesn't ma...
Definition: toolproperties.h:170
PropertyInfo::defaultBool
bool defaultBool()
Returns the default value as an bool, otherwise false if it hasn't been specified or the type doesn't...
Definition: toolproperties.h:186
StrokeToolProperties
Definition: toolproperties.h:395
ToolPropertiesBase
Definition: toolproperties.h:386
ToolProperties
Definition: toolproperties.h:204
TransformToolProperties
Definition: toolproperties.h:573
Generated on Tue Dec 23 2025 02:59:17 for Pencil2D by doxygen 1.9.6 based on revision ba57a3de29b42de06bbb5b1757aa7f34e6bc4514