splitting QskControl.cpp

This commit is contained in:
Uwe Rathmann 2019-09-04 06:59:43 +02:00
parent fcd455a545
commit b80b9b709c
10 changed files with 1622 additions and 1403 deletions

File diff suppressed because it is too large Load Diff

View File

@ -4,27 +4,23 @@
*****************************************************************************/
#ifndef QSK_CONTROL_H
#define QSK_CONTROL_H 1
#define QSK_CONTROL_H
#include "QskQuickItem.h"
#include "QskSkinnable.h"
#include "QskAspect.h"
#include "QskGradient.h"
#include "QskSizePolicy.h"
#include "QskSkinnable.h"
#include <qlocale.h>
#include <qquickitem.h>
#include <memory>
class QskControlPrivate;
class QskGeometryChangeEvent;
class QskWindowChangeEvent;
class QskGestureEvent;
template< typename T > class QVector;
class QSK_EXPORT QskControl : public QQuickItem, public QskSkinnable
class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable
{
Q_OBJECT
@ -32,16 +28,10 @@ class QSK_EXPORT QskControl : public QQuickItem, public QskSkinnable
WRITE setLocale RESET resetLocale NOTIFY localeChanged FINAL )
Q_PROPERTY( bool autoFillBackground READ autoFillBackground
WRITE setAutoFillBackground NOTIFY controlFlagsChanged FINAL )
WRITE setAutoFillBackground FINAL )
Q_PROPERTY( bool autoLayoutChildren READ autoLayoutChildren
WRITE setAutoLayoutChildren NOTIFY controlFlagsChanged FINAL )
Q_PROPERTY( bool polishOnResize READ polishOnResize
WRITE setPolishOnResize NOTIFY controlFlagsChanged FINAL )
Q_PROPERTY( bool transparentForPositioners READ isTransparentForPositioner
WRITE setTransparentForPositioner NOTIFY controlFlagsChanged FINAL )
WRITE setAutoLayoutChildren FINAL )
Q_PROPERTY( Qt::FocusPolicy focusPolicy READ focusPolicy
WRITE setFocusPolicy NOTIFY focusPolicyChanged FINAL )
@ -49,9 +39,6 @@ class QSK_EXPORT QskControl : public QQuickItem, public QskSkinnable
Q_PROPERTY( bool wheelEnabled READ isWheelEnabled
WRITE setWheelEnabled NOTIFY wheelEnabledChanged FINAL )
Q_PROPERTY( bool tabFence READ isTabFence
WRITE setTabFence NOTIFY controlFlagsChanged FINAL )
Q_PROPERTY( QMarginsF margins READ margins
WRITE setMargins RESET resetMargins NOTIFY marginsChanged )
@ -61,40 +48,17 @@ class QSK_EXPORT QskControl : public QQuickItem, public QskSkinnable
Q_PROPERTY( QskSizePolicy sizePolicy READ sizePolicy WRITE setSizePolicy )
Q_PROPERTY( QSizeF minimumSize READ minimumSize WRITE setMinimumSize )
Q_PROPERTY( QSizeF maximumSize READ maximumSize WRITE setMaximumSize )
Q_PROPERTY( QSizeF preferredSize READ preferredSize WRITE setPreferredSize )
Q_PROPERTY( QRectF geometry READ geometry WRITE setGeometry )
using Inherited = QQuickItem;
using Inherited = QskQuickItem;
public:
QSK_STATES( Disabled, Hovered, Focused )
enum Flag
{
DeferredUpdate = 1 << 0,
DeferredPolish = 1 << 1,
DeferredLayout = 1 << 2,
CleanupOnVisibility = 1 << 3,
PreferRasterForTextures = 1 << 4,
DebugForceBackground = 1 << 7,
LastFlag = DebugForceBackground
};
Q_ENUM( Flag )
Q_DECLARE_FLAGS( Flags, Flag )
QskControl( QQuickItem* parent = nullptr );
~QskControl() override;
const char* className() const;
void setMargins( qreal );
void setMargins( const QMarginsF& );
void resetMargins();
@ -106,9 +70,6 @@ class QSK_EXPORT QskControl : public QQuickItem, public QskSkinnable
void resetBackground();
QskGradient background() const;
QRectF geometry() const;
QRectF rect() const;
QRectF contentsRect() const;
QRectF layoutRect() const;
@ -122,32 +83,15 @@ class QSK_EXPORT QskControl : public QQuickItem, public QskSkinnable
void setAutoFillBackground( bool );
bool autoFillBackground() const;
void setPolishOnResize( bool );
bool polishOnResize() const;
void setAutoLayoutChildren( bool );
bool autoLayoutChildren() const;
void setTransparentForPositioner( bool );
bool isTransparentForPositioner() const;
void setWheelEnabled( bool );
bool isWheelEnabled() const;
void setFocusPolicy( Qt::FocusPolicy );
Qt::FocusPolicy focusPolicy() const;
void setTabFence( bool );
bool isTabFence() const;
void setControlFlags( Flags );
void resetControlFlags();
Flags controlFlags() const;
Q_INVOKABLE void setControlFlag( Flag, bool on = true );
Q_INVOKABLE void resetControlFlag( Flag );
Q_INVOKABLE bool testControlFlag( Flag ) const;
void setSizePolicy( QskSizePolicy::Policy, QskSizePolicy::Policy );
void setSizePolicy( QskSizePolicy );
void setSizePolicy( Qt::Orientation, QskSizePolicy::Policy );
@ -195,24 +139,9 @@ class QSK_EXPORT QskControl : public QQuickItem, public QskSkinnable
virtual QSizeF contentsSizeHint() const;
bool isVisibleTo( const QQuickItem* ) const;
QLocale locale() const;
void resetLocale();
void setLayoutMirroring( bool on, bool recursive = false );
void resetLayoutMirroring();
bool layoutMirroring() const;
QSizeF size() const;
QSizeF implicitSize() const;
void setGeometry( qreal x, qreal y, qreal width, qreal height );
bool isPolishScheduled() const;
bool isUpdateNodeScheduled() const;
bool isInitiallyPainted() const;
QVector< QskAspect::Subcontrol > subControls() const;
Q_SIGNALS:
@ -220,26 +149,15 @@ class QSK_EXPORT QskControl : public QQuickItem, public QskSkinnable
void marginsChanged();
void focusIndicatorRectChanged();
void localeChanged( const QLocale& );
void controlFlagsChanged();
void focusPolicyChanged();
void wheelEnabledChanged();
public Q_SLOTS:
void setGeometry( const QRectF& );
void setLocale( const QLocale& );
void show();
void hide();
void setVisible( bool );
void resetImplicitSize();
protected:
bool event( QEvent* ) override;
virtual void changeEvent( QEvent* );
virtual void geometryChangeEvent( QskGeometryChangeEvent* );
virtual void windowChangeEvent( QskWindowChangeEvent* );
virtual void gestureEvent( QskGestureEvent* );
void hoverEnterEvent( QHoverEvent* ) override;
@ -251,61 +169,26 @@ class QSK_EXPORT QskControl : public QQuickItem, public QskSkinnable
void itemChange( ItemChange, const ItemChangeData& ) override;
void geometryChanged( const QRectF&, const QRectF& ) override;
void windowDeactivateEvent() override;
void classBegin() override;
void componentComplete() override;
void releaseResources() override;
void initSizePolicy( QskSizePolicy::Policy, QskSizePolicy::Policy );
void cleanupNodes();
virtual void aboutToShow(); // called in updatePolish
virtual void updateLayout(); // called in updatePolish
bool maybeUnresized() const;
// called from updatePolish
virtual void updateResources();
virtual void updateLayout();
private:
// don't use boundingRect - it seems to be deprecated
QRectF boundingRect() const override final { return rect(); }
/*
childrenRect()/childrenRectChanged does not make much sense
in a system, where the parent is responsible for laying out
its children.
*/
void childrenRect() = delete;
void setActiveFocusOnTab( bool ) = delete; // use setFocusPolicy
void updateInputMethod( Qt::InputMethodQueries ) = delete; // use qskUpdateInputMethod
QSGNode* updatePaintNode( QSGNode*, UpdatePaintNodeData* ) override final;
void updatePolish() override final;
QSGNode* updateItemPaintNode( QSGNode* ) override final;
void updateItemPolish() override final;
QskControl* owningControl() const override final;
void layoutConstraintChanged();
void updateControlFlag( uint flag, bool on );
private:
Q_DECLARE_PRIVATE( QskControl )
};
inline void QskControl::setGeometry( const QRectF& rect )
{
setGeometry( rect.x(), rect.y(), rect.width(), rect.height() );
}
inline QSizeF QskControl::size() const
{
return QSizeF( width(), height() );
}
inline QSizeF QskControl::implicitSize() const
{
return QSizeF( implicitWidth(), implicitHeight() );
}
inline QSizeF QskControl::sizeHint() const
{
return effectiveSizeHint( Qt::PreferredSize );
@ -336,7 +219,4 @@ inline const QskControl* qskControlCast( const QObject* object )
return qobject_cast< const QskControl* >( object );
}
Q_DECLARE_OPERATORS_FOR_FLAGS( QskControl::Flags )
Q_DECLARE_METATYPE( QskControl::Flags )
#endif

