Skip to content

Commit

Permalink
Implement item size and positioning using layout units
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jul 18, 2017
1 parent 498c4cd commit d0c844e
Show file tree
Hide file tree
Showing 6 changed files with 685 additions and 4 deletions.
120 changes: 119 additions & 1 deletion python/core/layout/qgslayoutitem.sip
Expand Up @@ -21,7 +21,20 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem
%End
public:

QgsLayoutItem( QgsLayout *layout );
enum ReferencePoint
{
UpperLeft,
UpperMiddle,
UpperRight,
MiddleLeft,
Middle,
MiddleRight,
LowerLeft,
LowerMiddle,
LowerRight,
};

explicit QgsLayoutItem( QgsLayout *layout );
%Docstring
Constructor for QgsLayoutItem, with the specified parent ``layout``.
%End
Expand All @@ -34,6 +47,72 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem
the pure virtual method QgsLayoutItem.draw.
%End

void setReferencePoint( const ReferencePoint &point );
%Docstring
Sets the reference ``point`` for positioning of the layout item. This point is also
fixed during resizing of the item, and any size changes will be performed
so that the position of the reference point within the layout remains unchanged.
.. seealso:: referencePoint()
%End

ReferencePoint referencePoint() const;
%Docstring
Returns the reference point for positioning of the layout item. This point is also
fixed during resizing of the item, and any size changes will be performed
so that the position of the reference point within the layout remains unchanged.
.. seealso:: setReferencePoint()
:rtype: ReferencePoint
%End

QgsLayoutSize fixedSize() const;
%Docstring
Returns the fixed size of the item, if applicable, or an empty size if item can be freely
resized.
.. seealso:: setFixedSize()
.. seealso:: minimumSize()
:rtype: QgsLayoutSize
%End

virtual QgsLayoutSize minimumSize() const;
%Docstring
Returns the minimum allowed size of the item, if applicable, or an empty size if item can be freely
resized.
.. seealso:: setMinimumSize()
.. seealso:: fixedSize()
:rtype: QgsLayoutSize
%End

virtual void attemptResize( const QgsLayoutSize &size );
%Docstring
Attempts to resize the item to a specified target ``size``. Note that the final size of the
item may not match the specified target size, as items with a fixed or minimum
size will place restrictions on the allowed item size. Data defined item size overrides
will also override the specified target size.
.. seealso:: minimumSize()
.. seealso:: fixedSize()
.. seealso:: attemptMove()
%End

virtual void attemptMove( const QgsLayoutPoint &point );
%Docstring
Attempts to move the item to a specified ``point``. This method respects the item's
reference point, in that the item will be moved so that its current reference
point is placed at the specified target point.
Note that the final position of the item may not match the specified target position,
as data defined item position may override the specified value.
.. seealso:: attemptResize()
.. seealso:: referencePoint()
%End

public slots:

virtual void refresh();

%Docstring
Refreshes the item, causing a recalculation of any property overrides and
recalculation of its position and size.
%End

protected:

virtual void drawDebugRect( QPainter *painter );
Expand All @@ -48,6 +127,45 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem
Draws the item's contents on a specified ``painter``.
%End

virtual void setFixedSize( const QgsLayoutSize &size );
%Docstring
Sets a fixed ``size`` for the layout item, which prevents it from being freely
resized. Set an empty size if item can be freely resized.
.. seealso:: fixedSize()
.. seealso:: setMinimumSize()
%End

virtual void setMinimumSize( const QgsLayoutSize &size );
%Docstring
Sets the minimum allowed ``size`` for the layout item. Set an empty size if item can be freely
resized.
.. seealso:: minimumSize()
.. seealso:: setFixedSize()
%End

void refreshItemSize();
%Docstring
Refreshes an item's size by rechecking it against any possible item fixed
or minimum sizes.
.. seealso:: setFixedSize()
.. seealso:: setMinimumSize()
.. seealso:: refreshItemPosition()
%End

void refreshItemPosition();
%Docstring
Refreshes an item's position by rechecking it against any possible overrides
such as data defined positioning.
.. seealso:: refreshItemSize()
%End

QPointF adjustPointForReferencePosition( const QPointF &point, const QSizeF &size ) const;
%Docstring
Adjusts the specified ``point`` at which the reference position of the item
sits and returns the top left corner of the item.
:rtype: QPointF
%End

};


Expand Down
9 changes: 8 additions & 1 deletion python/core/layout/qgslayoutobject.sip
Expand Up @@ -78,7 +78,7 @@ class QgsLayoutObject: QObject, QgsExpressionContextGenerator
:rtype: QgsPropertiesDefinition
%End

QgsLayoutObject( QgsLayout *layout );
explicit QgsLayoutObject( QgsLayout *layout );
%Docstring
Constructor for QgsLayoutObject, with the specified parent ``layout``.
.. note::
Expand Down Expand Up @@ -159,6 +159,13 @@ class QgsLayoutObject: QObject, QgsExpressionContextGenerator
:rtype: QgsExpressionContext
%End

public slots:

virtual void refresh();
%Docstring
Refreshes the object, causing a recalculation of any property overrides.
%End

protected:


