[FEATURE] Indicators for layer tree view + filter indicator implementation

This adds a mini-framework for display of extra icons in layer tree views
next to layer and group names. Tool tip text can be associated with indicators
to give extra context for indicators. In addition, a signal gets emitted
when user clicks indicators and custom actions can be defined.

The main window's layer tree view (ToC) gets support for indicators
that are shown when a vector layer has a filter applied. This makes it easier
for users to understand that they are looking at a subset of all data.
Clicking the indicator's icon brings up query builder.
This commit is contained in:
Martin Dobias 2018-01-13 18:53:05 +01:00
parent 68ee969b30
commit f56d70f3f8
14 changed files with 708 additions and 3 deletions

View File

@ -287,6 +287,7 @@
%Include layertree/qgslayertreemapcanvasbridge.sip
%Include layertree/qgslayertreeview.sip
%Include layertree/qgslayertreeviewdefaultactions.sip
%Include layertree/qgslayertreeviewindicator.sip
%Include layout/qgslayoutcustomdrophandler.sip
%Include layout/qgslayoutdesignerinterface.sip
%Include layout/qgslayoutitemcombobox.sip

View File

@ -109,6 +109,37 @@ Return list of selected nodes filtered to just layer nodes
QList<QgsMapLayer *> selectedLayers() const;
%Docstring
Get list of selected layers
%End
void addIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator );
%Docstring
Adds an indicator to the given layer tree node. Indicators are icons shown next to layer/group names
in the layer tree view. They can be used to show extra information with tree nodes and they allow
user interaction.
Does not take ownership of the indicator. One indicator object may be used for multiple layer tree nodes.
\sa removeIndicator
\sa indicators
.. versionadded:: 3.2
%End
void removeIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator );
%Docstring
Removes a previously added indicator to a layer tree node. Does not delete the indicator.
\sa addIndicator
\sa indicators
.. versionadded:: 3.2
%End
QList<QgsLayerTreeViewIndicator *> indicators( QgsLayerTreeNode *node ) const;
%Docstring
Returns list of indicators associated with a particular layer tree node.
\sa addIndicator
\sa removeIndicator
.. versionadded:: 3.2
%End
public slots:
@ -153,7 +184,6 @@ Emitted when a current layer is changed
virtual void dropEvent( QDropEvent *event );
protected slots:
void modelRowsInserted( const QModelIndex &index, int start, int end );
@ -166,6 +196,7 @@ Emitted when a current layer is changed
void onModelReset();
protected:
};

View File