View File

@ -0,0 +1,184 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskControlPrivate.h"
#include "QskSetup.h"
#include "QskLayoutConstraint.h"
static inline void qskSendEventTo( QObject* object, QEvent::Type type )
{
QEvent event( type );
QCoreApplication::sendEvent( object, &event );
}
/*
Qt 5.12:
sizeof( QQuickItemPrivate::ExtraData ) -> 184
sizeof( QQuickItemPrivate ) -> 320
( these numbers include pointers to optional extra data, but not
the size for the extra data. So the effective memory footprint,
is often even worse ).
sizeof( QskControlPrivate ) -> sizeof( QQuickItemPrivate ) + 32
sizeof( QskSkinnable::PrivateData ) -> 40
It might be possible to save some bytes, but in the end QskControl
is heavy simply because of deriving from QQuickItem. So without
patching Qt the only way to limit the memory footprint of an application
substantially is to limit the number of QQuickItems.
That's why QSkinny builds more complex controls from scene graph nodes
instead of doing QQuickItem composition. As this can only be done
in C++ it is kind of obvious, why it is often a bad idea to build
custom controls in QML.
*/
QskControlPrivate::QskControlPrivate()
: explicitSizeHints( nullptr )
, sizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Preferred )
, explicitLocale( false )
, autoFillBackground( false )
, autoLayoutChildren( false )
, focusPolicy( Qt::NoFocus )
, isWheelEnabled( false )
{
}
QskControlPrivate::~QskControlPrivate()
{
delete [] explicitSizeHints;
}
void QskControlPrivate::implicitSizeChanged()
{
Q_Q( QskControl );
if ( !q->explicitSizeHint( Qt::PreferredSize ).isValid() )
{
// when we have no PreferredSize we fall back
// to the implicit size
layoutConstraintChanged();
}
}
QSizeF QskControlPrivate::implicitSizeHint() const
{
Q_Q( const QskControl );
const auto m = q->margins();
const auto dw = m.left() + m.right();
const auto dh = m.top() + m.bottom();
const auto hint = q->contentsSizeHint();
const qreal w = ( hint.width() >= 0 ) ? dw + hint.width() : -1.0;
const qreal h = ( hint.height() >= 0 ) ? dh + hint.height() : -1.0;
return QSizeF( w, h );
}
void QskControlPrivate::setExplicitSizeHint(
Qt::SizeHint whichHint, const QSizeF& size )
{
if ( explicitSizeHints == nullptr )
{
using namespace QskLayoutConstraint;
explicitSizeHints = new QSizeF[3];
explicitSizeHints[0] = defaultSizeHints[0];
explicitSizeHints[1] = defaultSizeHints[1];
explicitSizeHints[2] = defaultSizeHints[2];
}
explicitSizeHints[ whichHint ] = size;
}
void QskControlPrivate::resetExplicitSizeHint( Qt::SizeHint whichHint )
{
if ( explicitSizeHints )
{
using namespace QskLayoutConstraint;
explicitSizeHints[ whichHint ] = defaultSizeHints[ whichHint ];
}
}
QSizeF QskControlPrivate::explicitSizeHint( Qt::SizeHint whichHint ) const
{
if ( explicitSizeHints )
return explicitSizeHints[ whichHint ];
return QskLayoutConstraint::defaultSizeHints[ whichHint ];
}
bool QskControlPrivate::maybeGesture( QQuickItem* child, QEvent* event )
{
Q_Q( QskControl );
switch ( event->type() )
{
case QEvent::MouseButtonPress:
{
const auto mouseEvent = static_cast< const QMouseEvent* >( event );
const QPointF pos = q->mapFromScene( mouseEvent->windowPos() );
if ( !q->gestureRect().contains( pos ) )
return false;
break;
}
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::UngrabMouse:
case QEvent::TouchBegin:
case QEvent::TouchCancel:
case QEvent::TouchUpdate:
break;
default:
return false;
}
return q->gestureFilter( child, event );
}
QSGTransformNode* QskControlPrivate::createTransformNode()
{
return Inherited::createTransformNode();
}
void QskControlPrivate::transformChanged()
{
Inherited::transformChanged();
}
bool QskControlPrivate::inheritLocale( QskControl* control, const QLocale& locale )
{
auto d = static_cast< QskControlPrivate* >( QQuickItemPrivate::get( control ) );
if ( d->explicitLocale || d->locale == locale )
return true;
d->locale = locale;
qskSendEventTo( control, QEvent::LocaleChange );
return false;
}
void QskControlPrivate::resolveLocale( QskControl* control )
{
const auto locale = qskSetup->inheritedLocale( control );
auto d = static_cast< QskControlPrivate* >( QQuickItemPrivate::get( control ) );
if ( d->locale != locale )
{
d->locale = locale;
qskSendEventTo( control, QEvent::LocaleChange );
qskSetup->inheritLocale( control, locale );
}
}

