qskinny/src/controls/QskControl.cpp

2032 lines
50 KiB
C++
Raw Normal View History

2017-07-21 18:21:34 +02:00
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskControl.h"
#include "QskAspect.h"
2019-04-24 08:39:13 +02:00
#include "QskFunctions.h"
2017-07-21 18:21:34 +02:00
#include "QskDirtyItemFilter.h"
2018-08-03 08:15:28 +02:00
#include "QskEvent.h"
#include "QskQuick.h"
#include "QskSetup.h"
2018-07-19 14:10:48 +02:00
#include "QskSkin.h"
2018-08-03 08:15:28 +02:00
#include "QskSkinHintTable.h"
2017-07-21 18:21:34 +02:00
2018-08-03 08:15:28 +02:00
#include <qglobalstatic.h>
2018-07-19 14:10:48 +02:00
#include <qlocale.h>
#include <qvector.h>
2017-07-21 18:21:34 +02:00
QSK_QT_PRIVATE_BEGIN
#include <private/qquickitem_p.h>
#if defined( QT_DEBUG )
#include <private/qquickpositioners_p.h>
#endif
QSK_QT_PRIVATE_END
#include <limits>
#include <unordered_set>
2017-07-21 18:21:34 +02:00
2019-04-02 17:50:08 +02:00
QSK_SYSTEM_STATE( QskControl, Disabled, QskAspect::FirstSystemState )
QSK_SYSTEM_STATE( QskControl, Hovered, QskAspect::LastSystemState >> 1 )
QSK_SYSTEM_STATE( QskControl, Focused, QskAspect::LastSystemState )
2017-07-21 18:21:34 +02:00
// QGridLayoutEngine internally uses FLT_MAX
static constexpr qreal qskSizeHintMax = std::numeric_limits< float >::max();
2018-08-03 08:15:28 +02:00
static QSizeF qskDefaultSizeHints[ 3 ] =
{ { 0, 0 }, { -1, -1 }, { qskSizeHintMax, qskSizeHintMax } };
2017-07-21 18:21:34 +02:00
typedef quint16 controlFlags_t;
void qskResolveLocale( QskControl* ); // not static as being used from outside !
static void qskUpdateControlFlags( QskControl::Flags, QskControl* );
2017-07-21 18:21:34 +02:00
static inline void qskSendEventTo( QObject* object, QEvent::Type type )
2017-07-21 18:21:34 +02:00
{
QEvent event( type );
QCoreApplication::sendEvent( object, &event );
}
static inline controlFlags_t qskControlFlags()
2017-07-21 18:21:34 +02:00
{
// we are only interested in the first 8 bits
return static_cast< controlFlags_t >( qskSetup->controlFlags() );
}
static inline void qskFilterWindow( QQuickWindow* window )
2017-07-21 18:21:34 +02:00
{
if ( window == nullptr )
return;
static QskDirtyItemFilter itemFilter;
itemFilter.addWindow( window );
}
namespace
{
/*
A helper class to store the released window to be able to
put it later into the WindowChange event.
*/
class QskWindowStore
{
2018-08-03 08:15:28 +02:00
public:
QskWindowStore()
: m_refCount( 0 )
, m_window( nullptr )
2017-07-21 18:21:34 +02:00
{
}
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;
}
2018-08-03 08:15:28 +02:00
private:
2017-07-21 18:21:34 +02:00
int m_refCount;
QQuickWindow* m_window;
};
class QskControlRegistry
{
2018-08-03 08:15:28 +02:00
public:
QskControlRegistry()
{
/*
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,
2018-08-03 11:11:42 +02:00
qskSetup, [ this ] { updateControlFlags(); } );
QObject::connect( qskSetup, &QskSetup::skinChanged,
2018-08-03 11:11:42 +02:00
qskSetup, [ this ] { updateSkin(); } );
}
inline void insert( QskControl* control )
{
m_controls.insert( control );
}
inline void remove( QskControl* control )
{
m_controls.erase( control );
}
void updateControlFlags()
{
const auto flags = static_cast< QskControl::Flags >( qskControlFlags() );
for ( auto control : m_controls )
qskUpdateControlFlags( flags, control );
}
void updateSkin()
{
QEvent event( QEvent::StyleChange );
for ( auto control : m_controls )
{
event.setAccepted( true );
QCoreApplication::sendEvent( control, &event );
}
}
2018-08-03 08:15:28 +02:00
private:
std::unordered_set< QskControl* > m_controls;
};
2017-07-21 18:21:34 +02:00
}
2017-10-30 14:38:30 +01:00
Q_GLOBAL_STATIC( QskWindowStore, qskReleasedWindowCounter )
Q_GLOBAL_STATIC( QskControlRegistry, qskRegistry )
2017-07-21 18:21:34 +02:00
2018-10-18 15:50:09 +02:00
/*
QQuickItemPrivate somehow implies, that it is about private data,
but it is only the part of the public API, that does not fall
under the Qt binary compatibility rules.
Actually QQuickItemPrivate puts everything into its public section
and classes - even from totally different modules like QC2 -
are manipulating its members in a totally unguarded way
While hacking internals of QQuickItemPrivate can't be considered
being acceptable for any standards of software engineering
we need to do the same to convince QQuickItem/QuickWindow to do what we
need.
This is not the way how we want to create APIs in QSkinny, but that
does not mean, that the D-Pointer concept is bad in general:
it allows to allocate private data of base and derived class
together.
At the moment QSkinny uses D-Pointer only, when deriving from
Qt classes, but does not expose these private data to the public.
Once QSkinny is in a stable state this might be changed - but
without compromising the privacy of its members.
*/
2017-12-07 17:04:05 +01:00
class QskControlPrivate final : public QQuickItemPrivate
2017-07-21 18:21:34 +02:00
{
2018-03-24 18:05:57 +01:00
Q_DECLARE_PUBLIC( QskControl )
2018-08-03 08:15:28 +02:00
public:
class ExplicitSizeData
{
2018-08-03 08:15:28 +02:00
public:
QSizeF sizeHints[ 3 ] =
{ qskDefaultSizeHints[ 0 ], qskDefaultSizeHints[ 1 ], qskDefaultSizeHints[ 2 ] };
};
QskControlPrivate();
~QskControlPrivate() override;
void mirrorChange() override;
2018-07-31 17:32:25 +02:00
qreal getImplicitWidth() const override;
qreal getImplicitHeight() const override;
2017-07-21 18:21:34 +02:00
void updateImplicitSize( bool doNotify );
#if 0
// can we do something useful with overloading ???
2018-07-31 17:32:25 +02:00
QSGTransformNode* createTransformNode() override;
2017-07-21 18:21:34 +02:00
#endif
void implicitWidthChanged() override;
void implicitHeightChanged() override;
2018-11-25 16:35:06 +01:00
void setExplicitSizeHint( Qt::SizeHint, const QSizeF& );
QSizeF explicitSizeHint( Qt::SizeHint ) const;
bool maybeGesture( QQuickItem*, QEvent* );
2018-11-25 16:35:06 +01:00
void updateControlFlags( QskControl::Flags );
2018-10-18 15:50:09 +02:00
/*
Qt 5.11:
sizeof( QQuickItemPrivate::ExtraData ) -> 184
sizeof( QQuickItemPrivate ) -> 320
sizeof( QskControlPrivate ) -> sizeof( QQuickItemPrivate ) + 32
sizeof( QskSkinnable::PrivateData ) -> 40
( these numbers include pointers to optional extra data, that might
increase the effective memory footprint, when being accurate ).
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.
*/
2018-08-03 08:15:28 +02:00
private:
void implicitSizeChanged();
void setImplicitSize( qreal width, qreal height, bool doNotify );
ExplicitSizeData* explicitSizeData;
2018-08-03 08:15:28 +02:00
public:
QLocale locale;
2017-12-05 13:10:17 +01:00
QskSizePolicy sizePolicy;
quint16 controlFlags;
quint16 controlFlagsMask;
bool explicitLocale : 1;
2017-12-05 13:10:17 +01:00
bool autoFillBackground : 1;
bool autoLayoutChildren : 1;
bool polishOnResize : 1;
2017-12-05 13:10:17 +01:00
bool blockedPolish : 1;
bool blockedImplicitSize : 1;
bool clearPreviousNodes : 1;
2017-12-05 13:10:17 +01:00
bool isInitiallyPainted : 1;
uint focusPolicy : 4;
2017-12-05 13:10:17 +01:00
bool isWheelEnabled : 1;
};
2017-07-21 18:21:34 +02:00
static inline void qskUpdateControlFlags( QskControl::Flags flags, QskControl* control )
2018-01-24 10:14:50 +01:00
{
auto d = static_cast< QskControlPrivate* >( QQuickItemPrivate::get( control ) );
d->updateControlFlags( flags );
}
QskControlPrivate::QskControlPrivate()
: explicitSizeData( nullptr )
, sizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Preferred )
, controlFlags( qskControlFlags() )
, controlFlagsMask( 0 )
, explicitLocale( false )
, autoFillBackground( false )
, autoLayoutChildren( false )
, polishOnResize( false )
, blockedPolish( false )
, blockedImplicitSize( true )
, clearPreviousNodes( false )
, isInitiallyPainted( false )
, focusPolicy( Qt::NoFocus )
, isWheelEnabled( false )
{
if ( controlFlags & QskControl::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;
}
}
QskControlPrivate::~QskControlPrivate()
{
delete explicitSizeData;
}
void QskControlPrivate::mirrorChange()
{
Q_Q( QskControl );
qskSendEventTo( q, QEvent::LayoutDirectionChange );
}
inline void QskControlPrivate::implicitSizeChanged()
{
blockedImplicitSize = false;
Q_Q( QskControl );
if ( !q->explicitSizeHint( Qt::PreferredSize ).isValid() )
{
// when we have no PreferredSize we fall back
// to the implicit size
q->layoutConstraintChanged();
}
}
qreal QskControlPrivate::getImplicitWidth() const
{
if ( blockedImplicitSize )
{
auto that = const_cast< QskControlPrivate* >( this );
that->updateImplicitSize( false );
}
return implicitWidth;
}
qreal QskControlPrivate::getImplicitHeight() const
{
if ( blockedImplicitSize )
{
auto that = const_cast< QskControlPrivate* >( this );
that->updateImplicitSize( false );
}
return implicitHeight;
}
void QskControlPrivate::updateImplicitSize( bool doNotify )
{
Q_Q( const QskControl );
blockedImplicitSize = false;
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;
setImplicitSize( w, h, doNotify );
}
void QskControlPrivate::setImplicitSize( qreal w, qreal h, bool doNotify )
{
const bool doWidth = ( w != implicitWidth );
2019-03-21 14:07:00 +01:00
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( QskControl );
q->geometryChanged( newRect, oldRect );
}
}
if ( doNotify )
{
if ( doWidth )
QQuickItemPrivate::implicitWidthChanged();
if ( doHeight )
QQuickItemPrivate::implicitHeightChanged();
}
}
void QskControlPrivate::implicitWidthChanged()
{
QQuickItemPrivate::implicitWidthChanged();
implicitSizeChanged();
}
void QskControlPrivate::implicitHeightChanged()
{
QQuickItemPrivate::implicitWidthChanged();
implicitSizeChanged();
}
inline void QskControlPrivate::setExplicitSizeHint(
Qt::SizeHint whichHint, const QSizeF& size )
{
if ( explicitSizeData == nullptr )
explicitSizeData = new ExplicitSizeData;
explicitSizeData->sizeHints[ whichHint ] = size;
}
inline QSizeF QskControlPrivate::explicitSizeHint( Qt::SizeHint whichHint ) const
{
if ( explicitSizeData )
return explicitSizeData->sizeHints[ whichHint ];
return qskDefaultSizeHints[ 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 );
}
void QskControlPrivate::updateControlFlags( QskControl::Flags flags )
{
Q_Q( QskControl );
const auto oldFlags = controlFlags;
const auto newFlags = static_cast< controlFlags_t >( flags );
if ( oldFlags != newFlags )
{
const auto numBits = qCountTrailingZeroBits(
static_cast< quint32 >( QskControl::LastFlag ) );
for ( quint32 i = 0; i <= numBits; ++i )
{
const quint32 flag = ( 1 << i );
q->updateControlFlag( flag, flags & flag );
}
Q_EMIT q->controlFlagsChanged();
}
}
// --------
2018-08-03 08:15:28 +02:00
QskControl::QskControl( QQuickItem* parent )
: Inherited( *( new QskControlPrivate() ), parent )
2017-07-21 18:21:34 +02:00
{
setFlag( QQuickItem::ItemHasContents, true );
QQuickItem::setActiveFocusOnTab( false );
2017-07-21 18:21:34 +02:00
if ( parent )
{
// inheriting attributes from parent
qskResolveLocale( this );
}
// since Qt 5.10 we have QQuickItem::ItemEnabledHasChanged
2018-05-01 12:26:59 +02:00
#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,
2018-08-03 08:15:28 +02:00
[ this ] { setSkinStateFlag( Disabled, !isEnabled() ); } );
#endif
2017-07-21 18:21:34 +02:00
Q_D( QskControl );
if ( d->controlFlags & QskControl::DeferredUpdate )
2017-07-21 18:21:34 +02:00
qskFilterWindow( window() );
qskRegistry->insert( this );
}
2017-07-21 18:21:34 +02:00
QskControl::~QskControl()
{
2019-04-18 16:12:33 +02:00
#if defined( QT_DEBUG )
if ( auto w = window() )
{
// to catch suicide situations as a result of mouse clicks
Q_ASSERT( this != w->mouseGrabberItem() );
}
#endif
if ( qskRegistry )
qskRegistry->remove( this );
2018-01-24 10:14:50 +01:00
#if QT_VERSION < QT_VERSION_CHECK( 5, 10, 0 )
disconnect( this, &QQuickItem::enabledChanged, nullptr, nullptr );
#endif
2018-01-24 10:14:50 +01:00
/*
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.
*/
Q_D( QskControl );
d->componentComplete = false;
2017-07-21 18:21:34 +02:00
}
const char* QskControl::className() const
{
return metaObject()->className();
}
void QskControl::setVisible( bool on )
{
// QQuickItem::setVisible is no slot
Inherited::setVisible( on );
}
void QskControl::show()
{
Inherited::setVisible( true );
}
void QskControl::hide()
{
Inherited::setVisible( false );
}
2018-06-26 11:10:44 +02:00
bool QskControl::isVisibleTo( const QQuickItem* ancestor ) const
{
return qskIsVisibleTo( this, ancestor );
}
2017-07-21 18:21:34 +02:00
void QskControl::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 );
2018-03-24 18:05:57 +01:00
/*
Unfortunately geometryChanged is protected and we can't implement
this code as qskSetItemGeometry - further hacking required: TODO ...
*/
2017-07-21 18:21:34 +02:00
geometryChanged( QRectF( d->x, d->y, d->width, d->height ), oldRect );
}
}
QRectF QskControl::rect() const
{
Q_D( const QskControl );
return QRectF( 0, 0, d->width, d->height );
}
2017-07-21 18:21:34 +02:00
QRectF QskControl::geometry() const
{
Q_D( const QskControl );
2017-07-21 18:21:34 +02:00
return QRectF( d->x, d->y, d->width, d->height );
}
void QskControl::setAutoFillBackground( bool on )
{
Q_D( QskControl );
if ( on != d->autoFillBackground )
2017-07-21 18:21:34 +02:00
{
d->autoFillBackground = on;
2017-07-21 18:21:34 +02:00
update();
Q_EMIT controlFlagsChanged();
}
}
bool QskControl::autoFillBackground() const
{
return d_func()->autoFillBackground;
2017-07-21 18:21:34 +02:00
}
void QskControl::setAutoLayoutChildren( bool on )
{
Q_D( QskControl );
if ( on != d->autoLayoutChildren )
2017-07-21 18:21:34 +02:00
{
d->autoLayoutChildren = on;
2017-07-21 18:21:34 +02:00
if ( on )
polish();
Q_EMIT controlFlagsChanged();
}
}
bool QskControl::autoLayoutChildren() const
{
return d_func()->autoLayoutChildren;
2017-07-21 18:21:34 +02:00
}
void QskControl::setTransparentForPositioner( bool on )
{
Q_D( QskControl );
if ( on != d->isTransparentForPositioner() )
{
d->setTransparentForPositioner( on );
Q_EMIT controlFlagsChanged();
}
}
bool QskControl::isTransparentForPositioner() const
{
return d_func()->isTransparentForPositioner();
2017-07-21 18:21:34 +02:00
}
void QskControl::setPolishOnResize( bool on )
{
Q_D( QskControl );
if ( on != d->polishOnResize )
2017-07-21 18:21:34 +02:00
{
d->polishOnResize = on;
2017-07-21 18:21:34 +02:00
polish();
Q_EMIT controlFlagsChanged();
}
}
bool QskControl::polishOnResize() const
{
return d_func()->polishOnResize;
2017-07-21 18:21:34 +02:00
}
void QskControl::setWheelEnabled( bool on )
{
Q_D( QskControl );
if ( on != d->isWheelEnabled )
{
d->isWheelEnabled = on;
Q_EMIT wheelEnabledChanged();
}
}
bool QskControl::isWheelEnabled() const
{
return d_func()->isWheelEnabled;
}
void QskControl::setFocusPolicy( Qt::FocusPolicy policy )
{
Q_D( QskControl );
if ( policy != d->focusPolicy )
{
d->focusPolicy = policy & ~Qt::TabFocus;
QQuickItem::setActiveFocusOnTab( policy & Qt::TabFocus );
2017-10-30 08:33:43 +01:00
Q_EMIT focusPolicyChanged();
}
}
Qt::FocusPolicy QskControl::focusPolicy() const
{
uint policy = d_func()->focusPolicy;
if ( activeFocusOnTab() )
policy |= Qt::TabFocus;
return static_cast< Qt::FocusPolicy >( policy );
}
2017-07-21 18:21:34 +02:00
void QskControl::setTabFence( bool on )
{
Q_D( QskControl );
if ( on != d->isTabFence )
{
d->isTabFence = on;
Q_EMIT controlFlagsChanged();
}
}
bool QskControl::isTabFence() const
{
return d_func()->isTabFence;
2017-07-21 18:21:34 +02:00
}
QskControl::Flags QskControl::controlFlags() const
{
return QskControl::Flags( d_func()->controlFlags );
2017-07-21 18:21:34 +02:00
}
void QskControl::setControlFlags( Flags flags )
{
Q_D( QskControl );
2017-07-21 18:21:34 +02:00
// set all bits in the mask
d->controlFlagsMask = std::numeric_limits< controlFlags_t >::max();
d->updateControlFlags( flags );
2017-07-21 18:21:34 +02:00
}
void QskControl::resetControlFlags()
{
Q_D( QskControl );
2017-07-21 18:21:34 +02:00
// clear all bits in the mask
d->controlFlagsMask = 0;
d->updateControlFlags( static_cast< Flags >( qskControlFlags() ) );
2017-07-21 18:21:34 +02:00
}
void QskControl::setControlFlag( Flag flag, bool on )
{
Q_D( QskControl );
2017-07-21 18:21:34 +02:00
d->controlFlagsMask |= flag;
if ( ( d->controlFlags & flag ) != on )
2017-07-21 18:21:34 +02:00
{
updateControlFlag( flag, on );
Q_EMIT controlFlagsChanged();
}
}
void QskControl::resetControlFlag( Flag flag )
{
Q_D( QskControl );
d->controlFlagsMask &= ~flag;
2017-07-21 18:21:34 +02:00
const bool on = qskSetup->testControlFlag( static_cast< QskSetup::Flag >( flag ) );
if ( ( d->controlFlags & flag ) != on )
2017-07-21 18:21:34 +02:00
{
updateControlFlag( flag, on );
Q_EMIT controlFlagsChanged();
}
}
bool QskControl::testControlFlag( Flag flag ) const
{
return d_func()->controlFlags & flag;
2017-07-21 18:21:34 +02:00
}
void QskControl::updateControlFlag( uint flag, bool on )
{
Q_D( QskControl );
2018-08-03 08:15:28 +02:00
if ( ( flag > std::numeric_limits< controlFlags_t >::max() ) ||
2019-01-07 09:13:53 +01:00
( bool( d->controlFlags & flag ) == on ) )
2017-07-21 18:21:34 +02:00
{
return;
}
if ( on )
d->controlFlags |= flag;
2017-07-21 18:21:34 +02:00
else
d->controlFlags &= ~flag;
2017-07-21 18:21:34 +02:00
2018-08-03 08:15:28 +02:00
switch ( flag )
2017-07-21 18:21:34 +02:00
{
case QskControl::DeferredUpdate:
{
if ( on )
{
qskFilterWindow( window() );
}
else
{
if ( !isVisible() )
update();
}
break;
}
case QskControl::DeferredPolish:
{
if ( !on && d->blockedPolish )
2017-07-21 18:21:34 +02:00
polish();
break;
}
case QskControl::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 );
2017-07-21 18:21:34 +02:00
}
break;
}
case QskControl::CleanupOnVisibility:
{
if ( on && !isVisible() )
cleanupNodes();
break;
}
case QskControl::DebugForceBackground:
{
// no need to mark it dirty
if ( flags() & QQuickItem::ItemHasContents )
update();
2017-07-21 18:21:34 +02:00
break;
}
default:
break;
}
}
void QskControl::setBackgroundColor( const QColor& color )
{
setAutoFillBackground( true );
setBackground( QskGradient( color ) );
}
void QskControl::setBackground( const QskGradient& gradient )
{
using namespace QskAspect;
const Aspect aspect = Control | Color;
2017-10-30 12:06:19 +01:00
if ( hintTable().gradient( aspect ) != gradient )
{
setGradientHint( aspect, gradient );
2017-10-30 12:06:19 +01:00
if ( autoFillBackground() )
update();
Q_EMIT backgroundChanged();
}
}
void QskControl::resetBackground()
{
using namespace QskAspect;
const Aspect aspect = Control | Color;
auto& table = hintTable();
2017-10-30 12:06:19 +01:00
if ( table.hint( aspect ).isValid() )
{
table.removeHint( aspect );
2017-10-30 12:06:19 +01:00
update();
Q_EMIT backgroundChanged();
}
}
QskGradient QskControl::background() const
{
using namespace QskAspect;
return gradientHint( Control );
2017-07-21 18:21:34 +02:00
}
void QskControl::setMargins( qreal margin )
{
setMargins( QMarginsF( margin, margin, margin, margin ) );
}
void QskControl::setMargins( const QMarginsF& margins )
{
using namespace QskAspect;
const Subcontrol subControl = effectiveSubcontrol( QskAspect::Control );
const QMarginsF m(
qMax( qreal( margins.left() ), qreal( 0.0 ) ),
qMax( qreal( margins.top() ), qreal( 0.0 ) ),
qMax( qreal( margins.right() ), qreal( 0.0 ) ),
qMax( qreal( margins.bottom() ), qreal( 0.0 ) ) );
if ( m != this->margins() )
{
setMarginsHint( subControl | Margin, m );
2017-07-21 18:21:34 +02:00
resetImplicitSize();
Q_D( const QskControl );
if ( d->polishOnResize || d->autoLayoutChildren )
2017-07-21 18:21:34 +02:00
polish();
qskSendEventTo( this, QEvent::ContentsRectChange );
2017-10-30 08:48:49 +01:00
Q_EMIT marginsChanged();
2017-07-21 18:21:34 +02:00
}
}
void QskControl::resetMargins()
{
using namespace QskAspect;
const Aspect aspect = Control | Metric | Margin;
const auto oldMargin = marginsHint( aspect );
auto& table = hintTable();
if ( table.hint( aspect ).isValid() )
{
table.removeHint( aspect );
if ( marginsHint( aspect ) != oldMargin )
{
resetImplicitSize();
Q_D( const QskControl );
if ( d->polishOnResize || d->autoLayoutChildren )
polish();
qskSendEventTo( this, QEvent::ContentsRectChange );
2017-10-30 08:48:49 +01:00
Q_EMIT marginsChanged();
}
}
2017-07-21 18:21:34 +02:00
}
QMarginsF QskControl::margins() const
{
return marginsHint( QskAspect::Control | QskAspect::Margin );
2017-07-21 18:21:34 +02:00
}
QRectF QskControl::contentsRect() const
{
2019-04-24 08:39:13 +02:00
return qskValidOrEmptyInnerRect( rect(), margins() );
2017-07-21 18:21:34 +02:00
}
bool QskControl::layoutMirroring() const
{
return d_func()->effectiveLayoutMirror;
2017-07-21 18:21:34 +02:00
}
void QskControl::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( QskControl );
2018-08-03 08:15:28 +02:00
if ( recursive != d->inheritMirrorFromItem )
2017-07-21 18:21:34 +02:00
{
d->inheritMirrorFromItem = recursive;
d->resolveLayoutMirror();
}
d->isMirrorImplicit = false;
if ( on != d->effectiveLayoutMirror )
{
d->setLayoutMirror( on );
if ( recursive )
d->resolveLayoutMirror();
}
}
void QskControl::resetLayoutMirroring()
{
Q_D( QskControl );
2018-08-03 08:15:28 +02:00
if ( d && !d->isMirrorImplicit )
2017-07-21 18:21:34 +02:00
{
d->isMirrorImplicit = true;
2018-08-03 08:15:28 +02:00
// d->inheritMirrorFromItem = false;
2017-07-21 18:21:34 +02:00
d->resolveLayoutMirror();
}
}
QLocale QskControl::locale() const
{
return d_func()->locale;
2017-07-21 18:21:34 +02:00
}
void QskControl::setLocale( const QLocale& locale )
{
Q_D( QskControl );
2017-07-21 18:21:34 +02:00
d->explicitLocale = true;
if ( d->locale != locale )
2017-07-21 18:21:34 +02:00
{
d->locale = locale;
2017-07-21 18:21:34 +02:00
qskSendEventTo( this, QEvent::LocaleChange );
qskSetup->inheritLocale( this, locale );
}
}
void QskControl::resetLocale()
{
Q_D( QskControl );
if ( d->explicitLocale )
2017-07-21 18:21:34 +02:00
{
d->explicitLocale = false;
2017-07-21 18:21:34 +02:00
qskResolveLocale( this );
}
}
// not static as being called from QskSetup.cpp
bool qskInheritLocale( QskControl* control, const QLocale& locale )
{
2017-12-05 13:10:17 +01:00
auto d = static_cast< QskControlPrivate* >( QQuickItemPrivate::get( control ) );
if ( d->explicitLocale || d->locale == locale )
return true;
2017-07-21 18:21:34 +02:00
d->locale = locale;
2017-07-21 18:21:34 +02:00
qskSendEventTo( control, QEvent::LocaleChange );
return false;
2017-07-21 18:21:34 +02:00
}
void qskResolveLocale( QskControl* control )
{
const QLocale locale = qskSetup->inheritedLocale( control );
2017-12-05 13:10:17 +01:00
auto d = static_cast< QskControlPrivate* >( QQuickItemPrivate::get( control ) );
if ( d->locale != locale )
2017-07-21 18:21:34 +02:00
{
d->locale = locale;
2017-07-21 18:21:34 +02:00
qskSendEventTo( control, QEvent::LocaleChange );
2017-07-21 18:21:34 +02:00
qskSetup->inheritLocale( control, locale );
}
}
void QskControl::initSizePolicy(
QskSizePolicy::Policy horizontalPolicy,
QskSizePolicy::Policy verticalPolicy )
{
Q_D( QskControl );
/*
In constructors of derived classes you don't need
to propagate changes by layoutConstraintChanged.
Sometimes it is even worse as the parent might not be
even prepared to handle the LayouRequest event.
*/
2018-08-03 08:15:28 +02:00
d->sizePolicy.setHorizontalPolicy( horizontalPolicy );
d->sizePolicy.setVerticalPolicy( verticalPolicy );
}
void QskControl::setSizePolicy( const QskSizePolicy& policy )
{
Q_D( QskControl );
if ( policy != d->sizePolicy )
{
d->sizePolicy = policy;
layoutConstraintChanged();
}
}
void QskControl::setSizePolicy(
QskSizePolicy::Policy horizontalPolicy,
QskSizePolicy::Policy verticalPolicy )
{
setSizePolicy( QskSizePolicy( horizontalPolicy, verticalPolicy ) );
}
void QskControl::setSizePolicy(
Qt::Orientation orientation, QskSizePolicy::Policy policy )
{
Q_D( QskControl );
if ( d->sizePolicy.policy( orientation ) != policy )
{
d->sizePolicy.setPolicy( orientation, policy );
layoutConstraintChanged();
}
}
const QskSizePolicy& QskControl::sizePolicy() const
{
return d_func()->sizePolicy;
}
QskSizePolicy::Policy QskControl::sizePolicy( Qt::Orientation orientation ) const
{
return d_func()->sizePolicy.policy( orientation );
}
void QskControl::setPreferredSize( const QSizeF& size )
{
setExplicitSizeHint( Qt::PreferredSize, size );
}
void QskControl::setPreferredSize( qreal width, qreal height )
{
setPreferredSize( QSizeF( width, height ) );
}
void QskControl::setPreferredWidth( qreal width )
{
setPreferredSize( QSizeF( width, preferredSize().height() ) );
}
void QskControl::setPreferredHeight( qreal height )
{
setPreferredSize( QSizeF( preferredSize().width(), height ) );
}
void QskControl::setMinimumSize( const QSizeF& size )
{
setExplicitSizeHint( Qt::MinimumSize, size );
}
void QskControl::setMinimumSize( qreal width, qreal height )
{
setMinimumSize( QSizeF( width, height ) );
}
void QskControl::setMinimumWidth( qreal width )
{
setMinimumSize( QSizeF( width, minimumSize().height() ) );
}
void QskControl::setMinimumHeight( qreal height )
{
setMinimumSize( QSizeF( minimumSize().width(), height ) );
}
void QskControl::setMaximumSize( const QSizeF& size )
{
setExplicitSizeHint( Qt::MaximumSize, size );
}
void QskControl::setMaximumSize( qreal width, qreal height )
{
setMaximumSize( QSizeF( width, height ) );
}
void QskControl::setMaximumWidth( qreal width )
{
setMaximumSize( QSizeF( width, maximumSize().height() ) );
}
void QskControl::setMaximumHeight( qreal height )
{
setMaximumSize( QSizeF( maximumSize().width(), height ) );
}
void QskControl::setFixedSize( const QSizeF& size )
{
const QSizeF newSize = size.expandedTo( QSizeF( 0, 0 ) );
const QskSizePolicy policy( QskSizePolicy::Fixed, QskSizePolicy::Fixed );
Q_D( QskControl );
if ( policy != d->sizePolicy ||
d->explicitSizeHint( Qt::PreferredSize ) != newSize )
{
d->sizePolicy = policy;
d->setExplicitSizeHint( Qt::PreferredSize, newSize );
layoutConstraintChanged();
}
}
void QskControl::setFixedSize( qreal width, qreal height )
{
setFixedSize( QSizeF( width, height ) );
}
void QskControl::setFixedWidth( qreal width )
{
if ( width < 0 )
width = 0;
Q_D( QskControl );
auto size = d->explicitSizeHint( Qt::PreferredSize );
2018-08-03 08:15:28 +02:00
if ( ( d->sizePolicy.horizontalPolicy() != QskSizePolicy::Fixed ) ||
2019-01-04 13:42:16 +01:00
( size.width() != width ) )
{
size.setWidth( width );
d->sizePolicy.setHorizontalPolicy( QskSizePolicy::Fixed );
d->setExplicitSizeHint( Qt::PreferredSize, size );
layoutConstraintChanged();
}
}
void QskControl::setFixedHeight( qreal height )
{
if ( height < 0 )
height = 0;
Q_D( QskControl );
auto size = d->explicitSizeHint( Qt::PreferredSize );
2018-08-03 08:15:28 +02:00
if ( ( d->sizePolicy.verticalPolicy() != QskSizePolicy::Fixed ) ||
2019-01-04 13:42:16 +01:00
( size.height() != height ) )
{
size.setHeight( height );
d->sizePolicy.setVerticalPolicy( QskSizePolicy::Fixed );
d->setExplicitSizeHint( Qt::PreferredSize, size );
layoutConstraintChanged();
}
}
void QskControl::resetExplicitSizeHint( Qt::SizeHint whichHint )
{
if ( whichHint >= Qt::MinimumSize && whichHint <= Qt::MaximumSize )
setExplicitSizeHint( whichHint, qskDefaultSizeHints[ whichHint ] );
}
void QskControl::setExplicitSizeHint( Qt::SizeHint whichHint, const QSizeF& size )
{
if ( whichHint >= Qt::MinimumSize && whichHint <= Qt::MaximumSize )
{
const QSizeF newSize( ( size.width() < 0 ) ? -1.0 : size.width(),
2018-05-09 14:13:26 +02:00
( size.height() < 0 ) ? -1.0 : size.height() );
Q_D( QskControl );
if ( newSize != d->explicitSizeHint( whichHint ) )
{
d->setExplicitSizeHint( whichHint, newSize );
layoutConstraintChanged();
}
}
}
void QskControl::setExplicitSizeHint( Qt::SizeHint whichHint, qreal width, qreal height )
{
setExplicitSizeHint( whichHint, QSizeF( width, height ) );
}
QSizeF QskControl::explicitSizeHint( Qt::SizeHint whichHint ) const
{
if ( whichHint >= Qt::MinimumSize && whichHint <= Qt::MaximumSize )
return d_func()->explicitSizeHint( whichHint );
return QSizeF( -1, -1 );
}
QSizeF QskControl::effectiveSizeHint( Qt::SizeHint whichHint ) const
2017-07-21 18:21:34 +02:00
{
if ( whichHint < Qt::MinimumSize || whichHint > Qt::MaximumSize )
return QSizeF( 0, 0 );
QSizeF size = explicitSizeHint( whichHint );
if ( whichHint == Qt::PreferredSize && !size.isValid() )
2017-07-21 18:21:34 +02:00
{
// in most cases we don't have a preferred width/height
2017-07-21 18:21:34 +02:00
// and fall back to the implicit size.
if ( size.width() < 0 )
size.setWidth( implicitWidth() );
2017-07-21 18:21:34 +02:00
if ( size.height() < 0 )
size.setHeight( implicitHeight() );
2017-07-21 18:21:34 +02:00
}
return size;
2017-07-21 18:21:34 +02:00
}
void QskControl::resetImplicitSize()
{
Q_D( QskControl );
if ( d->controlFlags & QskControl::DeferredLayout )
2017-07-21 18:21:34 +02:00
{
/*
2019-01-04 13:42:16 +01:00
Is there a way to block consecutive calls ?
When the parent is requesting the preferred size, we could use
d->blockedImplicitSize, but in case of dynamic constraints we don't
have an indication when the event has been processed. TODO ...
*/
d->blockedImplicitSize = true;
layoutConstraintChanged();
2017-07-21 18:21:34 +02:00
}
else
{
d->updateImplicitSize( true );
2017-07-21 18:21:34 +02:00
}
}
qreal QskControl::heightForWidth( qreal width ) const
{
qreal h = -1;
if ( d_func()->autoLayoutChildren )
{
const auto innerSize = layoutRect().size();
const auto outerSize = size();
width -= outerSize.width() - innerSize.width();
const auto children = childItems();
for ( auto child : children )
{
if ( auto control = qobject_cast< const QskControl* >( child ) )
{
if ( !control->isTransparentForPositioner() )
h = qMax( h, control->heightForWidth( width ) );
}
}
if ( h >= 0 )
h += outerSize.height() - innerSize.height();
}
return h;
}
qreal QskControl::widthForHeight( qreal height ) const
{
qreal w = -1;
2018-08-03 08:15:28 +02:00
if ( d_func()->autoLayoutChildren )
{
const auto innerSize = layoutRect().size();
const auto outerSize = size();
2018-08-03 08:15:28 +02:00
height -= outerSize.height() - innerSize.height();
2018-08-03 08:15:28 +02:00
const auto children = childItems();
for ( auto child : children )
2018-08-03 08:15:28 +02:00
{
if ( auto control = qobject_cast< const QskControl* >( child ) )
2018-08-03 08:15:28 +02:00
{
if ( !control->isTransparentForPositioner() )
w = qMax( w, control->widthForHeight( height ) );
}
}
2018-08-03 08:15:28 +02:00
if ( w >= 0 )
w += outerSize.width() - innerSize.width();
}
2018-08-03 08:15:28 +02:00
return w;
}
2017-07-21 18:21:34 +02:00
bool QskControl::event( QEvent* event )
{
const int eventType = event->type();
2018-08-03 08:15:28 +02:00
switch ( eventType )
2017-07-21 18:21:34 +02:00
{
#if 0
case QEvent::PolishRequest:
{
d->polishScheduled = false;
updatePolish();
break;
}
#endif
case QEvent::EnabledChange:
{
setSkinStateFlag( Disabled, !isEnabled() );
break;
}
case QEvent::LocaleChange:
{
Q_EMIT localeChanged( locale() );
break;
}
case QEvent::LayoutRequest:
{
if ( d_func()->autoLayoutChildren )
2017-07-21 18:21:34 +02:00
resetImplicitSize();
break;
}
}
2018-08-03 08:15:28 +02:00
switch ( eventType )
2017-07-21 18:21:34 +02:00
{
case QEvent::StyleChange:
{
// The skin has changed
if ( skinlet() == nullptr )
{
/*
When we don't have a local skinlet, the skinlet
from the previous skin might be cached.
*/
setSkinlet( nullptr );
}
/*
We might have a totally different skinlet,
that can't deal with nodes created from other skinlets
*/
d_func()->clearPreviousNodes = true;
resetImplicitSize();
polish();
if ( flags() & QQuickItem::ItemHasContents )
update();
changeEvent( event );
return true;
}
2017-07-21 18:21:34 +02:00
case QEvent::EnabledChange:
case QEvent::FontChange:
case QEvent::PaletteChange:
case QEvent::LocaleChange:
case QEvent::ReadOnlyChange:
case QEvent::ParentChange:
case QEvent::ContentsRectChange:
{
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 QskEvent::Gesture:
{
gestureEvent( static_cast< QskGestureEvent* >( event ) );
return true;
}
default:
{
if ( d_func()->maybeGesture( this, event ) )
return true;
}
}
return Inherited::event( event );
}
bool QskControl::childMouseEventFilter( QQuickItem* item, QEvent* event )
{
#if QT_VERSION < QT_VERSION_CHECK( 5, 10, 0 )
if ( event->type() == QEvent::MouseButtonPress )
{
2019-04-04 17:59:17 +02:00
auto me = static_cast< QMouseEvent* >( event );
if ( me->source() == Qt::MouseEventSynthesizedByQt )
{
/*
Unhandled touch events result in creating synthetic
mouse events. For all versions < 5.10 those events are
passed through childMouseEventFilter without doing the
extra checks, that are done for real mouse events.
2019-04-04 17:59:17 +02:00
Furthermore the coordinates are relative
to this - not to item.
To avoid having a different behavior between using
mouse and touch, we do those checks here.
*/
2019-04-04 17:59:17 +02:00
auto itm = item;
auto pos = item->mapFromScene( me->windowPos() );
for ( itm = item; itm != this; itm = itm->parentItem() )
{
if ( itm->acceptedMouseButtons() & me->button() )
{
if ( itm->contains( pos ) )
break;
}
pos += QPointF( itm->x(), itm->y() );
}
if ( itm != item )
{
if ( itm == this )
return false;
QScopedPointer<QMouseEvent> clonedEvent(
QQuickWindowPrivate::cloneMouseEvent( me, &pos ) );
return d_func()->maybeGesture( itm, clonedEvent.data() );
}
}
}
#endif
2017-07-21 18:21:34 +02:00
return d_func()->maybeGesture( item, event );
}
bool QskControl::gestureFilter( QQuickItem* item, QEvent* event )
{
Q_UNUSED( item )
Q_UNUSED( event )
return false;
}
void QskControl::windowChangeEvent( QskWindowChangeEvent* )
{
}
void QskControl::geometryChangeEvent( QskGeometryChangeEvent* )
{
}
void QskControl::hoverEnterEvent( QHoverEvent* event )
{
Inherited::hoverEnterEvent( event );
setSkinStateFlag( Hovered );
}
void QskControl::hoverLeaveEvent( QHoverEvent* event )
{
Inherited::hoverLeaveEvent( event );
setSkinStateFlag( Hovered, false );
}
void QskControl::changeEvent( QEvent* )
2017-07-21 18:21:34 +02:00
{
}
void QskControl::gestureEvent( QskGestureEvent* )
{
}
void QskControl::classBegin()
{
Inherited::classBegin();
}
void QskControl::componentComplete()
{
#if defined( QT_DEBUG )
if ( qobject_cast< const QQuickBasePositioner* >( parent() ) )
{
if ( d_func()->controlFlags & QskControl::DeferredLayout )
2017-07-21 18:21:34 +02:00
{
qWarning( "QskControl in DeferredLayout mode under control of a positioner" );
}
}
#endif
Inherited::componentComplete();
}
void QskControl::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.
2017-10-30 14:38:30 +01:00
qskReleasedWindowCounter->setWindow( window() );
2017-07-21 18:21:34 +02:00
}
void QskControl::itemChange( QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData& value )
{
Inherited::itemChange( change, value );
Q_D( QskControl );
2018-08-03 08:15:28 +02:00
switch ( change )
2017-07-21 18:21:34 +02:00
{
case QQuickItem::ItemParentHasChanged:
{
2017-12-08 13:56:35 +01:00
if ( value.item )
{
if ( !d->explicitLocale )
qskResolveLocale( this );
}
2017-07-21 18:21:34 +02:00
// not necessarily correct, when parent != parentItem ???
qskSendEventTo( this, QEvent::ParentChange );
break;
}
case QQuickItem::ItemChildAddedChange:
{
if ( d->autoLayoutChildren && !qskIsTransparentForPositioner( value.item ) )
polish();
2017-12-05 13:10:17 +01:00
break;
}
#if 0
case QQuickItem::ItemChildRemovedChange:
{
// TODO ...
break;
}
#endif
2017-07-21 18:21:34 +02:00
case QQuickItem::ItemVisibleHasChanged:
{
#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
2017-07-21 18:21:34 +02:00
if ( value.boolValue )
{
if ( d->blockedPolish )
2017-07-21 18:21:34 +02:00
polish();
if ( d->controlFlags & QskControl::DeferredUpdate )
2017-07-21 18:21:34 +02:00
{
if ( d->dirtyAttributes && ( d->flags & QQuickItem::ItemHasContents ) )
2017-07-21 18:21:34 +02:00
update();
}
}
else
{
if ( d->controlFlags & QskControl::CleanupOnVisibility )
2017-07-21 18:21:34 +02:00
cleanupNodes();
d->isInitiallyPainted = false;
2017-07-21 18:21:34 +02:00
}
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 ...
*/
layoutConstraintChanged();
}
2017-07-21 18:21:34 +02:00
break;
}
case QQuickItem::ItemSceneChange:
{
if ( value.window )
{
if ( d->controlFlags & QskControl::DeferredUpdate )
2017-07-21 18:21:34 +02:00
qskFilterWindow( value.window );
}
#if 1
auto oldWindow = qskReleasedWindowCounter->window();
if ( oldWindow && ( oldWindow->activeFocusItem() == this ) )
{
/*
2019-01-10 20:07:55 +01:00
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 );
2017-07-21 18:21:34 +02:00
QCoreApplication::sendEvent( this, &event );
break;
}
case QQuickItem::ItemActiveFocusHasChanged:
{
setSkinStateFlag( Focused, hasActiveFocus() );
break;
}
2018-08-03 08:15:28 +02:00
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
case QQuickItem::ItemEnabledHasChanged:
{
setSkinStateFlag( Disabled, !value.boolValue );
break;
}
#endif
2017-07-21 18:21:34 +02:00
default:
{
break;
}
}
}
void QskControl::geometryChanged(
const QRectF& newGeometry, const QRectF& oldGeometry )
{
Inherited::geometryChanged( newGeometry, oldGeometry );
if ( newGeometry.size() != oldGeometry.size() )
{
Q_D( const QskControl );
if ( d->polishOnResize || d->autoLayoutChildren )
2017-07-21 18:21:34 +02:00
polish();
}
QskGeometryChangeEvent event( newGeometry, oldGeometry );
QCoreApplication::sendEvent( this, &event );
}
void QskControl::windowDeactivateEvent()
{
// stopping gesture recognition ???
Inherited::windowDeactivateEvent();
}
2017-07-21 18:21:34 +02:00
void QskControl::layoutConstraintChanged()
{
if ( auto item = parentItem() )
2017-07-21 18:21:34 +02:00
qskSendEventTo( item, QEvent::LayoutRequest );
}
void QskControl::updatePolish()
{
Q_D( QskControl );
if ( d->controlFlags & QskControl::DeferredPolish )
2017-07-21 18:21:34 +02:00
{
if ( !isVisible() )
{
d->blockedPolish = true;
2017-07-21 18:21:34 +02:00
return;
}
}
d->blockedPolish = false;
2017-07-21 18:21:34 +02:00
if ( d->autoLayoutChildren )
2017-07-21 18:21:34 +02:00
{
const QRectF rect = layoutRect();
2017-10-30 12:06:19 +01:00
const auto children = childItems();
for ( auto child : children )
2017-07-21 18:21:34 +02:00
{
if ( !QQuickItemPrivate::get( child )->isTransparentForPositioner() )
2018-03-24 18:05:57 +01:00
qskSetItemGeometry( child, rect );
2017-07-21 18:21:34 +02:00
}
}
2018-06-26 11:10:44 +02:00
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 ...
*/
2018-06-26 11:10:44 +02:00
aboutToShow();
}
2018-06-26 11:10:44 +02:00
2017-07-21 18:21:34 +02:00
updateLayout();
}
QSGNode* QskControl::updatePaintNode( QSGNode* node, UpdatePaintNodeData* data )
{
Q_UNUSED( data );
Q_D( QskControl );
Q_ASSERT( isVisible() || !( d->controlFlags & QskControl::DeferredUpdate ) );
2017-07-21 18:21:34 +02:00
if ( !d->isInitiallyPainted )
d->isInitiallyPainted = true;
if ( d->clearPreviousNodes )
2017-07-21 18:21:34 +02:00
{
delete node;
node = nullptr;
d->clearPreviousNodes = false;
2017-07-21 18:21:34 +02:00
}
if ( node == nullptr )
node = new QSGNode;
updateNode( node );
return node;
}
void QskControl::cleanupNodes()
{
Q_D( QskControl );
if ( d->itemNodeInstance == nullptr )
return;
// setting the dirty flags, so that nodes will be recreated
// the next time we participate in a scene graph update
if ( !d->itemNodeInstance->matrix().isIdentity() )
d->dirtyAttributes |= QQuickItemPrivate::Position;
if ( d->extra.isAllocated() )
{
if ( d->extra->clipNode )
d->dirtyAttributes |= QQuickItemPrivate::Clip;
if ( d->extra->opacityNode )
d->dirtyAttributes |= QQuickItemPrivate::OpacityValue;
if ( d->extra->rootNode )
d->dirtyAttributes |= QQuickItemPrivate::EffectReference;
}
2018-04-11 17:33:43 +02:00
if ( d->window )
{
// putting the nodes on the cleanup list of the window to be deleteted
// in the next cycle of the scene graph
2017-07-21 18:21:34 +02:00
2018-04-11 17:33:43 +02:00
QQuickWindowPrivate::get( d->window )->cleanup( d->itemNodeInstance );
}
2017-07-21 18:21:34 +02:00
// now we can forget about the nodes
d->itemNodeInstance = nullptr;
d->paintNode = nullptr;
if ( d->extra.isAllocated() )
{
d->extra->opacityNode = nullptr;
d->extra->clipNode = nullptr;
d->extra->rootNode = nullptr;
}
}
QskControl* QskControl::owningControl() const
{
return const_cast< QskControl* >( this );
}
QRectF QskControl::layoutRect() const
{
return contentsRect();
}
QRectF QskControl::gestureRect() const
{
return rect();
2017-07-21 18:21:34 +02:00
}
QRectF QskControl::focusIndicatorRect() const
{
return contentsRect();
}
2018-06-26 11:10:44 +02:00
void QskControl::aboutToShow()
{
}
2017-07-21 18:21:34 +02:00
void QskControl::updateLayout()
{
}
QSizeF QskControl::contentsSizeHint() const
{
qreal w = -1; // no hint
qreal h = -1;
if ( d_func()->autoLayoutChildren )
2017-07-21 18:21:34 +02:00
{
2017-10-30 12:06:19 +01:00
const auto children = childItems();
for ( const auto child : children )
2017-07-21 18:21:34 +02:00
{
if ( auto* control = qobject_cast< const QskControl* >( child ) )
{
if ( !QQuickItemPrivate::get( control )->isTransparentForPositioner() )
{
const QSizeF hint = control->sizeHint();
w = qMax( w, hint.width() );
h = qMax( h, hint.height() );
}
}
}
}
return QSizeF( w, h );
}
bool QskControl::isPolishScheduled() const
{
return d_func()->polishScheduled;
2017-07-21 18:21:34 +02:00
}
bool QskControl::isUpdateNodeScheduled() const
{
Q_D( const QskControl );
2018-08-03 08:15:28 +02:00
return ( d->dirtyAttributes & QQuickItemPrivate::ContentUpdateMask ) &&
( d->flags & QQuickItem::ItemHasContents );
2017-07-21 18:21:34 +02:00
}
bool QskControl::isInitiallyPainted() const
{
return d_func()->isInitiallyPainted;
2017-07-21 18:21:34 +02:00
}
QVector< QskAspect::Subcontrol > QskControl::subControls() const
{
using namespace QskAspect;
QVector< Subcontrol > subControls;
for ( auto mo = metaObject(); mo != nullptr; mo = mo->superClass() )
{
const auto moSubControls = QskAspect::subControls( mo );
for ( auto subControl : moSubControls )
{
const auto subControlEffective = effectiveSubcontrol( subControl );
if ( subControlEffective == subControl )
{
subControls += subControlEffective;
}
else
{
// when subControlEffective differs it usually has
// been mapped to a subcontrol of an inherited class
// that is already in the list.
if ( !subControls.contains( subControlEffective ) )
{
subControls += subControlEffective;
}
}
}
}
return subControls;
}
#include "moc_QskControl.cpp"