code cleanup
This commit is contained in:
parent
09117c4f6d
commit
cccf946144
@ -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() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user