View File

@ -0,0 +1,56 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_CONTROL_PRIVATE_H
#define QSK_CONTROL_PRIVATE_H
#include "QskGlobal.h"
#include "QskControl.h"
#include "QskQuickItemPrivate.h"
class QskControlPrivate : public QskQuickItemPrivate
{
using Inherited = QskQuickItemPrivate;
public:
static bool inheritLocale( QskControl*, const QLocale& );
static void resolveLocale( QskControl* );
protected:
QskControlPrivate();
~QskControlPrivate() override;
private:
QSGTransformNode* createTransformNode() override;
void transformChanged() override;
void setExplicitSizeHint( Qt::SizeHint, const QSizeF& );
void resetExplicitSizeHint( Qt::SizeHint );
QSizeF explicitSizeHint( Qt::SizeHint ) const;
bool maybeGesture( QQuickItem*, QEvent* );
QSizeF implicitSizeHint() const override final;
void implicitSizeChanged() override final;
private:
Q_DECLARE_PUBLIC( QskControl )
QSizeF* explicitSizeHints;
QLocale locale;
QskSizePolicy sizePolicy;
bool explicitLocale : 1;
bool autoFillBackground : 1;
bool autoLayoutChildren : 1;
uint focusPolicy : 4;
bool isWheelEnabled : 1;
};
#endif

View File

