QGIS API Documentation 4.1.0-Master (3b8ef1f72a3)
Loading...
Searching...
No Matches
qgsstylemodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsstylemodel.cpp
3 ---------------
4 begin : September 2018
5 copyright : (C) 2018 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsstylemodel.h"
17
19#include "qgsapplication.h"
22#include "qgsimagecache.h"
23#include "qgsmaterialregistry.h"
24#include "qgsstyle.h"
25#include "qgssvgcache.h"
26#include "qgssymbollayerutils.h"
27
28#include <QBuffer>
29#include <QDir>
30#include <QIcon>
31#include <QString>
32
33#include "moc_qgsstylemodel.cpp"
34
35using namespace Qt::StringLiterals;
36
37const double ICON_PADDING_FACTOR = 0.16;
38
41
42QgsAbstractStyleEntityIconGenerator *QgsStyleModel::sIconGenerator = nullptr;
43
44//
45// QgsAbstractStyleEntityIconGenerator
46//
47
51
53{
54 mIconSizes = sizes;
55}
56
58{
59 return mIconSizes;
60}
61
62void QgsAbstractStyleEntityIconGenerator::setTargetScreenProperties( const QSet<QgsScreenProperties> &properties )
63{
64 mTargetScreenProperties = properties;
65}
66
68{
69 return mTargetScreenProperties;
70}
71
72
73//
74// QgsStyleModel
75//
76
78 : QAbstractItemModel( parent )
79 , mStyle( style )
80{
81 Q_ASSERT( mStyle );
82
83 if ( mStyle->isInitialized() )
84 {
85 initStyleModel();
86 }
87 else
88 {
89 // lazy initialized style
90 connect( mStyle, &QgsStyle::initialized, this, &QgsStyleModel::initStyleModel );
91 }
92}
93
94void QgsStyleModel::initStyleModel()
95{
96 beginResetModel();
97 for ( QgsStyle::StyleEntity entity : ENTITIES )
98 {
99 mEntityNames.insert( entity, mStyle->allNames( entity ) );
100 }
101 endResetModel();
102
103 // ensure we always generate icons using default screen properties
104 // in addition to actual target screen properties (ie device pixel ratio of 1, 96 dpi)
105 mTargetScreenProperties.insert( QgsScreenProperties() );
106
107 connect( mStyle, &QgsStyle::entityAdded, this, &QgsStyleModel::onEntityAdded );
108 connect( mStyle, &QgsStyle::entityRemoved, this, &QgsStyleModel::onEntityRemoved );
109 connect( mStyle, &QgsStyle::entityRenamed, this, &QgsStyleModel::onEntityRename );
110 connect( mStyle, &QgsStyle::entityChanged, this, &QgsStyleModel::onEntityChanged );
111 connect( mStyle, &QgsStyle::favoritedChanged, this, &QgsStyleModel::onFavoriteChanged );
112 connect( mStyle, &QgsStyle::entityTagsChanged, this, &QgsStyleModel::onTagsChanged );
113 connect( mStyle, &QgsStyle::rebuildIconPreviews, this, &QgsStyleModel::rebuildSymbolIcons );
114
115 // when a remote svg or image has been fetched, update the model's decorations.
116 // this is required if a symbol utilizes remote svgs, and the current icons
117 // have been generated using the temporary "downloading" svg. In this case
118 // we require the preview to be regenerated to use the correct fetched
119 // svg
120 connect( QgsApplication::svgCache(), &QgsSvgCache::remoteSvgFetched, this, &QgsStyleModel::rebuildSymbolIcons );
121 connect( QgsApplication::imageCache(), &QgsImageCache::remoteImageFetched, this, &QgsStyleModel::rebuildSymbolIcons );
122
123 if ( sIconGenerator )
124 connect( sIconGenerator, &QgsAbstractStyleEntityIconGenerator::iconGenerated, this, &QgsStyleModel::iconGenerated, Qt::QueuedConnection );
125}
126
127QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
128{
129 if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) )
130 return QVariant();
131
132
133 QgsStyle::StyleEntity entityType = entityTypeFromRow( index.row() );
134
135 QString name;
136 switch ( entityType )
137 {
140 break;
141
142 default:
143 name = mEntityNames[entityType].value( index.row() - offsetForEntity( entityType ) );
144 break;
145 }
146
147 switch ( role )
148 {
149 case Qt::DisplayRole:
150 case Qt::ToolTipRole:
151 case Qt::EditRole:
152 {
153 switch ( index.column() )
154 {
155 case Name:
156 {
157 const QStringList tags = mStyle->tagsOfSymbol( entityType, name );
158
159 if ( role == Qt::ToolTipRole )
160 {
161 QString tooltip = u"<h3>%1</h3><p><i>%2</i>"_s.arg( name, tags.count() > 0 ? tags.join( ", "_L1 ) : tr( "Not tagged" ) );
162
163 // generate tooltips for the largest device pixel ratio for all attached screens
164 QgsScreenProperties maxDevicePixelRatioScreen;
165 for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
166 {
167 if ( !maxDevicePixelRatioScreen.isValid() || it->devicePixelRatio() > maxDevicePixelRatioScreen.devicePixelRatio() )
168 maxDevicePixelRatioScreen = *it;
169 }
170
171 switch ( entityType )
172 {
174 {
175 // create very large preview image
176 std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) );
177 if ( symbol )
178 {
179 int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
180 int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
181 QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( width, height ), height / 20, nullptr, false, mExpressionContext.get(), nullptr, maxDevicePixelRatioScreen );
182 QByteArray data;
183 QBuffer buffer( &data );
184 pm.save( &buffer, "PNG", 100 );
185 tooltip += u"<p><img src='data:image/png;base64, %3' width=\"%4\">"_s.arg( QString( data.toBase64() ) ).arg( width );
186 }
187 break;
188 }
189
191 {
192 int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
193 int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
194 const QgsTextFormat format = mStyle->textFormat( name );
195 QPixmap pm = QgsTextFormat::textFormatPreviewPixmap( format, QSize( width, height ), QString(), height / 20, maxDevicePixelRatioScreen );
196 QByteArray data;
197 QBuffer buffer( &data );
198 pm.save( &buffer, "PNG", 100 );
199 tooltip += u"<p><img src='data:image/png;base64, %3' width=\"%4\">"_s.arg( QString( data.toBase64() ) ).arg( width );
200 break;
201 }
202
204 {
205 int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
206 int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
207 const QgsPalLayerSettings settings = mStyle->labelSettings( name );
208 QPixmap pm = QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( width, height ), QString(), height / 20, maxDevicePixelRatioScreen );
209 QByteArray data;
210 QBuffer buffer( &data );
211 pm.save( &buffer, "PNG", 100 );
212 tooltip += u"<p><img src='data:image/png;base64, %3' width=\"%4\">"_s.arg( QString( data.toBase64() ) ).arg( width );
213 break;
214 }
215
217 {
218 int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
219 int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
220
221 const QgsLegendPatchShape shape = mStyle->legendPatchShape( name );
222 if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
223 {
224 QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( width, height ), height / 20, nullptr, false, nullptr, &shape, maxDevicePixelRatioScreen );
225 QByteArray data;
226 QBuffer buffer( &data );
227 pm.save( &buffer, "PNG", 100 );
228 tooltip += u"<p><img src='data:image/png;base64, %3' width=\"%4\">"_s.arg( QString( data.toBase64() ) ).arg( width );
229 }
230 break;
231 }
232
238 break;
239 }
240 return tooltip;
241 }
242 else
243 {
244 return name;
245 }
246 }
247 case Tags:
248 return mStyle->tagsOfSymbol( entityType, name ).join( ", "_L1 );
249
250 default:
251 break;
252 }
253 return QVariant();
254 }
255
256 case Qt::DecorationRole:
257 {
258 // Generate icons at all additional sizes specified for the model.
259 // This allows the model to have size responsive icons.
260
261 if ( !mExpressionContext )
262 {
263 // build the expression context once, and keep it around. Usually this is a no-no, but in this
264 // case we want to avoid creating potentially thousands of contexts one-by-one (usually one context
265 // is created for a batch of multiple evalutions like this), and we only use a very minimal context
266 // anyway...
267 mExpressionContext = std::make_unique< QgsExpressionContext >();
268 mExpressionContext->appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
269 }
270
271 switch ( index.column() )
272 {
273 case Name:
274 switch ( entityType )
275 {
277 {
278 // use cached icon if possible
279 QIcon icon = mIconCache[entityType].value( name );
280 if ( !icon.isNull() )
281 return icon;
282
283 std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) );
284 if ( symbol )
285 {
286 for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
287 {
288 if ( mAdditionalSizes.isEmpty() )
289 icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), nullptr, *it ) );
290
291 for ( const QSize &s : mAdditionalSizes )
292 {
293 icon.addPixmap(
294 QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), nullptr, *it )
295 );
296 }
297 }
298 }
299 mIconCache[entityType].insert( name, icon );
300 return icon;
301 }
303 {
304 // use cached icon if possible
305 QIcon icon = mIconCache[entityType].value( name );
306 if ( !icon.isNull() )
307 return icon;
308
309 std::unique_ptr< QgsColorRamp > ramp( mStyle->colorRamp( name ) );
310 if ( ramp )
311 {
312 if ( mAdditionalSizes.isEmpty() )
313 icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), QSize( 24, 24 ), 1 ) );
314 for ( const QSize &s : mAdditionalSizes )
315 {
316 icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
317 }
318 }
319 mIconCache[entityType].insert( name, icon );
320 return icon;
321 }
322
324 {
325 // use cached icon if possible
326 QIcon icon = mIconCache[entityType].value( name );
327 if ( !icon.isNull() )
328 return icon;
329
330 const QgsTextFormat format( mStyle->textFormat( name ) );
331 for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
332 {
333 if ( mAdditionalSizes.isEmpty() )
334 icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, QSize( 24, 24 ), QString(), 1, *it ) );
335 for ( const QSize &s : mAdditionalSizes )
336 {
337 icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), *it ) );
338 }
339 }
340 mIconCache[entityType].insert( name, icon );
341 return icon;
342 }
343
345 {
346 // use cached icon if possible
347 QIcon icon = mIconCache[entityType].value( name );
348 if ( !icon.isNull() )
349 return icon;
350
351 const QgsPalLayerSettings settings( mStyle->labelSettings( name ) );
352 for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
353 {
354 if ( mAdditionalSizes.isEmpty() )
355 icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( 24, 24 ), QString(), 1, *it ) );
356 for ( const QSize &s : mAdditionalSizes )
357 {
358 icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), *it ) );
359 }
360 }
361 mIconCache[entityType].insert( name, icon );
362 return icon;
363 }
364
366 {
367 // use cached icon if possible
368 QIcon icon = mIconCache[entityType].value( name );
369 if ( !icon.isNull() )
370 return icon;
371
372 const QgsLegendPatchShape shape = mStyle->legendPatchShape( name );
373 if ( !shape.isNull() )
374 {
375 for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
376 {
377 if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
378 {
379 if ( mAdditionalSizes.isEmpty() )
380 icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), &shape, *it ) );
381
382 for ( const QSize &s : mAdditionalSizes )
383 {
384 icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), &shape, *it ) );
385 }
386 }
387 }
388 }
389 mIconCache[entityType].insert( name, icon );
390 return icon;
391 }
392
395 {
396 // hack for now -- we just use a generic "3d icon" svg file.
397 // TODO - render proper thumbnails
398
399 // use cached icon if possible
400 QIcon icon = mIconCache[entityType].value( name );
401 if ( !icon.isNull() )
402 return icon;
403
404 if ( entityType == QgsStyle::Symbol3DEntity )
405 {
406 if ( sIconGenerator && !mPending3dSymbolIcons.contains( name ) )
407 {
408 mPending3dSymbolIcons.insert( name );
409 sIconGenerator->generateIcon( mStyle, entityType, name );
410 }
411 }
412 else if ( entityType == QgsStyle::MaterialSettingsEntity )
413 {
414 if ( sIconGenerator && !mPendingMaterialSettingsIcons.contains( name ) )
415 {
416 mPendingMaterialSettingsIcons.insert( name );
417 sIconGenerator->generateIcon( mStyle, entityType, name );
418 }
419 }
420
421 // TODO - use hourglass icon
422 if ( mAdditionalSizes.isEmpty() )
423 icon.addFile( QgsApplication::defaultThemePath() + QDir::separator() + u"3d.svg"_s, QSize( 24, 24 ) );
424 for ( const QSize &s : mAdditionalSizes )
425 {
426 icon.addFile( QgsApplication::defaultThemePath() + QDir::separator() + u"3d.svg"_s, s );
427 }
428 mIconCache[entityType].insert( name, icon );
429 return icon;
430 }
431
434 return QVariant();
435 }
436 break;
437
438 case Tags:
439 return QVariant();
440
441 default:
442 break;
443 }
444 return QVariant();
445 }
446
447 case static_cast< int >( CustomRole::Type ):
448 return entityType;
449
450 case static_cast< int >( CustomRole::Tag ):
451 return mStyle->tagsOfSymbol( entityType, name );
452
453 case static_cast< int >( CustomRole::IsFavorite ):
454 return mStyle->isFavorite( entityType, name );
455
456 case static_cast< int >( CustomRole::SymbolType ):
457 {
458 switch ( entityType )
459 {
461 {
462 const QgsSymbol *symbol = mStyle->symbolRef( name );
463 return symbol ? static_cast< int >( symbol->type() ) : QVariant();
464 }
465
467 return static_cast< int >( mStyle->legendPatchShapeSymbolType( name ) );
468
476 return QVariant();
477 }
478 return QVariant();
479 }
480
481 case static_cast< int >( CustomRole::LayerType ):
482 {
483 switch ( entityType )
484 {
486 return static_cast< int >( mStyle->labelSettingsLayerType( name ) );
487
496 return QVariant();
497 }
498 return QVariant();
499 }
500
501 case static_cast< int >( CustomRole::CompatibleGeometryTypes ):
502 {
503 switch ( entityType )
504 {
506 {
507 QVariantList res;
508 const QList< Qgis::GeometryType > types = mStyle->symbol3DCompatibleGeometryTypes( name );
509 res.reserve( types.size() );
510 for ( Qgis::GeometryType type : types )
511 {
512 res << static_cast< int >( type );
513 }
514 return res;
515 }
516
525 return QVariant();
526 }
527 return QVariant();
528 }
529
530 case static_cast< int >( CustomRole::MaterialType ):
531 {
532 switch ( entityType )
533 {
535 {
536 std::unique_ptr< QgsAbstractMaterialSettings > settings = mStyle->materialSettings( name );
537 return settings ? settings->type() : QVariant();
538 }
539
548 return QVariant();
549 }
550 return QVariant();
551 }
552
553 case static_cast< int >( CustomRole::EntityName ):
554 return name;
555
556 case static_cast< int >( CustomRole::StyleName ):
557 return mStyle->name();
558
559 case static_cast< int >( CustomRole::StyleFileName ):
560 return mStyle->fileName();
561
562 default:
563 return QVariant();
564 }
565#ifndef _MSC_VER // avoid warning
566 return QVariant(); // avoid warning
567#endif
568}
569
570bool QgsStyleModel::setData( const QModelIndex &index, const QVariant &value, int role )
571{
572 if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) || role != Qt::EditRole )
573 return false;
574
575 switch ( index.column() )
576 {
577 case Name:
578 {
579 QgsStyle::StyleEntity entityType = entityTypeFromRow( index.row() );
580 QString name;
581 switch ( entityType )
582 {
585 return false;
586
587 default:
588 name = mEntityNames[entityType].value( index.row() - offsetForEntity( entityType ) );
589 break;
590 }
591
592 const QString newName = value.toString();
593 return mStyle->renameEntity( entityType, name, newName );
594 }
595
596 case Tags:
597 return false;
598
599 default:
600 break;
601 }
602
603 return false;
604}
605
606Qt::ItemFlags QgsStyleModel::flags( const QModelIndex &index ) const
607{
608 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
609 if ( index.isValid() && index.column() == Name )
610 {
611 return flags | Qt::ItemIsEditable;
612 }
613 else
614 {
615 return flags;
616 }
617}
618
619QVariant QgsStyleModel::headerData( int section, Qt::Orientation orientation, int role ) const
620{
621 return headerDataStatic( section, orientation, role );
622}
623
624QVariant QgsStyleModel::headerDataStatic( int section, Qt::Orientation orientation, int role )
625{
626 if ( role == Qt::DisplayRole )
627 {
628 if ( orientation == Qt::Vertical ) //row
629 {
630 return QVariant( section );
631 }
632 else
633 {
634 switch ( section )
635 {
636 case Name:
637 return QVariant( tr( "Name" ) );
638
639 case Tags:
640 return QVariant( tr( "Tags" ) );
641
642 default:
643 return QVariant();
644 }
645 }
646 }
647 else
648 {
649 return QVariant();
650 }
651}
652
653QModelIndex QgsStyleModel::index( int row, int column, const QModelIndex &parent ) const
654{
655 if ( !hasIndex( row, column, parent ) )
656 return QModelIndex();
657
658 if ( !parent.isValid() )
659 {
660 return createIndex( row, column );
661 }
662
663 return QModelIndex();
664}
665
666QModelIndex QgsStyleModel::parent( const QModelIndex & ) const
667{
668 //all items are top level for now
669 return QModelIndex();
670}
671
672int QgsStyleModel::rowCount( const QModelIndex &parent ) const
673{
674 if ( !parent.isValid() )
675 {
676 int count = 0;
677 for ( QgsStyle::StyleEntity type : ENTITIES )
678 count += static_cast< int >( mEntityNames[type].size() );
679 return count;
680 }
681 return 0;
682}
683
684int QgsStyleModel::columnCount( const QModelIndex & ) const
685{
686 return 2;
687}
688
690{
691 if ( mAdditionalSizes.contains( size ) )
692 return;
693
694 mAdditionalSizes << size;
695
696 if ( sIconGenerator )
697 sIconGenerator->setIconSizes( mAdditionalSizes );
698
699 mIconCache.clear();
700}
701
703{
704 if ( mTargetScreenProperties.contains( properties ) )
705 return;
706
707 mTargetScreenProperties.insert( properties );
708
709 if ( sIconGenerator )
710 sIconGenerator->setTargetScreenProperties( mTargetScreenProperties );
711
712 mIconCache.clear();
713}
714
716{
717 sIconGenerator = generator;
718 connect( sIconGenerator, &QgsAbstractStyleEntityIconGenerator::iconGenerated, QgsApplication::defaultStyleModel(), &QgsStyleModel::iconGenerated, Qt::QueuedConnection );
719}
720
721void QgsStyleModel::onEntityAdded( QgsStyle::StyleEntity type, const QString &name )
722{
723 mIconCache[type].remove( name );
724 const QStringList newSymbolNames = mStyle->allNames( type );
725
726 // find index of newly added symbol
727 const int newNameIndex = static_cast< int >( newSymbolNames.indexOf( name ) );
728 if ( newNameIndex < 0 )
729 return; // shouldn't happen
730
731 const int offset = offsetForEntity( type );
732 beginInsertRows( QModelIndex(), newNameIndex + offset, newNameIndex + offset );
733 mEntityNames[type] = newSymbolNames;
734 endInsertRows();
735}
736
737void QgsStyleModel::onEntityRemoved( QgsStyle::StyleEntity type, const QString &name )
738{
739 mIconCache[type].remove( name );
740 const QStringList oldSymbolNames = mEntityNames[type];
741 const QStringList newSymbolNames = mStyle->allNames( type );
742
743 // find index of removed symbol
744 const int oldNameIndex = static_cast< int >( oldSymbolNames.indexOf( name ) );
745 if ( oldNameIndex < 0 )
746 return; // shouldn't happen
747
748 const int offset = offsetForEntity( type );
749 beginRemoveRows( QModelIndex(), oldNameIndex + offset, oldNameIndex + offset );
750 mEntityNames[type] = newSymbolNames;
751 endRemoveRows();
752}
753
754void QgsStyleModel::onEntityChanged( QgsStyle::StyleEntity type, const QString &name )
755{
756 mIconCache[type].remove( name );
757
758 const int offset = offsetForEntity( type );
759 QModelIndex i = index( offset + static_cast< int >( mEntityNames[type].indexOf( name ) ), Tags );
760 emit dataChanged( i, i, QVector< int >() << Qt::DecorationRole );
761}
762
763void QgsStyleModel::onFavoriteChanged( QgsStyle::StyleEntity type, const QString &name, bool )
764{
765 const int offset = offsetForEntity( type );
766 QModelIndex i = index( offset + static_cast< int >( mEntityNames[type].indexOf( name ) ), Name );
767 emit dataChanged( i, i, QVector< int >() << static_cast< int >( CustomRole::IsFavorite ) );
768}
769
770void QgsStyleModel::onEntityRename( QgsStyle::StyleEntity type, const QString &oldName, const QString &newName )
771{
772 mIconCache[type].remove( oldName );
773 const QStringList oldSymbolNames = mEntityNames[type];
774 const QStringList newSymbolNames = mStyle->allNames( type );
775
776 // find index of removed symbol
777 const int oldNameIndex = static_cast< int >( oldSymbolNames.indexOf( oldName ) );
778 if ( oldNameIndex < 0 )
779 return; // shouldn't happen
780
781 // find index of added symbol
782 const int newNameIndex = static_cast< int >( newSymbolNames.indexOf( newName ) );
783 if ( newNameIndex < 0 )
784 return; // shouldn't happen
785
786 if ( newNameIndex == oldNameIndex )
787 {
788 mEntityNames[type] = newSymbolNames;
789 return;
790 }
791
792 const int offset = offsetForEntity( type );
793 beginMoveRows( QModelIndex(), oldNameIndex + offset, oldNameIndex + offset, QModelIndex(), ( newNameIndex > oldNameIndex ? newNameIndex + 1 : newNameIndex ) + offset );
794 mEntityNames[type] = newSymbolNames;
795 endMoveRows();
796}
797
798void QgsStyleModel::onTagsChanged( int entity, const QString &name, const QStringList & )
799{
800 QgsStyle::StyleEntity type = static_cast< QgsStyle::StyleEntity >( entity );
801 int row = static_cast< int >( mEntityNames[type].indexOf( name ) ) + offsetForEntity( type );
802 switch ( static_cast< QgsStyle::StyleEntity >( entity ) )
803 {
806 return;
807
808 default:
809 break;
810 }
811 emit dataChanged( index( row, Name ), index( row, Tags ) );
812}
813
814void QgsStyleModel::rebuildSymbolIcons()
815{
816 mIconCache[QgsStyle::SymbolEntity].clear();
817 mExpressionContext.reset();
818 const int lastRow = static_cast< int >( mEntityNames[QgsStyle::SymbolEntity].count() ) - 1;
819 if ( lastRow >= 0 )
820 {
821 emit dataChanged( index( 0, 0 ), index( lastRow, 0 ), QVector<int>() << Qt::DecorationRole );
822 }
823}
824
825void QgsStyleModel::iconGenerated( QgsStyle::StyleEntity type, const QString &name, const QIcon &icon )
826{
827 int row = static_cast< int >( mEntityNames[type].indexOf( name ) ) + offsetForEntity( type );
828
829 switch ( type )
830 {
832 mPending3dSymbolIcons.remove( name );
833 mIconCache[QgsStyle::Symbol3DEntity].insert( name, icon );
834 emit dataChanged( index( row, 0 ), index( row, 0 ) );
835 break;
836
838 mPendingMaterialSettingsIcons.remove( name );
839 mIconCache[QgsStyle::MaterialSettingsEntity].insert( name, icon );
840 emit dataChanged( index( row, 0 ), index( row, 0 ) );
841 break;
842
850 break;
851 }
852}
853
854QgsStyle::StyleEntity QgsStyleModel::entityTypeFromRow( int row ) const
855{
856 int maxRowForEntity = 0;
857 for ( QgsStyle::StyleEntity type : ENTITIES )
858 {
859 maxRowForEntity += static_cast< int >( mEntityNames[type].size() );
860 if ( row < maxRowForEntity )
861 return type;
862 }
863
864 // should never happen
865 Q_ASSERT( false );
867}
868
869int QgsStyleModel::offsetForEntity( QgsStyle::StyleEntity entity ) const
870{
871 int offset = 0;
872 for ( QgsStyle::StyleEntity type : ENTITIES )
873 {
874 if ( type == entity )
875 return offset;
876
877 offset += static_cast< int >( mEntityNames[type].size() );
878 }
879 return 0;
880}
881
882//
883// QgsStyleProxyModel
884//
885
887 : QSortFilterProxyModel( parent )
888 , mStyle( style )
889{
890 mModel = new QgsStyleModel( mStyle, this );
891 setSourceModel( mModel );
892 initialize();
893}
894
895void QgsStyleProxyModel::initialize()
896{
897 setSortCaseSensitivity( Qt::CaseInsensitive );
898 // setSortLocaleAware( true );
899 setDynamicSortFilter( true );
900 sort( 0 );
901
902 if ( mStyle )
903 {
904 connect( mStyle, &QgsStyle::entityTagsChanged, this, [this] {
905 // update tagged symbols if filtering by tag
906 if ( mTagId >= 0 )
907 setTagId( mTagId );
908 if ( mSmartGroupId >= 0 )
909 setSmartGroupId( mSmartGroupId );
910 } );
911
912 connect( mStyle, &QgsStyle::entityRenamed, this, [this]( QgsStyle::StyleEntity entity, const QString &, const QString & ) {
913 switch ( entity )
914 {
917 return;
918
919 default:
920 break;
921 }
922
923 if ( mSmartGroupId >= 0 )
924 setSmartGroupId( mSmartGroupId );
925 } );
926 }
927}
928
930{
931 return mRenderingTechnique;
932}
933
935{
936 if ( mRenderingTechnique == technique )
937 return;
938
939 mRenderingTechnique = technique;
940 invalidateFilter();
941}
942
944 : QSortFilterProxyModel( parent )
945 , mModel( model )
946 , mStyle( model->style() )
947{
948 setSourceModel( mModel );
949 initialize();
950}
951
953 : QSortFilterProxyModel( parent )
954 , mCombinedModel( model )
955{
956 setSourceModel( mCombinedModel );
957 initialize();
958}
959
960bool QgsStyleProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
961{
962 if ( mFilterString.isEmpty() && !mEntityFilterEnabled && !mSymbolTypeFilterEnabled && !mRenderingTechniqueFilterEnabled && mTagId < 0 && mSmartGroupId < 0 && !mFavoritesOnly && mTagFilter.isEmpty() )
963 return true;
964
965 const QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
966
967 if ( sourceModel()->data( index, static_cast< int >( QgsStyleModel::CustomRole::IsTitle ) ).toBool() )
968 return true;
969
970 const QString name = sourceModel()->data( index ).toString();
971 const QStringList tags = sourceModel()->data( index, static_cast< int >( QgsStyleModel::CustomRole::Tag ) ).toStringList();
972
973 QgsStyle::StyleEntity styleEntityType = static_cast< QgsStyle::StyleEntity >( sourceModel()->data( index, static_cast< int >( QgsStyleModel::CustomRole::Type ) ).toInt() );
974 if ( mEntityFilterEnabled && ( mEntityFilters.empty() || !mEntityFilters.contains( styleEntityType ) ) )
975 return false;
976
977 Qgis::SymbolType symbolType = static_cast< Qgis::SymbolType >( sourceModel()->data( index, static_cast< int >( QgsStyleModel::CustomRole::SymbolType ) ).toInt() );
978 if ( mSymbolTypeFilterEnabled && symbolType != mSymbolType )
979 return false;
980
981 if ( mLayerType != Qgis::GeometryType::Unknown )
982 {
983 switch ( styleEntityType )
984 {
992 break;
993
995 {
996 if ( mLayerType != static_cast< Qgis::GeometryType >( sourceModel()->data( index, static_cast< int >( QgsStyleModel::CustomRole::LayerType ) ).toInt() ) )
997 return false;
998 break;
999 }
1000
1002 {
1003 const QVariantList types = sourceModel()->data( index, static_cast< int >( QgsStyleModel::CustomRole::CompatibleGeometryTypes ) ).toList();
1004 if ( !types.empty() && !types.contains( QVariant::fromValue( mLayerType ) ) )
1005 return false;
1006 break;
1007 }
1008 }
1009 }
1010
1011 if ( mTagId >= 0 && !mTaggedSymbolNames.contains( name ) )
1012 return false;
1013
1014 if ( mSmartGroupId >= 0 && !mSmartGroupSymbolNames.contains( name ) )
1015 return false;
1016
1017 if ( !mTagFilter.isEmpty() && !tags.contains( mTagFilter, Qt::CaseInsensitive ) )
1018 return false;
1019
1020 if ( mFavoritesOnly && !sourceModel()->data( index, static_cast< int >( QgsStyleModel::CustomRole::IsFavorite ) ).toBool() )
1021 return false;
1022
1023 if ( !mFilterString.isEmpty() )
1024 {
1025 // filter by word, in both filter string and style entity name/tags
1026 // this allows matching of a filter string "hash line" to the symbol "hashed red lines"
1027 const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
1028
1029 QStringList partsToSearch = name.split( ' ' );
1030 for ( const QString &tag : tags )
1031 {
1032 partsToSearch.append( tag.split( ' ' ) );
1033 }
1034
1035 for ( const QString &part : partsToMatch )
1036 {
1037 bool found = false;
1038 for ( const QString &partToSearch : std::as_const( partsToSearch ) )
1039 {
1040 if ( partToSearch.contains( part, Qt::CaseInsensitive ) )
1041 {
1042 found = true;
1043 break;
1044 }
1045 }
1046 if ( !found )
1047 return false; // couldn't find a match for this word, so hide entity
1048 }
1049 }
1050
1051 if ( mRenderingTechniqueFilterEnabled && styleEntityType == QgsStyle::MaterialSettingsEntity )
1052 {
1053 const QString materialType = sourceModel()->data( index, static_cast< int >( QgsStyleModel::CustomRole::MaterialType ) ).toString();
1054 if ( !materialType.isEmpty() )
1055 {
1056 if ( const QgsMaterialSettingsAbstractMetadata *metadata = QgsApplication::materialRegistry()->materialSettingsMetadata( materialType ) )
1057 {
1058 if ( !metadata->supportsTechnique( mRenderingTechnique ) )
1059 return false;
1060 }
1061 }
1062 }
1063
1064 return true;
1065}
1066
1067bool QgsStyleProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
1068{
1069 const QString leftSource = sourceModel()->data( left, static_cast< int >( QgsStyleModel::CustomRole::StyleFileName ) ).toString();
1070 const QString rightSource = sourceModel()->data( right, static_cast< int >( QgsStyleModel::CustomRole::StyleFileName ) ).toString();
1071 if ( leftSource != rightSource )
1072 return QString::localeAwareCompare( leftSource, rightSource ) < 0;
1073
1074 const QString leftName = sourceModel()->data( left, static_cast< int >( QgsStyleModel::CustomRole::EntityName ) ).toString();
1075 const QString rightName = sourceModel()->data( right, static_cast< int >( QgsStyleModel::CustomRole::EntityName ) ).toString();
1076 return QString::localeAwareCompare( leftName, rightName ) < 0;
1077}
1078
1079void QgsStyleProxyModel::setFilterString( const QString &filter )
1080{
1081 mFilterString = filter;
1082 invalidateFilter();
1083}
1084
1085
1087{
1088 return mFavoritesOnly;
1089}
1090
1092{
1093 mFavoritesOnly = favoritesOnly;
1094 invalidateFilter();
1095}
1096
1098{
1099 if ( mModel )
1100 mModel->addDesiredIconSize( size );
1101 if ( mCombinedModel )
1102 mCombinedModel->addDesiredIconSize( size );
1103}
1104
1106{
1107 if ( mModel )
1108 mModel->addTargetScreenProperties( properties );
1109 if ( mCombinedModel )
1110 mCombinedModel->addTargetScreenProperties( properties );
1111}
1112
1114{
1115 return mSymbolTypeFilterEnabled;
1116}
1117
1119{
1120 mSymbolTypeFilterEnabled = enabled;
1121 invalidateFilter();
1122}
1123
1125{
1126 return mLayerType;
1127}
1128
1130{
1131 mLayerType = type;
1132 invalidateFilter();
1133}
1134
1136{
1137 return mRenderingTechniqueFilterEnabled;
1138}
1139
1141{
1142 if ( mRenderingTechniqueFilterEnabled == enabled )
1143 return;
1144
1145 mRenderingTechniqueFilterEnabled = enabled;
1146 invalidateFilter();
1147}
1148
1150{
1151 if ( !mStyle )
1152 return;
1153 mTagId = id;
1154
1155 mTaggedSymbolNames.clear();
1156 if ( mTagId >= 0 )
1157 {
1158 for ( QgsStyle::StyleEntity entity : ENTITIES )
1159 mTaggedSymbolNames.append( mStyle->symbolsWithTag( entity, mTagId ) );
1160 }
1161
1162 invalidateFilter();
1163}
1164
1166{
1167 return mTagId;
1168}
1169
1170void QgsStyleProxyModel::setTagString( const QString &tag )
1171{
1172 mTagFilter = tag;
1173
1174 invalidateFilter();
1175}
1176
1178{
1179 return mTagFilter;
1180}
1181
1183{
1184 if ( !mStyle )
1185 return;
1186
1187 mSmartGroupId = id;
1188
1189 mSmartGroupSymbolNames.clear();
1190 if ( mSmartGroupId >= 0 )
1191 {
1192 for ( QgsStyle::StyleEntity entity : ENTITIES )
1193 mSmartGroupSymbolNames.append( mStyle->symbolsOfSmartgroup( entity, mSmartGroupId ) );
1194 }
1195
1196 invalidateFilter();
1197}
1198
1200{
1201 return mSmartGroupId;
1202}
1203
1205{
1206 return mSymbolType;
1207}
1208
1210{
1211 mSymbolType = symbolType;
1212 invalidateFilter();
1213}
1214
1216{
1217 return mEntityFilterEnabled;
1218}
1219
1221{
1222 mEntityFilterEnabled = entityFilterEnabled;
1223 invalidateFilter();
1224}
1225
1227{
1228 return mEntityFilters.empty() ? QgsStyle::SymbolEntity : mEntityFilters.at( 0 );
1229}
1230
1232{
1233 mEntityFilters = QList< QgsStyle::StyleEntity >() << entityFilter;
1234 invalidateFilter();
1235}
1236
1237void QgsStyleProxyModel::setEntityFilters( const QList<QgsStyle::StyleEntity> &filters )
1238{
1239 mEntityFilters = filters;
1240 invalidateFilter();
1241}
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Unknown
Unknown types.
Definition qgis.h:383
SymbolType
Symbol types.
Definition qgis.h:636
MaterialRenderingTechnique
Material rendering techniques.
Definition qgis.h:4327
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:6690
An abstract base class for icon generators for a QgsStyleModel.
void setTargetScreenProperties(const QSet< QgsScreenProperties > &properties)
Sets the target screen properties to use when generating icons.
void setIconSizes(const QList< QSize > &sizes)
Sets the list of icon sizes to generate.
QgsAbstractStyleEntityIconGenerator(QObject *parent)
Constructor for QgsAbstractStyleEntityIconGenerator, with the specified parent object.
QSet< QgsScreenProperties > targetScreenProperties() const
Returns the target screen properties to use when generating icons.
QList< QSize > iconSizes() const
Returns the list of icon sizes to generate.
void iconGenerated(QgsStyle::StyleEntity type, const QString &name, const QIcon &icon)
Emitted when the icon for the style entity with matching type and name has been generated.
static QString defaultThemePath()
Returns the path to the default theme directory.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsMaterialRegistry * materialRegistry()
Returns registry of available 3D materials.
static QgsStyleModel * defaultStyleModel()
Returns a shared QgsStyleModel containing the default style library (see QgsStyle::defaultStyle()).
A model which contains entities from multiple QgsStyle databases.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
void remoteImageFetched(const QString &url)
Emitted when the cache has finished retrieving an image file from a remote url.
Represents a patch shape for use in map legends.
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
Stores metadata about one 3D material settings class.
Contains settings for how a map layer will be labeled.
static QPixmap labelSettingsPreviewPixmap(const QgsPalLayerSettings &settings, QSize size, const QString &previewText=QString(), int padding=0, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for label settings.
Stores properties relating to a screen.
double devicePixelRatio() const
Returns the ratio between physical pixels and device-independent pixels for the screen.
bool isValid() const
Returns true if the properties are valid.
A QAbstractItemModel subclass for showing symbol and color ramp entities contained within a QgsStyle ...
@ IsFavorite
Whether entity is flagged as a favorite.
@ LayerType
Layer type (for label settings entities).
@ IsTitle
True if the index corresponds to a title item.
@ SymbolType
Symbol type (for symbol or legend patch shape entities).
@ MaterialType
Material type (for material entities).
@ StyleFileName
File name of associated QgsStyle (QgsStyle::fileName()).
@ Type
Style entity type, see QgsStyle::StyleEntity.
@ CompatibleGeometryTypes
Compatible layer geometry types (for 3D symbols).
@ Tag
String list of tags.
@ StyleName
Name of associated QgsStyle (QgsStyle::name()).
Qt::ItemFlags flags(const QModelIndex &index) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role) const override
static void setIconGenerator(QgsAbstractStyleEntityIconGenerator *generator)
Sets the icon generator to use for deferred style entity icon generation.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QModelIndex parent(const QModelIndex &index) const override
void addTargetScreenProperties(const QgsScreenProperties &properties)
Adds additional target screen properties to use when generating icons for Qt::DecorationRole data.
QgsStyleModel(QgsStyle *style, QObject *parent=nullptr)
Constructor for QgsStyleModel, for the specified style and parent object.
QgsStyle * style()
Returns the style managed by the model.
@ Name
Name column.
@ Tags
Tags column.
void addDesiredIconSize(QSize size)
Adds an additional icon size to generate for Qt::DecorationRole data.
void setRenderingTechniqueFilterEnabled(bool enabled)
Sets whether filtering by material rendering technique is enabled.
void setEntityFilter(QgsStyle::StyleEntity filter)
Sets the style entity type filter.
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
bool favoritesOnly() const
Returns true if the model is showing only favorited entities.
void setSymbolTypeFilterEnabled(bool enabled)
Sets whether filtering by symbol type is enabled.
void setTagId(int id)
Sets a tag id to filter style entities by.
QString tagString() const
Returns the tag string used to filter style entities by.
Qgis::SymbolType symbolType() const
Returns the symbol type filter.
void setTagString(const QString &tag)
Sets a tag to filter style entities by.
void setRenderingTechnique(Qgis::MaterialRenderingTechnique technique)
Sets the rendering technique filter.
QgsStyle::StyleEntity entityFilter() const
Returns the style entity type filter.
QgsStyleProxyModel(QgsStyle *style, QObject *parent=nullptr)
Constructor for QgsStyleProxyModel, for the specified style and parent object.
int tagId() const
Returns the tag id used to filter style entities by.
void setEntityFilters(const QList< QgsStyle::StyleEntity > &filters)
Sets the style entity type filters.
Qgis::MaterialRenderingTechnique renderingTechnique() const
Returns the rendering technique filter.
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
void setFavoritesOnly(bool favoritesOnly)
Sets whether the model should show only favorited entities.
void setSymbolType(Qgis::SymbolType type)
Sets the symbol type filter.
bool entityFilterEnabled() const
Returns true if filtering by entity type is enabled.
Qgis::GeometryType layerType() const
Returns the layer type filter, or Qgis::GeometryType::Unknown if no layer type filter is present.
void addDesiredIconSize(QSize size)
Adds an additional icon size to generate for Qt::DecorationRole data.
void addTargetScreenProperties(const QgsScreenProperties &properties)
Adds additional target screen properties to use when generating icons for Qt::DecorationRole data.
void setEntityFilterEnabled(bool enabled)
Sets whether filtering by entity type is enabled.
void setSmartGroupId(int id)
Sets a smart group id to filter style entities by.
bool symbolTypeFilterEnabled() const
Returns true if filtering by symbol type is enabled.
int smartGroupId() const
Returns the smart group id used to filter style entities by.
void setFilterString(const QString &filter)
Sets a filter string, such that only symbol entities with names matching the specified string will be...
bool renderingTechniqueFilterEnabled() const
Returns true if filtering by material rendering technique is enabled.
void setLayerType(Qgis::GeometryType type)
Sets the layer type filter.
A database of saved style entities, including symbols, color ramps, text formats and others.
Definition qgsstyle.h:91
QStringList allNames(StyleEntity type) const
Returns a list of the names of all existing entities of the specified type.
void entityChanged(QgsStyle::StyleEntity entity, const QString &name)
Emitted whenever an entity's definition is changed.
void rebuildIconPreviews()
Emitted whenever icon previews for entities in the style must be rebuilt.
StyleEntity
Enum for Entities involved in a style.
Definition qgsstyle.h:206
@ LabelSettingsEntity
Label settings.
Definition qgsstyle.h:212
@ TextFormatEntity
Text formats.
Definition qgsstyle.h:211
@ SmartgroupEntity
Smart groups.
Definition qgsstyle.h:210
@ Symbol3DEntity
3D symbol entity
Definition qgsstyle.h:214
@ SymbolEntity
Symbols.
Definition qgsstyle.h:207
@ TagEntity
Tags.
Definition qgsstyle.h:208
@ ColorrampEntity
Color ramps.
Definition qgsstyle.h:209
@ LegendPatchShapeEntity
Legend patch shape.
Definition qgsstyle.h:213
@ MaterialSettingsEntity
Material settings.
Definition qgsstyle.h:215
void entityRenamed(QgsStyle::StyleEntity entity, const QString &oldName, const QString &newName)
Emitted whenever a entity of the specified type has been renamed from oldName to newName.
void initialized()
Emitted when the style database has been fully initialized.
void entityTagsChanged(QgsStyle::StyleEntity entity, const QString &name, const QStringList &newTags)
Emitted whenever an entity's tags are changed.
void favoritedChanged(QgsStyle::StyleEntity entity, const QString &name, bool isFavorite)
Emitted whenever an entity is either favorited or un-favorited.
void entityRemoved(QgsStyle::StyleEntity entity, const QString &name)
Emitted whenever an entity of the specified type is removed from the style and the database has been ...
void entityAdded(QgsStyle::StyleEntity entity, const QString &name)
Emitted every time a new entity has been added to the database.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
static QPixmap colorRampPreviewPixmap(QgsColorRamp *ramp, QSize size, int padding=0, Qt::Orientation direction=Qt::Horizontal, bool flipDirection=false, bool drawTransparentBackground=true)
Returns a pixmap preview for a color ramp.
static QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *shape=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for a color ramp.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:227
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:296
Container for all settings relating to text rendering.
static QPixmap textFormatPreviewPixmap(const QgsTextFormat &format, QSize size, const QString &previewText=QString(), int padding=0, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for a text format.
const double ICON_PADDING_FACTOR
const auto ENTITIES