@ -0,0 +1,62 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/layertree/qgslayertreeviewindicator.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsLayerTreeViewIndicator : QObject
{
%Docstring
Indicator that can be used in a layer tree view to display icons next to items of the layer tree.
They add extra context to the item and interactivity (using clicked() signal).
Indicators can be added/removed to individual layer tree items using :py:func:`QgsLayerTreeView.addIndicator()`
and QgsLayerTreeView.removeIndicator() calls.
.. versionadded:: 3.2
%End
%TypeHeaderCode
#include "qgslayertreeviewindicator.h"
%End
public:
explicit QgsLayerTreeViewIndicator( QObject *parent /TransferThis/ = 0 );
QIcon icon() const;
%Docstring
Indicator icon that will be displayed in the layer tree view
%End
void setIcon( const QIcon &icon );
%Docstring
Sets indicator icon that will be displayed in the layer tree view
%End
QString toolTip() const;
%Docstring
Returns tool tip text that will be shown when user hovers mouse over the indicator
%End
void setToolTip( const QString &tip );
%Docstring
Sets tool tip text
%End
signals:
void clicked( const QModelIndex &index );
%Docstring
Signal that is emitted when user clicks on the indicator
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/layertree/qgslayertreeviewindicator.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -52,6 +52,7 @@ SET(QGIS_APP_SRCS
qgslabelengineconfigdialog.cpp
qgslabelinggui.cpp
qgslabelingwidget.cpp
qgslayertreeviewfilterindicator.cpp
qgsloadstylefromdbdialog.cpp
qgsmapcanvasdockwidget.cpp
qgsmaplayerstyleguiutils.cpp
@ -263,6 +264,7 @@ SET (QGIS_APP_MOC_HDRS
qgslabelinggui.h
qgslabelingwidget.h
qgslabelpropertydialog.h
qgslayertreeviewfilterindicator.h
qgsloadstylefromdbdialog.h
qgsmapcanvasdockwidget.h
qgsmaplayerstyleguiutils.h

View File

@ -201,6 +201,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgslayertreeutils.h"
#include "qgslayertreeview.h"
#include "qgslayertreeviewdefaultactions.h"
#include "qgslayertreeviewfilterindicator.h"
#include "qgslayout.h"
#include "qgslayoutatlas.h"
#include "qgslayoutcustomdrophandler.h"
@ -3623,7 +3624,6 @@ void QgisApp::addUserInputWidget( QWidget *widget )
mUserInputDockWidget->addUserInputWidget( widget );
}
void QgisApp::initLayerTreeView()
{
mLayerTreeView->setWhatsThis( tr( "Map legend that displays all the layers currently on the map canvas. Click on the checkbox to turn a layer on or off. Double-click on a layer in the legend to customize its appearance and set other properties." ) );
@ -3646,6 +3646,7 @@ void QgisApp::initLayerTreeView()
mLayerTreeView->setModel( model );
mLayerTreeView->setMenuProvider( new QgsAppLayerTreeViewMenuProvider( mLayerTreeView, mMapCanvas ) );
new QgsLayerTreeViewFilterIndicatorManager( mLayerTreeView ); // gets parented to the layer view
setupLayerTreeViewFromSettings();

View File

@ -0,0 +1,165 @@
/***************************************************************************
qgslayertreeviewfilterindicator.cpp
--------------------------------------
Date : Januray 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgslayertreeviewfilterindicator.h"
#include "qgslayertree.h"
#include "qgslayertreemodel.h"
#include "qgslayertreeview.h"
#include "qgsquerybuilder.h"
#include "qgsvectorlayer.h"
QgsLayerTreeViewFilterIndicatorManager::QgsLayerTreeViewFilterIndicatorManager( QgsLayerTreeView *view )
: QObject( view )
, mLayerTreeView( view )
{
mIndicator = new QgsLayerTreeViewIndicator( this );
mIndicator->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFilter2.svg" ) ) );
mIndicator->setToolTip( "Filtered" );
connect( mIndicator, &QgsLayerTreeViewIndicator::clicked, this, &QgsLayerTreeViewFilterIndicatorManager::onIndicatorClicked );
QgsLayerTree *tree = mLayerTreeView->layerTreeModel()->rootGroup();
onAddedChildren( tree, 0, tree->children().count() - 1 );
connect( tree, &QgsLayerTree::addedChildren, this, &QgsLayerTreeViewFilterIndicatorManager::onAddedChildren );
connect( tree, &QgsLayerTree::willRemoveChildren, this, &QgsLayerTreeViewFilterIndicatorManager::onWillRemoveChildren );
}
void QgsLayerTreeViewFilterIndicatorManager::onAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
{
// recursively connect to providers' dataChanged() signal
QList<QgsLayerTreeNode *> children = node->children();
for ( int i = indexFrom; i <= indexTo; ++i )
{
QgsLayerTreeNode *childNode = children[i];
if ( QgsLayerTree::isGroup( childNode ) )
{
onAddedChildren( childNode, 0, childNode->children().count() - 1 );
}
else if ( QgsLayerTree::isLayer( childNode ) )
{
QgsLayerTreeLayer *childLayerNode = QgsLayerTree::toLayer( childNode );
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( childLayerNode->layer() ) )
{
if ( vlayer->dataProvider() )
{
connect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorManager::onProviderDataChanged );
addOrRemoveIndicator( childLayerNode, vlayer->dataProvider() );
}
}
else if ( !childLayerNode->layer() )
{
// wait for layer to be loaded (e.g. when loading project, first the tree is loaded, afterwards the references to layers are resolved)
connect( childLayerNode, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeViewFilterIndicatorManager::onLayerLoaded );
}
}
}
}
void QgsLayerTreeViewFilterIndicatorManager::onWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
{
// recursively disconnect from providers' dataChanged() signal
QList<QgsLayerTreeNode *> children = node->children();
for ( int i = indexFrom; i <= indexTo; ++i )
{
QgsLayerTreeNode *childNode = children[i];
if ( QgsLayerTree::isGroup( childNode ) )
{
onWillRemoveChildren( childNode, 0, childNode->children().count() - 1 );
}
else if ( QgsLayerTree::isLayer( childNode ) )
{
QgsLayerTreeLayer *childLayerNode = QgsLayerTree::toLayer( childNode );
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( childLayerNode->layer() ) )
{
if ( vlayer->dataProvider() )
disconnect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorManager::onProviderDataChanged );
}
}
}
}
void QgsLayerTreeViewFilterIndicatorManager::onLayerLoaded()
{
QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
if ( !nodeLayer )
return;
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ) )
{
if ( vlayer->dataProvider() )
{
connect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorManager::onProviderDataChanged );
addOrRemoveIndicator( nodeLayer, vlayer->dataProvider() );
}
}
}
void QgsLayerTreeViewFilterIndicatorManager::onProviderDataChanged()
{
QgsVectorDataProvider *provider = qobject_cast<QgsVectorDataProvider *>( sender() );
if ( !provider )
return;
// walk the tree and find layer node that needs to be updated
const QList<QgsLayerTreeLayer *> layerNodes = mLayerTreeView->layerTreeModel()->rootGroup()->findLayers();
for ( QgsLayerTreeLayer *node : layerNodes )
{
if ( node->layer() && node->layer()->dataProvider() == provider )
{
addOrRemoveIndicator( node, provider );
break;
}
}
}
void QgsLayerTreeViewFilterIndicatorManager::onIndicatorClicked( const QModelIndex &index )
{
QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
if ( !QgsLayerTree::isLayer( node ) )
return;
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsLayerTree::toLayer( node )->layer() );
if ( !vlayer || !vlayer->dataProvider() )
return;
// launch the query builder
QgsQueryBuilder qb( vlayer );
qb.setSql( vlayer->dataProvider()->subsetString() );
if ( qb.exec() )
vlayer->dataProvider()->setSubsetString( qb.sql() );
}
void QgsLayerTreeViewFilterIndicatorManager::addOrRemoveIndicator( QgsLayerTreeNode *node, QgsVectorDataProvider *provider )
{
QString filter = provider->subsetString();
if ( !filter.isEmpty() )
mLayerTreeView->addIndicator( node, mIndicator );
else
mLayerTreeView->removeIndicator( node, mIndicator );
}

View File

@ -0,0 +1,53 @@
/***************************************************************************
qgslayertreeviewfilterindicator.h
--------------------------------------
Date : Januray 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSLAYERTREEVIEWFILTERINDICATOR_H
#define QGSLAYERTREEVIEWFILTERINDICATOR_H
#include "qgslayertreeviewindicator.h"
class QgsLayerTreeNode;
class QgsLayerTreeView;
class QgsVectorDataProvider;
//! Adds indicators showing whether vector layers have a filter applied.
class QgsLayerTreeViewFilterIndicatorManager : public QObject
{
Q_OBJECT
public:
explicit QgsLayerTreeViewFilterIndicatorManager( QgsLayerTreeView *view );
private slots:
//! Connects to signals of layers newly added to the tree
void onAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo );
//! Disconnects from layers about to be removed from the tree
void onWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo );
//! Starts listening to layer provider's dataChanged signal
void onLayerLoaded();
//! Adds/removes indicator of a layer
void onProviderDataChanged();
void onIndicatorClicked( const QModelIndex &index );
private:
void addOrRemoveIndicator( QgsLayerTreeNode *node, QgsVectorDataProvider *provider );
private:
QgsLayerTreeView *mLayerTreeView;
QgsLayerTreeViewIndicator *mIndicator = nullptr;
};
#endif // QGSLAYERTREEVIEWFILTERINDICATOR_H

View File

@ -159,6 +159,8 @@ SET(QGIS_GUI_SRCS
layertree/qgslayertreemapcanvasbridge.cpp
layertree/qgslayertreeview.cpp
layertree/qgslayertreeviewdefaultactions.cpp
layertree/qgslayertreeviewindicator.cpp
layertree/qgslayertreeviewitemdelegate.cpp
layout/qgslayoutcustomdrophandler.cpp
layout/qgslayoutitemguiregistry.cpp
@ -677,6 +679,8 @@ SET(QGIS_GUI_MOC_HDRS
layertree/qgslayertreemapcanvasbridge.h
layertree/qgslayertreeview.h
layertree/qgslayertreeviewdefaultactions.h
layertree/qgslayertreeviewindicator.h
layertree/qgslayertreeviewitemdelegate.h
layout/qgslayoutcustomdrophandler.h
layout/qgslayoutdesignerinterface.h

View File

@ -26,6 +26,9 @@
#include <QMenu>
#include <QContextMenuEvent>
#include "qgslayertreeviewindicator.h"
#include "qgslayertreeviewitemdelegate.h"
QgsLayerTreeView::QgsLayerTreeView( QWidget *parent )
: QTreeView( parent )
@ -42,6 +45,10 @@ QgsLayerTreeView::QgsLayerTreeView( QWidget *parent )
setSelectionMode( ExtendedSelection );
setDefaultDropAction( Qt::MoveAction );
// we need a custom item delegate in order to draw indicators
setItemDelegate( new QgsLayerTreeViewItemDelegate( this ) );
setStyle( new QgsLayerTreeViewProxyStyle( this ) );
connect( this, &QTreeView::collapsed, this, &QgsLayerTreeView::updateExpandedStateToNode );
connect( this, &QTreeView::expanded, this, &QgsLayerTreeView::updateExpandedStateToNode );
}
@ -332,6 +339,22 @@ QList<QgsMapLayer *> QgsLayerTreeView::selectedLayers() const
return list;
}
void QgsLayerTreeView::addIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator )
{
if ( !mIndicators[node].contains( indicator ) )
mIndicators[node].append( indicator );
}
void QgsLayerTreeView::removeIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator )
{
mIndicators[node].removeOne( indicator );
}
QList<QgsLayerTreeViewIndicator *> QgsLayerTreeView::indicators( QgsLayerTreeNode *node ) const
{
return mIndicators.value( node );
}
void QgsLayerTreeView::refreshLayerSymbology( const QString &layerId )
{
@ -388,6 +411,10 @@ void QgsLayerTreeView::collapseAllNodes()
void QgsLayerTreeView::mouseReleaseEvent( QMouseEvent *event )
{
// we need to keep last mouse position in order to know whether to emit an indicator's clicked() signal
// (the item delegate needs to know which indicator has been clicked)
mLastReleaseMousePos = event->pos();
const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
if ( event->modifiers() & Qt::ControlModifier )
layerTreeModel()->setFlags( oldFlags | QgsLayerTreeModel::ActionHierarchical );

View File

@ -26,6 +26,7 @@ class QgsLayerTreeModel;
class QgsLayerTreeNode;
class QgsLayerTreeModelLegendNode;
class QgsLayerTreeViewDefaultActions;
class QgsLayerTreeViewIndicator;
class QgsLayerTreeViewMenuProvider;
class QgsMapLayer;
@ -106,6 +107,34 @@ class GUI_EXPORT QgsLayerTreeView : public QTreeView
//! Get list of selected layers
QList<QgsMapLayer *> selectedLayers() const;
/**
* Adds an indicator to the given layer tree node. Indicators are icons shown next to layer/group names
* in the layer tree view. They can be used to show extra information with tree nodes and they allow
* user interaction.
*
* Does not take ownership of the indicator. One indicator object may be used for multiple layer tree nodes.
* \sa removeIndicator
* \sa indicators
* \since QGIS 3.2
*/
void addIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator );
/**
* Removes a previously added indicator to a layer tree node. Does not delete the indicator.
* \sa addIndicator
* \sa indicators
* \since QGIS 3.2
*/
void removeIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator );
/**
* Returns list of indicators associated with a particular layer tree node.
* \sa addIndicator
* \sa removeIndicator
* \since QGIS 3.2
*/
QList<QgsLayerTreeViewIndicator *> indicators( QgsLayerTreeNode *node ) const;
public slots:
//! Force refresh of layer symbology. Normally not needed as the changes of layer's renderer are monitored by the model
void refreshLayerSymbology( const QString &layerId );
@ -138,7 +167,6 @@ class GUI_EXPORT QgsLayerTreeView : public QTreeView
void dropEvent( QDropEvent *event ) override;
protected slots:
void modelRowsInserted( const QModelIndex &index, int start, int end );
@ -157,6 +185,13 @@ class GUI_EXPORT QgsLayerTreeView : public QTreeView
QgsLayerTreeViewMenuProvider *mMenuProvider = nullptr;
//! Keeps track of current layer ID (to check when to emit signal about change of current layer)
QString mCurrentLayerID;
//! Storage of indicators used with the tree view
QHash< QgsLayerTreeNode *, QList<QgsLayerTreeViewIndicator *> > mIndicators;
//! Used by the item delegate for identification of which indicator has been clicked
QPoint mLastReleaseMousePos;
// friend so it can access viewOptions() method and mLastReleaseMousePos without making them public
friend class QgsLayerTreeViewItemDelegate;
};