@ -0,0 +1,842 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskQuickItem.h"
#include "QskQuickItemPrivate.h"
#include "QskQuick.h"
#include "QskEvent.h"
#include "QskSetup.h"
#include "QskSkin.h"
#include "QskDirtyItemFilter.h"
#include <qglobalstatic.h>
#include <qquickwindow.h>
#if defined( QT_DEBUG )
QSK_QT_PRIVATE_BEGIN
#include <private/qquickpositioners_p.h>
QSK_QT_PRIVATE_END
#endif
#include <unordered_set>
static inline void qskSendEventTo( QObject* object, QEvent::Type type )
{
QEvent event( type );
QCoreApplication::sendEvent( object, &event );
}
static inline void qskUpdateControlFlags(
QskQuickItem::Flags flags, QskQuickItem* item )
{
auto d = static_cast< QskQuickItemPrivate* >( QskQuickItemPrivate::get( item ) );
d->updateControlFlags( flags );
}
static inline quint16 qskControlFlags()
{
// we are only interested in the first 8 bits
return static_cast< quint16 >( qskSetup->controlFlags() );
}
static inline void qskFilterWindow( QQuickWindow* window )
{
if ( window == nullptr )
return;
static QskDirtyItemFilter itemFilter;
itemFilter.addWindow( window );
}
namespace
{
class QskQuickItemRegistry
{
public:
QskQuickItemRegistry()
{
/*
Its faster and saves some memory to have this registry instead
of setting up direct connections between qskSetup and each control
*/
QObject::connect( qskSetup, &QskSetup::controlFlagsChanged,
qskSetup, [ this ] { updateControlFlags(); } );
QObject::connect( qskSetup, &QskSetup::skinChanged,
qskSetup, [ this ] { updateSkin(); } );
}
inline void insert( QskQuickItem* item )
{
m_items.insert( item );
}
inline void remove( QskQuickItem* item )
{
m_items.erase( item );
}
void updateControlFlags()
{
const auto flags = static_cast< QskQuickItem::Flags >( qskControlFlags() );
for ( auto item : m_items )
qskUpdateControlFlags( flags, item );
}
void updateSkin()
{
QEvent event( QEvent::StyleChange );
for ( auto item : m_items )
{
event.setAccepted( true );
QCoreApplication::sendEvent( item, &event );
}
}
private:
std::unordered_set< QskQuickItem* > m_items;
};
}
namespace
{
/*
A helper class to store the released window to be able to
put it later into the WindowChange event.
*/
class QskWindowStore
{
public:
QskWindowStore()
: m_refCount( 0 )
, m_window( nullptr )
{
}
void setWindow( QQuickWindow* window )
{
if ( m_window != window )
{
m_window = window;
m_refCount = 0;
}
if ( m_window )
m_refCount++;
}
QQuickWindow* window()
{
QQuickWindow* w = m_window;
if ( m_window )
{
if ( --m_refCount == 0 )
m_window = nullptr;
}
return w;
}
private:
int m_refCount;
QQuickWindow* m_window;
};
}
Q_GLOBAL_STATIC( QskQuickItemRegistry, qskRegistry )
Q_GLOBAL_STATIC( QskWindowStore, qskReleasedWindowCounter )
QskQuickItem::QskQuickItem( QskQuickItemPrivate& dd, QQuickItem* parent )
: QQuickItem( dd, parent )
{
setFlag( QQuickItem::ItemHasContents, true );
// since Qt 5.10 we have QQuickItem::ItemEnabledHasChanged
#if QT_VERSION < QT_VERSION_CHECK( 5, 10, 0 )
/*
Setting up this connections slows down the time needed
for construction by almost 100%. Would be nice to
avoid this penalty also for earlier Qt versions.
*/
connect( this, &QQuickItem::enabledChanged,
[ this ] { qskSendEventTo( this, QEvent::EnabledChange ); } );
#endif
Q_D( QskQuickItem );
if ( d->controlFlags & QskQuickItem::DeferredUpdate )
qskFilterWindow( window() );
qskRegistry->insert( this );
}
QskQuickItem::~QskQuickItem()
{
/*
We set componentComplete to false, so that operations
that are triggered by detaching the item from its parent
can be aware of the about-to-delete state.
*/
d_func()->componentComplete = false;
if ( qskRegistry )
qskRegistry->remove( this );
#if QT_VERSION < QT_VERSION_CHECK( 5, 10, 0 )
disconnect( this, &QQuickItem::enabledChanged, nullptr, nullptr );
#endif
}
const char* QskQuickItem::className() const
{
return metaObject()->className();
}
void QskQuickItem::classBegin()
{
Inherited::classBegin();
}
void QskQuickItem::componentComplete()
{
#if defined( QT_DEBUG )
if ( qobject_cast< const QQuickBasePositioner* >( parent() ) )
{
if ( d_func()->controlFlags & QskQuickItem::DeferredLayout )
{
qWarning( "QskQuickItem in DeferredLayout mode under control of a positioner" );
}
}
#endif
Inherited::componentComplete();
}
void QskQuickItem::releaseResources()
{
Inherited::releaseResources();
// QQuickItem::derefWindow runs over the children between
// calling releaseResources and itemChange. So we need to have
// a reference count to know, when we have processed all
// sequences to be able to provide the correct "oldWindow"
// in the WindowChange event.
qskReleasedWindowCounter->setWindow( window() );
}
void QskQuickItem::setVisible( bool on )
{
// QQuickItem::setVisible is no slot
Inherited::setVisible( on );
}
void QskQuickItem::show()
{
Inherited::setVisible( true );
}
void QskQuickItem::hide()
{
Inherited::setVisible( false );
}
bool QskQuickItem::isVisibleTo( const QQuickItem* ancestor ) const
{
return qskIsVisibleTo( this, ancestor );
}
void QskQuickItem::setGeometry( qreal x, qreal y, qreal width, qreal height )
{
// QQuickItem does not even offer changing the geometry
// in one call - what leads to 2 calls of the updateGeometry
// hook. Grmpf ...
Q_D( QQuickItem );
d->heightValid = true;
d->widthValid = true;
const QRectF oldRect( d->x, d->y, d->width, d->height );
int dirtyType = 0;
if ( d->x != x || d->y != y )
{
d->x = x;
d->y = y;
dirtyType |= QQuickItemPrivate::Position;
}
if ( d->width != width || d->height != height )
{
d->height = height;
d->width = width;
dirtyType |= QQuickItemPrivate::Size;
}
if ( dirtyType )
{
if ( dirtyType & QQuickItemPrivate::Position )
d->dirty( QQuickItemPrivate::Position );
if ( dirtyType & QQuickItemPrivate::Size )
d->dirty( QQuickItemPrivate::Size );
/*
Unfortunately geometryChanged is protected and we can't implement
this code as qskSetItemGeometry - further hacking required: TODO ...
*/
geometryChanged( QRectF( d->x, d->y, d->width, d->height ), oldRect );
}
}
QRectF QskQuickItem::rect() const
{
Q_D( const QQuickItem );
return QRectF( 0, 0, d->width, d->height );
}
QRectF QskQuickItem::geometry() const
{
Q_D( const QQuickItem );
return QRectF( d->x, d->y, d->width, d->height );
}
void QskQuickItem::setTransparentForPositioner( bool on )
{
Q_D( QQuickItem );
if ( on != d->isTransparentForPositioner() )
{
d->setTransparentForPositioner( on );
Q_EMIT itemFlagsChanged();
}
}
bool QskQuickItem::isTransparentForPositioner() const
{
return d_func()->isTransparentForPositioner();
}
void QskQuickItem::setTabFence( bool on )
{
Q_D( QQuickItem );
if ( on != d->isTabFence )
{
d->isTabFence = on;
Q_EMIT itemFlagsChanged();
}
}
bool QskQuickItem::isTabFence() const
{
return d_func()->isTabFence;
}
void QskQuickItem::setPolishOnResize( bool on )
{
Q_D( QskQuickItem );
if ( on != d->polishOnResize )
{
d->polishOnResize = on;
polish();
Q_EMIT itemFlagsChanged();
}
}
bool QskQuickItem::polishOnResize() const
{
return d_func()->polishOnResize;
}
bool QskQuickItem::layoutMirroring() const
{
return d_func()->effectiveLayoutMirror;
}
void QskQuickItem::setLayoutMirroring( bool on, bool recursive )
{
// Again we have to deal with an existing API made for QML,
// that is weired for C++: LayoutMirroring/QQuickLayoutMirroringAttached
// Internally it is managed by 5(!) different flags - condolences
// to the poor guy who has been sentenced to maintain this.
// Anyway, the code below might achieve the desired behavior without
// breaking the QML path.
Q_D( QQuickItem );
if ( recursive != d->inheritMirrorFromItem )
{
d->inheritMirrorFromItem = recursive;
d->resolveLayoutMirror();
}
d->isMirrorImplicit = false;
if ( on != d->effectiveLayoutMirror )
{
d->setLayoutMirror( on );
if ( recursive )
d->resolveLayoutMirror();
}
}
void QskQuickItem::resetLayoutMirroring()
{
Q_D( QQuickItem );
if ( d && !d->isMirrorImplicit )
{
d->isMirrorImplicit = true;
// d->inheritMirrorFromItem = false;
d->resolveLayoutMirror();
}
}
bool QskQuickItem::isPolishScheduled() const
{
return d_func()->polishScheduled;
}
bool QskQuickItem::isUpdateNodeScheduled() const
{
Q_D( const QskQuickItem );
return ( d->dirtyAttributes & QQuickItemPrivate::ContentUpdateMask ) &&
( d->flags & QQuickItem::ItemHasContents );
}
bool QskQuickItem::isInitiallyPainted() const
{
return d_func()->isInitiallyPainted;
}
bool QskQuickItem::maybeUnresized() const
{
Q_D( const QskQuickItem );
if ( d->width <= 0.0 && d->height <= 0.0 )
{
/*
Unfortunately the list of items to-be-polished is not processed
in top/down order and we might run into updatePolish() before
having a proper size. But when the parentItem() is waiting
for to-be-polished, we assume, that we will be resized then
and run into another updatePolish() then.
*/
if ( d->polishOnResize && qskIsPolishScheduled( parentItem() ) )
return true;
}
return false;
}
QskQuickItem::Flags QskQuickItem::controlFlags() const
{
return QskQuickItem::Flags( d_func()->controlFlags );
}
void QskQuickItem::setControlFlags( Flags flags )
{
Q_D( QskQuickItem );
// set all bits in the mask
d->controlFlagsMask = std::numeric_limits< quint16 >::max();
d->updateControlFlags( flags );
}
void QskQuickItem::resetControlFlags()
{
Q_D( QskQuickItem );
// clear all bits in the mask
d->controlFlagsMask = 0;
d->updateControlFlags( static_cast< Flags >( qskControlFlags() ) );
}
void QskQuickItem::setControlFlag( Flag flag, bool on )
{
Q_D( QskQuickItem );
d->controlFlagsMask |= flag;
if ( ( d->controlFlags & flag ) != on )
{
updateControlFlag( flag, on );
Q_EMIT controlFlagsChanged();
}
}
void QskQuickItem::resetControlFlag( Flag flag )
{
Q_D( QskQuickItem );
d->controlFlagsMask &= ~flag;
const bool on = qskSetup->testControlFlag( static_cast< QskSetup::Flag >( flag ) );
if ( ( d->controlFlags & flag ) != on )
{
updateControlFlag( flag, on );
Q_EMIT controlFlagsChanged();
}
}
bool QskQuickItem::testControlFlag( Flag flag ) const
{
return d_func()->controlFlags & flag;
}
void QskQuickItem::updateControlFlag( uint flag, bool on )
{
Q_D( QskQuickItem );
if ( ( flag > std::numeric_limits< quint16 >::max() ) ||
( bool( d->controlFlags & flag ) == on ) )
{
return;
}
if ( on )
d->controlFlags |= flag;
else
d->controlFlags &= ~flag;
switch ( flag )
{
case QskQuickItem::DeferredUpdate:
{
if ( on )
{
qskFilterWindow( window() );
}
else
{
if ( !isVisible() )
update();
}
break;
}
case QskQuickItem::DeferredPolish:
{
if ( !on && d->blockedPolish )
polish();
break;
}
case QskQuickItem::DeferredLayout:
{
if ( !on )
{
// Update the implicitSize and rebind the size to it.
// Having set the size explicitly gets lost.
d->widthValid = d->heightValid = false;
d->updateImplicitSize( false );
}
break;
}
case QskQuickItem::CleanupOnVisibility:
{
if ( on && !isVisible() )
d->cleanupNodes();
break;
}
case QskQuickItem::DebugForceBackground:
{
// no need to mark it dirty
if ( flags() & QQuickItem::ItemHasContents )
update();
break;
}
default:
break;
}
}
void QskQuickItem::resetImplicitSize()
{
Q_D( QskQuickItem );
if ( d->controlFlags & QskQuickItem::DeferredLayout )
{
d->blockedImplicitSize = true;
d->layoutConstraintChanged();
}
else
{
d->updateImplicitSize( true );
}
}
bool QskQuickItem::event( QEvent* event )
{
const int eventType = event->type();
switch( eventType )
{
case QEvent::StyleChange:
{
d_func()->clearPreviousNodes = true;
resetImplicitSize();
polish();
if ( flags() & QQuickItem::ItemHasContents )
update();
changeEvent( event );
return true;
}
case QEvent::ContentsRectChange:
{
if ( polishOnResize() )
polish();
changeEvent( event );
return true;
}
case QEvent::EnabledChange:
case QEvent::FontChange:
case QEvent::PaletteChange:
case QEvent::LocaleChange:
case QEvent::ReadOnlyChange:
case QEvent::ParentChange:
{
changeEvent( event );
return true;
}
case QskEvent::GeometryChange:
{
geometryChangeEvent( static_cast< QskGeometryChangeEvent* >( event ) );
return true;
}
case QskEvent::WindowChange:
{
windowChangeEvent( static_cast< QskWindowChangeEvent* >( event ) );
return true;
}
case QEvent::LayoutRequest:
{
if ( d_func()->polishOnResize )
polish();
return true;
}
}
return Inherited::event( event );
}
void QskQuickItem::windowChangeEvent( QskWindowChangeEvent* )
{
}
void QskQuickItem::geometryChangeEvent( QskGeometryChangeEvent* )
{
}
void QskQuickItem::changeEvent( QEvent* )
{
}
void QskQuickItem::itemChange( QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData& value )
{
switch ( change )
{
case QQuickItem::ItemSceneChange:
{
if ( value.window )
{
Q_D( const QskQuickItem );
if ( d->controlFlags & QskQuickItem::DeferredUpdate )
qskFilterWindow( value.window );
}
#if 1
auto oldWindow = qskReleasedWindowCounter->window();
if ( oldWindow && ( oldWindow->activeFocusItem() == this ) )
{
/*
Removing an item from the scene might result in
changes of the active focus item. Unfortunately the corresponding
FocusIn/Out events are sent, while the item tree is in an
invalid state.
When having event handlers, that do modifications of the focus
( f.e. assigning the local focus, inside of a focus scope )
we might end up with having a dangling pointer for
oldWindow->activeFocusItem().
*/
QQuickWindowPrivate::get( oldWindow )->clearFocusInScope(
qskNearestFocusScope( this ), this, Qt::OtherFocusReason );
}
#endif
QskWindowChangeEvent event( oldWindow, value.window );
QCoreApplication::sendEvent( this, &event );
break;
}
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
case QQuickItem::ItemEnabledHasChanged:
{
qskSendEventTo( this, QEvent::EnabledChange );
break;
}
#endif
case QQuickItem::ItemVisibleHasChanged:
{
Q_D( QskQuickItem );
#if 1
/*
~QQuickItem sends QQuickItem::ItemVisibleHasChanged recursively
to all childItems. When being a child ( not only a childItem() )
we are short before being destructed too and any updates
done here are totally pointless. TODO ...
*/
#endif
if ( value.boolValue )
{
if ( d->blockedPolish )
polish();
if ( d->controlFlags & QskQuickItem::DeferredUpdate )
{
if ( d->dirtyAttributes && ( d->flags & QQuickItem::ItemHasContents ) )
update();
}
}
else
{
if ( d->controlFlags & QskQuickItem::CleanupOnVisibility )
d->cleanupNodes();
d->isInitiallyPainted = false;
}
if ( parentItem() && parentItem()->isVisible() )
{
/*
Layout code might consider the visiblility of the children
and therefore needs to be updated. Posting a statement about
changed layout constraints has this effect, but is not correct.
The right way to go would be to create show/hide events and to
handle them, where visibility of the children matters.
TODO ...
*/
d->layoutConstraintChanged();
}
break;
}
case QQuickItem::ItemParentHasChanged:
case QQuickItem::ItemChildAddedChange:
case QQuickItem::ItemChildRemovedChange:
case ItemOpacityHasChanged:
case ItemActiveFocusHasChanged:
case ItemRotationHasChanged:
case ItemAntialiasingHasChanged:
case ItemDevicePixelRatioHasChanged:
{
break;
}
}
Inherited::itemChange( change, value );
}
void QskQuickItem::geometryChanged(
const QRectF& newGeometry, const QRectF& oldGeometry )
{
Inherited::geometryChanged( newGeometry, oldGeometry );
Q_D( const QskQuickItem );
if ( !d->polishScheduled && d->polishOnResize )
{
if ( newGeometry.size() != oldGeometry.size() )
polish();
}
QskGeometryChangeEvent event( newGeometry, oldGeometry );
QCoreApplication::sendEvent( this, &event );
}
void QskQuickItem::updatePolish()
{
Q_D( QskQuickItem );
if ( d->controlFlags & QskQuickItem::DeferredPolish )
{
if ( !isVisible() )
{
d->blockedPolish = true;
return;
}
}
d->blockedPolish = false;
if ( !d->isInitiallyPainted )
{
/*
We should find a better way for identifying, when
an item is about to be shown, than making it dependend
from polishing and the existence of scene graph nodes. TODO ...
*/
aboutToShow();
}
updateItemPolish();
}
void QskQuickItem::aboutToShow()
{
}
void QskQuickItem::updateItemPolish()
{
}
QSGNode* QskQuickItem::updatePaintNode( QSGNode* node, UpdatePaintNodeData* data )
{
Q_UNUSED( data );
Q_D( QskQuickItem );
Q_ASSERT( isVisible() || !( d->controlFlags & QskQuickItem::DeferredUpdate ) );
d->isInitiallyPainted = true;
if ( d->clearPreviousNodes )
{
delete node;
node = nullptr;
d->clearPreviousNodes = false;
}
return updateItemPaintNode( node );
}
QSGNode* QskQuickItem::updateItemPaintNode( QSGNode* node )
{
return node;
}
#include "moc_QskQuickItem.cpp"

