Navigation Menu

Skip to content

Commit

Permalink
Make style model decoration icons size responsive
Browse files Browse the repository at this point in the history
We hack the model a bit here, but as much as possible avoid bleeding
view properties into the model API.

So we use a QObject property ("icon_size") to specify icons sizes
for the model to generate. This is set on instances
of the model to indicate the required sizes for decorations in all
views connected to the model, and allows the model to have size responsive icons.

By using a QObject property we avoid having public GUI/view related API within
the model, and mostly avoid view related properties contaminating the pure model,
yet still have pixel-perfect symbol renders for the required view icon sizes.
  • Loading branch information
nyalldawson committed Sep 12, 2018
1 parent 28836d2 commit d7edeac
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 3 deletions.
28 changes: 26 additions & 2 deletions src/core/symbology/qgsstylemodel.cpp
Expand Up @@ -18,6 +18,7 @@
#include "qgssymbollayerutils.h"
#include <QIcon>

const double ICON_PADDING_FACTOR = 0.16;

QgsStyleModel::QgsStyleModel( QgsStyle *style, QObject *parent )
: QAbstractItemModel( parent )
Expand Down Expand Up @@ -70,18 +71,41 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const

case Qt::DecorationRole:
{
// check the model custom property for icon sizes to generate. This is used
// by instances of the model to indicate the required sizes for decorations in all
// views connected to the model, and allows the model to have size responsive icons.
// By using a QObject property we avoid having public GUI/view related API within
// the model, and mostly avoid view related properties contaminating the pure model...
const QVariantList iconSizes = property( "icon_sizes" ).toList();

switch ( index.column() )
{
case Name:
if ( !isColorRamp )
{
std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) );
return QgsSymbolLayerUtils::symbolPreviewIcon( symbol.get(), QSize( 24, 24 ), 2 );
QIcon icon;
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( 24, 24 ), 1 ) );

for ( const QVariant &size : iconSizes )
{
QSize s = size.toSize();
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
}

return icon;
}
else
{
std::unique_ptr< QgsColorRamp > ramp( mStyle->colorRamp( name ) );
return QgsSymbolLayerUtils::colorRampPreviewIcon( ramp.get(), QSize( 24, 24 ), 2 );
QIcon icon;
icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), QSize( 24, 24 ), 1 ) );
for ( const QVariant &size : iconSizes )
{
QSize s = size.toSize();
icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
}
return icon;
}
case Tags:
return QVariant();
Expand Down
45 changes: 44 additions & 1 deletion tests/src/python/test_qgsstylemodel.py
Expand Up @@ -23,7 +23,7 @@
QgsStyle,
QgsStyleProxyModel)
from qgis.testing import start_app, unittest
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtCore import Qt, QSize
from qgis.PyQt.QtGui import QColor

start_app()
Expand Down Expand Up @@ -715,6 +715,49 @@ def test_filter_proxy(self):
model.setSmartGroupId(-1)
self.assertEqual(model.rowCount(), 8)

def testIconSize(self):
"""
Test that model has responsive icon sizes for decorations
"""
style = QgsStyle()
style.createMemoryDatabase()

symbol_a = createMarkerSymbol()
symbol_a.setColor(QColor(255, 10, 10))
self.assertTrue(style.addSymbol('a', symbol_a, True))
ramp_a = QgsLimitedRandomColorRamp(5)
self.assertTrue(style.addColorRamp('ramp a', ramp_a, True))

model = QgsStyleModel(style)
self.assertEqual(model.rowCount(), 2)
for i in range(2):
icon = model.data(model.index(i, 0), Qt.DecorationRole)
# by default, only 24x24 icon
self.assertEqual(icon.availableSizes(), [QSize(24, 24)])
self.assertEqual(icon.actualSize(QSize(10, 10)), QSize(10, 10))
self.assertEqual(icon.actualSize(QSize(24, 24)), QSize(24, 24))
self.assertEqual(icon.actualSize(QSize(90, 90)), QSize(24, 24))

model.setProperty('icon_sizes', [QSize(24, 24), QSize(100, 90)])
icon = model.data(model.index(i, 0), Qt.DecorationRole)
self.assertEqual(icon.availableSizes(), [QSize(24, 24), QSize(100, 90)])
self.assertEqual(icon.actualSize(QSize(10, 10)), QSize(10, 10))
self.assertEqual(icon.actualSize(QSize(24, 24)), QSize(24, 24))
self.assertEqual(icon.actualSize(QSize(25, 25)), QSize(25, 22))
self.assertEqual(icon.actualSize(QSize(90, 90)), QSize(90, 81))
self.assertEqual(icon.actualSize(QSize(125, 125)), QSize(100, 90))

model.setProperty('icon_sizes', [QSize(100, 90), QSize(200, 180)])
icon = model.data(model.index(i, 0), Qt.DecorationRole)
self.assertEqual(icon.availableSizes(), [QSize(24, 24), QSize(100, 90), QSize(200, 180)])
self.assertEqual(icon.actualSize(QSize(10, 10)), QSize(10, 10))
self.assertEqual(icon.actualSize(QSize(24, 24)), QSize(24, 24))
self.assertEqual(icon.actualSize(QSize(25, 25)), QSize(25, 22))
self.assertEqual(icon.actualSize(QSize(90, 90)), QSize(90, 81))
self.assertEqual(icon.actualSize(QSize(125, 125)), QSize(125, 112))
self.assertEqual(icon.actualSize(QSize(225, 225)), QSize(200, 180))
model.setProperty('icon_sizes', None)


if __name__ == '__main__':
unittest.main()

0 comments on commit d7edeac

Please sign in to comment.