qskinny/src/controls/QskFocusIndicator.cpp

214 lines
5.7 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 "QskFocusIndicator.h"
#include "QskBoxNode.h"
2017-07-21 18:21:34 +02:00
#include "QskAspect.h"
#include "QskEvent.h"
#include <QQuickWindow>
QSK_SUBCONTROL( QskFocusIndicator, Panel )
static inline QRectF qskFocusIndicatorRect( const QQuickItem* item )
{
if ( auto control = qobject_cast< const QskControl* >( item ) )
return control->focusIndicatorRect();
const QVariant v = item->property( "focusIndicatorRect" );
if ( v.canConvert( QMetaType::QRectF ) )
return v.toRectF();
return qskItemRect( item );
}
2018-02-01 08:07:54 +01:00
class QskFocusIndicator::PrivateData
{
public:
void resetConnections()
{
for ( const auto connection : connections )
QObject::disconnect( connection );
connections.clear();
}
QVector< QMetaObject::Connection > connections;
};
2017-07-21 18:21:34 +02:00
QskFocusIndicator::QskFocusIndicator( QQuickItem* parent ):
2018-02-01 08:07:54 +01:00
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()
{
}
void QskFocusIndicator::onFocusItemGeometryChanged()
{
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
if ( window() == nullptr )
return;
2017-07-21 18:21:34 +02:00
const QQuickItem* focusItem = window()->activeFocusItem();
if ( ( focusItem == nullptr ) || ( focusItem == window()->contentItem() ) )
2017-07-21 18:21:34 +02:00
{
/*
We might get here, when the previously focused item was destroyed.
Might happen in common situations, like when a subwindow
was closed. We put ourself below the root item then.
*/
2017-07-21 18:21:34 +02:00
if ( parentItem() != window()->contentItem() )
{
setParentItem( window()->contentItem() );
updateFocusFrame();
}
return;
}
2018-02-01 08:07:54 +01:00
m_data->connections += connectItem( focusItem );
2017-07-21 18:21:34 +02:00
const QQuickItem* item = focusItem;
while ( item->parentItem() )
{
auto itemParent = item->parentItem();
if ( itemParent == window()->contentItem() || itemParent->clip() )
2017-07-21 18:21:34 +02:00
{
/*
When the focus item is clipped - maybe because of being at the
border of a scrollarea - the focus indicator needs to be
clipped as well. The easiest way to have this is to put us
below the item having the clip.
*/
setParentItem( itemParent );
}
2017-07-21 18:21:34 +02:00
if ( itemParent == parentItem() )
{
// We want to be on top, but do we cover all corner cases ???
2017-07-21 18:21:34 +02:00
setZ( item->z() + 10e-6 );
break;
2017-07-21 18:21:34 +02:00
}
item = itemParent;
2018-02-01 08:07:54 +01:00
m_data->connections += connectItem( item );
2017-07-21 18:21:34 +02:00
}
updateFocusFrame();
}
void QskFocusIndicator::updateFocusFrame()
{
QRectF r = focusRect();
setVisible( !r.isEmpty() );
if ( !r.isEmpty() )
{
r = r.marginsAdded( marginsHint( Panel | QskAspect::Padding ) );
2017-07-21 18:21:34 +02:00
setGeometry( r );
}
update();
}
QRectF QskFocusIndicator::focusRect() const
{
if ( window() && parentItem() )
{
2018-01-24 10:14:50 +01:00
const QQuickItem* item = window()->activeFocusItem();
if ( item && ( item != this ) && item->isVisible()
&& ( item != window()->contentItem() ) )
{
2018-01-24 10:14:50 +01:00
const auto rect = qskFocusIndicatorRect( item );
return parentItem()->mapRectFromItem( item, rect );
}
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 );
}
else
{
disconnect( window, &QQuickWindow::activeFocusItemChanged,
this, &QskFocusIndicator::onFocusItemChanged );
}
}
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 );
if ( const auto control = qobject_cast< const QskControl* >( sender ) )
{
c += QObject::connect( control, &QskControl::focusIndicatorRectChanged, this, method );
}
else
{
if ( sender->metaObject()->indexOfSignal( "focusIndicatorRectChanged()" ) >= 0 )
{
c += QObject::connect( sender, SIGNAL( focusIndicatorRectChanged() ),
this, SLOT( onFocusItemGeometryChanged() ) );
}
}
return c;
}
2017-07-21 18:21:34 +02:00
#include "moc_QskFocusIndicator.cpp"