162
src/controls/QskQuickItem.h Normal file
View File

@ -0,0 +1,162 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_QUICK_ITEM_H
#define QSK_QUICK_ITEM_H
#include "QskGlobal.h"
#include <qquickitem.h>
class QskQuickItemPrivate;
class QskGeometryChangeEvent;
class QskWindowChangeEvent;
class QSK_EXPORT QskQuickItem : public QQuickItem
{
Q_OBJECT
Q_PROPERTY( QRectF geometry READ geometry WRITE setGeometry )
Q_PROPERTY( bool transparentForPositioners READ isTransparentForPositioner
WRITE setTransparentForPositioner NOTIFY itemFlagsChanged )
Q_PROPERTY( bool tabFence READ isTabFence
WRITE setTabFence NOTIFY itemFlagsChanged )
Q_PROPERTY( bool polishOnResize READ polishOnResize
WRITE setPolishOnResize NOTIFY itemFlagsChanged FINAL )
using Inherited = QQuickItem;
public:
enum Flag
{
DeferredUpdate = 1 << 0,
DeferredPolish = 1 << 1,
DeferredLayout = 1 << 2,
CleanupOnVisibility = 1 << 3,
PreferRasterForTextures = 1 << 4,
DebugForceBackground = 1 << 7,
LastFlag = DebugForceBackground
};
Q_ENUM( Flag )
Q_DECLARE_FLAGS( Flags, Flag )
~QskQuickItem() override;
const char* className() const;
bool isVisibleTo( const QQuickItem* ) const;
QRectF geometry() const;
QRectF rect() const;
QSizeF size() const;
QSizeF implicitSize() const;
void setGeometry( qreal x, qreal y, qreal width, qreal height );
void setPolishOnResize( bool );
bool polishOnResize() const;
void setTransparentForPositioner( bool );
bool isTransparentForPositioner() const;
void setTabFence( bool );
bool isTabFence() const;
void setLayoutMirroring( bool on, bool recursive = false );
void resetLayoutMirroring();
bool layoutMirroring() const;
void setControlFlags( Flags );
void resetControlFlags();
Flags controlFlags() const;
Q_INVOKABLE void setControlFlag( Flag, bool on = true );
Q_INVOKABLE void resetControlFlag( Flag );
Q_INVOKABLE bool testControlFlag( Flag ) const;
void classBegin() override;
void componentComplete() override;
void releaseResources() override;
bool isPolishScheduled() const;
bool isUpdateNodeScheduled() const;
bool isInitiallyPainted() const;
bool maybeUnresized() const;
Q_SIGNALS:
void itemFlagsChanged();
void controlFlagsChanged();
public Q_SLOTS:
void setGeometry( const QRectF& );
void show();
void hide();
void setVisible( bool );
void resetImplicitSize();
protected:
QskQuickItem( QskQuickItemPrivate&, QQuickItem* = nullptr );
bool event( QEvent* ) override;
virtual void changeEvent( QEvent* );
virtual void geometryChangeEvent( QskGeometryChangeEvent* );
virtual void windowChangeEvent( QskWindowChangeEvent* );
void itemChange( ItemChange, const ItemChangeData& ) override;
void geometryChanged( const QRectF&, const QRectF& ) override;
virtual void aboutToShow(); // called in updatePolish
private:
// don't use boundingRect - it seems to be deprecated
QRectF boundingRect() const override final { return rect(); }
/*
childrenRect()/childrenRectChanged does not make much sense
in a system, where the parent is responsible for laying out
its children.
*/
void childrenRect() = delete;
void updateControlFlag( uint flag, bool on );
QSGNode* updatePaintNode( QSGNode*, UpdatePaintNodeData* ) override final;
virtual QSGNode* updateItemPaintNode( QSGNode* );
void updatePolish() override final;
virtual void updateItemPolish();
Q_DECLARE_PRIVATE( QskQuickItem )
};
inline void QskQuickItem::setGeometry( const QRectF& rect )
{
setGeometry( rect.x(), rect.y(), rect.width(), rect.height() );
}
inline QSizeF QskQuickItem::size() const
{
return QSizeF( width(), height() );
}
inline QSizeF QskQuickItem::implicitSize() const
{
return QSizeF( implicitWidth(), implicitHeight() );
}
Q_DECLARE_OPERATORS_FOR_FLAGS( QskQuickItem::Flags )
Q_DECLARE_METATYPE( QskQuickItem::Flags )
#endif

