code cleanup

This commit is contained in:
Uwe Rathmann 2020-04-19 14:40:58 +02:00
parent 09117c4f6d
commit cccf946144
2 changed files with 198 additions and 211 deletions

View File

@ -12,9 +12,9 @@
#include <QskEvent.h> #include <QskEvent.h>
#include <QskQuick.h> #include <QskQuick.h>
#include <QskLayoutHint.h>
#include <vector> #include <vector>
#include <map>
static inline Qt::Orientation qskOrientation( int edge ) static inline Qt::Orientation qskOrientation( int edge )
{ {
@ -100,14 +100,158 @@ namespace
QQuickItem* item2 = nullptr; QQuickItem* item2 = nullptr;
Qt::AnchorPoint edge2; Qt::AnchorPoint edge2;
}; };
class LayoutSolver : public Solver
{
public:
void setup( bool layoutChildren,
const QVector< Anchor >&, std::map< QQuickItem*, Geometry >& );
void addSizeConstraints();
QSizeF resolvedSize();
QSizeF resolvedSize( qreal width, qreal height );
void resolve( qreal width, qreal height );
private:
void addSizeConstraints( Geometry& rect, const QSizeF& size,
RelationalOperator op, double strength );
Variable m_width, m_height;
};
}
void LayoutSolver::setup( bool layoutChildren,
const QVector< Anchor >& anchors,
std::map< QQuickItem*, Geometry >& geometries )
{
for ( const auto& anchor : anchors )
{
auto& r1 = geometries[ anchor.item1 ];
const auto expr1 = r1.expressionAt( anchor.edge1 );
Expression expr2;
if ( anchor.item2 == nullptr )
{
switch( anchor.edge2 )
{
case Qt::AnchorLeft:
case Qt::AnchorTop:
expr2 = 0;
break;
case Qt::AnchorHorizontalCenter:
expr2 = Term( 0.5 * m_width );
break;
case Qt::AnchorRight:
expr2 = Term( m_width );
break;
case Qt::AnchorVerticalCenter:
expr2 = Term( 0.5 * m_height );
break;
case Qt::AnchorBottom:
expr2 = Term( m_height );
break;
}
addConstraint( expr1 == expr2 );
}
else
{
auto& r2 = geometries[ anchor.item2 ];
const auto expr2 = r2.expressionAt( anchor.edge2 );
addConstraint( expr1 == expr2 );
if ( layoutChildren )
{
const auto o = qskOrientation( anchor.edge1 );
/*
A constraint with medium strength to make anchored item
being stretched according to their stretch factors s1, s2.
( For the moment we don't support having specific factors. )
*/
const auto s1 = 1.0;
const auto s2 = 1.0;
Constraint c( r1.length( o ) * s1 == r2.length( o ) * s2, Strength::medium );
addConstraint( c );
}
}
}
for ( auto it = geometries.begin(); it != geometries.end(); ++it )
{
const auto minSize = qskSizeConstraint( it->first, Qt::MinimumSize );
addSizeConstraints( it->second, minSize, OP_GE, Strength::required );
const auto maxSize = qskSizeConstraint( it->first, Qt::MaximumSize );
addSizeConstraints( it->second, maxSize, OP_LE, Strength::required );
const auto prefSize = qskSizeConstraint( it->first, Qt::PreferredSize );
addSizeConstraints( it->second, prefSize, OP_EQ, Strength::strong );
}
}
void LayoutSolver::addSizeConstraints()
{
const double strength = 0.9 * Strength::required;
addEditVariable( m_width, strength );
addEditVariable( m_height, strength );
}
void LayoutSolver::resolve( qreal width, qreal height )
{
suggestValue( m_width, width );
suggestValue( m_height, height );
updateVariables();
}
QSizeF LayoutSolver::resolvedSize()
{
updateVariables();
return QSizeF( m_width.value(), m_height.value() );
}
QSizeF LayoutSolver::resolvedSize( qreal width, qreal height )
{
resolve( width, height );
return QSizeF( m_width.value(), m_height.value() );
}
void LayoutSolver::addSizeConstraints( Geometry& rect, const QSizeF& size,
RelationalOperator op, double strength )
{
if ( size.width() >= 0.0 )
{
const Constraint c( rect.width() - size.width(), op, strength );
addConstraint( c );
}
if ( size.height() >= 0.0 )
{
const Constraint c( rect.height() - size.height(), op, strength );
addConstraint( c );
}
} }
class AnchorBox::PrivateData class AnchorBox::PrivateData
{ {
public: public:
QMap< QQuickItem*, Geometry > geometries; ~PrivateData() { delete solver; }
std::map< QQuickItem*, Geometry > geometries;
QVector< Anchor > anchors; QVector< Anchor > anchors;
Solver solver; LayoutSolver* solver = nullptr;
QSizeF hints[3]; QSizeF hints[3];
bool hasValidHints = false; bool hasValidHints = false;
@ -117,7 +261,6 @@ AnchorBox::AnchorBox( QQuickItem* parent )
: QskControl( parent ) : QskControl( parent )
, m_data( new PrivateData ) , m_data( new PrivateData )
{ {
(void)m_data->geometries[ this ];
} }
AnchorBox::~AnchorBox() AnchorBox::~AnchorBox()
@ -137,6 +280,7 @@ void AnchorBox::addAnchors( QQuickItem* item1,
addAnchor( item1, Qt::AnchorLeft, item2, Qt::AnchorLeft ); addAnchor( item1, Qt::AnchorLeft, item2, Qt::AnchorLeft );
addAnchor( item1, Qt::AnchorRight, item2, Qt::AnchorRight ); addAnchor( item1, Qt::AnchorRight, item2, Qt::AnchorRight );
} }
if ( orientations & Qt::Vertical ) if ( orientations & Qt::Vertical )
{ {
addAnchor( item1, Qt::AnchorTop, item2, Qt::AnchorTop ); addAnchor( item1, Qt::AnchorTop, item2, Qt::AnchorTop );
@ -174,26 +318,27 @@ void AnchorBox::addAnchor( QQuickItem* item1, Qt::AnchorPoint edge1,
if ( item1 == this ) if ( item1 == this )
std::swap( item1, item2 ); std::swap( item1, item2 );
if ( item1 != this ) if ( item2 == this )
{ item2 = nullptr;
if ( item1->parent() == nullptr ) if ( item1->parent() == nullptr )
item1->setParent( this ); item1->setParent( this );
if ( item1->parentItem() != this ) if ( item1->parentItem() != this )
item1->setParentItem( this ); item1->setParentItem( this );
}
if ( item2 != this ) (void)m_data->geometries[ item1 ];
if ( item2 )
{ {
if ( item2->parent() == nullptr ) if ( item2->parent() == nullptr )
item2->setParent( this ); item2->setParent( this );
if ( item2->parentItem() != this ) if ( item2->parentItem() != this )
item2->setParentItem( this ); item2->setParentItem( this );
}
(void)m_data->geometries[ item1 ];
(void)m_data->geometries[ item2 ]; (void)m_data->geometries[ item2 ];
}
Anchor anchor; Anchor anchor;
anchor.item1 = item1; anchor.item1 = item1;
@ -204,6 +349,20 @@ void AnchorBox::addAnchor( QQuickItem* item1, Qt::AnchorPoint edge1,
m_data->anchors += anchor; m_data->anchors += anchor;
} }
void AnchorBox::geometryChangeEvent( QskGeometryChangeEvent* event )
{
Inherited::geometryChangeEvent( event );
if ( event->isResized() )
polish();
}
void AnchorBox::updateLayout()
{
if ( !maybeUnresized() )
updateGeometries( layoutRect() );
}
QSizeF AnchorBox::layoutSizeHint( Qt::SizeHint which, const QSizeF& constraint ) const QSizeF AnchorBox::layoutSizeHint( Qt::SizeHint which, const QSizeF& constraint ) const
{ {
if ( constraint.width() >= 0.0 || constraint.height() >= 0.0 ) if ( constraint.width() >= 0.0 || constraint.height() >= 0.0 )
@ -225,211 +384,43 @@ QSizeF AnchorBox::layoutSizeHint( Qt::SizeHint which, const QSizeF& constraint )
void AnchorBox::updateHints() void AnchorBox::updateHints()
{ {
const auto& r0 = m_data->geometries[ const_cast< AnchorBox* >( this ) ];
Solver solver;
setupAnchorConstraints( false, solver );
setupSizeConstraints( true, solver );
{
solver.updateVariables();
m_data->hints[ Qt::PreferredSize ] = r0.size();
}
const double strength = 0.9 * Strength::required;
solver.addEditVariable( r0.width(), strength );
solver.addEditVariable( r0.height(), strength );
{
solver.suggestValue( r0.width(), 0.0 );
solver.suggestValue( r0.height(), 0.0 );
solver.updateVariables();
m_data->hints[ Qt::MinimumSize ] = r0.size();
}
{
/* /*
The solver seems to run into overflows with The solver seems to run into overflows with
std::numeric_limits< unsigned float >::max() std::numeric_limits< unsigned float >::max()
*/ */
const qreal max = std::numeric_limits< unsigned int >::max(); const qreal max = std::numeric_limits< unsigned int >::max();
solver.suggestValue( r0.width(), max ); LayoutSolver solver;
solver.suggestValue( r0.height(), max ); solver.setup( false, m_data->anchors, m_data->geometries );
solver.updateVariables(); m_data->hints[ Qt::PreferredSize ] = solver.resolvedSize();
m_data->hints[ Qt::MaximumSize ] = r0.size();
solver.addSizeConstraints();
m_data->hints[ Qt::MinimumSize ] = solver.resolvedSize( 0.0, 0.0 );
m_data->hints[ Qt::MaximumSize ] = solver.resolvedSize( max, max );
}
void AnchorBox::updateGeometries( const QRectF& rect )
{
auto& solver = m_data->solver;
if ( solver == nullptr )
{
solver = new LayoutSolver();
solver->setup( true, m_data->anchors, m_data->geometries );
solver->addSizeConstraints();
} }
}
void AnchorBox::geometryChangeEvent( QskGeometryChangeEvent* event ) solver->resolve( rect.width(), rect.height() );
{
Inherited::geometryChangeEvent( event );
if ( event->isResized() )
polish();
}
void AnchorBox::updateLayout()
{
if ( maybeUnresized() )
return;
const auto rect = layoutRect();
updateVariables( rect.width(), rect.height() );
const auto& geometries = m_data->geometries; const auto& geometries = m_data->geometries;
for ( auto it = geometries.begin(); it != geometries.end(); ++it ) for ( auto it = geometries.begin(); it != geometries.end(); ++it )
{ {
auto item = it.key(); auto r = it->second.rect();
if ( item != this )
{
auto r = it.value().rect();
r.translate( rect.left(), rect.top() ); r.translate( rect.left(), rect.top() );
qskSetItemGeometry( item, r ); qskSetItemGeometry( it->first, r );
}
}
}
void AnchorBox::updateVariables( qreal width, qreal height )
{
const auto& r0 = m_data->geometries[ this ];
Solver& solver = m_data->solver;
if ( !solver.hasConstraints() )
{
setupAnchorConstraints( true, solver );
setupSizeConstraints( true, solver );
const double strength = 0.9 * Strength::required;
solver.addEditVariable( r0.width(), strength );
solver.addEditVariable( r0.height(), strength );
}
solver.suggestValue( r0.width(), width );
solver.suggestValue( r0.height(), height );
solver.updateVariables();
}
void AnchorBox::setupAnchorConstraints( bool layoutChildren, Solver& solver )
{
auto& geometries = m_data->geometries;
for ( const auto& anchor : m_data->anchors )
{
auto& r1 = geometries[ anchor.item1 ];
auto& r2 = geometries[ anchor.item2 ];
const auto expr1 = r1.expressionAt( anchor.edge1 );
Expression expr2;
if ( anchor.item2 == this )
{
switch( anchor.edge2 )
{
case Qt::AnchorLeft:
case Qt::AnchorTop:
expr2 = 0;
break;
case Qt::AnchorHorizontalCenter:
expr2 = Term( 0.5 * r2.width() );
break;
case Qt::AnchorRight:
expr2 = Term( r2.width() );
break;
case Qt::AnchorVerticalCenter:
expr2 = Term( 0.5 * r2.height() );
break;
case Qt::AnchorBottom:
expr2 = Term( r2.height() );
break;
}
}
else
{
expr2 = r2.expressionAt( anchor.edge2 );
}
solver.addConstraint( expr1 == expr2 );
#if 1
if ( layoutChildren && anchor.item2 != this )
{
const auto o = qskOrientation( anchor.edge1 );
/*
A constraint with medium strength to make anchored item
being stretched according to their stretch factors s1, s2.
( For the moment we don't support having specific factors. )
*/
const auto s1 = 1.0;
const auto s2 = 1.0;
Constraint c( r1.length( o ) * s1 == r2.length( o ) * s2, Strength::medium );
solver.addConstraint( c );
}
#endif
}
}
void AnchorBox::setupSizeConstraints( bool layoutChildren, Solver& solver )
{
auto& geometries = m_data->geometries;
for ( auto it = geometries.begin(); it != geometries.end(); ++it )
{
const auto item = it.key();
if ( item == this )
continue;
auto& r = it.value();
{
// minimum size
const auto minSize = qskSizeConstraint( item, Qt::MinimumSize );
if ( minSize.width() >= 0.0 )
solver.addConstraint( r.width() >= minSize.width() );
if ( minSize.height() >= 0.0 )
solver.addConstraint( r.height() >= minSize.height() );
}
if ( layoutChildren )
{
// preferred size
const auto prefSize = qskSizeConstraint( item, Qt::PreferredSize );
Constraint c1( r.width() == prefSize.width(), Strength::strong );
solver.addConstraint( c1 );
Constraint c2( r.height() == prefSize.height(), Strength::strong );
solver.addConstraint( c2 );
}
{
// maximum size
const auto maxSize = qskSizeConstraint( item, Qt::MaximumSize );
if ( maxSize.width() >= 0.0 )
solver.addConstraint( r.width() <= maxSize.width() );
if ( maxSize.height() >= 0.0 )
solver.addConstraint( r.height() <= maxSize.height() );
}
} }
} }

View File

@ -8,8 +8,6 @@
#include "QskControl.h" #include "QskControl.h"
class Solver;
class AnchorBox : public QskControl class AnchorBox : public QskControl
{ {
Q_OBJECT Q_OBJECT
@ -39,9 +37,7 @@ class AnchorBox : public QskControl
private: private:
void updateHints(); void updateHints();
void updateVariables( qreal width, qreal height ); void updateGeometries( const QRectF& );
void setupAnchorConstraints( bool layoutChildren, Solver& );
void setupSizeConstraints( bool layoutChildren, Solver& );
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;