qskinny/src/controls/QskControl.cpp

1401 lines
33 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"
#include "QskSetup.h"
#include "QskSkinlet.h"
#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_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* );
static void qskSendEventTo( QObject* object, QEvent::Type type )
{
QEvent event( type );
QCoreApplication::sendEvent( object, &event );
}
static controlFlags_t qskControlFlags()
{
// we are only interested in the first 8 bits
return static_cast< controlFlags_t >( qskSetup->controlFlags() );
}
static void qskFilterWindow( QQuickWindow* window )
{
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;
};
}
2017-10-30 14:38:30 +01:00
Q_GLOBAL_STATIC( QskWindowStore, qskReleasedWindowCounter )
2017-07-21 18:21:34 +02:00
class QskControlPrivate : public QQuickItemPrivate
{
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 ),
isInitiallyPainted( false ),
focusPolicy( Qt::NoFocus ),
isWheelEnabled( false )
{
}
inline void setupImplicitSizeConnections( bool on )
{
// in case of someone manipulating the implicit size from
// outside, we might need to adjust some layouts
Q_Q( QskControl );
if ( on )
{
QObject::connect( q, &QskControl::implicitWidthChanged,
q, &QskControl::onImplicitSizeChanged );
QObject::connect( q, &QskControl::implicitHeightChanged,
q, &QskControl::onImplicitSizeChanged );
}
else
{
QObject::disconnect( q, &QskControl::implicitWidthChanged,
q, &QskControl::onImplicitSizeChanged );
QObject::disconnect( q, &QskControl::implicitHeightChanged,
q, &QskControl::onImplicitSizeChanged );
}
}
2017-07-21 18:21:34 +02:00
void mirrorChange() override
{
Q_Q( QskControl );
qskSendEventTo( q, QEvent::LayoutDirectionChange );
}
#if 0
// can we do something useful with overloading those ???
virtual qreal getImplicitWidth() const override final;
virtual qreal getImplicitHeight() const override final;
virtual void implicitWidthChanged();
virtual void implicitHeightChanged();
virtual QSGTransformNode *createTransformNode();
#endif
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 );
}
QLocale locale;
quint16 controlFlags;
quint16 controlFlagsMask;
bool explicitLocale : 1;
bool autoFillBackground : 1;
bool autoLayoutChildren : 1;
bool polishOnResize : 1;
bool blockedPolish : 1;
bool blockedImplicitSize : 1;
bool clearPreviousNodes : 1;
bool isInitiallyPainted : 1;
uint focusPolicy : 4;
bool isWheelEnabled;
};
2017-07-21 18:21:34 +02:00
QskControl::QskControl( QQuickItem* parent ):
QskControl( *( new QskControlPrivate() ), parent )
{
}
QskControl::QskControl( QQuickItemPrivate& dd, QQuickItem* parent ):
Inherited( dd, parent )
2017-07-21 18:21:34 +02:00
{
Q_D( QskControl );
if ( d->controlFlags & QskControl::DeferredLayout )
2017-07-21 18:21:34 +02:00
{
// 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.
d->widthValid = d->heightValid = true;
}
setFlag( QQuickItem::ItemHasContents, true );
QQuickItem::setActiveFocusOnTab( false );
2017-07-21 18:21:34 +02:00
if ( parent )
{
// inheriting attributes from parent
qskResolveLocale( this );
}
d->setupImplicitSizeConnections( true );
2017-07-21 18:21:34 +02:00
connect( this, &QQuickItem::enabledChanged,
[this] { setSkinStateFlag( Disabled, !isEnabled() ); } );
// using a conventional connection as being automatically
// removed by the destructor
connect( qskSetup, SIGNAL( controlFlagsChanged() ),
this, SLOT( updateControlFlags() ) );
if ( d->controlFlags & QskControl::DeferredUpdate )
2017-07-21 18:21:34 +02:00
qskFilterWindow( window() );
}
QskControl::~QskControl()
{
}
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 );
geometryChanged( QRectF( d->x, d->y, d->width, d->height ), oldRect );
}
}
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
}
bool QskControl::isTransparentForPositioner( const QQuickItem* item )
{
if ( item == nullptr )
return true;
return QQuickItemPrivate::get( item )->isTransparentForPositioner();
}
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 )
{
// set all bits in the mask
d_func()->controlFlagsMask = std::numeric_limits< controlFlags_t >::max();
2017-07-21 18:21:34 +02:00
updateControlFlags( flags );
}
void QskControl::resetControlFlags()
{
// clear all bits in the mask
d_func()->controlFlagsMask = 0;
2017-07-21 18:21:34 +02:00
updateControlFlags( static_cast< Flags >( qskControlFlags() ) );
}
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::updateControlFlags()
{
updateControlFlags( static_cast< Flags >( qskControlFlags() ) );
}
void QskControl::updateControlFlags( Flags flags )
{
const auto oldFlags = d_func()->controlFlags;
2017-07-21 18:21:34 +02:00
const auto newFlags = static_cast< controlFlags_t >( flags );
if ( oldFlags != newFlags )
{
for ( uint i = 0; i <= LastFlag; ++i )
{
const uint flag = ( 1 << i );
updateControlFlag( flag, flags & flag );
}
Q_EMIT controlFlagsChanged();
}
}
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 = boundingRect();
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 )
{
auto d = static_cast< QskControlPrivate *>( QQuickItemPrivate::get( control ) );
if ( d->explicitLocale || d->locale == locale )
2017-07-21 18:21:34 +02:00
return false;
d->locale = locale;
2017-07-21 18:21:34 +02:00
qskSendEventTo( control, QEvent::LocaleChange );
return true;
}
void qskResolveLocale( QskControl* control )
{
const QLocale locale = qskSetup->inheritedLocale( control );
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::effectiveConstraint( Qt::SizeHint whichHint ) const
{
if ( whichHint < Qt::MinimumSize || whichHint > Qt::MaximumSize )
return QSizeF( 0, 0 );
QSizeF constraint = QskResizable::sizeHint( whichHint );
if ( whichHint == Qt::PreferredSize && !constraint.isValid() )
{
// in most cases we don't have a preferred width or height
// 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 ( constraint.width() < 0 )
constraint.setWidth( implicitWidth() );
if ( constraint.height() < 0 )
constraint.setHeight( implicitHeight() );
}
return constraint;
}
void QskControl::resetImplicitSize()
{
Q_D( QskControl );
if ( d->controlFlags & QskControl::DeferredLayout )
2017-07-21 18:21:34 +02:00
{
d->blockedImplicitSize = true;
2017-07-21 18:21:34 +02:00
layoutConstraintChanged();
}
else
{
const QSizeF sz = implicitSize();
updateImplicitSize();
if ( sz != implicitSize() )
onImplicitSizeChanged();
}
}
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::EnabledChange:
case QEvent::FontChange:
case QEvent::PaletteChange:
case QEvent::LocaleChange:
case QEvent::ReadOnlyChange:
case QEvent::ParentChange:
case QEvent::StyleChange:
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
}
const QSGNode* QskControl::itemNode( const QQuickItem* item )
{
if ( item == nullptr )
return nullptr;
return QQuickItemPrivate::get( item )->itemNodeInstance;
}
const QSGNode* QskControl::paintNode( const QQuickItem* item )
{
if ( item == nullptr )
return nullptr;
return QQuickItemPrivate::get( item )->paintNode;
}
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:
{
if ( !d->explicitLocale )
2017-07-21 18:21:34 +02:00
qskResolveLocale( this );
// not necessarily correct, when parent != parentItem ???
qskSendEventTo( this, QEvent::ParentChange );
break;
}
case QQuickItem::ItemVisibleHasChanged:
{
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 1
// 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();
#endif
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;
}
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() )
{
child->setPosition( rect.topLeft() );
child->setSize( rect.size() );
}
}
}
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;
}
// 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( d->itemNodeInstance );
// 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;
}
}
void QskControl::onImplicitSizeChanged()
{
if ( !QskResizable::sizeHint( Qt::PreferredSize ).isValid() )
{
// when we have no PreferredSize we fall back
// the constraint depends on the implicit size
layoutConstraintChanged();
}
}
QskControl* QskControl::owningControl() const
{
return const_cast< QskControl* >( this );
}
QRectF QskControl::layoutRect() const
{
return contentsRect();
}
QRectF QskControl::gestureRect() const
{
return boundingRect();
}
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->setupImplicitSizeConnections( false );
2017-07-21 18:21:34 +02:00
setImplicitSize( w, h );
d->setupImplicitSizeConnections( true );
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"