View File

@ -0,0 +1,22 @@
/***************************************************************************
qgslayertreeviewindicator.cpp
--------------------------------------
Date : Januray 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgslayertreeviewindicator.h"
QgsLayerTreeViewIndicator::QgsLayerTreeViewIndicator( QObject *parent )
: QObject( parent )
{
}

View File

@ -0,0 +1,60 @@
/***************************************************************************
qgslayertreeviewindicator.h
--------------------------------------
Date : Januray 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSLAYERTREEVIEWINDICATOR_H
#define QGSLAYERTREEVIEWINDICATOR_H
#include "qgis_gui.h"
#include "qgis_sip.h"
#include <QIcon>
#include <QObject>
/**
* \ingroup gui
* Indicator that can be used in a layer tree view to display icons next to items of the layer tree.
* They add extra context to the item and interactivity (using clicked() signal).
*
* Indicators can be added/removed to individual layer tree items using QgsLayerTreeView::addIndicator()
* and QgsLayerTreeView::removeIndicator() calls.
*
* \since QGIS 3.2
*/
class GUI_EXPORT QgsLayerTreeViewIndicator : public QObject
{
Q_OBJECT
public:
explicit QgsLayerTreeViewIndicator( QObject *parent SIP_TRANSFERTHIS = nullptr );
//! Indicator icon that will be displayed in the layer tree view
QIcon icon() const { return mIcon; }
//! Sets indicator icon that will be displayed in the layer tree view
void setIcon( const QIcon &icon ) { mIcon = icon; }
//! Returns tool tip text that will be shown when user hovers mouse over the indicator
QString toolTip() const { return mToolTip; }
//! Sets tool tip text
void setToolTip( const QString &tip ) { mToolTip = tip; }
signals:
//! Signal that is emitted when user clicks on the indicator
void clicked( const QModelIndex &index );
private:
QIcon mIcon;
QString mToolTip;
};
#endif // QGSLAYERTREEVIEWINDICATOR_H

