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 "QskFocusIndicator.h"
|
|
|
|
#include "QskAspect.h"
|
|
|
|
#include "QskEvent.h"
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskQuick.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
#include <qpointer.h>
|
2018-07-19 14:10:48 +02:00
|
|
|
#include <qquickwindow.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2020-03-16 16:37:28 +01:00
|
|
|
QSK_QT_PRIVATE_BEGIN
|
|
|
|
#include <private/qquickitem_p.h>
|
|
|
|
QSK_QT_PRIVATE_END
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
QSK_SUBCONTROL( QskFocusIndicator, Panel )
|
|
|
|
|
2018-01-19 10:15:29 +01:00
|
|
|
static inline QRectF qskFocusIndicatorRect( const QQuickItem* item )
|
|
|
|
{
|
2019-04-26 11:56:09 +02:00
|
|
|
if ( auto control = qskControlCast( item ) )
|
2018-01-19 10:15:29 +01:00
|
|
|
return control->focusIndicatorRect();
|
|
|
|
|
|
|
|
const QVariant v = item->property( "focusIndicatorRect" );
|
|
|
|
if ( v.canConvert( QMetaType::QRectF ) )
|
|
|
|
return v.toRectF();
|
|
|
|
|
2018-02-07 19:30:34 +01:00
|
|
|
return qskItemRect( item );
|
2018-01-19 10:15:29 +01:00
|
|
|
}
|
2018-02-01 08:07:54 +01:00
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
static inline QRectF qskFocusIndicatorClipRect( const QQuickItem* item )
|
|
|
|
{
|
|
|
|
QRectF rect( 0.0, 0.0, -1.0, -1.0 );
|
|
|
|
|
|
|
|
if ( item )
|
|
|
|
{
|
|
|
|
if ( auto control = qskControlCast( item ) )
|
|
|
|
rect = control->focusIndicatorClipRect();
|
|
|
|
else
|
|
|
|
rect = item->clipRect();
|
|
|
|
}
|
|
|
|
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
|
2018-02-01 08:07:54 +01:00
|
|
|
class QskFocusIndicator::PrivateData
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2018-02-01 08:07:54 +01:00
|
|
|
void resetConnections()
|
|
|
|
{
|
2018-08-03 11:11:42 +02:00
|
|
|
for ( const auto& connection : qskAsConst( connections ) )
|
2018-02-01 08:07:54 +01:00
|
|
|
QObject::disconnect( connection );
|
|
|
|
|
|
|
|
connections.clear();
|
|
|
|
}
|
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
QPointer< QQuickItem > clippingItem;
|
2018-02-01 08:07:54 +01:00
|
|
|
QVector< QMetaObject::Connection > connections;
|
|
|
|
};
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
QskFocusIndicator::QskFocusIndicator( QQuickItem* parent )
|
|
|
|
: Inherited( parent ) // parentItem() might change, but parent() stays
|
|
|
|
, m_data( new PrivateData() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
setTransparentForPositioner( true );
|
|
|
|
connectWindow( window(), true );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskFocusIndicator::~QskFocusIndicator()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-09-10 17:01:47 +02:00
|
|
|
bool QskFocusIndicator::contains( const QPointF& ) const
|
2019-04-04 18:00:22 +02:00
|
|
|
{
|
2020-03-16 16:37:28 +01:00
|
|
|
// so that tools like Squish do not see it
|
2019-04-04 18:00:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
QRectF QskFocusIndicator::clipRect() const
|
|
|
|
{
|
|
|
|
if ( m_data->clippingItem )
|
|
|
|
{
|
|
|
|
auto rect = qskFocusIndicatorClipRect( m_data->clippingItem );
|
|
|
|
rect = mapRectFromItem( m_data->clippingItem, rect );
|
|
|
|
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::clipRect();
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskFocusIndicator::onFocusItemGeometryChanged()
|
|
|
|
{
|
|
|
|
updateFocusFrame();
|
|
|
|
}
|
|
|
|
|
2018-04-19 09:39:46 +02:00
|
|
|
void QskFocusIndicator::onWindowSizeChanged( int )
|
|
|
|
{
|
|
|
|
updateFocusFrame();
|
|
|
|
}
|
|
|
|
|
2018-02-01 08:07:54 +01:00
|
|
|
void QskFocusIndicator::onFocusItemDestroyed()
|
|
|
|
{
|
|
|
|
m_data->resetConnections();
|
|
|
|
setVisible( false );
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskFocusIndicator::onFocusItemChanged()
|
|
|
|
{
|
2018-02-01 08:07:54 +01:00
|
|
|
m_data->resetConnections();
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-01-11 16:29:33 +01:00
|
|
|
if ( window() == nullptr )
|
|
|
|
return;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
// We want to be on top, but do we cover all corner cases ???
|
|
|
|
setParentItem( window()->contentItem() );
|
|
|
|
setZ( 10e-6 );
|
2018-01-11 16:29:33 +01:00
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
const auto focusItem = window()->activeFocusItem();
|
|
|
|
QQuickItem* clippingItem = nullptr;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
if ( focusItem && ( focusItem != window()->contentItem() ) )
|
2018-01-11 16:29:33 +01:00
|
|
|
{
|
2020-03-16 13:17:51 +01:00
|
|
|
auto item = focusItem;
|
|
|
|
m_data->connections += connectItem( item );
|
2020-03-13 07:39:31 +01:00
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
while ( auto itemParent = item->parentItem() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2020-03-16 13:17:51 +01:00
|
|
|
m_data->connections += connectItem( itemParent );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
if ( clippingItem == nullptr && itemParent->clip() )
|
2020-03-23 16:10:17 +01:00
|
|
|
clippingItem = itemParent;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
item = itemParent;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-16 13:17:51 +01:00
|
|
|
m_data->clippingItem = clippingItem;
|
2017-07-21 18:21:34 +02:00
|
|
|
updateFocusFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskFocusIndicator::updateFocusFrame()
|
|
|
|
{
|
|
|
|
QRectF r = focusRect();
|
|
|
|
setVisible( !r.isEmpty() );
|
|
|
|
|
|
|
|
if ( !r.isEmpty() )
|
|
|
|
{
|
2017-08-23 14:53:29 +02:00
|
|
|
r = r.marginsAdded( marginsHint( Panel | QskAspect::Padding ) );
|
2017-07-21 18:21:34 +02:00
|
|
|
setGeometry( r );
|
2020-03-16 13:17:51 +01:00
|
|
|
|
|
|
|
const auto clipRect = qskFocusIndicatorClipRect( m_data->clippingItem );
|
|
|
|
setClip( !clipRect.isEmpty() );
|
2020-03-16 16:37:28 +01:00
|
|
|
|
|
|
|
if ( clip() )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
The clip node is updated on QQuickItemPrivate::Size
|
|
|
|
So we need to set it here even in situations, where
|
|
|
|
the size did not change. For now we always trigger an
|
|
|
|
update of the clipNode, but we could limit it to
|
|
|
|
changes of the clipRect(). TODO ...
|
|
|
|
*/
|
|
|
|
QQuickItemPrivate::get( this )->dirty( QQuickItemPrivate::Size );
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskFocusIndicator::focusRect() const
|
|
|
|
{
|
|
|
|
if ( window() && parentItem() )
|
|
|
|
{
|
2019-02-13 10:24:59 +01:00
|
|
|
const auto item = window()->activeFocusItem();
|
2018-01-24 10:14:50 +01:00
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( item && ( item != this ) && item->isVisible() &&
|
|
|
|
( item != window()->contentItem() ) )
|
2018-01-19 10:15:29 +01:00
|
|
|
{
|
2018-01-24 10:14:50 +01:00
|
|
|
const auto rect = qskFocusIndicatorRect( item );
|
|
|
|
return parentItem()->mapRectFromItem( item, rect );
|
2018-01-19 10:15:29 +01:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return QRectF();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskFocusIndicator::windowChangeEvent( QskWindowChangeEvent* event )
|
|
|
|
{
|
|
|
|
Inherited::windowChangeEvent( event );
|
|
|
|
|
|
|
|
connectWindow( event->oldWindow(), false );
|
|
|
|
connectWindow( event->window(), true );
|
2018-02-01 08:07:54 +01:00
|
|
|
|
|
|
|
onFocusItemChanged();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskFocusIndicator::connectWindow( const QQuickWindow* window, bool on )
|
|
|
|
{
|
|
|
|
if ( window == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( on )
|
|
|
|
{
|
|
|
|
connect( window, &QQuickWindow::activeFocusItemChanged,
|
|
|
|
this, &QskFocusIndicator::onFocusItemChanged );
|
2018-04-19 09:39:46 +02:00
|
|
|
|
|
|
|
connect( window, &QQuickWindow::widthChanged,
|
|
|
|
this, &QskFocusIndicator::onWindowSizeChanged );
|
|
|
|
|
|
|
|
connect( window, &QQuickWindow::heightChanged,
|
|
|
|
this, &QskFocusIndicator::onWindowSizeChanged );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
disconnect( window, &QQuickWindow::activeFocusItemChanged,
|
|
|
|
this, &QskFocusIndicator::onFocusItemChanged );
|
2018-04-19 09:39:46 +02:00
|
|
|
|
|
|
|
disconnect( window, &QQuickWindow::widthChanged,
|
|
|
|
this, &QskFocusIndicator::onWindowSizeChanged );
|
|
|
|
|
|
|
|
disconnect( window, &QQuickWindow::heightChanged,
|
|
|
|
this, &QskFocusIndicator::onWindowSizeChanged );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-01 08:07:54 +01:00
|
|
|
QVector< QMetaObject::Connection > QskFocusIndicator::connectItem( const QQuickItem* sender )
|
|
|
|
{
|
|
|
|
QVector< QMetaObject::Connection > c;
|
|
|
|
c.reserve( 7 );
|
|
|
|
|
|
|
|
c += QObject::connect( sender, &QObject::destroyed,
|
|
|
|
this, &QskFocusIndicator::onFocusItemDestroyed );
|
|
|
|
|
|
|
|
const auto method = &QskFocusIndicator::onFocusItemGeometryChanged;
|
|
|
|
|
|
|
|
c += QObject::connect( sender, &QQuickItem::xChanged, this, method );
|
|
|
|
c += QObject::connect( sender, &QQuickItem::yChanged, this, method );
|
|
|
|
c += QObject::connect( sender, &QQuickItem::widthChanged, this, method );
|
|
|
|
c += QObject::connect( sender, &QQuickItem::heightChanged, this, method );
|
|
|
|
c += QObject::connect( sender, &QQuickItem::visibleChanged, this, method );
|
|
|
|
|
2019-04-26 11:56:09 +02:00
|
|
|
if ( const auto control = qskControlCast( sender ) )
|
2018-02-01 08:07:54 +01:00
|
|
|
{
|
|
|
|
c += QObject::connect( control, &QskControl::focusIndicatorRectChanged, this, method );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( sender->metaObject()->indexOfSignal( "focusIndicatorRectChanged()" ) >= 0 )
|
|
|
|
{
|
2019-01-04 13:42:16 +01:00
|
|
|
c += QObject::connect( sender, SIGNAL(focusIndicatorRectChanged()),
|
|
|
|
this, SLOT(onFocusItemGeometryChanged()) );
|
2018-02-01 08:07:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
#include "moc_QskFocusIndicator.cpp"
|