qskinny/src/controls/QskItem.cpp

1145 lines
28 KiB
C++
Raw Normal View History

2019-09-04 06:59:43 +02:00
/******************************************************************************
2024-01-17 14:31:45 +01:00
* QSkinny - Copyright (C) The authors
2023-04-06 09:23:37 +02:00
* SPDX-License-Identifier: BSD-3-Clause
2019-09-04 06:59:43 +02:00
*****************************************************************************/
2024-02-02 14:13:33 +01:00
#include "QskItem.h"
#include "QskItemPrivate.h"
2019-09-04 06:59:43 +02:00
#include "QskQuick.h"
#include "QskEvent.h"
#include "QskSetup.h"
#include "QskSkinManager.h"
2019-09-04 06:59:43 +02:00
#include "QskSkin.h"
#include "QskDirtyItemFilter.h"
#include <qglobalstatic.h>
#include <qquickwindow.h>
#if defined( QT_DEBUG )
2019-09-04 06:59:43 +02:00
QSK_QT_PRIVATE_BEGIN
2021-09-21 10:28:13 +02:00
#if QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 )
#ifndef emit
#define emit
#include <private/qabstractanimation_p.h>
#undef emit
2021-09-21 10:28:13 +02:00
#endif
#endif
2023-04-05 09:56:28 +02:00
#if defined( QT_DEBUG )
2019-09-04 06:59:43 +02:00
#include <private/qquickpositioners_p.h>
2023-04-05 09:56:28 +02:00
#endif
#include <private/qquickitemchangelistener_p.h>
2019-09-04 06:59:43 +02:00
QSK_QT_PRIVATE_END
2019-09-04 06:59:43 +02:00
#endif
#include <unordered_set>
static inline void qskSendEventTo( QObject* object, QEvent::Type type )
{
QEvent event( type );
QCoreApplication::sendEvent( object, &event );
}
2021-02-09 08:13:20 +01:00
static inline void qskApplyUpdateFlags(
2024-02-02 14:13:33 +01:00
QskItem::UpdateFlags flags, QskItem* item )
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
auto d = static_cast< QskItemPrivate* >( QskItemPrivate::get( item ) );
2021-02-09 08:13:20 +01:00
d->applyUpdateFlags( flags );
2019-09-04 06:59:43 +02:00
}
static inline void qskFilterWindow( QQuickWindow* window )
{
if ( window == nullptr )
return;
static QskDirtyItemFilter itemFilter;
itemFilter.addWindow( window );
}
namespace
{
2024-02-02 14:13:33 +01:00
class QskItemRegistry
2019-09-04 06:59:43 +02:00
{
public:
2024-02-02 14:13:33 +01:00
QskItemRegistry()
2019-09-04 06:59:43 +02:00
{
/*
Its faster and saves some memory to have this registry instead
of setting up direct connections between qskSetup and each control
2022-03-08 12:08:15 +01:00
We would also need to send QEvent::StyleChange, when
a window has a new skin. TODO ...
*/
QObject::connect( qskSkinManager, &QskSkinManager::skinChanged,
qskSkinManager, [ this ] { updateSkin(); } );
QObject::connect( qskSkinManager, &QskSkinManager::colorSchemeChanged,
qskSkinManager, [ this ] { updateSkin(); } );
2019-09-04 06:59:43 +02:00
}
2024-02-02 14:13:33 +01:00
inline void insert( QskItem* item )
2019-09-04 06:59:43 +02:00
{
m_items.insert( item );
}
2024-02-02 14:13:33 +01:00
inline void remove( QskItem* item )
2019-09-04 06:59:43 +02:00
{
m_items.erase( item );
}
2024-02-02 15:01:43 +01:00
void updateItemFlags()
2019-09-04 06:59:43 +02:00
{
2024-02-02 15:01:43 +01:00
const auto flags = QskSetup::updateFlags();
2019-09-04 06:59:43 +02:00
for ( auto item : m_items )
2021-02-09 08:13:20 +01:00
qskApplyUpdateFlags( flags, item );
2019-09-04 06:59:43 +02:00
}
void updateSkin()
{
QEvent event( QEvent::StyleChange );
for ( auto item : m_items )
{
event.setAccepted( true );
QCoreApplication::sendEvent( item, &event );
}
}
private:
2024-02-02 14:13:33 +01:00
std::unordered_set< QskItem* > m_items;
2019-09-04 06:59:43 +02:00
};
}
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;
};
}
namespace
{
// A helper class for the polishOnParentResize feature
class QskParentListener final : public QQuickItemChangeListener
{
public:
void update( QQuickItem* parentItem )
{
if ( parentItem == nullptr )
return;
const auto changeTypes =
QQuickItemPrivate::Geometry | QQuickItemPrivate::Children;
auto d = QQuickItemPrivate::get( parentItem );
if ( needListening( parentItem ) )
d->updateOrAddItemChangeListener( this, changeTypes );
else
d->removeItemChangeListener( this, changeTypes );
}
private:
inline bool needListening( const QQuickItem* parentItem ) const
{
const auto children = parentItem->childItems();
for ( auto child : children )
{
if ( auto item = qobject_cast< const QskItem* >( child ) )
{
if ( item->polishOnParentResize() )
return true;
}
}
return false;
}
void itemGeometryChanged( QQuickItem* parentItem,
QQuickGeometryChange, const QRectF& ) override
{
const auto children = parentItem->childItems();
for ( auto child : children )
{
if ( auto item = qobject_cast< QskItem* >( child ) )
{
if ( item->polishOnParentResize() )
{
item->resetImplicitSize();
item->polish();
}
}
}
}
void itemChildRemoved( QQuickItem* parentItem, QQuickItem* )
{
update( parentItem );
}
};
}
2024-02-02 14:13:33 +01:00
Q_GLOBAL_STATIC( QskItemRegistry, qskRegistry )
2019-09-04 06:59:43 +02:00
Q_GLOBAL_STATIC( QskWindowStore, qskReleasedWindowCounter )
Q_GLOBAL_STATIC( QskParentListener, qskParentListener )
2019-09-04 06:59:43 +02:00
2024-02-02 14:13:33 +01:00
QskItem::QskItem( QskItemPrivate& dd, QQuickItem* parent )
2019-09-04 06:59:43 +02:00
: QQuickItem( dd, parent )
{
setFlag( QQuickItem::ItemHasContents, true );
Inherited::setActiveFocusOnTab( false );
2019-09-04 06:59:43 +02:00
2024-02-02 14:13:33 +01:00
if ( dd.updateFlags & QskItem::DeferredUpdate )
2019-09-04 06:59:43 +02:00
qskFilterWindow( window() );
qskRegistry->insert( this );
}
2024-02-02 14:13:33 +01:00
QskItem::~QskItem()
2019-09-04 06:59:43 +02: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.
Note, that since Qt >= 6.5 this information is stored
in QQuickItemPrivate::inDestructor.
s.a: qskIsItemInDestructor
2019-09-04 06:59:43 +02:00
*/
d_func()->componentComplete = false;
if ( qskRegistry )
qskRegistry->remove( this );
}
2024-02-02 14:13:33 +01:00
const char* QskItem::className() const
2019-09-04 06:59:43 +02:00
{
return metaObject()->className();
}
2024-02-02 14:13:33 +01:00
void QskItem::classBegin()
2019-09-04 06:59:43 +02:00
{
Inherited::classBegin();
}
2024-02-02 14:13:33 +01:00
void QskItem::componentComplete()
2019-09-04 06:59:43 +02:00
{
#if defined( QT_DEBUG )
2024-02-02 14:13:33 +01:00
if ( d_func()->updateFlags & QskItem::DeferredLayout )
2019-09-04 06:59:43 +02:00
{
if ( qobject_cast< const QQuickBasePositioner* >( parent() ) )
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
qWarning( "QskItem in DeferredLayout mode under control of a positioner" );
2019-09-04 06:59:43 +02:00
}
}
#endif
Inherited::componentComplete();
}
2024-02-02 14:13:33 +01:00
void QskItem::releaseResources()
2019-09-04 06:59:43 +02:00
{
Inherited::releaseResources();
// QQuickItem::derefWindow runs over the children between
// calling releaseResources and itemChange. So we need to have
// a reference count to know, when we have processed all
// sequences to be able to provide the correct "oldWindow"
// in the WindowChange event.
qskReleasedWindowCounter->setWindow( window() );
}
2024-02-02 14:13:33 +01:00
void QskItem::setDisabled( bool on )
2019-09-04 06:59:43 +02:00
{
2021-02-15 09:17:29 +01:00
Inherited::setEnabled( !on );
}
2024-02-02 14:13:33 +01:00
void QskItem::setHidden( bool on )
2021-02-15 09:17:29 +01:00
{
Inherited::setVisible( !on );
2019-09-04 06:59:43 +02:00
}
2024-02-02 14:13:33 +01:00
void QskItem::show()
2019-09-04 06:59:43 +02:00
{
Inherited::setVisible( true );
}
2024-02-02 14:13:33 +01:00
void QskItem::hide()
2019-09-04 06:59:43 +02:00
{
Inherited::setVisible( false );
}
2024-02-02 14:13:33 +01:00
bool QskItem::isVisibleTo( const QQuickItem* ancestor ) const
2019-09-04 06:59:43 +02:00
{
return qskIsVisibleTo( this, ancestor );
}
2024-02-02 14:13:33 +01:00
bool QskItem::isVisibleToParent() const
2019-09-06 19:56:03 +02:00
{
return d_func()->explicitVisible;
}
2024-02-02 14:13:33 +01:00
void QskItem::setGeometry( qreal x, qreal y, qreal width, qreal height )
2019-09-04 06:59:43 +02:00
{
// QQuickItem does not even offer changing the geometry
// in one call - what leads to 2 calls of the updateGeometry
// hook. Grmpf ...
Q_D( QQuickItem );
2021-09-21 08:51:01 +02:00
#if QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 )
d->heightValidFlag = true;
d->widthValidFlag = true;
#else
2019-09-04 06:59:43 +02:00
d->heightValid = true;
d->widthValid = true;
2021-09-21 08:51:01 +02:00
#endif
2019-09-04 06:59:43 +02:00
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 );
/*
2020-10-26 17:59:19 +01:00
Unfortunately geometryChange(d) is protected and we can't implement
2019-09-04 06:59:43 +02:00
this code as qskSetItemGeometry - further hacking required: TODO ...
*/
2020-10-26 17:59:19 +01:00
const QRectF newRect( d->x, d->y, d->width, d->height );
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
geometryChange( newRect, oldRect );
#else
geometryChanged( newRect, oldRect );
#endif
2019-09-04 06:59:43 +02:00
}
}
2024-02-02 14:13:33 +01:00
QRectF QskItem::rect() const
2019-09-04 06:59:43 +02:00
{
Q_D( const QQuickItem );
return QRectF( 0, 0, d->width, d->height );
}
2024-02-02 14:13:33 +01:00
QRectF QskItem::geometry() const
2019-09-04 06:59:43 +02:00
{
Q_D( const QQuickItem );
return QRectF( d->x, d->y, d->width, d->height );
}
2024-02-02 14:13:33 +01:00
void QskItem::setTabFence( bool on )
2019-09-04 06:59:43 +02:00
{
Q_D( QQuickItem );
if ( on != d->isTabFence )
{
d->isTabFence = on;
Q_EMIT itemFlagsChanged();
}
}
2024-02-02 14:13:33 +01:00
bool QskItem::isTabFence() const
2019-09-04 06:59:43 +02:00
{
return d_func()->isTabFence;
}
2024-02-02 14:13:33 +01:00
void QskItem::setFocusPolicy( Qt::FocusPolicy policy )
{
/*
Qt::FocusPolicy has always been there with widgets, got lost with
Qt/Quick and has been reintroduced with Qt/Quick Controls 2 ( QC2 ).
Unfortunately this was done by adding code on top instead of fixing
the foundation.
*/
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
if ( policy != d->focusPolicy )
{
d->focusPolicy = ( policy & ~Qt::TabFocus );
const bool tabFocus = policy & Qt::TabFocus;
if ( !tabFocus && window() )
{
// removing the activeFocusItem from the focus tab chain is not possible
if ( window()->activeFocusItem() == this )
{
if ( auto focusItem = nextItemInFocusChain( true ) )
focusItem->setFocus( true );
}
}
Inherited::setActiveFocusOnTab( tabFocus );
Q_EMIT focusPolicyChanged( focusPolicy() );
}
}
2024-02-02 14:13:33 +01:00
Qt::FocusPolicy QskItem::focusPolicy() const
{
uint policy = d_func()->focusPolicy;
if ( Inherited::activeFocusOnTab() )
policy |= Qt::TabFocus;
return static_cast< Qt::FocusPolicy >( policy );
}
2024-02-02 14:13:33 +01:00
void QskItem::setWheelEnabled( bool on )
{
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
if ( on != d->wheelEnabled )
{
d->wheelEnabled = on;
Q_EMIT wheelEnabledChanged( on );
}
}
2024-02-02 14:13:33 +01:00
bool QskItem::isWheelEnabled() const
{
return d_func()->wheelEnabled;
}
2024-02-02 14:13:33 +01:00
void QskItem::setPolishOnResize( bool on )
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
2019-09-04 06:59:43 +02:00
if ( on != d->polishOnResize )
{
d->polishOnResize = on;
polish();
Q_EMIT itemFlagsChanged();
}
}
2024-02-02 14:13:33 +01:00
bool QskItem::polishOnResize() const
2019-09-04 06:59:43 +02:00
{
return d_func()->polishOnResize;
}
void QskItem::setPolishOnParentResize( bool on )
{
Q_D( QskItem );
if ( on != d->polishOnParentResize )
{
d->polishOnParentResize = on;
if ( parentItem() && qskParentListener )
{
qskParentListener->update( parentItem() );
resetImplicitSize();
polish();
}
Q_EMIT itemFlagsChanged();
}
}
bool QskItem::polishOnParentResize() const
{
return d_func()->polishOnParentResize;
}
2024-02-02 14:13:33 +01:00
bool QskItem::layoutMirroring() const
2019-09-04 06:59:43 +02:00
{
#if 1
/*
What about using Qt::LayoutDirection instead. It sounds
like a more expressive API and we do not run into conflicts
with the layoutMirroring() attached property for QML.
But what is the situation with locales, where the default direction
is RightToLeft ?
*/
2019-09-04 06:59:43 +02:00
return d_func()->effectiveLayoutMirror;
#endif
2019-09-04 06:59:43 +02:00
}
2024-02-02 14:13:33 +01:00
void QskItem::setLayoutMirroring( bool on, bool childrenInherit )
2019-09-04 06:59:43 +02:00
{
// Again we have to deal with an existing API made for QML,
// that is weired for C++: LayoutMirroring/QQuickLayoutMirroringAttached
// Internally it is managed by 5(!) different flags - condolences
// to the poor guy who has been sentenced to maintain this.
// Anyway, the code below might achieve the desired behavior without
// breaking the QML path.
Q_D( QQuickItem );
if ( childrenInherit != d->inheritMirrorFromItem )
2019-09-04 06:59:43 +02:00
{
d->inheritMirrorFromItem = childrenInherit;
2019-09-04 06:59:43 +02:00
d->resolveLayoutMirror();
}
d->isMirrorImplicit = false;
if ( on != d->effectiveLayoutMirror )
{
d->setLayoutMirror( on );
if ( childrenInherit )
2019-09-04 06:59:43 +02:00
d->resolveLayoutMirror();
}
}
2024-02-02 14:13:33 +01:00
void QskItem::resetLayoutMirroring()
2019-09-04 06:59:43 +02:00
{
Q_D( QQuickItem );
if ( d && !d->isMirrorImplicit )
{
d->isMirrorImplicit = true;
// d->inheritMirrorFromItem = false;
d->resolveLayoutMirror();
}
}
2024-02-02 14:13:33 +01:00
bool QskItem::isPolishScheduled() const
2019-09-04 06:59:43 +02:00
{
return d_func()->polishScheduled;
}
2024-02-02 14:13:33 +01:00
bool QskItem::isUpdateNodeScheduled() const
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
Q_D( const QskItem );
2019-09-04 06:59:43 +02:00
return ( d->dirtyAttributes & QQuickItemPrivate::ContentUpdateMask ) &&
( d->flags & QQuickItem::ItemHasContents );
}
2024-02-02 14:13:33 +01:00
bool QskItem::isInitiallyPainted() const
2019-09-04 06:59:43 +02:00
{
2021-02-01 10:09:37 +01:00
return d_func()->initiallyPainted;
2019-09-04 06:59:43 +02:00
}
2024-02-02 14:13:33 +01:00
bool QskItem::maybeUnresized() const
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
Q_D( const QskItem );
2019-09-04 06:59:43 +02:00
if ( d->width <= 0.0 && d->height <= 0.0 )
{
/*
Unfortunately the list of items to-be-polished is not processed
in top/down order and we might run into updatePolish() before
having a proper size. But when the parentItem() is waiting
for to-be-polished, we assume, that we will be resized then
and run into another updatePolish() then.
*/
if ( d->polishOnResize && qskIsPolishScheduled( parentItem() ) )
return true;
}
return false;
}
2024-02-02 14:13:33 +01:00
QskItem::UpdateFlags QskItem::updateFlags() const
2019-09-04 06:59:43 +02:00
{
2021-02-09 08:13:20 +01:00
return UpdateFlags( d_func()->updateFlags );
2019-09-04 06:59:43 +02:00
}
2024-02-02 14:13:33 +01:00
void QskItem::resetUpdateFlags()
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
2019-09-04 06:59:43 +02:00
// clear all bits in the mask
2021-02-09 08:13:20 +01:00
d->updateFlagsMask = 0;
2024-02-02 15:01:43 +01:00
d->applyUpdateFlags( QskSetup::updateFlags() );
2019-09-04 06:59:43 +02:00
}
2024-02-02 14:13:33 +01:00
void QskItem::setUpdateFlag( UpdateFlag flag, bool on )
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
2019-09-04 06:59:43 +02:00
2021-02-09 08:13:20 +01:00
d->updateFlagsMask |= flag;
2019-09-04 06:59:43 +02:00
2021-02-09 08:13:20 +01:00
if ( testUpdateFlag( flag ) != on )
2019-09-04 06:59:43 +02:00
{
2021-02-09 08:13:20 +01:00
applyUpdateFlag( flag, on );
Q_EMIT updateFlagsChanged( updateFlags() );
2019-09-04 06:59:43 +02:00
}
}
2024-02-02 14:13:33 +01:00
void QskItem::resetUpdateFlag( UpdateFlag flag )
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
2019-09-04 06:59:43 +02:00
2021-02-09 08:13:20 +01:00
d->updateFlagsMask &= ~flag;
2019-09-04 06:59:43 +02:00
2024-02-02 15:01:43 +01:00
const bool on = QskSetup::testUpdateFlag( flag );
2019-09-04 06:59:43 +02:00
2021-02-09 08:13:20 +01:00
if ( testUpdateFlag( flag ) != on )
2019-09-04 06:59:43 +02:00
{
2021-02-09 08:13:20 +01:00
applyUpdateFlag( flag, on );
Q_EMIT updateFlagsChanged( updateFlags() );
2019-09-04 06:59:43 +02:00
}
}
2024-02-02 14:13:33 +01:00
bool QskItem::testUpdateFlag( UpdateFlag flag ) const
2019-09-04 06:59:43 +02:00
{
2021-02-09 08:13:20 +01:00
return d_func()->updateFlags & flag;
2019-09-04 06:59:43 +02:00
}
2024-02-02 14:13:33 +01:00
void QskItem::applyUpdateFlag( UpdateFlag flag, bool on )
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
2019-09-04 06:59:43 +02:00
2021-02-09 08:13:20 +01:00
if ( testUpdateFlag( flag ) == on )
2019-09-04 06:59:43 +02:00
return;
if ( on )
2021-02-09 08:13:20 +01:00
d->updateFlags |= flag;
2019-09-04 06:59:43 +02:00
else
2021-02-09 08:13:20 +01:00
d->updateFlags &= ~flag;
2019-09-04 06:59:43 +02:00
switch ( flag )
{
2024-02-02 14:13:33 +01:00
case QskItem::DeferredUpdate:
2019-09-04 06:59:43 +02:00
{
if ( on )
{
qskFilterWindow( window() );
}
else
{
if ( !isVisible() )
update();
}
break;
}
2024-02-02 14:13:33 +01:00
case QskItem::DeferredPolish:
2019-09-04 06:59:43 +02:00
{
if ( !on && d->blockedPolish )
polish();
break;
}
2024-02-02 14:13:33 +01:00
case QskItem::DeferredLayout:
2019-09-04 06:59:43 +02:00
{
if ( !on )
{
// Update the implicitSize and rebind the size to it.
// Having set the size explicitly gets lost.
2021-09-21 08:51:01 +02:00
#if QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 )
d->widthValidFlag = d->heightValidFlag = false;
#else
2019-09-04 06:59:43 +02:00
d->widthValid = d->heightValid = false;
2021-09-21 08:51:01 +02:00
#endif
2019-09-04 06:59:43 +02:00
d->updateImplicitSize( false );
}
break;
}
2024-02-02 14:13:33 +01:00
case QskItem::CleanupOnVisibility:
2019-09-04 06:59:43 +02:00
{
if ( on && !isVisible() )
d->cleanupNodes();
break;
}
2024-02-02 14:13:33 +01:00
case QskItem::DebugForceBackground:
2019-09-04 06:59:43 +02:00
{
// no need to mark it dirty
if ( flags() & QQuickItem::ItemHasContents )
update();
break;
}
default:
break;
}
}
2024-02-02 14:13:33 +01:00
void QskItem::resetImplicitSize()
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
2019-09-04 06:59:43 +02:00
2024-02-02 14:13:33 +01:00
if ( d->updateFlags & QskItem::DeferredLayout )
2019-09-04 06:59:43 +02:00
{
d->blockedImplicitSize = true;
d->layoutConstraintChanged();
}
else
{
d->updateImplicitSize( true );
}
}
2024-02-02 14:13:33 +01:00
bool QskItem::event( QEvent* event )
2019-09-04 06:59:43 +02:00
{
const int eventType = event->type();
2021-03-03 08:25:39 +01:00
const bool hasContents = flags() & QQuickItem::ItemHasContents;
2019-09-04 06:59:43 +02:00
switch( eventType )
{
case QEvent::StyleChange:
{
d_func()->clearPreviousNodes = true;
resetImplicitSize();
polish();
2021-03-03 08:25:39 +01:00
if ( hasContents )
2019-09-04 06:59:43 +02:00
update();
changeEvent( event );
return true;
}
case QEvent::ContentsRectChange:
{
resetImplicitSize();
2021-03-03 08:25:39 +01:00
if ( d_func()->polishOnResize )
2019-09-04 06:59:43 +02:00
polish();
2021-03-03 08:25:39 +01:00
if ( hasContents )
update();
2019-09-04 06:59:43 +02:00
changeEvent( event );
return true;
}
case QEvent::ReadOnlyChange:
case QEvent::EnabledChange:
case QEvent::LocaleChange:
2019-09-04 06:59:43 +02:00
case QEvent::ParentChange:
2021-03-03 08:25:39 +01:00
case QEvent::LayoutDirectionChange:
2019-09-04 06:59:43 +02:00
{
changeEvent( event );
return true;
}
case QskEvent::GeometryChange:
{
geometryChangeEvent( static_cast< QskGeometryChangeEvent* >( event ) );
return true;
}
case QskEvent::WindowChange:
{
windowChangeEvent( static_cast< QskWindowChangeEvent* >( event ) );
return true;
}
case QEvent::LayoutRequest:
{
if ( d_func()->polishOnResize )
polish();
return true;
}
case QEvent::FocusIn:
{
if ( window() == nullptr )
{
/*
During deconstruction of the window we run into
focus changes when the items in the tree get destroyed.
Calling focusInEvent() in this state does not make sense
and often results in crashes in overloaded event handlers.
*/
return true;
}
break;
}
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
{
if ( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
{
if ( QGuiApplication::styleHints()->setFocusOnTouchRelease() )
{
if ( event->type() == QEvent::MouseButtonRelease )
forceActiveFocus( Qt::MouseFocusReason );
}
else
{
if ( event->type() == QEvent::MouseButtonPress )
forceActiveFocus( Qt::MouseFocusReason );
}
}
break;
}
case QEvent::Wheel:
{
if ( !isWheelEnabled() )
{
/*
We block further processing of the event. This is in line
with not receiving any mouse event that have not been
explicitly enabled with setAcceptedMouseButtons().
*/
event->ignore();
return true;
}
if ( ( focusPolicy() & Qt::WheelFocus ) == Qt::WheelFocus )
forceActiveFocus( Qt::MouseFocusReason );
break;
}
2019-09-04 06:59:43 +02:00
}
return Inherited::event( event );
}
2024-02-02 14:13:33 +01:00
void QskItem::windowChangeEvent( QskWindowChangeEvent* )
2019-09-04 06:59:43 +02:00
{
}
2024-02-02 14:13:33 +01:00
void QskItem::geometryChangeEvent( QskGeometryChangeEvent* )
2019-09-04 06:59:43 +02:00
{
}
2024-02-02 14:13:33 +01:00
void QskItem::changeEvent( QEvent* )
2019-09-04 06:59:43 +02:00
{
}
2024-02-02 14:13:33 +01:00
void QskItem::itemChange( QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData& changeData )
2019-09-04 06:59:43 +02:00
{
switch ( change )
{
case QQuickItem::ItemSceneChange:
{
if ( changeData.window )
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
Q_D( const QskItem );
if ( d->updateFlags & QskItem::DeferredUpdate )
qskFilterWindow( changeData.window );
2019-09-04 06:59:43 +02:00
}
#if 1
auto oldWindow = qskReleasedWindowCounter->window();
2021-04-26 15:11:19 +02:00
if ( oldWindow && oldWindow->contentItem()
&& ( oldWindow->activeFocusItem() == this ) )
2019-09-04 06:59:43 +02: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().
*/
2021-04-26 11:44:25 +02:00
#if QT_VERSION >= QT_VERSION_CHECK( 6, 1, 0 )
auto wd = QQuickWindowPrivate::get( oldWindow )->deliveryAgentPrivate();
2021-08-04 09:31:16 +02:00
#else
auto wd = QQuickWindowPrivate::get( oldWindow );
2021-04-26 11:44:25 +02:00
#endif
if ( auto scope = qskNearestFocusScope( this ) )
{
wd->clearFocusInScope( scope, this, Qt::OtherFocusReason );
}
else
{
wd->activeFocusItem = nullptr;
}
2019-09-04 06:59:43 +02:00
}
#endif
#if 1
if ( changeData.window == nullptr )
{
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
if( d->focus )
{
/*
The focus flag is not cleared, when removing an
item from the window. In situations where the item gets
reinserted into the window - or transferred to another one -
we might run into situations, where 2 items in the same scope
have the "focus" flag being set.
A better solution might be to check the flag when reinserting
into a window ...
*/
d->focus = false;
}
}
#endif
QskWindowChangeEvent event( oldWindow, changeData.window );
2019-09-04 06:59:43 +02:00
QCoreApplication::sendEvent( this, &event );
break;
}
case QQuickItem::ItemEnabledHasChanged:
{
2022-03-08 11:53:46 +01:00
qskSendEventTo( this, QEvent::EnabledChange );
2019-09-04 06:59:43 +02:00
break;
}
case QQuickItem::ItemVisibleHasChanged:
{
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
2019-09-04 06:59:43 +02:00
#if 1
/*
~QQuickItem sends QQuickItem::ItemVisibleHasChanged recursively
to all childItems. When being a child ( not only a childItem() )
we are short before being destructed too and any updates
done here are totally pointless. TODO ...
*/
#endif
if ( changeData.boolValue )
2019-09-04 06:59:43 +02:00
{
if ( d->blockedPolish )
polish();
2024-02-02 14:13:33 +01:00
if ( d->updateFlags & QskItem::DeferredUpdate )
2019-09-04 06:59:43 +02:00
{
if ( d->dirtyAttributes && ( d->flags & QQuickItem::ItemHasContents ) )
update();
}
}
else
{
2024-02-02 14:13:33 +01:00
if ( d->updateFlags & QskItem::CleanupOnVisibility )
2019-09-04 06:59:43 +02:00
d->cleanupNodes();
2021-02-01 10:09:37 +01:00
d->initiallyPainted = false;
2019-09-04 06:59:43 +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 ...
*/
d->layoutConstraintChanged();
}
break;
}
case QQuickItem::ItemParentHasChanged:
{
if( polishOnParentResize() && qskParentListener )
qskParentListener->update( parentItem() );
break;
}
2019-09-04 06:59:43 +02:00
case QQuickItem::ItemChildAddedChange:
case QQuickItem::ItemChildRemovedChange:
{
// do we want to have events for those ???
break;
}
case QQuickItem::ItemOpacityHasChanged:
case QQuickItem::ItemActiveFocusHasChanged:
case QQuickItem::ItemRotationHasChanged:
case QQuickItem::ItemAntialiasingHasChanged:
case QQuickItem::ItemDevicePixelRatioHasChanged:
2019-09-04 06:59:43 +02:00
{
break;
}
}
Inherited::itemChange( change, changeData );
2019-09-04 06:59:43 +02:00
}
2020-10-26 17:59:19 +01:00
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
2024-02-02 14:13:33 +01:00
void QskItem::geometryChanged(
2019-09-04 06:59:43 +02:00
const QRectF& newGeometry, const QRectF& oldGeometry )
{
2020-10-26 17:59:19 +01:00
geometryChange( newGeometry, oldGeometry );
}
#endif
2024-02-02 14:13:33 +01:00
void QskItem::geometryChange(
2020-10-26 17:59:19 +01:00
const QRectF& newGeometry, const QRectF& oldGeometry )
{
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
2019-09-04 06:59:43 +02:00
Inherited::geometryChanged( newGeometry, oldGeometry );
2020-10-26 17:59:19 +01:00
#else
Inherited::geometryChange( newGeometry, oldGeometry );
#endif
2019-09-04 06:59:43 +02:00
2024-02-02 14:13:33 +01:00
Q_D( const QskItem );
2019-09-04 06:59:43 +02:00
if ( !d->polishScheduled && d->polishOnResize )
{
if ( newGeometry.size() != oldGeometry.size() )
polish();
}
QskGeometryChangeEvent event( newGeometry, oldGeometry );
QCoreApplication::sendEvent( this, &event );
}
2024-02-02 14:13:33 +01:00
void QskItem::mouseUngrabEvent()
{
Inherited::mouseUngrabEvent();
}
2024-02-02 14:13:33 +01:00
void QskItem::touchUngrabEvent()
{
Inherited::touchUngrabEvent();
}
2021-03-02 17:09:38 +01:00
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
2024-02-02 14:13:33 +01:00
void QskItem::windowDeactivateEvent()
{
Inherited::windowDeactivateEvent();
}
2021-03-02 17:09:38 +01:00
#endif
2024-02-02 14:13:33 +01:00
void QskItem::updatePolish()
2019-09-04 06:59:43 +02:00
{
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
2019-09-04 06:59:43 +02:00
2024-02-02 14:13:33 +01:00
if ( d->updateFlags & QskItem::DeferredPolish )
2019-09-04 06:59:43 +02:00
{
if ( !isVisible() )
{
d->blockedPolish = true;
return;
}
}
d->blockedPolish = false;
2021-02-01 10:09:37 +01:00
if ( !d->initiallyPainted )
2019-09-04 06:59:43 +02:00
{
/*
We should find a better way for identifying, when
an item is about to be shown, than making it dependend
from polishing and the existence of scene graph nodes. TODO ...
*/
aboutToShow();
}
updateItemPolish();
}
2024-02-02 14:13:33 +01:00
void QskItem::aboutToShow()
2019-09-04 06:59:43 +02:00
{
}
2024-02-02 14:13:33 +01:00
void QskItem::updateItemPolish()
2019-09-04 06:59:43 +02:00
{
}
2024-02-02 14:13:33 +01:00
QSGNode* QskItem::updatePaintNode( QSGNode* node, UpdatePaintNodeData* data )
2019-09-04 06:59:43 +02:00
{
2021-12-27 10:21:03 +01:00
Q_UNUSED( data )
2019-09-04 06:59:43 +02:00
2024-02-02 14:13:33 +01:00
Q_D( QskItem );
2019-09-04 06:59:43 +02:00
2024-02-02 14:13:33 +01:00
Q_ASSERT( isVisible() || !( d->updateFlags & QskItem::DeferredUpdate ) );
2019-09-04 06:59:43 +02:00
2021-02-01 10:09:37 +01:00
d->initiallyPainted = true;
2019-09-04 06:59:43 +02:00
if ( d->clearPreviousNodes )
{
delete node;
node = nullptr;
#if 1
/*
controls might find subnodes using qskPaintNode - not good
as d->paintNode is not updated before leaving here. TODO ...
In the initial call we will always have a nullptr - even if
it has already been allocated. When deleting it we have a dangling pointer.
instead of the new one.
To avoid creashes for the second situation we manually clear d->paintNode.
*/
d->paintNode = nullptr;
#endif
2019-09-04 06:59:43 +02:00
d->clearPreviousNodes = false;
}
return updateItemPaintNode( node );
}
2024-02-02 14:13:33 +01:00
QSGNode* QskItem::updateItemPaintNode( QSGNode* node )
2019-09-04 06:59:43 +02:00
{
return node;
}
2024-02-02 15:01:43 +01:00
void qskUpdateItemFlags()
{
if ( qskRegistry )
qskRegistry->updateItemFlags();
}
2024-02-02 14:13:33 +01:00
#include "moc_QskItem.cpp"