View File

@ -0,0 +1,168 @@
/***************************************************************************
qgslayertreeviewitemdelegate.cpp
--------------------------------------
Date : Januray 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgslayertreeviewitemdelegate.h"
#include "qgslayertreemodel.h"
#include "qgslayertreeview.h"
#include "qgslayertreeviewindicator.h"
#include <QHelpEvent>
#include <QToolTip>
QgsLayerTreeViewProxyStyle::QgsLayerTreeViewProxyStyle( QgsLayerTreeView *treeView )
: mLayerTreeView( treeView )
{
setParent( treeView );
}
QRect QgsLayerTreeViewProxyStyle::subElementRect( QStyle::SubElement element, const QStyleOption *option, const QWidget *widget ) const
{
if ( element == SE_ItemViewItemText || element == SE_LayerTreeItemIndicator )
{
if ( const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>( option ) )
{
if ( QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( vopt->index ) )
{
int count = mLayerTreeView->indicators( node ).count();
if ( count )
{
QRect r = QProxyStyle::subElementRect( SE_ItemViewItemText, option, widget );
int indiWidth = r.height() * count;
int textWidth = r.width() - indiWidth;
if ( element == SE_LayerTreeItemIndicator )
{
return QRect( r.left() + textWidth, r.top(), indiWidth, r.height() );
}
else if ( element == SE_ItemViewItemText )
{
return QRect( r.left(), r.top(), textWidth, r.height() );
}
}
}
}
}
return QProxyStyle::subElementRect( element, option, widget );
}
// -----
QgsLayerTreeViewItemDelegate::QgsLayerTreeViewItemDelegate( QgsLayerTreeView *parent )
: QStyledItemDelegate( parent )
, mLayerTreeView( parent )
{
connect( mLayerTreeView, &QgsLayerTreeView::clicked, this, &QgsLayerTreeViewItemDelegate::onClicked );
}
void QgsLayerTreeViewItemDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
QStyledItemDelegate::paint( painter, option, index );
QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
if ( !node )
return;
const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
if ( indicators.isEmpty() )
return;
QStyleOptionViewItem opt = option;
initStyleOption( &opt, index );
QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
int spacing = indRect.height() / 10;
int h = indRect.height();
int x = indRect.left();
for ( QgsLayerTreeViewIndicator *indicator : indicators )
{
QRect rect( x + spacing, indRect.top() + spacing, h - spacing * 2, h - spacing * 2 );
x += h;
QIcon::Mode mode = QIcon::Normal;
if ( !( opt.state & QStyle::State_Enabled ) )
mode = QIcon::Disabled;
else if ( opt.state & QStyle::State_Selected )
mode = QIcon::Selected;
indicator->icon().paint( painter, rect, Qt::AlignCenter, mode );
}
}
bool QgsLayerTreeViewItemDelegate::helpEvent( QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index )
{
if ( event && event->type() == QEvent::ToolTip )
{
QHelpEvent *he = static_cast<QHelpEvent *>( event );
QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
if ( node )
{
const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
if ( !indicators.isEmpty() )
{
QStyleOptionViewItem opt = option;
initStyleOption( &opt, index );
QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
if ( indRect.contains( he->pos() ) )
{
int indicatorIndex = ( he->pos().x() - indRect.left() ) / indRect.height();
if ( indicatorIndex >= 0 && indicatorIndex < indicators.count() )
{
const QString tooltip = indicators[indicatorIndex]->toolTip();
if ( !tooltip.isEmpty() )
{
QToolTip::showText( he->globalPos(), tooltip, view );
return true;
}
}
}
}
}
}
return QStyledItemDelegate::helpEvent( event, view, option, index );
}
void QgsLayerTreeViewItemDelegate::onClicked( const QModelIndex &index )
{
QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
if ( !node )
return;
const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
if ( indicators.isEmpty() )
return;
QStyleOptionViewItem opt( mLayerTreeView->viewOptions() );
opt.rect = mLayerTreeView->visualRect( index );
initStyleOption( &opt, index );
QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
QPoint pos = mLayerTreeView->mLastReleaseMousePos;
if ( indRect.contains( pos ) )
{
int indicatorIndex = ( pos.x() - indRect.left() ) / indRect.height();
if ( indicatorIndex >= 0 && indicatorIndex < indicators.count() )
emit indicators[indicatorIndex]->clicked( index );
}
}

View File

@ -0,0 +1,74 @@
/***************************************************************************
qgslayertreeviewitemdelegate.h
--------------------------------------
Date : Januray 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSLAYERTREEVIEWITEMDELEGATE_H
#define QGSLAYERTREEVIEWITEMDELEGATE_H
/// @cond PRIVATE
//
// W A R N I N G
// -------------
//
// This file is not part of the QGIS API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
class QgsLayerTreeView;
#include <QProxyStyle>
#include <QStyledItemDelegate>
/**
* Proxy style to make the item text rect shorter so that indicators fit in without colliding with text
*/
class QgsLayerTreeViewProxyStyle : public QProxyStyle
{
public:
explicit QgsLayerTreeViewProxyStyle( QgsLayerTreeView *treeView );
QRect subElementRect( SubElement element, const QStyleOption *option, const QWidget *widget ) const override;
static const unsigned long SE_LayerTreeItemIndicator = SE_CustomBase + 1;
private:
QgsLayerTreeView *mLayerTreeView;
};
/**
* Item delegate that adds drawing of indicators
*/
class QgsLayerTreeViewItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit QgsLayerTreeViewItemDelegate( QgsLayerTreeView *parent );
void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
bool helpEvent( QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index ) override;
private slots:
void onClicked( const QModelIndex &index );
private:
QgsLayerTreeView *mLayerTreeView;
};
/// @endcond
#endif // QGSLAYERTREEVIEWITEMDELEGATE_H