qskinny/src/controls/QskControl.cpp

1529 lines
36 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"
2018-05-01 12:41:20 +02:00
#include "QskQuick.h"
2017-07-21 18:21:34 +02:00
#include "QskAspect.h"
#include "QskSetup.h"
#include "QskSkinlet.h"
#include "QskSkin.h"
2017-07-21 18:21:34 +02:00
#include "QskEvent.h"
#include "QskDirtyItemFilter.h"
#include "QskSkinHintTable.h"
2017-07-21 18:21:34 +02:00
#include <QLocale>
#include <QVector>
2017-10-30 14:38:30 +01:00
#include <QGlobalStatic>
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
#include <unordered_map>
QSK_STATE( QskControl, Disabled, QskAspect::FirstSystemState )
QSK_STATE( QskControl, Hovered, QskAspect::LastSystemState >> 1 )
QSK_STATE( QskControl, Focused, QskAspect::LastSystemState )
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
{
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;
};
class QskControlRegistry
{
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,
[this] { updateControlFlags(); } );
QObject::connect( qskSetup, &QskSetup::skinChanged,
[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 );
}
}
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
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 )
2017-07-21 18:21:34 +02:00
public:
QskControlPrivate():
controlFlags( qskControlFlags() ),
controlFlagsMask( 0 ),
explicitLocale( false ),
autoFillBackground( false ),
autoLayoutChildren( false ),
polishOnResize( false ),
blockedPolish( false ),
blockedImplicitSize( true ),
clearPreviousNodes( false ),
blockImplicitSizeNotification( 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;
}
}
2018-03-24 18:05:57 +01:00
virtual void mirrorChange() override final
2017-07-21 18:21:34 +02:00
{
Q_Q( QskControl );
qskSendEventTo( q, QEvent::LayoutDirectionChange );
}
inline void implicitSizeChanged()
{
Q_Q( QskControl );
if ( !q->explicitSizeHint( Qt::PreferredSize ).isValid() )
{
// when we have no PreferredSize we fall back
// to the implicit size
q->layoutConstraintChanged();
}
}
2017-07-21 18:21:34 +02:00
#if 0
// can we do something useful with overloading those ???
virtual qreal getImplicitWidth() const override final;
virtual qreal getImplicitHeight() const override final;
2017-12-05 13:10:17 +01:00
virtual QSGTransformNode* createTransformNode();
2017-07-21 18:21:34 +02:00
#endif
virtual void implicitWidthChanged() override final
{
QQuickItemPrivate::implicitWidthChanged();
if ( !blockImplicitSizeNotification )
implicitSizeChanged();
}
virtual void implicitHeightChanged() override final
{
QQuickItemPrivate::implicitWidthChanged();
if ( !blockImplicitSizeNotification )
implicitSizeChanged();
}
2017-07-21 18:21:34 +02:00
bool 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 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();
}
}
QLocale locale;
2017-12-05 13:10:17 +01:00
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 blockImplicitSizeNotification : 1;
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
2018-01-24 10:14:50 +01:00
static void qskUpdateControlFlags( QskControl::Flags flags, QskControl* control )
{
auto d = static_cast< QskControlPrivate* >( QQuickItemPrivate::get( control ) );
d->updateControlFlags( flags );
}
2017-07-21 18:21:34 +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,
[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()
{
if ( qskRegistry )
qskRegistry->remove( this );
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 );
}
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 );
if ( ( flag > std::numeric_limits< controlFlags_t >::max() )
|| ( 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
switch( flag )
{
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;
updateImplicitSize();
}
break;
}
case QskControl::CleanupOnVisibility:
{
if ( on && !isVisible() )
cleanupNodes();
break;
}
case QskControl::DebugForceBackground:
{
// no need to mark it dirty
update();
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
{
const QRectF r = rect();
2017-07-21 18:21:34 +02:00
const auto m = margins();
qreal left = r.left() + m.left();
qreal top = r.top() + m.top();
qreal right = r.right() - m.right();
qreal bottom = r.bottom() - m.bottom();
if ( left > r.right() )
left = r.right();
if ( top > r.bottom() )
top = r.bottom();
if ( right < left )
right = left;
if ( bottom < top )
bottom = top;
return QRectF( QPointF( left, top ), QPointF( right, bottom ) );
}
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 );
if ( recursive != d->inheritMirrorFromItem)
{
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 );
if ( d && !d->isMirrorImplicit)
{
d->isMirrorImplicit = true;
//d->inheritMirrorFromItem = false;
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 );
}
}
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 ( d_func()->blockedImplicitSize )
2017-07-21 18:21:34 +02:00
{
QskControl* that = const_cast< QskControl* >( this );
that->updateImplicitSize();
}
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
{
if ( !d->blockedImplicitSize )
{
d->blockedImplicitSize = true;
layoutConstraintChanged();
}
2017-07-21 18:21:34 +02:00
}
else
{
const QSizeF sz = implicitSize();
updateImplicitSize();
if ( sz != implicitSize() )
d->implicitSizeChanged();
2017-07-21 18:21:34 +02:00
}
}
qreal QskControl::heightForWidth( qreal width ) const
{
Q_UNUSED( width )
return -1;
}
qreal QskControl::widthForHeight( qreal height ) const
{
Q_UNUSED( height )
return -1;
}
2017-07-21 18:21:34 +02:00
bool QskControl::event( QEvent* event )
{
const int eventType = event->type();
switch( eventType )
{
#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;
}
}
switch( eventType )
{
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();
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 )
{
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* event )
{
if ( event->type() == QEvent::StyleChange )
{
/*
the skin has changed and we might have totally
different skinlets, that can't deal with nodes
created from other skinlets
*/
d_func()->clearPreviousNodes = true;
2017-07-21 18:21:34 +02:00
resetImplicitSize();
polish();
update();
}
}
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 );
2017-07-21 18:21:34 +02:00
switch( change )
{
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;
}
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 )
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 );
}
QskWindowChangeEvent event(
2017-10-30 14:38:30 +01:00
qskReleasedWindowCounter->window(), value.window );
2017-07-21 18:21:34 +02:00
QCoreApplication::sendEvent( this, &event );
break;
}
case QQuickItem::ItemActiveFocusHasChanged:
{
setSkinStateFlag( Focused, hasActiveFocus() );
break;
}
#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::layoutConstraintChanged()
{
QQuickItem* item = parentItem();
if ( item )
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
}
}
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();
}
2017-07-21 18:21:34 +02:00
void QskControl::updateLayout()
{
}
void QskControl::updateImplicitSize()
{
Q_D( QskControl );
d->blockedImplicitSize = false;
2017-07-21 18:21:34 +02:00
const auto m = margins();
const auto dw = m.left() + m.right();
const auto dh = m.top() + m.bottom();
const QSizeF hint = contentsSizeHint();
const qreal w = ( hint.width() >= 0 ) ? dw + hint.width() : 0.0;
const qreal h = ( hint.height() >= 0 ) ? dh + hint.height() : 0.0;
d->blockImplicitSizeNotification = true;
setImplicitSize( w, h );
d->blockImplicitSizeNotification = false;
2017-07-21 18:21:34 +02:00
}
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 );
return ( d->dirtyAttributes & QQuickItemPrivate::ContentUpdateMask )
&& ( d->flags & QQuickItem::ItemHasContents );
}
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"