View File

@ -0,0 +1,242 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskQuickItemPrivate.h"
#include "QskSetup.h"
static inline quint16 qskControlFlags()
{
// we are only interested in the first 8 bits
return static_cast< quint16 >( qskSetup->controlFlags() );
}
static inline void qskSendEventTo( QObject* object, QEvent::Type type )
{
QEvent event( type );
QCoreApplication::sendEvent( object, &event );
}
QskQuickItemPrivate::QskQuickItemPrivate()
: controlFlags( qskControlFlags() )
, controlFlagsMask( 0 )
, polishOnResize( false )
, blockedPolish( false )
, blockedImplicitSize( true )
, clearPreviousNodes( false )
, isInitiallyPainted( false )
, blockLayoutRequestEvents( true )
{
if ( controlFlags & QskQuickItem::DeferredLayout )
{
/*
In general the geometry of an item should be the job of
the parent - unfortunatly not done by Qt Quick
probably in the spirit of "making things easier".
To avoid potentially expensive calculations happening
too often and early QskControl blocks updates of
the implicitSize and any auto resizing of the control
according to it.
There should be no strong reason for using concepts
like Positioners, that rely on implicit resizing,
but to make it working: the DeferredLayout flag needs to be disabled.
*/
widthValid = heightValid = true;
}
}
QskQuickItemPrivate::~QskQuickItemPrivate()
{
}
void QskQuickItemPrivate::mirrorChange()
{
Q_Q( QskQuickItem );
qskSendEventTo( q, QEvent::LayoutDirectionChange );
}
void QskQuickItemPrivate::updateControlFlags( QskQuickItem::Flags flags )
{
const auto oldFlags = controlFlags;
const auto newFlags = static_cast< quint16 >( flags );
if ( oldFlags != newFlags )
{
Q_Q( QskQuickItem );
const auto numBits = qCountTrailingZeroBits(
static_cast< quint32 >( QskQuickItem::LastFlag ) );
for ( quint32 i = 0; i <= numBits; ++i )
{
const quint32 flag = ( 1 << i );
q->updateControlFlag( flag, flags & flag );
}
Q_EMIT q->controlFlagsChanged();
}
}
void QskQuickItemPrivate::layoutConstraintChanged()
{
if ( !blockLayoutRequestEvents )
{
Q_Q( QskQuickItem );
if ( auto item = q->parentItem() )
qskSendEventTo( item, QEvent::LayoutRequest );
/*
We don't send further LayoutRequest events until someone
actively requests a layout relevant information
*/
blockLayoutRequestEvents = true;
}
}
void QskQuickItemPrivate::implicitSizeChanged()
{
layoutConstraintChanged();
}
qreal QskQuickItemPrivate::getImplicitWidth() const
{
if ( blockedImplicitSize )
{
auto that = const_cast< QskQuickItemPrivate* >( this );
that->updateImplicitSize( false );
}
return implicitWidth;
}
qreal QskQuickItemPrivate::getImplicitHeight() const
{
if ( blockedImplicitSize )
{
auto that = const_cast< QskQuickItemPrivate* >( this );
that->updateImplicitSize( false );
}
return implicitHeight;
}
void QskQuickItemPrivate::updateImplicitSize( bool doNotify )
{
blockedImplicitSize = false;
const auto hint = implicitSizeHint();
setImplicitSize( hint.width(), hint.height(), doNotify );
}
void QskQuickItemPrivate::setImplicitSize( qreal w, qreal h, bool doNotify )
{
const bool doWidth = ( w != implicitWidth );
const bool doHeight = ( h != implicitHeight );
if ( !( doWidth || doHeight ) )
return; // nothing to do
implicitWidth = w;
implicitHeight = h;
if ( !( widthValid && heightValid ) )
{
// auto adjusting the size
const qreal oldWidth = width;
const qreal oldHeight = height;
if ( doWidth && !widthValid )
width = qMax( w, qreal( 0.0 ) );
if ( doHeight && !heightValid )
height = qMax( h, qreal( 0.0 ) );
if ( ( width != oldWidth ) || ( height != oldHeight ) )
{
dirty( QQuickItemPrivate::Size );
const QRectF oldRect( x, y, oldWidth, oldHeight );
const QRectF newRect( x, y, width, height );
Q_Q( QskQuickItem );
q->geometryChanged( newRect, oldRect );
}
}
if ( doNotify )
{
// calling implicitSizeChanged only once, TODO ...
if ( doWidth )
Inherited::implicitWidthChanged();
if ( doHeight )
Inherited::implicitHeightChanged();
}
}
void QskQuickItemPrivate::implicitWidthChanged()
{
Inherited::implicitWidthChanged();
blockedImplicitSize = false;
implicitSizeChanged();
}
void QskQuickItemPrivate::implicitHeightChanged()
{
Inherited::implicitWidthChanged();
blockedImplicitSize = false;
implicitSizeChanged();
}
void QskQuickItemPrivate::cleanupNodes()
{
if ( itemNodeInstance == nullptr )
return;
// setting the dirty flags, so that nodes will be recreated
// the next time we participate in a scene graph update
if ( !itemNodeInstance->matrix().isIdentity() )
dirtyAttributes |= QQuickItemPrivate::Position;
if ( extra.isAllocated() )
{
if ( extra->clipNode )
dirtyAttributes |= QQuickItemPrivate::Clip;
if ( extra->opacityNode )
dirtyAttributes |= QQuickItemPrivate::OpacityValue;
if ( extra->rootNode )
dirtyAttributes |= QQuickItemPrivate::EffectReference;
}
if ( window )
{
// putting the nodes on the cleanup list of the window to be deleteted
// in the next cycle of the scene graph
QQuickWindowPrivate::get( window )->cleanup( itemNodeInstance );
}
// now we can forget about the nodes
itemNodeInstance = nullptr;
paintNode = nullptr;
if ( extra.isAllocated() )
{
extra->opacityNode = nullptr;
extra->clipNode = nullptr;
extra->rootNode = nullptr;
}
}

