1 /*
2 * propertybrowser.cpp
3 * Copyright 2013, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
4 *
5 * This file is part of Tiled.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "propertybrowser.h"
22
23 #include "changeimagelayerproperties.h"
24 #include "changelayer.h"
25 #include "changemapobject.h"
26 #include "changemapproperty.h"
27 #include "changeobjectgroupproperties.h"
28 #include "changeproperties.h"
29 #include "changetile.h"
30 #include "changetileimagesource.h"
31 #include "changetileprobability.h"
32 #include "changewangcolordata.h"
33 #include "changewangsetdata.h"
34 #include "documentmanager.h"
35 #include "flipmapobjects.h"
36 #include "grouplayer.h"
37 #include "imagelayer.h"
38 #include "map.h"
39 #include "mapdocument.h"
40 #include "mapobject.h"
41 #include "movemapobject.h"
42 #include "objectgroup.h"
43 #include "objecttemplate.h"
44 #include "preferences.h"
45 #include "properties.h"
46 #include "replacetileset.h"
47 #include "resizemapobject.h"
48 #include "rotatemapobject.h"
49 #include "tile.h"
50 #include "tilelayer.h"
51 #include "tilesetchanges.h"
52 #include "tilesetdocument.h"
53 #include "tilesetformat.h"
54 #include "tilesetmanager.h"
55 #include "tilesetwangsetmodel.h"
56 #include "utils.h"
57 #include "varianteditorfactory.h"
58 #include "variantpropertymanager.h"
59 #include "wangcolormodel.h"
60 #include "wangoverlay.h"
61 #include "wangset.h"
62
63 #include <QtGroupPropertyManager>
64
65 #include <QCoreApplication>
66 #include <QDebug>
67 #include <QKeyEvent>
68 #include <QMessageBox>
69 #include <QScopedValueRollback>
70
71 #include <algorithm>
72
73 namespace Tiled {
74
75 namespace {
76
77 /**
78 * Makes sure the resize mode is set to Fixed during its lifetime. Used to work
79 * around performance issues caused by the view continuously making sure its
80 * name column is adjusted to the contents.
81 */
82 class SetFixedResizeMode
83 {
84 public:
SetFixedResizeMode(QtTreePropertyBrowser * browser)85 SetFixedResizeMode(QtTreePropertyBrowser *browser)
86 : mBrowser(browser)
87 , mPreviousResizeMode(browser->resizeMode())
88 {
89 mBrowser->setResizeMode(QtTreePropertyBrowser::Fixed);
90 }
91
~SetFixedResizeMode()92 ~SetFixedResizeMode()
93 {
94 mBrowser->setResizeMode(mPreviousResizeMode);
95 }
96
97 private:
98 QtTreePropertyBrowser * const mBrowser;
99 QtTreePropertyBrowser::ResizeMode const mPreviousResizeMode;
100 };
101
102 }
103
PropertyBrowser(QWidget * parent)104 PropertyBrowser::PropertyBrowser(QWidget *parent)
105 : QtTreePropertyBrowser(parent)
106 , mVariantManager(new VariantPropertyManager(this))
107 , mGroupManager(new QtGroupPropertyManager(this))
108 , mCustomPropertiesGroup(nullptr)
109 {
110 VariantEditorFactory *variantEditorFactory = new VariantEditorFactory(this);
111
112 setFactoryForManager(mVariantManager, variantEditorFactory);
113 setResizeMode(ResizeToContents);
114 setRootIsDecorated(false);
115 setPropertiesWithoutValueMarked(true);
116 setAllowMultiSelection(true);
117
118 retranslateUi();
119
120 mWangSetIcons.insert(WangSet::Corner, wangSetIcon(WangSet::Corner));
121 mWangSetIcons.insert(WangSet::Edge, wangSetIcon(WangSet::Edge));
122 mWangSetIcons.insert(WangSet::Mixed, wangSetIcon(WangSet::Mixed));
123
124 connect(mVariantManager, &QtVariantPropertyManager::valueChanged,
125 this, &PropertyBrowser::valueChanged);
126
127 connect(variantEditorFactory, &VariantEditorFactory::resetProperty,
128 this, &PropertyBrowser::resetProperty);
129
130 connect(Preferences::instance(), &Preferences::objectTypesChanged,
131 this, &PropertyBrowser::objectTypesChanged);
132 }
133
134 /**
135 * Sets the \a object for which to display the properties.
136 */
setObject(Object * object)137 void PropertyBrowser::setObject(Object *object)
138 {
139 if (mObject == object)
140 return;
141
142 removeProperties();
143 mObject = object;
144 addProperties();
145 }
146
147 /**
148 * Sets the \a document, used for keeping track of changes and for
149 * undo/redo support.
150 */
setDocument(Document * document)151 void PropertyBrowser::setDocument(Document *document)
152 {
153 MapDocument *mapDocument = qobject_cast<MapDocument*>(document);
154 TilesetDocument *tilesetDocument = qobject_cast<TilesetDocument*>(document);
155
156 if (mDocument == document)
157 return;
158
159 if (mDocument) {
160 mDocument->disconnect(this);
161 if (mTilesetDocument) {
162 mTilesetDocument->wangSetModel()->disconnect(this);
163 }
164 }
165
166 mDocument = document;
167 mMapDocument = mapDocument;
168 mTilesetDocument = tilesetDocument;
169
170 if (mapDocument) {
171 connect(mapDocument, &MapDocument::mapChanged,
172 this, &PropertyBrowser::mapChanged);
173 connect(mapDocument, &MapDocument::imageLayerChanged,
174 this, &PropertyBrowser::imageLayerChanged);
175 connect(mapDocument, &MapDocument::tileTypeChanged,
176 this, &PropertyBrowser::tileTypeChanged);
177
178 connect(mapDocument, &MapDocument::selectedObjectsChanged,
179 this, &PropertyBrowser::selectedObjectsChanged);
180 connect(mapDocument, &MapDocument::selectedLayersChanged,
181 this, &PropertyBrowser::selectedLayersChanged);
182 }
183
184 if (tilesetDocument) {
185 connect(tilesetDocument, &TilesetDocument::tilesetNameChanged,
186 this, &PropertyBrowser::tilesetChanged);
187 connect(tilesetDocument, &TilesetDocument::tilesetTileOffsetChanged,
188 this, &PropertyBrowser::tilesetChanged);
189 connect(tilesetDocument, &TilesetDocument::tilesetObjectAlignmentChanged,
190 this, &PropertyBrowser::tilesetChanged);
191 connect(tilesetDocument, &TilesetDocument::tilesetChanged,
192 this, &PropertyBrowser::tilesetChanged);
193
194 connect(tilesetDocument, &TilesetDocument::tileProbabilityChanged,
195 this, &PropertyBrowser::tileChanged);
196 connect(tilesetDocument, &TilesetDocument::tileImageSourceChanged,
197 this, &PropertyBrowser::tileChanged);
198 connect(tilesetDocument, &TilesetDocument::tileTypeChanged,
199 this, &PropertyBrowser::tileTypeChanged);
200
201 connect(tilesetDocument, &TilesetDocument::selectedTilesChanged,
202 this, &PropertyBrowser::selectedTilesChanged);
203
204 TilesetWangSetModel *wangSetModel = tilesetDocument->wangSetModel();
205 connect(wangSetModel, &TilesetWangSetModel::wangSetChanged,
206 this, &PropertyBrowser::wangSetChanged);
207 }
208
209 if (document) {
210 connect(document, &Document::changed,
211 this, &PropertyBrowser::documentChanged);
212
213 // For custom properties:
214 connect(document, &Document::propertyAdded,
215 this, &PropertyBrowser::propertyAdded);
216 connect(document, &Document::propertyRemoved,
217 this, &PropertyBrowser::propertyRemoved);
218 connect(document, &Document::propertyChanged,
219 this, &PropertyBrowser::propertyChanged);
220 connect(document, &Document::propertiesChanged,
221 this, &PropertyBrowser::propertiesChanged);
222 }
223 }
224
225 /**
226 * Returns whether the given \a item displays a custom property.
227 */
isCustomPropertyItem(const QtBrowserItem * item) const228 bool PropertyBrowser::isCustomPropertyItem(const QtBrowserItem *item) const
229 {
230 return item && mPropertyToId[item->property()] == CustomProperty;
231 }
232
233 /**
234 * Returns whether the given list of \a items are all custom properties.
235 */
allCustomPropertyItems(const QList<QtBrowserItem * > & items) const236 bool PropertyBrowser::allCustomPropertyItems(const QList<QtBrowserItem *> &items) const
237 {
238 return std::all_of(items.begin(), items.end(), [this] (QtBrowserItem *item) {
239 return mPropertyToId[item->property()] == CustomProperty;
240 });
241 }
242
243 /**
244 * Selects the custom property with the given \a name, if it exists.
245 */
selectCustomProperty(const QString & name)246 void PropertyBrowser::selectCustomProperty(const QString &name)
247 {
248 QtVariantProperty *property = mNameToProperty.value(name);
249 if (!property)
250 return;
251
252 const QList<QtBrowserItem*> propertyItems = items(property);
253 if (!propertyItems.isEmpty())
254 setCurrentItem(propertyItems.first());
255 }
256
257 /**
258 * Makes the custom property with the \a name the currently edited one,
259 * if it exists.
260 */
editCustomProperty(const QString & name)261 void PropertyBrowser::editCustomProperty(const QString &name)
262 {
263 QtVariantProperty *property = mNameToProperty.value(name);
264 if (!property)
265 return;
266
267 const QList<QtBrowserItem*> propertyItems = items(property);
268 if (!propertyItems.isEmpty())
269 editItem(propertyItems.first());
270 }
271
sizeHint() const272 QSize PropertyBrowser::sizeHint() const
273 {
274 return Utils::dpiScaled(QSize(260, 100));
275 }
276
event(QEvent * event)277 bool PropertyBrowser::event(QEvent *event)
278 {
279 if (event->type() == QEvent::LanguageChange)
280 retranslateUi();
281
282 if (event->type() == QEvent::ShortcutOverride) {
283 if (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Tab) {
284 if (editedItem()) {
285 event->accept();
286 return true;
287 }
288 }
289 }
290
291 return QtTreePropertyBrowser::event(event);
292 }
293
documentChanged(const ChangeEvent & change)294 void PropertyBrowser::documentChanged(const ChangeEvent &change)
295 {
296 switch (change.type) {
297 case ChangeEvent::LayerChanged:
298 case ChangeEvent::TileLayerChanged:
299 if (mObject == static_cast<const LayerChangeEvent&>(change).layer)
300 updateProperties();
301 break;
302 case ChangeEvent::MapObjectsChanged:
303 objectsChanged(static_cast<const MapObjectsChangeEvent&>(change));
304 break;
305 case ChangeEvent::ObjectGroupChanged:
306 if (mObject == static_cast<const ObjectGroupChangeEvent&>(change).objectGroup)
307 updateProperties();
308 break;
309 case ChangeEvent::WangSetChanged:
310 if (mObject == static_cast<const WangSetChangeEvent&>(change).wangSet)
311 updateProperties();
312 break;
313 default:
314 break;
315 }
316 }
317
mapChanged()318 void PropertyBrowser::mapChanged()
319 {
320 if (mObject == mMapDocument->map())
321 updateProperties();
322 }
323
objectsChanged(const MapObjectsChangeEvent & mapObjectsChange)324 void PropertyBrowser::objectsChanged(const MapObjectsChangeEvent &mapObjectsChange)
325 {
326 if (!mObject || mObject->typeId() != Object::MapObjectType)
327 return;
328 if (!mapObjectsChange.mapObjects.contains(static_cast<MapObject*>(mObject)))
329 return;
330
331 updateProperties();
332
333 if (mapObjectsChange.properties & (MapObject::CustomProperties | MapObject::TypeProperty))
334 updateCustomProperties();
335 }
336
imageLayerChanged(ImageLayer * imageLayer)337 void PropertyBrowser::imageLayerChanged(ImageLayer *imageLayer)
338 {
339 if (mObject == imageLayer)
340 updateProperties();
341 }
342
tilesetChanged(Tileset * tileset)343 void PropertyBrowser::tilesetChanged(Tileset *tileset)
344 {
345 if (mObject == tileset) {
346 updateProperties();
347 updateCustomProperties(); // Tileset may have been swapped
348 }
349 }
350
tileChanged(Tile * tile)351 void PropertyBrowser::tileChanged(Tile *tile)
352 {
353 if (mObject == tile)
354 updateProperties();
355 }
356
tileTypeChanged(Tile * tile)357 void PropertyBrowser::tileTypeChanged(Tile *tile)
358 {
359 if (mObject == tile) {
360 updateProperties();
361 updateCustomProperties();
362 } else if (mObject && mObject->typeId() == Object::MapObjectType) {
363 auto mapObject = static_cast<MapObject*>(mObject);
364 if (mapObject->cell().tile() == tile && mapObject->type().isEmpty())
365 updateProperties();
366 }
367 }
368
wangSetChanged(Tileset * tileset,int index)369 void PropertyBrowser::wangSetChanged(Tileset *tileset, int index)
370 {
371 if (mObject == tileset->wangSet(index))
372 updateProperties();
373 }
374
predefinedPropertyValue(Object * object,const QString & name)375 static QVariant predefinedPropertyValue(Object *object, const QString &name)
376 {
377 QString objectType;
378
379 switch (object->typeId()) {
380 case Object::TileType:
381 objectType = static_cast<Tile*>(object)->type();
382 break;
383 case Object::MapObjectType: {
384 auto mapObject = static_cast<MapObject*>(object);
385 objectType = mapObject->type();
386
387 if (Tile *tile = mapObject->cell().tile()) {
388 if (tile->hasProperty(name))
389 return tile->property(name);
390
391 if (objectType.isEmpty())
392 objectType = tile->type();
393 }
394 break;
395 }
396 case Object::LayerType:
397 case Object::MapType:
398 case Object::TilesetType:
399 case Object::WangSetType:
400 case Object::WangColorType:
401 case Object::ObjectTemplateType:
402 break;
403 }
404
405 if (objectType.isEmpty())
406 return QVariant();
407
408 for (const ObjectType &type : Object::objectTypes()) {
409 if (type.name == objectType)
410 if (type.defaultProperties.contains(name))
411 return type.defaultProperties.value(name);
412 }
413
414 return QVariant();
415 }
416
anyObjectHasProperty(const QList<Object * > & objects,const QString & name)417 static bool anyObjectHasProperty(const QList<Object*> &objects, const QString &name)
418 {
419 for (Object *obj : objects) {
420 if (obj->hasProperty(name))
421 return true;
422 }
423 return false;
424 }
425
propertyValueAffected(Object * currentObject,Object * changedObject,const QString & propertyName)426 static bool propertyValueAffected(Object *currentObject,
427 Object *changedObject,
428 const QString &propertyName)
429 {
430 if (currentObject == changedObject)
431 return true;
432
433 // Changed property may be inherited
434 if (currentObject && currentObject->typeId() == Object::MapObjectType && changedObject->typeId() == Object::TileType) {
435 auto tile = static_cast<MapObject*>(currentObject)->cell().tile();
436 if (tile == changedObject && !currentObject->hasProperty(propertyName))
437 return true;
438 }
439
440 return false;
441 }
442
objectPropertiesRelevant(Document * document,Object * object)443 static bool objectPropertiesRelevant(Document *document, Object *object)
444 {
445 auto currentObject = document->currentObject();
446 if (!currentObject)
447 return false;
448
449 if (currentObject == object)
450 return true;
451
452 if (currentObject->typeId() == Object::MapObjectType)
453 if (static_cast<MapObject*>(currentObject)->cell().tile() == object)
454 return true;
455
456 if (document->currentObjects().contains(object))
457 return true;
458
459 return false;
460 }
461
propertyAdded(Object * object,const QString & name)462 void PropertyBrowser::propertyAdded(Object *object, const QString &name)
463 {
464 if (!objectPropertiesRelevant(mDocument, object))
465 return;
466 if (QtVariantProperty *property = mNameToProperty.value(name)) {
467 if (propertyValueAffected(mObject, object, name))
468 setCustomPropertyValue(property, object->property(name));
469 } else {
470 QVariant value;
471 if (mObject->hasProperty(name))
472 value = mObject->property(name);
473 else
474 value = predefinedPropertyValue(mObject, name);
475
476 createCustomProperty(name, toDisplayValue(value));
477 }
478 updateCustomPropertyColor(name);
479 }
480
propertyRemoved(Object * object,const QString & name)481 void PropertyBrowser::propertyRemoved(Object *object, const QString &name)
482 {
483 auto property = mNameToProperty.value(name);
484 if (!property)
485 return;
486 if (!objectPropertiesRelevant(mDocument, object))
487 return;
488
489 QVariant predefinedValue = predefinedPropertyValue(mObject, name);
490
491 if (!predefinedValue.isValid() &&
492 !anyObjectHasProperty(mDocument->currentObjects(), name)) {
493 // It's not a predefined property and no selected object has this
494 // property, so delete it.
495
496 // First move up or down the currently selected item
497 QtBrowserItem *item = currentItem();
498 if (item && item->property() == property) {
499 const QList<QtBrowserItem *> siblings = item->parent()->children();
500 if (siblings.count() > 1) {
501 int currentItemIndex = siblings.indexOf(item);
502 if (item == siblings.last()) {
503 setCurrentItem(siblings.at(currentItemIndex - 1));
504 } else {
505 setCurrentItem(siblings.at(currentItemIndex + 1));
506 }
507 }
508 }
509
510 deleteCustomProperty(property);
511 return;
512 }
513
514 if (propertyValueAffected(mObject, object, name)) {
515 // Property deleted from the current object, so reset the value.
516 setCustomPropertyValue(property, predefinedValue);
517 }
518
519 updateCustomPropertyColor(name);
520 }
521
propertyChanged(Object * object,const QString & name)522 void PropertyBrowser::propertyChanged(Object *object, const QString &name)
523 {
524 auto property = mNameToProperty.value(name);
525 if (!property)
526 return;
527
528 if (propertyValueAffected(mObject, object, name))
529 setCustomPropertyValue(property, object->property(name));
530
531 if (mDocument->currentObjects().contains(object))
532 updateCustomPropertyColor(name);
533 }
534
propertiesChanged(Object * object)535 void PropertyBrowser::propertiesChanged(Object *object)
536 {
537 if (objectPropertiesRelevant(mDocument, object))
538 updateCustomProperties();
539 }
540
selectedObjectsChanged()541 void PropertyBrowser::selectedObjectsChanged()
542 {
543 updateCustomProperties();
544 }
545
selectedLayersChanged()546 void PropertyBrowser::selectedLayersChanged()
547 {
548 updateCustomProperties();
549 }
550
selectedTilesChanged()551 void PropertyBrowser::selectedTilesChanged()
552 {
553 updateCustomProperties();
554 }
555
objectTypesChanged()556 void PropertyBrowser::objectTypesChanged()
557 {
558 if (mObject && mObject->typeId() == Object::MapObjectType)
559 updateCustomProperties();
560 }
561
valueChanged(QtProperty * property,const QVariant & val)562 void PropertyBrowser::valueChanged(QtProperty *property, const QVariant &val)
563 {
564 if (mUpdating)
565 return;
566 if (!mObject || !mDocument)
567 return;
568 if (!mPropertyToId.contains(property))
569 return;
570
571 const PropertyId id = mPropertyToId.value(property);
572
573 if (id == CustomProperty) {
574 QUndoStack *undoStack = mDocument->undoStack();
575 undoStack->push(new SetProperty(mDocument,
576 mDocument->currentObjects(),
577 property->propertyName(),
578 fromDisplayValue(val)));
579 return;
580 }
581
582 switch (mObject->typeId()) {
583 case Object::MapType: applyMapValue(id, val); break;
584 case Object::MapObjectType: applyMapObjectValue(id, val); break;
585 case Object::LayerType: applyLayerValue(id, val); break;
586 case Object::TilesetType: applyTilesetValue(id, val); break;
587 case Object::TileType: applyTileValue(id, val); break;
588 case Object::WangSetType: applyWangSetValue(id, val); break;
589 case Object::WangColorType: applyWangColorValue(id, val); break;
590 case Object::ObjectTemplateType: break;
591 }
592 }
593
resetProperty(QtProperty * property)594 void PropertyBrowser::resetProperty(QtProperty *property)
595 {
596 auto typeId = mVariantManager->propertyType(property);
597 if (typeId == QMetaType::QColor)
598 mVariantManager->setValue(property, QColor());
599 else if (typeId == VariantPropertyManager::displayObjectRefTypeId()) {
600 mVariantManager->setValue(property, toDisplayValue(QVariant::fromValue(ObjectRef())));
601 } else
602 qWarning() << "Resetting of property type not supported right now";
603 }
604
addMapProperties()605 void PropertyBrowser::addMapProperties()
606 {
607 QtProperty *groupProperty = mGroupManager->addProperty(tr("Map"));
608
609 QtVariantProperty *orientationProperty =
610 addProperty(OrientationProperty,
611 QtVariantPropertyManager::enumTypeId(),
612 tr("Orientation"),
613 groupProperty);
614
615 orientationProperty->setAttribute(QLatin1String("enumNames"), mOrientationNames);
616
617 addProperty(WidthProperty, QMetaType::Int, tr("Width"), groupProperty)->setEnabled(false);
618 addProperty(HeightProperty, QMetaType::Int, tr("Height"), groupProperty)->setEnabled(false);
619 auto tileWidthProperty = addProperty(TileWidthProperty, QMetaType::Int, tr("Tile Width"), groupProperty);
620 auto tileHeightProperty = addProperty(TileHeightProperty, QMetaType::Int, tr("Tile Height"), groupProperty);
621 addProperty(InfiniteProperty, QMetaType::Bool, tr("Infinite"), groupProperty);
622
623 tileWidthProperty->setAttribute(QStringLiteral("minimum"), 1);
624 tileHeightProperty->setAttribute(QStringLiteral("minimum"), 1);
625
626 addProperty(HexSideLengthProperty, QMetaType::Int, tr("Tile Side Length (Hex)"), groupProperty);
627
628 QtVariantProperty *staggerAxisProperty =
629 addProperty(StaggerAxisProperty,
630 QtVariantPropertyManager::enumTypeId(),
631 tr("Stagger Axis"),
632 groupProperty);
633
634 staggerAxisProperty->setAttribute(QLatin1String("enumNames"), mStaggerAxisNames);
635
636 QtVariantProperty *staggerIndexProperty =
637 addProperty(StaggerIndexProperty,
638 QtVariantPropertyManager::enumTypeId(),
639 tr("Stagger Index"),
640 groupProperty);
641
642 staggerIndexProperty->setAttribute(QLatin1String("enumNames"), mStaggerIndexNames);
643
644 QtVariantProperty *layerFormatProperty =
645 addProperty(LayerFormatProperty,
646 QtVariantPropertyManager::enumTypeId(),
647 tr("Tile Layer Format"),
648 groupProperty);
649
650 layerFormatProperty->setAttribute(QLatin1String("enumNames"), mLayerFormatNames);
651
652 QtVariantProperty *chunkWidthProperty = addProperty(ChunkWidthProperty, QMetaType::Int, tr("Output Chunk Width"), groupProperty);
653 QtVariantProperty *chunkHeightProperty = addProperty(ChunkHeightProperty, QMetaType::Int, tr("Output Chunk Height"), groupProperty);
654
655 chunkWidthProperty->setAttribute(QLatin1String("minimum"), CHUNK_SIZE_MIN);
656 chunkHeightProperty->setAttribute(QLatin1String("minimum"), CHUNK_SIZE_MIN);
657
658 QtVariantProperty *renderOrderProperty =
659 addProperty(RenderOrderProperty,
660 QtVariantPropertyManager::enumTypeId(),
661 tr("Tile Render Order"),
662 groupProperty);
663
664 addProperty(CompressionLevelProperty, QMetaType::Int, tr("Compression Level"), groupProperty);
665
666 renderOrderProperty->setAttribute(QLatin1String("enumNames"), mRenderOrderNames);
667
668 addProperty(BackgroundColorProperty, QMetaType::QColor, tr("Background Color"), groupProperty);
669 addProperty(groupProperty);
670 }
671
objectTypeNames()672 static QStringList objectTypeNames()
673 {
674 QStringList names;
675 for (const ObjectType &type : Object::objectTypes())
676 names.append(type.name);
677 return names;
678 }
679
680 enum MapObjectFlags {
681 ObjectHasDimensions = 0x1,
682 ObjectHasTile = 0x2,
683 ObjectIsText = 0x4
684 };
685
mapObjectFlags(const MapObject * mapObject)686 static int mapObjectFlags(const MapObject *mapObject)
687 {
688 int flags = 0;
689 if (mapObject->hasDimensions())
690 flags |= ObjectHasDimensions;
691 if (!mapObject->cell().isEmpty())
692 flags |= ObjectHasTile;
693 if (mapObject->shape() == MapObject::Text)
694 flags |= ObjectIsText;
695 return flags;
696 }
697
addMapObjectProperties()698 void PropertyBrowser::addMapObjectProperties()
699 {
700 QtProperty *groupProperty = mGroupManager->addProperty(tr("Object"));
701
702 addProperty(IdProperty, QMetaType::Int, tr("ID"), groupProperty)->setEnabled(false);
703 addProperty(TemplateProperty, filePathTypeId(), tr("Template"), groupProperty)->setEnabled(false);
704 addProperty(NameProperty, QMetaType::QString, tr("Name"), groupProperty);
705
706 QtVariantProperty *typeProperty =
707 addProperty(TypeProperty, QMetaType::QString, tr("Type"), groupProperty);
708 typeProperty->setAttribute(QLatin1String("suggestions"), objectTypeNames());
709
710 if (mMapDocument->allowHidingObjects())
711 addProperty(VisibleProperty, QMetaType::Bool, tr("Visible"), groupProperty);
712
713 addProperty(XProperty, QMetaType::Double, tr("X"), groupProperty);
714 addProperty(YProperty, QMetaType::Double, tr("Y"), groupProperty);
715
716 auto mapObject = static_cast<const MapObject*>(mObject);
717 mMapObjectFlags = mapObjectFlags(mapObject);
718
719 if (mMapObjectFlags & ObjectHasDimensions) {
720 addProperty(WidthProperty, QMetaType::Double, tr("Width"), groupProperty);
721 addProperty(HeightProperty, QMetaType::Double, tr("Height"), groupProperty);
722 }
723
724 bool isPoint = mapObject->shape() == MapObject::Point;
725 addProperty(RotationProperty, QMetaType::Double, tr("Rotation"), groupProperty)->setEnabled(!isPoint);
726
727 if (mMapObjectFlags & ObjectHasTile) {
728 QtVariantProperty *flippingProperty =
729 addProperty(FlippingProperty, VariantPropertyManager::flagTypeId(),
730 tr("Flipping"), groupProperty);
731
732 flippingProperty->setAttribute(QLatin1String("flagNames"), mFlippingFlagNames);
733 }
734
735 if (mMapObjectFlags & ObjectIsText) {
736 addProperty(TextProperty, QMetaType::QString, tr("Text"), groupProperty)->setAttribute(QLatin1String("multiline"), true);
737 addProperty(TextAlignmentProperty, VariantPropertyManager::alignmentTypeId(), tr("Alignment"), groupProperty);
738 addProperty(FontProperty, QMetaType::QFont, tr("Font"), groupProperty);
739 addProperty(WordWrapProperty, QMetaType::Bool, tr("Word Wrap"), groupProperty);
740 addProperty(ColorProperty, QMetaType::QColor, tr("Color"), groupProperty);
741 }
742
743 addProperty(groupProperty);
744 }
745
addLayerProperties(QtProperty * parent)746 void PropertyBrowser::addLayerProperties(QtProperty *parent)
747 {
748 addProperty(IdProperty, QMetaType::Int, tr("ID"), parent)->setEnabled(false);
749 addProperty(NameProperty, QMetaType::QString, tr("Name"), parent);
750 addProperty(VisibleProperty, QMetaType::Bool, tr("Visible"), parent);
751 addProperty(LockedProperty, QMetaType::Bool, tr("Locked"), parent);
752
753 QtVariantProperty *opacityProperty =
754 addProperty(OpacityProperty, QMetaType::Double, tr("Opacity"), parent);
755 opacityProperty->setAttribute(QLatin1String("minimum"), 0.0);
756 opacityProperty->setAttribute(QLatin1String("maximum"), 1.0);
757 opacityProperty->setAttribute(QLatin1String("singleStep"), 0.1);
758 addProperty(TintColorProperty, QMetaType::QColor, tr("Tint Color"), parent);
759
760 addProperty(OffsetXProperty, QMetaType::Double, tr("Horizontal Offset"), parent);
761 addProperty(OffsetYProperty, QMetaType::Double, tr("Vertical Offset"), parent);
762
763 addProperty(ParallaxFactorProperty, QMetaType::QPointF, tr("Parallax Factor"), parent);
764 }
765
addTileLayerProperties()766 void PropertyBrowser::addTileLayerProperties()
767 {
768 QtProperty *groupProperty = mGroupManager->addProperty(tr("Tile Layer"));
769 addLayerProperties(groupProperty);
770 addProperty(groupProperty);
771 }
772
addObjectGroupProperties()773 void PropertyBrowser::addObjectGroupProperties()
774 {
775 QtProperty *groupProperty = mGroupManager->addProperty(tr("Object Layer"));
776 addLayerProperties(groupProperty);
777
778 addProperty(ColorProperty, QMetaType::QColor, tr("Color"), groupProperty);
779
780 QtVariantProperty *drawOrderProperty =
781 addProperty(DrawOrderProperty,
782 QtVariantPropertyManager::enumTypeId(),
783 tr("Drawing Order"),
784 groupProperty);
785
786 drawOrderProperty->setAttribute(QLatin1String("enumNames"), mDrawOrderNames);
787
788 addProperty(groupProperty);
789 }
790
addImageLayerProperties()791 void PropertyBrowser::addImageLayerProperties()
792 {
793 QtProperty *groupProperty = mGroupManager->addProperty(tr("Image Layer"));
794 addLayerProperties(groupProperty);
795
796 QtVariantProperty *imageSourceProperty = addProperty(ImageSourceProperty,
797 filePathTypeId(),
798 tr("Image"), groupProperty);
799
800 imageSourceProperty->setAttribute(QLatin1String("filter"),
801 Utils::readableImageFormatsFilter());
802
803 addProperty(ColorProperty, QMetaType::QColor, tr("Transparent Color"), groupProperty);
804
805 addProperty(groupProperty);
806 }
807
addGroupLayerProperties()808 void PropertyBrowser::addGroupLayerProperties()
809 {
810 QtProperty *groupProperty = mGroupManager->addProperty(tr("Group Layer"));
811 addLayerProperties(groupProperty);
812 addProperty(groupProperty);
813 }
814
addTilesetProperties()815 void PropertyBrowser::addTilesetProperties()
816 {
817 const Tileset *tileset = static_cast<const Tileset*>(mObject);
818
819 QtProperty *groupProperty = mGroupManager->addProperty(tr("Tileset"));
820
821 if (mMapDocument) {
822 auto property = addProperty(FileNameProperty, filePathTypeId(), tr("Filename"), groupProperty);
823
824 QString filter = QCoreApplication::translate("MainWindow", "All Files (*)");
825 FormatHelper<TilesetFormat> helper(FileFormat::Read, filter);
826
827 property->setAttribute(QStringLiteral("filter"), helper.filter());
828 }
829
830 QtVariantProperty *nameProperty = addProperty(NameProperty, QMetaType::QString, tr("Name"), groupProperty);
831 nameProperty->setEnabled(mTilesetDocument);
832
833 QtVariantProperty *alignmentProperty =
834 addProperty(ObjectAlignmentProperty,
835 QtVariantPropertyManager::enumTypeId(),
836 tr("Object Alignment"),
837 groupProperty);
838
839 alignmentProperty->setAttribute(QLatin1String("enumNames"), mAlignmentNames);
840
841 QtVariantProperty *tileOffsetProperty = addProperty(TileOffsetProperty, QMetaType::QPoint, tr("Drawing Offset"), groupProperty);
842 tileOffsetProperty->setEnabled(mTilesetDocument);
843
844 QtVariantProperty *backgroundProperty = addProperty(BackgroundColorProperty, QMetaType::QColor, tr("Background Color"), groupProperty);
845 backgroundProperty->setEnabled(mTilesetDocument);
846
847 QtVariantProperty *orientationProperty =
848 addProperty(OrientationProperty,
849 QtVariantPropertyManager::enumTypeId(),
850 tr("Orientation"),
851 groupProperty);
852
853 orientationProperty->setAttribute(QLatin1String("enumNames"), mTilesetOrientationNames);
854
855 QtVariantProperty *gridWidthProperty = addProperty(GridWidthProperty, QMetaType::Int, tr("Grid Width"), groupProperty);
856 gridWidthProperty->setEnabled(mTilesetDocument);
857 gridWidthProperty->setAttribute(QLatin1String("minimum"), 1);
858 QtVariantProperty *gridHeightProperty = addProperty(GridHeightProperty, QMetaType::Int, tr("Grid Height"), groupProperty);
859 gridHeightProperty->setEnabled(mTilesetDocument);
860 gridHeightProperty->setAttribute(QLatin1String("minimum"), 1);
861
862 QtVariantProperty *columnsProperty = addProperty(ColumnCountProperty, QMetaType::Int, tr("Columns"), groupProperty);
863 columnsProperty->setAttribute(QLatin1String("minimum"), 1);
864
865 QtVariantProperty *transformationsGroupProperty = mVariantManager->addProperty(VariantPropertyManager::unstyledGroupTypeId(), tr("Allowed Transformations"));
866
867 QtVariantProperty *flipHorizontallyProperty = addProperty(AllowFlipHorizontallyProperty, QMetaType::Bool, tr("Flip Horizontally"), transformationsGroupProperty);
868 QtVariantProperty *flipVerticallyProperty = addProperty(AllowFlipVerticallyProperty, QMetaType::Bool, tr("Flip Vertically"), transformationsGroupProperty);
869 QtVariantProperty *rotateProperty = addProperty(AllowRotateProperty, QMetaType::Bool, tr("Rotate"), transformationsGroupProperty);
870 QtVariantProperty *randomProperty = addProperty(PreferUntransformedProperty, QMetaType::Bool, tr("Prefer Untransformed Tiles"), transformationsGroupProperty);
871 flipHorizontallyProperty->setEnabled(mTilesetDocument);
872 flipVerticallyProperty->setEnabled(mTilesetDocument);
873 rotateProperty->setEnabled(mTilesetDocument);
874 randomProperty->setEnabled(mTilesetDocument);
875
876 groupProperty->addSubProperty(transformationsGroupProperty);
877
878 // Next properties we should add only for non 'Collection of Images' tilesets
879 if (!tileset->isCollection()) {
880 QtVariantProperty *parametersProperty =
881 addProperty(TilesetImageParametersProperty, VariantPropertyManager::tilesetParametersTypeId(), tr("Image"), groupProperty);
882
883 parametersProperty->setEnabled(mTilesetDocument);
884
885 QtVariantProperty *imageSourceProperty = addProperty(ImageSourceProperty, QMetaType::QString, tr("Source"), parametersProperty);
886 QtVariantProperty *tileWidthProperty = addProperty(TileWidthProperty, QMetaType::Int, tr("Tile Width"), parametersProperty);
887 QtVariantProperty *tileHeightProperty = addProperty(TileHeightProperty, QMetaType::Int, tr("Tile Height"), parametersProperty);
888 QtVariantProperty *marginProperty = addProperty(MarginProperty, QMetaType::Int, tr("Margin"), parametersProperty);
889 QtVariantProperty *spacingProperty = addProperty(SpacingProperty, QMetaType::Int, tr("Spacing"), parametersProperty);
890 QtVariantProperty *colorProperty = addProperty(ColorProperty, QMetaType::QColor, tr("Transparent Color"), parametersProperty);
891
892 // These properties can't be directly edited. To change the parameters,
893 // the TilesetParametersEdit is used.
894 imageSourceProperty->setEnabled(false);
895 tileWidthProperty->setEnabled(false);
896 tileHeightProperty->setEnabled(false);
897 marginProperty->setEnabled(false);
898 spacingProperty->setEnabled(false);
899 colorProperty->setEnabled(false);
900 }
901 addProperty(groupProperty);
902 }
903
addTileProperties()904 void PropertyBrowser::addTileProperties()
905 {
906 QtProperty *groupProperty = mGroupManager->addProperty(tr("Tile"));
907 addProperty(IdProperty, QMetaType::Int, tr("ID"), groupProperty)->setEnabled(false);
908
909 QtVariantProperty *typeProperty =
910 addProperty(TypeProperty, QMetaType::QString, tr("Type"), groupProperty);
911 typeProperty->setAttribute(QLatin1String("suggestions"), objectTypeNames());
912 typeProperty->setEnabled(mTilesetDocument);
913
914 addProperty(WidthProperty, QMetaType::Int, tr("Width"), groupProperty)->setEnabled(false);
915 addProperty(HeightProperty, QMetaType::Int, tr("Height"), groupProperty)->setEnabled(false);
916
917 QtVariantProperty *probabilityProperty = addProperty(TileProbabilityProperty,
918 QMetaType::Double,
919 tr("Probability"),
920 groupProperty);
921 probabilityProperty->setAttribute(QLatin1String("decimals"), 3);
922 probabilityProperty->setToolTip(tr("Relative chance this tile will be picked"));
923 probabilityProperty->setEnabled(mTilesetDocument);
924
925 const Tile *tile = static_cast<const Tile*>(mObject);
926 if (!tile->imageSource().isEmpty()) {
927 QtVariantProperty *imageSourceProperty = addProperty(ImageSourceProperty,
928 filePathTypeId(),
929 tr("Image"), groupProperty);
930
931 imageSourceProperty->setAttribute(QLatin1String("filter"),
932 Utils::readableImageFormatsFilter());
933 imageSourceProperty->setEnabled(mTilesetDocument);
934 }
935
936 addProperty(groupProperty);
937 }
938
addWangSetProperties()939 void PropertyBrowser::addWangSetProperties()
940 {
941 QtProperty *groupProperty = mGroupManager->addProperty(tr("Terrain Set"));
942 QtVariantProperty *nameProperty = addProperty(NameProperty, QMetaType::QString, tr("Name"), groupProperty);
943 QtVariantProperty *typeProperty = addProperty(WangSetTypeProperty,
944 QtVariantPropertyManager::enumTypeId(),
945 tr("Type"),
946 groupProperty);
947 QtVariantProperty *colorCountProperty = addProperty(ColorCountProperty, QMetaType::Int, tr("Terrain Count"), groupProperty);
948
949 typeProperty->setAttribute(QLatin1String("enumNames"), mWangSetTypeNames);
950 typeProperty->setAttribute(QLatin1String("enumIcons"), QVariant::fromValue(mWangSetIcons));
951
952 colorCountProperty->setAttribute(QLatin1String("minimum"), 0);
953 colorCountProperty->setAttribute(QLatin1String("maximum"), WangId::MAX_COLOR_COUNT);
954
955 nameProperty->setEnabled(mTilesetDocument);
956 colorCountProperty->setEnabled(mTilesetDocument);
957
958 addProperty(groupProperty);
959 }
960
addWangColorProperties()961 void PropertyBrowser::addWangColorProperties()
962 {
963 QtProperty *groupProperty = mGroupManager->addProperty(tr("Terrain"));
964 QtVariantProperty *nameProperty = addProperty(NameProperty,
965 QMetaType::QString,
966 tr("Name"),
967 groupProperty);
968 QtVariantProperty *colorProperty = addProperty(ColorProperty,
969 QMetaType::QColor,
970 tr("Color"),
971 groupProperty);
972 QtVariantProperty *probabilityProperty = addProperty(WangColorProbabilityProperty,
973 QMetaType::Double,
974 tr("Probability"),
975 groupProperty);
976
977 probabilityProperty->setAttribute(QLatin1String("minimum"), 0.01);
978
979 nameProperty->setEnabled(mTilesetDocument);
980 colorProperty->setEnabled(mTilesetDocument);
981 probabilityProperty->setEnabled(mTilesetDocument);
982
983 addProperty(groupProperty);
984 }
985
applyMapValue(PropertyId id,const QVariant & val)986 void PropertyBrowser::applyMapValue(PropertyId id, const QVariant &val)
987 {
988 QUndoCommand *command = nullptr;
989
990 switch (id) {
991 case TileWidthProperty:
992 command = new ChangeMapProperty(mMapDocument, Map::TileWidthProperty,
993 val.toInt());
994 break;
995 case TileHeightProperty:
996 command = new ChangeMapProperty(mMapDocument, Map::TileHeightProperty,
997 val.toInt());
998 break;
999 case InfiniteProperty: {
1000 bool infinite = val.toInt();
1001
1002 QUndoStack *undoStack = mDocument->undoStack();
1003 undoStack->beginMacro(tr("Change Infinite Property"));
1004
1005 if (!infinite) {
1006 QRect mapBounds(QPoint(0, 0), mMapDocument->map()->size());
1007
1008 LayerIterator iterator(mMapDocument->map());
1009 while (Layer *layer = iterator.next()) {
1010 if (TileLayer *tileLayer = dynamic_cast<TileLayer*>(layer))
1011 mapBounds = mapBounds.united(tileLayer->region().boundingRect());
1012 }
1013
1014 if (mapBounds.size() == QSize(0, 0))
1015 mapBounds.setSize(QSize(1, 1));
1016
1017 mMapDocument->resizeMap(mapBounds.size(), -mapBounds.topLeft(), false);
1018 }
1019
1020 undoStack->push(new ChangeMapProperty(mMapDocument, Map::InfiniteProperty,
1021 val.toInt()));
1022 undoStack->endMacro();
1023 break;
1024 }
1025 case OrientationProperty: {
1026 Map::Orientation orientation = static_cast<Map::Orientation>(val.toInt() + 1);
1027 command = new ChangeMapProperty(mMapDocument, orientation);
1028 break;
1029 }
1030 case HexSideLengthProperty: {
1031 command = new ChangeMapProperty(mMapDocument, Map::HexSideLengthProperty,
1032 val.toInt());
1033 break;
1034 }
1035 case StaggerAxisProperty: {
1036 Map::StaggerAxis staggerAxis = static_cast<Map::StaggerAxis>(val.toInt());
1037 command = new ChangeMapProperty(mMapDocument, staggerAxis);
1038 break;
1039 }
1040 case StaggerIndexProperty: {
1041 Map::StaggerIndex staggerIndex = static_cast<Map::StaggerIndex>(val.toInt());
1042 command = new ChangeMapProperty(mMapDocument, staggerIndex);
1043 break;
1044 }
1045 case LayerFormatProperty: {
1046 Map::LayerDataFormat format = mLayerFormatValues.at(val.toInt());
1047 command = new ChangeMapProperty(mMapDocument, format);
1048 break;
1049 }
1050 case RenderOrderProperty: {
1051 Map::RenderOrder renderOrder = static_cast<Map::RenderOrder>(val.toInt());
1052 command = new ChangeMapProperty(mMapDocument, renderOrder);
1053 break;
1054 }
1055 case BackgroundColorProperty:
1056 command = new ChangeMapProperty(mMapDocument, val.value<QColor>());
1057 break;
1058 case CompressionLevelProperty:
1059 command = new ChangeMapProperty(mMapDocument, Map::CompressionLevelProperty, val.toInt());
1060 break;
1061 case ChunkWidthProperty: {
1062 QSize chunkSize = mMapDocument->map()->chunkSize();
1063 chunkSize.setWidth(val.toInt());
1064 command = new ChangeMapProperty(mMapDocument, chunkSize);
1065 break;
1066 }
1067 case ChunkHeightProperty: {
1068 QSize chunkSize = mMapDocument->map()->chunkSize();
1069 chunkSize.setHeight(val.toInt());
1070 command = new ChangeMapProperty(mMapDocument, chunkSize);
1071 break;
1072 }
1073 default:
1074 break;
1075 }
1076
1077 if (command)
1078 mDocument->undoStack()->push(command);
1079 }
1080
applyMapObjectValueTo(PropertyId id,const QVariant & val,MapObject * mapObject)1081 QUndoCommand *PropertyBrowser::applyMapObjectValueTo(PropertyId id, const QVariant &val, MapObject *mapObject)
1082 {
1083 QUndoCommand *command = nullptr;
1084
1085 switch (id) {
1086 default: {
1087 MapObject::Property property;
1088
1089 switch (id) {
1090 case NameProperty: property = MapObject::NameProperty; break;
1091 case TypeProperty: property = MapObject::TypeProperty; break;
1092 case VisibleProperty: property = MapObject::VisibleProperty; break;
1093 case TextProperty: property = MapObject::TextProperty; break;
1094 case FontProperty: property = MapObject::TextFontProperty; break;
1095 case TextAlignmentProperty: property = MapObject::TextAlignmentProperty; break;
1096 case WordWrapProperty: property = MapObject::TextWordWrapProperty; break;
1097 case ColorProperty: property = MapObject::TextColorProperty; break;
1098 default:
1099 return nullptr; // unrecognized property
1100 }
1101
1102 command = new ChangeMapObject(mDocument, mapObject, property, val);
1103 break;
1104 }
1105 case XProperty: {
1106 const QPointF oldPos = mapObject->position();
1107 const QPointF newPos(val.toReal(), oldPos.y());
1108 command = new MoveMapObject(mDocument, mapObject, newPos, oldPos);
1109 break;
1110 }
1111 case YProperty: {
1112 const QPointF oldPos = mapObject->position();
1113 const QPointF newPos(oldPos.x(), val.toReal());
1114 command = new MoveMapObject(mDocument, mapObject, newPos, oldPos);
1115 break;
1116 }
1117 case WidthProperty: {
1118 const QSizeF oldSize = mapObject->size();
1119 const QSizeF newSize(val.toReal(), oldSize.height());
1120 command = new ResizeMapObject(mDocument, mapObject, newSize, oldSize);
1121 break;
1122 }
1123 case HeightProperty: {
1124 const QSizeF oldSize = mapObject->size();
1125 const QSizeF newSize(oldSize.width(), val.toReal());
1126 command = new ResizeMapObject(mDocument, mapObject, newSize, oldSize);
1127 break;
1128 }
1129 case RotationProperty:
1130 if (mapObject->canRotate()) {
1131 const qreal newRotation = val.toDouble();
1132 const qreal oldRotation = mapObject->rotation();
1133 command = new RotateMapObject(mDocument, mapObject, newRotation, oldRotation);
1134 }
1135 break;
1136 case FlippingProperty: {
1137 const int flippingFlags = val.toInt();
1138
1139 MapObjectCell mapObjectCell;
1140 mapObjectCell.object = mapObject;
1141 mapObjectCell.cell = mapObject->cell();
1142 mapObjectCell.cell.setFlippedHorizontally(flippingFlags & 1);
1143 mapObjectCell.cell.setFlippedVertically(flippingFlags & 2);
1144
1145 command = new ChangeMapObjectCells(mDocument, { mapObjectCell });
1146
1147 command->setText(QCoreApplication::translate("Undo Commands",
1148 "Flip %n Object(s)",
1149 nullptr,
1150 mMapDocument->selectedObjects().size()));
1151 break;
1152 }
1153 }
1154
1155 return command;
1156 }
1157
applyMapObjectValue(PropertyId id,const QVariant & val)1158 void PropertyBrowser::applyMapObjectValue(PropertyId id, const QVariant &val)
1159 {
1160 MapObject *mapObject = static_cast<MapObject*>(mObject);
1161
1162 QUndoCommand *command = applyMapObjectValueTo(id, val, mapObject);
1163 if (!command)
1164 return;
1165
1166 mDocument->undoStack()->beginMacro(command->text());
1167 mDocument->undoStack()->push(command);
1168
1169 for (MapObject *obj : mMapDocument->selectedObjects()) {
1170 if (obj != mapObject) {
1171 if (QUndoCommand *cmd = applyMapObjectValueTo(id, val, obj))
1172 mDocument->undoStack()->push(cmd);
1173 }
1174 }
1175
1176 mDocument->undoStack()->endMacro();
1177 }
1178
applyLayerValue(PropertyId id,const QVariant & val)1179 void PropertyBrowser::applyLayerValue(PropertyId id, const QVariant &val)
1180 {
1181 Layer *currentLayer = static_cast<Layer*>(mObject);
1182
1183 QUndoCommand *command = applyLayerValueTo(id, val, currentLayer);
1184 if (!command)
1185 return;
1186
1187 mDocument->undoStack()->beginMacro(command->text());
1188 mDocument->undoStack()->push(command);
1189
1190 for (Layer *layer : mMapDocument->selectedLayers()) {
1191 if (layer != currentLayer) {
1192 if (QUndoCommand *cmd = applyLayerValueTo(id, val, layer))
1193 mDocument->undoStack()->push(cmd);
1194 }
1195 }
1196
1197 mDocument->undoStack()->endMacro();
1198 }
1199
applyLayerValueTo(PropertyBrowser::PropertyId id,const QVariant & val,Layer * layer)1200 QUndoCommand *PropertyBrowser::applyLayerValueTo(PropertyBrowser::PropertyId id, const QVariant &val, Layer *layer)
1201 {
1202 QUndoCommand *command = nullptr;
1203
1204 switch (id) {
1205 case NameProperty:
1206 command = new SetLayerName(mMapDocument, layer, val.toString());
1207 break;
1208 case VisibleProperty:
1209 command = new SetLayerVisible(mMapDocument, layer, val.toBool());
1210 break;
1211 case LockedProperty:
1212 command = new SetLayerLocked(mMapDocument, layer, val.toBool());
1213 break;
1214 case OpacityProperty:
1215 command = new SetLayerOpacity(mMapDocument, layer, val.toDouble());
1216 break;
1217 case TintColorProperty:
1218 command = new SetLayerTintColor(mMapDocument, layer, val.value<QColor>());
1219 break;
1220 case OffsetXProperty:
1221 case OffsetYProperty: {
1222 QPointF offset = layer->offset();
1223
1224 if (id == OffsetXProperty)
1225 offset.setX(val.toDouble());
1226 else
1227 offset.setY(val.toDouble());
1228
1229 command = new SetLayerOffset(mMapDocument, layer, offset);
1230 break;
1231 }
1232 case ParallaxFactorProperty:
1233 command = new SetLayerParallaxFactor(mMapDocument, layer, val.toPointF());
1234 break;
1235 default:
1236 switch (layer->layerType()) {
1237 case Layer::TileLayerType:
1238 command = applyTileLayerValueTo(id, val, static_cast<TileLayer*>(layer));
1239 break;
1240 case Layer::ObjectGroupType:
1241 command = applyObjectGroupValueTo(id, val, static_cast<ObjectGroup*>(layer));
1242 break;
1243 case Layer::ImageLayerType:
1244 command = applyImageLayerValueTo(id, val, static_cast<ImageLayer*>(layer));
1245 break;
1246 case Layer::GroupLayerType:
1247 command = applyGroupLayerValueTo(id, val, static_cast<GroupLayer*>(layer));
1248 break;
1249 }
1250 break;
1251 }
1252
1253 return command;
1254 }
1255
applyTileLayerValueTo(PropertyId id,const QVariant & val,TileLayer * tileLayer)1256 QUndoCommand *PropertyBrowser::applyTileLayerValueTo(PropertyId id, const QVariant &val, TileLayer *tileLayer)
1257 {
1258 Q_UNUSED(id)
1259 Q_UNUSED(val)
1260 Q_UNUSED(tileLayer)
1261
1262 return nullptr;
1263 }
1264
applyObjectGroupValueTo(PropertyId id,const QVariant & val,ObjectGroup * objectGroup)1265 QUndoCommand *PropertyBrowser::applyObjectGroupValueTo(PropertyId id, const QVariant &val, ObjectGroup *objectGroup)
1266 {
1267 QUndoCommand *command = nullptr;
1268
1269 switch (id) {
1270 case ColorProperty: {
1271 const QColor color = val.value<QColor>();
1272 command = new ChangeObjectGroupProperties(mMapDocument,
1273 objectGroup,
1274 color,
1275 objectGroup->drawOrder());
1276 break;
1277 }
1278 case DrawOrderProperty: {
1279 ObjectGroup::DrawOrder drawOrder = static_cast<ObjectGroup::DrawOrder>(val.toInt());
1280 command = new ChangeObjectGroupProperties(mMapDocument,
1281 objectGroup,
1282 objectGroup->color(),
1283 drawOrder);
1284 break;
1285 }
1286 default:
1287 break;
1288 }
1289
1290 return command;
1291 }
1292
applyImageLayerValueTo(PropertyId id,const QVariant & val,ImageLayer * imageLayer)1293 QUndoCommand *PropertyBrowser::applyImageLayerValueTo(PropertyId id, const QVariant &val, ImageLayer *imageLayer)
1294 {
1295 QUndoCommand *command = nullptr;
1296
1297 switch (id) {
1298 case ImageSourceProperty: {
1299 const FilePath imageSource = val.value<FilePath>();
1300 const QColor &color = imageLayer->transparentColor();
1301 command = new ChangeImageLayerProperties(mMapDocument,
1302 imageLayer,
1303 color,
1304 imageSource.url);
1305 break;
1306 }
1307 case ColorProperty: {
1308 const QColor color = val.value<QColor>();
1309 const QUrl &imageSource = imageLayer->imageSource();
1310 command = new ChangeImageLayerProperties(mMapDocument,
1311 imageLayer,
1312 color,
1313 imageSource);
1314 break;
1315 }
1316 default:
1317 break;
1318 }
1319
1320 return command;
1321 }
1322
applyGroupLayerValueTo(PropertyId id,const QVariant & val,GroupLayer * groupLayer)1323 QUndoCommand *PropertyBrowser::applyGroupLayerValueTo(PropertyId id, const QVariant &val, GroupLayer *groupLayer)
1324 {
1325 Q_UNUSED(id)
1326 Q_UNUSED(val)
1327 Q_UNUSED(groupLayer)
1328
1329 return nullptr;
1330 }
1331
applyTilesetValue(PropertyId id,const QVariant & val)1332 void PropertyBrowser::applyTilesetValue(PropertyId id, const QVariant &val)
1333 {
1334 Tileset *tileset = static_cast<Tileset*>(mObject);
1335 QUndoStack *undoStack = mDocument->undoStack();
1336
1337 switch (id) {
1338 case FileNameProperty: {
1339 FilePath filePath = val.value<FilePath>();
1340 QString error;
1341 SharedTileset newTileset = TilesetManager::instance()->loadTileset(filePath.url.toLocalFile(), &error);
1342 if (!newTileset) {
1343 QMessageBox::critical(window(), tr("Error Reading Tileset"), error);
1344 return;
1345 }
1346
1347 int index = mMapDocument->map()->tilesets().indexOf(tileset->sharedPointer());
1348 if (index != -1)
1349 undoStack->push(new ReplaceTileset(mMapDocument, index, newTileset));
1350
1351 break;
1352 }
1353 case NameProperty:
1354 Q_ASSERT(mTilesetDocument);
1355 undoStack->push(new RenameTileset(mTilesetDocument, val.toString()));
1356 break;
1357 case ObjectAlignmentProperty: {
1358 Q_ASSERT(mTilesetDocument);
1359 auto objectAlignment = static_cast<Alignment>(val.toInt());
1360 undoStack->push(new ChangeTilesetObjectAlignment(mTilesetDocument,
1361 objectAlignment));
1362 break;
1363 }
1364 case TileOffsetProperty:
1365 Q_ASSERT(mTilesetDocument);
1366 undoStack->push(new ChangeTilesetTileOffset(mTilesetDocument,
1367 val.toPoint()));
1368 break;
1369 case OrientationProperty: {
1370 Q_ASSERT(mTilesetDocument);
1371 auto orientation = static_cast<Tileset::Orientation>(val.toInt());
1372 undoStack->push(new ChangeTilesetOrientation(mTilesetDocument,
1373 orientation));
1374 break;
1375 }
1376 case GridWidthProperty: {
1377 Q_ASSERT(mTilesetDocument);
1378 QSize gridSize = tileset->gridSize();
1379 gridSize.setWidth(val.toInt());
1380 undoStack->push(new ChangeTilesetGridSize(mTilesetDocument,
1381 gridSize));
1382 break;
1383 }
1384 case GridHeightProperty: {
1385 Q_ASSERT(mTilesetDocument);
1386 QSize gridSize = tileset->gridSize();
1387 gridSize.setHeight(val.toInt());
1388 undoStack->push(new ChangeTilesetGridSize(mTilesetDocument,
1389 gridSize));
1390 break;
1391 }
1392 case ColumnCountProperty:
1393 Q_ASSERT(mTilesetDocument);
1394 undoStack->push(new ChangeTilesetColumnCount(mTilesetDocument,
1395 val.toInt()));
1396 break;
1397 case BackgroundColorProperty:
1398 Q_ASSERT(mTilesetDocument);
1399 undoStack->push(new ChangeTilesetBackgroundColor(mTilesetDocument,
1400 val.value<QColor>()));
1401 break;
1402 case AllowFlipHorizontallyProperty:
1403 case AllowFlipVerticallyProperty:
1404 case AllowRotateProperty:
1405 case PreferUntransformedProperty: {
1406 Q_ASSERT(mTilesetDocument);
1407
1408 Tileset::TransformationFlag flag = Tileset::NoTransformation;
1409 switch (id) {
1410 case AllowFlipHorizontallyProperty:
1411 flag = Tileset::AllowFlipHorizontally;
1412 break;
1413 case AllowFlipVerticallyProperty:
1414 flag = Tileset::AllowFlipVertically;
1415 break;
1416 case AllowRotateProperty:
1417 flag = Tileset::AllowRotate;
1418 break;
1419 case PreferUntransformedProperty:
1420 flag = Tileset::PreferUntransformed;
1421 break;
1422 default:
1423 return;
1424 }
1425
1426 auto flags = tileset->transformationFlags();
1427
1428 #if QT_VERSION >= 0x050700
1429 flags.setFlag(flag, val.toBool());
1430 #else
1431 if (val.toBool())
1432 flags |= flag;
1433 else
1434 flags &= ~flag;
1435 #endif
1436
1437 undoStack->push(new ChangeTilesetTransformationFlags(mTilesetDocument, flags));
1438 break;
1439 }
1440 default:
1441 break;
1442 }
1443 }
1444
applyTileValue(PropertyId id,const QVariant & val)1445 void PropertyBrowser::applyTileValue(PropertyId id, const QVariant &val)
1446 {
1447 Q_ASSERT(mTilesetDocument);
1448
1449 Tile *tile = static_cast<Tile*>(mObject);
1450 QUndoStack *undoStack = mDocument->undoStack();
1451
1452 switch (id) {
1453 case TypeProperty:
1454 undoStack->push(new ChangeTileType(mTilesetDocument,
1455 mTilesetDocument->selectedTiles(),
1456 val.toString()));
1457 break;
1458 case TileProbabilityProperty:
1459 undoStack->push(new ChangeTileProbability(mTilesetDocument,
1460 mTilesetDocument->selectedTiles(),
1461 val.toFloat()));
1462 break;
1463 case ImageSourceProperty: {
1464 const FilePath filePath = val.value<FilePath>();
1465 undoStack->push(new ChangeTileImageSource(mTilesetDocument,
1466 tile, filePath.url));
1467 break;
1468 }
1469 default:
1470 break;
1471 }
1472 }
1473
applyWangSetValue(PropertyId id,const QVariant & val)1474 void PropertyBrowser::applyWangSetValue(PropertyId id, const QVariant &val)
1475 {
1476 Q_ASSERT(mTilesetDocument);
1477
1478 WangSet *wangSet = static_cast<WangSet*>(mObject);
1479
1480 switch (id) {
1481 case NameProperty:
1482 mDocument->undoStack()->push(new RenameWangSet(mTilesetDocument,
1483 wangSet,
1484 val.toString()));
1485 break;
1486 case WangSetTypeProperty: {
1487 auto type = static_cast<WangSet::Type>(val.toInt());
1488 mDocument->undoStack()->push(new ChangeWangSetType(mTilesetDocument,
1489 wangSet,
1490 type));
1491 break;
1492 }
1493 case ColorCountProperty:
1494 mDocument->undoStack()->push(new ChangeWangSetColorCount(mTilesetDocument,
1495 wangSet,
1496 val.toInt()));
1497 break;
1498 default:
1499 break;
1500 }
1501 }
1502
applyWangColorValue(PropertyId id,const QVariant & val)1503 void PropertyBrowser::applyWangColorValue(PropertyId id, const QVariant &val)
1504 {
1505 Q_ASSERT(mTilesetDocument);
1506
1507 WangColor *wangColor = static_cast<WangColor*>(mObject);
1508
1509 switch (id) {
1510 case NameProperty:
1511 mDocument->undoStack()->push(new ChangeWangColorName(mTilesetDocument,
1512 wangColor,
1513 val.toString()));
1514 break;
1515 case ColorProperty:
1516 mDocument->undoStack()->push(new ChangeWangColorColor(mTilesetDocument,
1517 wangColor,
1518 val.value<QColor>()));
1519 break;
1520 case WangColorProbabilityProperty:
1521 mDocument->undoStack()->push(new ChangeWangColorProbability(mTilesetDocument,
1522 wangColor,
1523 val.toDouble()));
1524 break;
1525 default:
1526 break;
1527 }
1528 }
1529
1530 /**
1531 * @warning This function does not add the property to the view.
1532 */
createProperty(PropertyId id,int type,const QString & name)1533 QtVariantProperty *PropertyBrowser::createProperty(PropertyId id, int type,
1534 const QString &name)
1535 {
1536 QtVariantProperty *property = mVariantManager->addProperty(type, name);
1537 if (!property) {
1538 // fall back to string property for unsupported property types
1539 property = mVariantManager->addProperty(QMetaType::QString, name);
1540 }
1541
1542 if (type == QMetaType::Bool)
1543 property->setAttribute(QLatin1String("textVisible"), false);
1544 if (type == QMetaType::QString && id == CustomProperty)
1545 property->setAttribute(QLatin1String("multiline"), true);
1546 if (type == QMetaType::Double && id == CustomProperty)
1547 property->setAttribute(QLatin1String("decimals"), 9);
1548
1549 mPropertyToId.insert(property, id);
1550
1551 if (id != CustomProperty) {
1552 Q_ASSERT(!mIdToProperty.contains(id));
1553 mIdToProperty.insert(id, property);
1554 } else {
1555 Q_ASSERT(!mNameToProperty.contains(name));
1556 mNameToProperty.insert(name, property);
1557 }
1558
1559 return property;
1560 }
1561
addProperty(PropertyId id,int type,const QString & name,QtProperty * parent)1562 QtVariantProperty *PropertyBrowser::addProperty(PropertyId id, int type,
1563 const QString &name,
1564 QtProperty *parent)
1565 {
1566 QtVariantProperty *property = createProperty(id, type, name);
1567
1568 parent->addSubProperty(property);
1569
1570 if (id == CustomProperty) {
1571 // Collapse custom color properties, to save space
1572 if (type == QMetaType::QColor)
1573 setExpanded(items(property).constFirst(), false);
1574
1575 if (mObject->isPartOfTileset())
1576 property->setEnabled(mTilesetDocument);
1577 }
1578
1579 return property;
1580 }
1581
createCustomProperty(const QString & name,const QVariant & value)1582 QtVariantProperty *PropertyBrowser::createCustomProperty(const QString &name, const QVariant &value)
1583 {
1584 // Determine the property preceding the new property, if any
1585 const QList<QtProperty *> properties = mCustomPropertiesGroup->subProperties();
1586 QtProperty *precedingProperty = nullptr;
1587 for (int i = 0; i < properties.size(); ++i) {
1588 if (properties.at(i)->propertyName() < name)
1589 precedingProperty = properties.at(i);
1590 else
1591 break;
1592 }
1593
1594 QScopedValueRollback<bool> updating(mUpdating, true);
1595 QtVariantProperty *property = createProperty(CustomProperty, value.userType(), name);
1596 property->setValue(value);
1597 mCustomPropertiesGroup->insertSubProperty(property, precedingProperty);
1598
1599 // Collapse custom color properties, to save space
1600 if (value.userType() == QMetaType::QColor)
1601 setExpanded(items(property).constFirst(), false);
1602
1603 return property;
1604 }
1605
deleteCustomProperty(QtVariantProperty * property)1606 void PropertyBrowser::deleteCustomProperty(QtVariantProperty *property)
1607 {
1608 Q_ASSERT(mNameToProperty.contains(property->propertyName()));
1609 mNameToProperty.remove(property->propertyName());
1610 delete property;
1611 }
1612
setCustomPropertyValue(QtVariantProperty * property,const QVariant & value)1613 void PropertyBrowser::setCustomPropertyValue(QtVariantProperty *property,
1614 const QVariant &value)
1615 {
1616 const QVariant displayValue = toDisplayValue(value);
1617
1618 if (displayValue.userType() != property->valueType()) {
1619 // Re-creating the property is necessary to change its type
1620 const QString name = property->propertyName();
1621 const bool wasCurrent = currentItem() && currentItem()->property() == property;
1622
1623 deleteCustomProperty(property);
1624 property = createCustomProperty(name, displayValue);
1625 updateCustomPropertyColor(name);
1626
1627 if (wasCurrent)
1628 setCurrentItem(items(property).constFirst());
1629 } else {
1630 QScopedValueRollback<bool> updating(mUpdating, true);
1631 property->setValue(displayValue);
1632 }
1633 }
1634
addProperties()1635 void PropertyBrowser::addProperties()
1636 {
1637 if (!mObject)
1638 return;
1639
1640 QScopedValueRollback<bool> updating(mUpdating, true);
1641 SetFixedResizeMode resizeMode(this);
1642
1643 // Add the built-in properties for each object type
1644 switch (mObject->typeId()) {
1645 case Object::MapType: addMapProperties(); break;
1646 case Object::MapObjectType: addMapObjectProperties(); break;
1647 case Object::LayerType:
1648 switch (static_cast<Layer*>(mObject)->layerType()) {
1649 case Layer::TileLayerType: addTileLayerProperties(); break;
1650 case Layer::ObjectGroupType: addObjectGroupProperties(); break;
1651 case Layer::ImageLayerType: addImageLayerProperties(); break;
1652 case Layer::GroupLayerType: addGroupLayerProperties(); break;
1653 }
1654 break;
1655 case Object::TilesetType: addTilesetProperties(); break;
1656 case Object::TileType: addTileProperties(); break;
1657 case Object::WangSetType: addWangSetProperties(); break;
1658 case Object::WangColorType: addWangColorProperties(); break;
1659 case Object::ObjectTemplateType: break;
1660 }
1661
1662 // Make sure the color and font properties are collapsed, to save space
1663 if (QtProperty *colorProperty = mIdToProperty.value(ColorProperty))
1664 setExpanded(items(colorProperty).constFirst(), false);
1665 if (QtProperty *colorProperty = mIdToProperty.value(BackgroundColorProperty))
1666 setExpanded(items(colorProperty).constFirst(), false);
1667 if (QtProperty *fontProperty = mIdToProperty.value(FontProperty))
1668 setExpanded(items(fontProperty).constFirst(), false);
1669 if (QtProperty *tintColorProperty = mIdToProperty.value(TintColorProperty))
1670 setExpanded(items(tintColorProperty).constFirst(), false);
1671
1672 // Add a node for the custom properties
1673 mCustomPropertiesGroup = mGroupManager->addProperty(tr("Custom Properties"));
1674 addProperty(mCustomPropertiesGroup);
1675
1676 updateProperties();
1677 updateCustomProperties();
1678 }
1679
removeProperties()1680 void PropertyBrowser::removeProperties()
1681 {
1682 SetFixedResizeMode resizeMode(this);
1683
1684 mVariantManager->clear();
1685 mGroupManager->clear();
1686 mPropertyToId.clear();
1687 mIdToProperty.clear();
1688 mNameToProperty.clear();
1689 mCustomPropertiesGroup = nullptr;
1690 }
1691
updateProperties()1692 void PropertyBrowser::updateProperties()
1693 {
1694 Q_ASSERT(mObject);
1695
1696 QScopedValueRollback<bool> updating(mUpdating, true);
1697
1698 switch (mObject->typeId()) {
1699 case Object::MapType: {
1700 const Map *map = static_cast<const Map*>(mObject);
1701 mIdToProperty[WidthProperty]->setValue(map->width());
1702 mIdToProperty[HeightProperty]->setValue(map->height());
1703 mIdToProperty[TileWidthProperty]->setValue(map->tileWidth());
1704 mIdToProperty[TileHeightProperty]->setValue(map->tileHeight());
1705 mIdToProperty[InfiniteProperty]->setValue(map->infinite());
1706 mIdToProperty[OrientationProperty]->setValue(map->orientation() - 1);
1707 mIdToProperty[HexSideLengthProperty]->setValue(map->hexSideLength());
1708 mIdToProperty[StaggerAxisProperty]->setValue(map->staggerAxis());
1709 mIdToProperty[StaggerIndexProperty]->setValue(map->staggerIndex());
1710 mIdToProperty[LayerFormatProperty]->setValue(mLayerFormatValues.indexOf(map->layerDataFormat()));
1711 mIdToProperty[CompressionLevelProperty]->setValue(map->compressionLevel());
1712 mIdToProperty[RenderOrderProperty]->setValue(map->renderOrder());
1713 mIdToProperty[BackgroundColorProperty]->setValue(map->backgroundColor());
1714 mIdToProperty[ChunkWidthProperty]->setValue(map->chunkSize().width());
1715 mIdToProperty[ChunkHeightProperty]->setValue(map->chunkSize().height());
1716 break;
1717 }
1718 case Object::MapObjectType: {
1719 const MapObject *mapObject = static_cast<const MapObject*>(mObject);
1720 const int flags = mapObjectFlags(mapObject);
1721
1722 if (mMapObjectFlags != flags) {
1723 removeProperties();
1724 addProperties();
1725 return;
1726 }
1727
1728 const QString &type = mapObject->effectiveType();
1729 const auto typeColorGroup = mapObject->type().isEmpty() ? QPalette::Disabled
1730 : QPalette::Active;
1731
1732 FilePath templateFilePath;
1733 if (auto objectTemplate = mapObject->objectTemplate())
1734 templateFilePath.url = QUrl::fromLocalFile(objectTemplate->fileName());
1735
1736 mIdToProperty[IdProperty]->setValue(mapObject->id());
1737 mIdToProperty[TemplateProperty]->setValue(QVariant::fromValue(templateFilePath));
1738 mIdToProperty[NameProperty]->setValue(mapObject->name());
1739 mIdToProperty[TypeProperty]->setValue(type);
1740 mIdToProperty[TypeProperty]->setValueColor(palette().color(typeColorGroup, QPalette::WindowText));
1741 if (auto visibleProperty = mIdToProperty[VisibleProperty])
1742 visibleProperty->setValue(mapObject->isVisible());
1743 mIdToProperty[XProperty]->setValue(mapObject->x());
1744 mIdToProperty[YProperty]->setValue(mapObject->y());
1745
1746 if (flags & ObjectHasDimensions) {
1747 mIdToProperty[WidthProperty]->setValue(mapObject->width());
1748 mIdToProperty[HeightProperty]->setValue(mapObject->height());
1749 }
1750
1751 mIdToProperty[RotationProperty]->setValue(mapObject->rotation());
1752
1753 if (flags & ObjectHasTile) {
1754 int flippingFlags = 0;
1755 if (mapObject->cell().flippedHorizontally())
1756 flippingFlags |= 1;
1757 if (mapObject->cell().flippedVertically())
1758 flippingFlags |= 2;
1759 mIdToProperty[FlippingProperty]->setValue(flippingFlags);
1760 }
1761
1762 if (flags & ObjectIsText) {
1763 const auto& textData = mapObject->textData();
1764 mIdToProperty[TextProperty]->setValue(textData.text);
1765 mIdToProperty[FontProperty]->setValue(textData.font);
1766 mIdToProperty[TextAlignmentProperty]->setValue(QVariant::fromValue(textData.alignment));
1767 mIdToProperty[WordWrapProperty]->setValue(textData.wordWrap);
1768 mIdToProperty[ColorProperty]->setValue(textData.color);
1769 }
1770 break;
1771 }
1772 case Object::LayerType: {
1773 const Layer *layer = static_cast<const Layer*>(mObject);
1774
1775 mIdToProperty[IdProperty]->setValue(layer->id());
1776 mIdToProperty[NameProperty]->setValue(layer->name());
1777 mIdToProperty[VisibleProperty]->setValue(layer->isVisible());
1778 mIdToProperty[LockedProperty]->setValue(layer->isLocked());
1779 mIdToProperty[OpacityProperty]->setValue(layer->opacity());
1780 mIdToProperty[TintColorProperty]->setValue(layer->tintColor());
1781 mIdToProperty[OffsetXProperty]->setValue(layer->offset().x());
1782 mIdToProperty[OffsetYProperty]->setValue(layer->offset().y());
1783 mIdToProperty[ParallaxFactorProperty]->setValue(layer->parallaxFactor());
1784
1785 switch (layer->layerType()) {
1786 case Layer::TileLayerType:
1787 break;
1788 case Layer::ObjectGroupType: {
1789 const ObjectGroup *objectGroup = static_cast<const ObjectGroup*>(layer);
1790 const QColor color = objectGroup->color();
1791 mIdToProperty[ColorProperty]->setValue(color);
1792 mIdToProperty[DrawOrderProperty]->setValue(objectGroup->drawOrder());
1793 break;
1794 }
1795 case Layer::ImageLayerType: {
1796 const ImageLayer *imageLayer = static_cast<const ImageLayer*>(layer);
1797 mIdToProperty[ImageSourceProperty]->setValue(QVariant::fromValue(FilePath { imageLayer->imageSource() }));
1798 mIdToProperty[ColorProperty]->setValue(imageLayer->transparentColor());
1799 break;
1800 }
1801 case Layer::GroupLayerType:
1802 break;
1803 }
1804 break;
1805 }
1806 case Object::TilesetType: {
1807 Tileset *tileset = static_cast<Tileset*>(mObject);
1808
1809 if (QtVariantProperty *fileNameProperty = mIdToProperty.value(FileNameProperty))
1810 fileNameProperty->setValue(QVariant::fromValue(FilePath { QUrl::fromLocalFile(tileset->fileName()) }));
1811
1812 mIdToProperty[BackgroundColorProperty]->setValue(tileset->backgroundColor());
1813
1814 mIdToProperty[NameProperty]->setValue(tileset->name());
1815 mIdToProperty[ObjectAlignmentProperty]->setValue(tileset->objectAlignment());
1816 mIdToProperty[TileOffsetProperty]->setValue(tileset->tileOffset());
1817 mIdToProperty[OrientationProperty]->setValue(tileset->orientation());
1818 mIdToProperty[GridWidthProperty]->setValue(tileset->gridSize().width());
1819 mIdToProperty[GridHeightProperty]->setValue(tileset->gridSize().height());
1820 mIdToProperty[ColumnCountProperty]->setValue(tileset->columnCount());
1821 mIdToProperty[ColumnCountProperty]->setEnabled(mTilesetDocument && tileset->isCollection());
1822
1823 if (!tileset->isCollection()) {
1824 mIdToProperty[TilesetImageParametersProperty]->setValue(QVariant::fromValue(mTilesetDocument));
1825 mIdToProperty[ImageSourceProperty]->setValue(tileset->imageSource().toString(QUrl::PreferLocalFile));
1826 mIdToProperty[TileWidthProperty]->setValue(tileset->tileWidth());
1827 mIdToProperty[TileHeightProperty]->setValue(tileset->tileHeight());
1828 mIdToProperty[MarginProperty]->setValue(tileset->margin());
1829 mIdToProperty[SpacingProperty]->setValue(tileset->tileSpacing());
1830 mIdToProperty[ColorProperty]->setValue(tileset->transparentColor());
1831 }
1832
1833 const auto flags = tileset->transformationFlags();
1834 mIdToProperty[AllowFlipHorizontallyProperty]->setValue(flags.testFlag(Tileset::AllowFlipHorizontally));
1835 mIdToProperty[AllowFlipVerticallyProperty]->setValue(flags.testFlag(Tileset::AllowFlipVertically));
1836 mIdToProperty[AllowRotateProperty]->setValue(flags.testFlag(Tileset::AllowRotate));
1837 mIdToProperty[PreferUntransformedProperty]->setValue(flags.testFlag(Tileset::PreferUntransformed));
1838 break;
1839 }
1840 case Object::TileType: {
1841 const Tile *tile = static_cast<const Tile*>(mObject);
1842 const QSize tileSize = tile->size();
1843 mIdToProperty[IdProperty]->setValue(tile->id());
1844 mIdToProperty[TypeProperty]->setValue(tile->type());
1845 mIdToProperty[WidthProperty]->setValue(tileSize.width());
1846 mIdToProperty[HeightProperty]->setValue(tileSize.height());
1847 mIdToProperty[TileProbabilityProperty]->setValue(tile->probability());
1848 if (QtVariantProperty *imageSourceProperty = mIdToProperty.value(ImageSourceProperty))
1849 imageSourceProperty->setValue(QVariant::fromValue(FilePath { tile->imageSource() }));
1850 break;
1851 }
1852 case Object::WangSetType: {
1853 const WangSet *wangSet = static_cast<const WangSet*>(mObject);
1854 mIdToProperty[NameProperty]->setValue(wangSet->name());
1855 mIdToProperty[WangSetTypeProperty]->setValue(wangSet->type());
1856 mIdToProperty[ColorCountProperty]->setValue(wangSet->colorCount());
1857 break;
1858 }
1859 case Object::WangColorType: {
1860 const WangColor *wangColor = static_cast<const WangColor*>(mObject);
1861 mIdToProperty[NameProperty]->setValue(wangColor->name());
1862 mIdToProperty[ColorProperty]->setValue(wangColor->color());
1863 mIdToProperty[WangColorProbabilityProperty]->setValue(wangColor->probability());
1864 break;
1865 }
1866 case Object::ObjectTemplateType:
1867 break;
1868 }
1869 }
1870
updateCustomProperties()1871 void PropertyBrowser::updateCustomProperties()
1872 {
1873 if (!mObject)
1874 return;
1875
1876 QScopedValueRollback<bool> updating(mUpdating, true);
1877 SetFixedResizeMode resizeMode(this);
1878
1879 qDeleteAll(mNameToProperty);
1880 mNameToProperty.clear();
1881
1882 mCombinedProperties = mObject->properties();
1883 // Add properties from selected objects which mObject does not contain to mCombinedProperties.
1884 const auto currentObjects = mDocument->currentObjects();
1885 for (Object *obj : currentObjects) {
1886 if (obj == mObject)
1887 continue;
1888
1889 QMapIterator<QString,QVariant> it(obj->properties());
1890
1891 while (it.hasNext()) {
1892 it.next();
1893 if (!mCombinedProperties.contains(it.key()))
1894 mCombinedProperties.insert(it.key(), QString());
1895 }
1896 }
1897
1898 QString objectType;
1899
1900 switch (mObject->typeId()) {
1901 case Object::TileType:
1902 objectType = static_cast<Tile*>(mObject)->type();
1903 break;
1904 case Object::MapObjectType: {
1905 auto mapObject = static_cast<MapObject*>(mObject);
1906 objectType = mapObject->type();
1907
1908 // Inherit properties from the template
1909 if (const MapObject *templateObject = mapObject->templateObject()) {
1910 QMapIterator<QString,QVariant> it(templateObject->properties());
1911 while (it.hasNext()) {
1912 it.next();
1913 if (!mCombinedProperties.contains(it.key()))
1914 mCombinedProperties.insert(it.key(), it.value());
1915 }
1916 }
1917
1918 if (Tile *tile = mapObject->cell().tile()) {
1919 if (objectType.isEmpty())
1920 objectType = tile->type();
1921
1922 // Inherit properties from the tile
1923 QMapIterator<QString,QVariant> it(tile->properties());
1924 while (it.hasNext()) {
1925 it.next();
1926 if (!mCombinedProperties.contains(it.key()))
1927 mCombinedProperties.insert(it.key(), it.value());
1928 }
1929 }
1930 break;
1931 }
1932 case Object::LayerType:
1933 case Object::MapType:
1934 case Object::TilesetType:
1935 case Object::WangSetType:
1936 case Object::WangColorType:
1937 case Object::ObjectTemplateType:
1938 break;
1939 }
1940
1941 if (!objectType.isEmpty()) {
1942 // Inherit properties from the object type
1943 for (const ObjectType &type : Object::objectTypes()) {
1944 if (type.name == objectType) {
1945 QMapIterator<QString,QVariant> it(type.defaultProperties);
1946 while (it.hasNext()) {
1947 it.next();
1948 if (!mCombinedProperties.contains(it.key()))
1949 mCombinedProperties.insert(it.key(), it.value());
1950 }
1951 }
1952 }
1953 }
1954
1955 QMapIterator<QString,QVariant> it(mCombinedProperties);
1956
1957 while (it.hasNext()) {
1958 it.next();
1959
1960 const QVariant displayValue = toDisplayValue(it.value());
1961
1962 QtVariantProperty *property = addProperty(CustomProperty,
1963 displayValue.userType(),
1964 it.key(),
1965 mCustomPropertiesGroup);
1966 property->setValue(displayValue);
1967 updateCustomPropertyColor(it.key());
1968 }
1969 }
1970
1971 // If there are other objects selected check if their properties are equal. If not give them a gray color.
updateCustomPropertyColor(const QString & name)1972 void PropertyBrowser::updateCustomPropertyColor(const QString &name)
1973 {
1974 QtVariantProperty *property = mNameToProperty.value(name);
1975 if (!property)
1976 return;
1977 if (!property->isEnabled())
1978 return;
1979
1980 QString propertyName = property->propertyName();
1981 QString propertyValue = property->valueText();
1982
1983 const auto &objects = mDocument->currentObjects();
1984
1985 QColor textColor = palette().color(QPalette::Active, QPalette::WindowText);
1986 QColor disabledTextColor = palette().color(QPalette::Disabled, QPalette::WindowText);
1987
1988 // If one of the objects doesn't have this property then gray out the name and value.
1989 for (Object *obj : objects) {
1990 if (!obj->hasProperty(propertyName)) {
1991 property->setNameColor(disabledTextColor);
1992 property->setValueColor(disabledTextColor);
1993 return;
1994 }
1995 }
1996
1997 // If one of the objects doesn't have the same property value then gray out the value.
1998 for (Object *obj : objects) {
1999 if (obj == mObject)
2000 continue;
2001 if (obj->property(propertyName) != propertyValue) {
2002 property->setNameColor(textColor);
2003 property->setValueColor(disabledTextColor);
2004 return;
2005 }
2006 }
2007
2008 property->setNameColor(textColor);
2009 property->setValueColor(textColor);
2010 }
2011
toDisplayValue(const QVariant & value) const2012 QVariant PropertyBrowser::toDisplayValue(const QVariant &value) const
2013 {
2014 if (value.userType() == objectRefTypeId())
2015 return QVariant::fromValue(DisplayObjectRef { value.value<ObjectRef>(), mMapDocument });
2016
2017 return value;
2018 }
2019
fromDisplayValue(const QVariant & value) const2020 QVariant PropertyBrowser::fromDisplayValue(const QVariant &value) const
2021 {
2022 if (value.userType() == VariantPropertyManager::displayObjectRefTypeId())
2023 return QVariant::fromValue(value.value<DisplayObjectRef>().ref);
2024
2025 return value;
2026 }
2027
retranslateUi()2028 void PropertyBrowser::retranslateUi()
2029 {
2030 mStaggerAxisNames.clear();
2031 mStaggerAxisNames.append(tr("X"));
2032 mStaggerAxisNames.append(tr("Y"));
2033
2034 mStaggerIndexNames.clear();
2035 mStaggerIndexNames.append(tr("Odd"));
2036 mStaggerIndexNames.append(tr("Even"));
2037
2038 mOrientationNames.clear();
2039 mOrientationNames.append(QCoreApplication::translate("Tiled::NewMapDialog", "Orthogonal"));
2040 mOrientationNames.append(QCoreApplication::translate("Tiled::NewMapDialog", "Isometric"));
2041 mOrientationNames.append(QCoreApplication::translate("Tiled::NewMapDialog", "Isometric (Staggered)"));
2042 mOrientationNames.append(QCoreApplication::translate("Tiled::NewMapDialog", "Hexagonal (Staggered)"));
2043
2044 mTilesetOrientationNames.clear();
2045 mTilesetOrientationNames.append(mOrientationNames.at(0));
2046 mTilesetOrientationNames.append(mOrientationNames.at(1));
2047
2048 mLayerFormatNames.clear();
2049 mLayerFormatValues.clear();
2050
2051 mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "XML (deprecated)"));
2052 mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (uncompressed)"));
2053 mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (gzip compressed)"));
2054 mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (zlib compressed)"));
2055 #ifdef TILED_ZSTD_SUPPORT
2056 mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (Zstandard compressed)"));
2057 #endif
2058 mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "CSV"));
2059
2060 mLayerFormatValues.append(Map::XML);
2061 mLayerFormatValues.append(Map::Base64);
2062 mLayerFormatValues.append(Map::Base64Gzip);
2063 mLayerFormatValues.append(Map::Base64Zlib);
2064 #ifdef TILED_ZSTD_SUPPORT
2065 mLayerFormatValues.append(Map::Base64Zstandard);
2066 #endif
2067 mLayerFormatValues.append(Map::CSV);
2068
2069 mRenderOrderNames.clear();
2070 mRenderOrderNames.append(QCoreApplication::translate("PreferencesDialog", "Right Down"));
2071 mRenderOrderNames.append(QCoreApplication::translate("PreferencesDialog", "Right Up"));
2072 mRenderOrderNames.append(QCoreApplication::translate("PreferencesDialog", "Left Down"));
2073 mRenderOrderNames.append(QCoreApplication::translate("PreferencesDialog", "Left Up"));
2074
2075 mAlignmentNames.clear();
2076 mAlignmentNames.append(tr("Unspecified"));
2077 mAlignmentNames.append(tr("Top Left"));
2078 mAlignmentNames.append(tr("Top"));
2079 mAlignmentNames.append(tr("Top Right"));
2080 mAlignmentNames.append(tr("Left"));
2081 mAlignmentNames.append(tr("Center"));
2082 mAlignmentNames.append(tr("Right"));
2083 mAlignmentNames.append(tr("Bottom Left"));
2084 mAlignmentNames.append(tr("Bottom"));
2085 mAlignmentNames.append(tr("Bottom Right"));
2086
2087 mFlippingFlagNames.clear();
2088 mFlippingFlagNames.append(tr("Horizontal"));
2089 mFlippingFlagNames.append(tr("Vertical"));
2090
2091 mDrawOrderNames.clear();
2092 mDrawOrderNames.append(tr("Top Down"));
2093 mDrawOrderNames.append(tr("Manual"));
2094
2095 mWangSetTypeNames.clear();
2096 mWangSetTypeNames.append(tr("Corner"));
2097 mWangSetTypeNames.append(tr("Edge"));
2098 mWangSetTypeNames.append(tr("Mixed"));
2099
2100 removeProperties();
2101 addProperties();
2102 }
2103
2104 } // namespace Tiled
2105
2106 #include "moc_propertybrowser.cpp"
2107