Expand Down
139 changes: 139 additions & 0 deletions src/core/layout/qgslayoutitem.cpp
Expand Up @@ -23,6 +23,13 @@ QgsLayoutItem::QgsLayoutItem( QgsLayout *layout )
, QGraphicsRectItem( 0 )
{
setCacheMode( QGraphicsItem::DeviceCoordinateCache );

//record initial position
QgsUnitTypes::LayoutUnit initialUnits = layout ? layout->units() : QgsUnitTypes::LayoutMillimeters;
mItemPosition = QgsLayoutPoint( scenePos().x(), scenePos().y(), initialUnits );
mItemSize = QgsLayoutSize( rect().width(), rect().height(), initialUnits );

initConnectionsToLayout();
}

void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
Expand All @@ -48,6 +55,62 @@ void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *it
painter->restore();
}

void QgsLayoutItem::setReferencePoint( const QgsLayoutItem::ReferencePoint &point )
{
mReferencePoint = point;
}

void QgsLayoutItem::attemptResize( const QgsLayoutSize &size )
{
if ( !mLayout )
{
mItemSize = size;
setRect( 0, 0, size.width(), size.height() );
return;
}

QSizeF targetSizeLayoutUnits = mLayout->convertToLayoutUnits( size );
QSizeF actualSizeLayoutUnits = applyMinimumSize( targetSizeLayoutUnits );
actualSizeLayoutUnits = applyFixedSize( actualSizeLayoutUnits );

if ( actualSizeLayoutUnits == rect().size() )
{
return;
}

QgsLayoutSize actualSizeTargetUnits = mLayout->convertFromLayoutUnits( actualSizeLayoutUnits, size.units() );
mItemSize = actualSizeTargetUnits;

setRect( 0, 0, actualSizeLayoutUnits.width(), actualSizeLayoutUnits.height() );
}

void QgsLayoutItem::attemptMove( const QgsLayoutPoint &point )
{
if ( !mLayout )
{
mItemPosition = point;
setPos( point.toQPointF() );
return;
}

QPointF targetPointLayoutUnits = mLayout->convertToLayoutUnits( point );
//TODO - apply data defined position here
targetPointLayoutUnits = adjustPointForReferencePosition( targetPointLayoutUnits, rect().size() );
QPointF actualPointLayoutUnits = targetPointLayoutUnits;

QgsLayoutPoint actualPointTargetUnits = mLayout->convertFromLayoutUnits( actualPointLayoutUnits, point.units() );
mItemPosition = actualPointTargetUnits;

setPos( targetPointLayoutUnits );
}

void QgsLayoutItem::refresh()
{
QgsLayoutObject::refresh();
refreshItemSize();
refreshItemPosition();
}

void QgsLayoutItem::drawDebugRect( QPainter *painter )
{
if ( !painter )
Expand All @@ -63,6 +126,62 @@ void QgsLayoutItem::drawDebugRect( QPainter *painter )
painter->restore();
}

void QgsLayoutItem::setFixedSize( const QgsLayoutSize &size )
{
mFixedSize = size;
refreshItemSize();
}

void QgsLayoutItem::setMinimumSize( const QgsLayoutSize &size )
{
mMinimumSize = size;
refreshItemSize();
}

void QgsLayoutItem::refreshItemSize()
{
attemptResize( mItemSize );
}

void QgsLayoutItem::refreshItemPosition()
{
attemptMove( mItemPosition );
}

QPointF QgsLayoutItem::adjustPointForReferencePosition( const QPointF &position, const QSizeF &size ) const
{
switch ( mReferencePoint )
{
case UpperMiddle:
return QPointF( position.x() - size.width() / 2.0, position.y() );
case UpperRight:
return QPointF( position.x() - size.width(), position.y() );
case MiddleLeft:
return QPointF( position.x(), position.y() - size.height() / 2.0 );
case Middle:
return QPointF( position.x() - size.width() / 2.0, position.y() - size.height() / 2.0 );
case MiddleRight:
return QPointF( position.x() - size.width(), position.y() - size.height() / 2.0 );
case LowerLeft:
return QPointF( position.x(), position.y() - size.height() );
case LowerMiddle:
return QPointF( position.x() - size.width() / 2.0, position.y() - size.height() );
case LowerRight:
return QPointF( position.x() - size.width(), position.y() - size.height() );
case UpperLeft:
return position;
}
// no warnings
return position;
}

void QgsLayoutItem::initConnectionsToLayout()
{
if ( !mLayout )
return;

}

void QgsLayoutItem::preparePainter( QPainter *painter )
{
if ( !painter || !painter->device() )
Expand All @@ -86,3 +205,23 @@ bool QgsLayoutItem::shouldDrawDebugRect() const
{
return mLayout && mLayout->context().testFlag( QgsLayoutContext::FlagDebug );
}

QSizeF QgsLayoutItem::applyMinimumSize( const QSizeF &targetSize )
{
if ( !mLayout || minimumSize().isEmpty() )
{
return targetSize;
}
QSizeF minimumSizeLayoutUnits = mLayout->convertToLayoutUnits( minimumSize() );
return targetSize.expandedTo( minimumSizeLayoutUnits );
}

QSizeF QgsLayoutItem::applyFixedSize( const QSizeF &targetSize )
{
if ( !mLayout || fixedSize().isEmpty() )
{
return targetSize;
}
QSizeF fixedSizeLayoutUnits = mLayout->convertToLayoutUnits( fixedSize() );
return targetSize.expandedTo( fixedSizeLayoutUnits );
}

0 comments on commit d0c844e

Please sign in to comment.