View File

@ -0,0 +1,64 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_QUICK_ITEM_PRIVATE_H
#define QSK_QUICK_ITEM_PRIVATE_H
#include "QskGlobal.h"
#include "QskQuickItem.h"
QSK_QT_PRIVATE_BEGIN
#include <private/qquickitem_p.h>
QSK_QT_PRIVATE_END
class QskQuickItemPrivate : public QQuickItemPrivate
{
using Inherited = QQuickItemPrivate;
protected:
QskQuickItemPrivate();
~QskQuickItemPrivate() override;
public:
void updateControlFlags( QskQuickItem::Flags );
protected:
void layoutConstraintChanged();
virtual void implicitSizeChanged();
virtual QSizeF implicitSizeHint() const = 0;
private:
void cleanupNodes();
void mirrorChange() override;
qreal getImplicitWidth() const override final;
qreal getImplicitHeight() const override final;
void implicitWidthChanged() override final;
void implicitHeightChanged() override final;
void updateImplicitSize( bool doNotify );
void setImplicitSize( qreal width, qreal height, bool doNotify );
private:
Q_DECLARE_PUBLIC( QskQuickItem )
quint16 controlFlags;
quint16 controlFlagsMask;
bool polishOnResize : 1;
bool blockedPolish : 1;
bool blockedImplicitSize : 1;
bool clearPreviousNodes : 1;
bool isInitiallyPainted : 1;
protected:
mutable bool blockLayoutRequestEvents : 1;
};
#endif

