Features/anchoring (#434)

* initial version of QskItemAnchors introduced

* QskItemAnchors completed ( baseline anchoring is the only missing API )

* missing initialization added
This commit is contained in:
uwerat 2024-10-10 15:55:22 +02:00 committed by GitHub
parent d6b9ea4525
commit 51d1bdbc95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 370 additions and 0 deletions

View File

@ -219,6 +219,7 @@ list(APPEND HEADERS
controls/QskGraphicLabelSkinlet.h
controls/QskHintAnimator.h
controls/QskItem.h
controls/QskItemAnchors.h
controls/QskListView.h
controls/QskListViewSkinlet.h
controls/QskMenu.h
@ -325,6 +326,7 @@ list(APPEND SOURCES
controls/QskInputGrabber.cpp
controls/QskItem.cpp
controls/QskItemPrivate.cpp
controls/QskItemAnchors.cpp
controls/QskListView.cpp
controls/QskListViewSkinlet.cpp
controls/QskMenuSkinlet.cpp

View File

@ -0,0 +1,279 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskItemAnchors.h"
#include "QskMargins.h"
QSK_QT_PRIVATE_BEGIN
#include <private/qquickanchors_p.h>
#include <private/qquickanchors_p_p.h>
#include <private/qquickitem_p.h>
QSK_QT_PRIVATE_END
QQuickAnchors* qskGetOrCreateAnchors( QQuickItem* item )
{
if ( item == nullptr )
return nullptr;
return QQuickItemPrivate::get( item )->anchors();
}
QQuickAnchors* qskGetAnchors( QQuickItem* item )
{
if ( item == nullptr )
return nullptr;
return QQuickItemPrivate::get( item )->_anchors;
}
const QQuickAnchors* qskGetAnchors( const QQuickItem* item )
{
return qskGetAnchors( const_cast< QQuickItem* >( item ) );
}
namespace
{
inline QQuickAnchors::Anchor toQuickAnchor( Qt::AnchorPoint edge )
{
using A = QQuickAnchors;
switch( edge )
{
case Qt::AnchorLeft: return A::LeftAnchor;
case Qt::AnchorHorizontalCenter: return A::HCenterAnchor;
case Qt::AnchorRight: return A::RightAnchor;
case Qt::AnchorTop: return A::TopAnchor;
case Qt::AnchorVerticalCenter: return A::VCenterAnchor;
case Qt::AnchorBottom: return A::BottomAnchor;
default: return A::InvalidAnchor;
}
}
inline Qt::AnchorPoint toAnchorPoint( QQuickAnchors::Anchor anchor )
{
using A = QQuickAnchors;
switch( anchor )
{
case A::LeftAnchor: return Qt::AnchorLeft;
case A::HCenterAnchor: return Qt::AnchorHorizontalCenter;
case A::RightAnchor: return Qt::AnchorRight;
case A::TopAnchor: return Qt::AnchorTop;
case A::VCenterAnchor: return Qt::AnchorVerticalCenter;
case A::BottomAnchor: return Qt::AnchorBottom;
case A::BaselineAnchor: return Qt::AnchorTop; // not supported
default: return Qt::AnchorLeft;
}
}
struct AnchorOperators
{
QQuickAnchorLine ( QQuickAnchors::*line ) () const;
void ( QQuickAnchors::*setLine )( const QQuickAnchorLine& );
void ( QQuickAnchors::*resetLine )();
};
const AnchorOperators& operators( Qt::AnchorPoint edge )
{
using A = QQuickAnchors;
static constexpr AnchorOperators table[] =
{
// in order of Qt::AnchorPoint
{ &A::left, &A::setLeft, &A::resetLeft },
{ &A::horizontalCenter, &A::setHorizontalCenter, &A::resetHorizontalCenter },
{ &A::right, &A::setRight, &A::resetRight },
{ &A::top, &A::setTop, &A::resetTop },
{ &A::verticalCenter, &A::setVerticalCenter, &A::resetVerticalCenter },
{ &A::bottom, &A::setBottom, &A::resetBottom }
};
return table[edge];
}
}
QskItemAnchors::QskItemAnchors( QQuickItem* anchoredItem )
: m_anchoredItem( anchoredItem )
{
}
QskItemAnchors::~QskItemAnchors()
{
}
bool QskItemAnchors::operator==( const QskItemAnchors& other ) const noexcept
{
return m_anchoredItem.data() == other.m_anchoredItem.data();
}
QQuickItem* QskItemAnchors::anchoredItem() const
{
return m_anchoredItem;
}
QMarginsF QskItemAnchors::margins() const
{
if ( const auto anchors = qskGetAnchors( m_anchoredItem ) )
{
return QMarginsF( anchors->leftMargin(), anchors->topMargin(),
anchors->rightMargin(), anchors->bottomMargin() );
}
return QMarginsF();
}
void QskItemAnchors::setMargins( const QMarginsF& margins )
{
if ( const auto anchors = qskGetOrCreateAnchors( m_anchoredItem ) )
{
anchors->setLeftMargin( margins.left() );
anchors->setRightMargin( margins.right() );
anchors->setTopMargin( margins.top() );
anchors->setBottomMargin( margins.bottom() );
}
}
void QskItemAnchors::setCenterOffset( Qt::Orientation orientation, qreal offset )
{
if ( const auto anchors = qskGetOrCreateAnchors( m_anchoredItem ) )
{
if ( orientation == Qt::Horizontal )
anchors->setHorizontalCenterOffset( offset );
else
anchors->setVerticalCenterOffset( offset );
}
}
qreal QskItemAnchors::centerOffset( Qt::Orientation orientation )
{
if ( const auto anchors = qskGetOrCreateAnchors( m_anchoredItem ) )
{
if ( orientation == Qt::Horizontal )
return anchors->horizontalCenterOffset();
else
return anchors->verticalCenterOffset();
}
return 0.0;
}
void QskItemAnchors::addAnchors( Qt::Corner corner,
QQuickItem* baseItem, Qt::Corner baseCorner )
{
auto anchorPoint =
[]( Qt::Corner corner, Qt::Orientation orientation )
{
if ( orientation == Qt::Horizontal )
return ( corner & 0x1 ) ? Qt::AnchorRight : Qt::AnchorLeft;
else
return ( corner >= 0x2 ) ? Qt::AnchorBottom : Qt::AnchorTop;
};
addAnchor( anchorPoint( corner, Qt::Horizontal ),
baseItem, anchorPoint( baseCorner, Qt::Horizontal ) );
addAnchor( anchorPoint( corner, Qt::Vertical ),
baseItem, anchorPoint( baseCorner, Qt::Vertical ) );
}
void QskItemAnchors::addAnchors( QQuickItem* baseItem, Qt::Orientations orientations )
{
if ( orientations & Qt::Horizontal )
{
addAnchor( Qt::AnchorLeft, baseItem, Qt::AnchorLeft );
addAnchor( Qt::AnchorRight, baseItem, Qt::AnchorRight );
}
if ( orientations & Qt::Vertical )
{
addAnchor( Qt::AnchorTop, baseItem, Qt::AnchorTop );
addAnchor( Qt::AnchorBottom, baseItem, Qt::AnchorBottom );
}
}
void QskItemAnchors::addAnchor( Qt::AnchorPoint edge, QQuickItem* baseItem,
Qt::AnchorPoint baseEdge )
{
if ( const auto anchors = qskGetOrCreateAnchors( m_anchoredItem ) )
{
const auto& ops = operators( edge );
( anchors->*ops.setLine )( { baseItem, toQuickAnchor( baseEdge ) } );
}
}
void QskItemAnchors::removeAnchor( Qt::AnchorPoint edge )
{
if ( const auto anchors = qskGetAnchors( m_anchoredItem ) )
{
const auto& ops = operators( edge );
( anchors->*ops.resetLine ) ();
}
}
QQuickItem* QskItemAnchors::baseItem( Qt::AnchorPoint edge ) const
{
if ( const auto anchors = qskGetAnchors( m_anchoredItem ) )
{
const auto& ops = operators( edge );
return ( ( anchors->*ops.line ) () ).item;
}
return nullptr;
}
Qt::AnchorPoint QskItemAnchors::basePosition( Qt::AnchorPoint edge ) const
{
if ( const auto anchors = qskGetAnchors( m_anchoredItem ) )
{
/*
Anchoring to the baseline of the anchoredItem might have been
done in QML code. As Qt::AnchorPoint does not have a corresponding
value for it and QSkinny does not support the baseline concept at all
we are lying and report Qt::AnchorTop instead. Hm ...
*/
const auto& ops = operators( edge );
return toAnchorPoint( ( ( anchors->*ops.line ) () ).anchorLine );
}
return Qt::AnchorLeft; // something
}
void QskItemAnchors::setControlItem( QQuickItem* item, bool adjustSize )
{
if ( const auto anchors = qskGetOrCreateAnchors( m_anchoredItem ) )
{
if ( adjustSize )
anchors->setFill( item );
else
anchors->setCenterIn( item );
}
}
void QskItemAnchors::removeControlItem( bool adjustSize )
{
if ( auto anchors = qskGetAnchors( m_anchoredItem ) )
{
if ( adjustSize )
anchors->resetFill();
else
anchors->resetCenterIn();
}
}
QQuickItem* QskItemAnchors::controlItem( bool adjustSize ) const
{
if ( const auto anchors = qskGetAnchors( m_anchoredItem ) )
{
if ( adjustSize )
return anchors->fill();
else
return anchors->centerIn();
}
return nullptr;
}

View File

@ -0,0 +1,89 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_ITEM_ANCHORS_H
#define QSK_ITEM_ANCHORS_H
#include "QskGlobal.h"
#include <qnamespace.h>
#include <qpointer.h>
class QMarginsF;
class QQuickItem;
/*
QskItemAnchors is a C++ API to access the Qt/Quick anchoring,
that has been designed to be used from QML.
Qt/Quick anchoring is a simple concept, that adjusts the
edges of the anchoredItem whenever the geometry of a baseItem
has changed. A baseItem needs to be the parent or a sibling
of the anchoredItem.
Note that Qt/Quick anchoring is labeled as "positioner", what means
that it is not capable of handling typical layout scenarios, like
distributing the space of a bounding rectangle to a chainn of
anchored children.
For some reason the implementation allows to define conflicting definitions
and resolves them by applying only one of the definitions in
the following precedence:
1) fill
2) centerIn
3) anchors
Limitations:
- access to baseline settings are not implemented
*/
class QSK_EXPORT QskItemAnchors
{
public:
QskItemAnchors( QQuickItem* = nullptr );
~QskItemAnchors();
QQuickItem* anchoredItem() const;
QQuickItem* baseItem( Qt::AnchorPoint ) const;
Qt::AnchorPoint basePosition( Qt::AnchorPoint ) const;
bool operator==( const QskItemAnchors& ) const noexcept;
bool operator!=( const QskItemAnchors& ) const noexcept;
QMarginsF margins() const;
void setMargins( const QMarginsF& );
void setCenterOffset( Qt::Orientation, qreal offset );
qreal centerOffset( Qt::Orientation );
void addAnchor( Qt::AnchorPoint, QQuickItem*, Qt::AnchorPoint );
void addAnchors( Qt::Corner, QQuickItem*, Qt::Corner );
void addAnchors( QQuickItem*, Qt::Orientations = Qt::Horizontal | Qt::Vertical );
void removeAnchor( Qt::AnchorPoint );
/*
Qt/Quick anchoring knows the convenience modes "fill" and "centerIn".
Internally these modes are not(!) mapped to anchor definitions.
Both modes are setting the center point of the anchoredItem to the center
of the controlItem. "fill" also adjusts the size.
*/
QQuickItem* controlItem( bool adjustSize ) const;
void setControlItem( QQuickItem*, bool adjustSize );
void removeControlItem( bool adjustSize );
private:
QPointer< QQuickItem > m_anchoredItem;
};
inline bool QskItemAnchors::operator!=(
const QskItemAnchors& other ) const noexcept
{
return !( *this == other );
}
#endif