View File

@ -5,6 +5,7 @@
#include "QskSetup.h"
#include "QskControl.h"
#include "QskControlPrivate.h"
#include "QskGraphicProviderMap.h"
#include "QskObjectTree.h"
#include "QskSkin.h"
@ -73,7 +74,6 @@ static void qskApplicationFilter()
Q_CONSTRUCTOR_FUNCTION( qskApplicationHook )
Q_COREAPP_STARTUP_FUNCTION( qskApplicationFilter )
extern bool qskInheritLocale( QskControl*, const QLocale& );
extern bool qskInheritLocale( QskWindow*, const QLocale& );
namespace
@ -90,7 +90,7 @@ namespace
bool setImplicitValue( QskControl* control,
const QLocale& locale ) override
{
return qskInheritLocale( control, locale );
return QskControlPrivate::inheritLocale( control, locale );
}
bool setImplicitValue( QskWindow* window,

View File

@ -111,6 +111,7 @@ HEADERS += \
controls/QskBox.h \
controls/QskBoxSkinlet.h \
controls/QskControl.h \
controls/QskControlPrivate.h \
controls/QskDirtyItemFilter.h \
controls/QskEvent.h \
controls/QskFlickAnimator.h \
@ -133,6 +134,8 @@ HEADERS += \
controls/QskPushButton.h \
controls/QskPushButtonSkinlet.h \
controls/QskQuick.h \
controls/QskQuickItem.h \
controls/QskQuickItemPrivate.h \
controls/QskRangeControl.h \
controls/QskScrollArea.h \
controls/QskScrollView.h \
@ -175,6 +178,7 @@ SOURCES += \
controls/QskBox.cpp \
controls/QskBoxSkinlet.cpp \
controls/QskControl.cpp \
controls/QskControlPrivate.cpp \
controls/QskDirtyItemFilter.cpp \
controls/QskEvent.cpp \
controls/QskFlickAnimator.cpp \
@ -197,6 +201,8 @@ SOURCES += \
controls/QskPushButton.cpp \
controls/QskPushButtonSkinlet.cpp \
controls/QskQuick.cpp \
controls/QskQuickItem.cpp \
controls/QskQuickItemPrivate.cpp \
controls/QskRangeControl.cpp \
controls/QskScrollArea.cpp \
controls/QskScrollView.cpp \