QGIS API Documentation  3.6.0-Noosa (5873452)
qgslayertreeviewitemdelegate.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayertreeviewitemdelegate.cpp
3  --------------------------------------
4  Date : January 2018
5  Copyright : (C) 2018 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgslayertreemodel.h"
19 #include "qgslayertreeview.h"
21 
22 #include <QHelpEvent>
23 #include <QToolTip>
24 
26 
27 QgsLayerTreeViewProxyStyle::QgsLayerTreeViewProxyStyle( QgsLayerTreeView *treeView )
28  : QgsProxyStyle( treeView )
29  , mLayerTreeView( treeView )
30 {
31 }
32 
33 
34 QRect QgsLayerTreeViewProxyStyle::subElementRect( QStyle::SubElement element, const QStyleOption *option, const QWidget *widget ) const
35 {
36  if ( element == SE_ItemViewItemText || element == SE_LayerTreeItemIndicator )
37  {
38  if ( const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>( option ) )
39  {
40  if ( QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( vopt->index ) )
41  {
42  int count = mLayerTreeView->indicators( node ).count();
43  if ( count )
44  {
45  QRect r = QProxyStyle::subElementRect( SE_ItemViewItemText, option, widget );
46  int indiWidth = r.height() * count;
47  int textWidth = r.width() - indiWidth;
48  if ( element == SE_LayerTreeItemIndicator )
49  {
50  return QRect( r.left() + textWidth, r.top(), indiWidth, r.height() );
51  }
52  else if ( element == SE_ItemViewItemText )
53  {
54  return QRect( r.left(), r.top(), textWidth, r.height() );
55  }
56  }
57  }
58  }
59  }
60  return QProxyStyle::subElementRect( element, option, widget );
61 }
62 
63 
64 // -----
65 
66 
67 QgsLayerTreeViewItemDelegate::QgsLayerTreeViewItemDelegate( QgsLayerTreeView *parent )
68  : QStyledItemDelegate( parent )
69  , mLayerTreeView( parent )
70 {
71  connect( mLayerTreeView, &QgsLayerTreeView::clicked, this, &QgsLayerTreeViewItemDelegate::onClicked );
72 }
73 
74 
75 void QgsLayerTreeViewItemDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
76 {
77  QStyledItemDelegate::paint( painter, option, index );
78 
79  QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
80  if ( !node )
81  return;
82 
83  const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
84  if ( indicators.isEmpty() )
85  return;
86 
87  QStyleOptionViewItem opt = option;
88  initStyleOption( &opt, index );
89 
90  QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
91  int spacing = indRect.height() / 10;
92  int h = indRect.height();
93  int x = indRect.left();
94 
95  for ( QgsLayerTreeViewIndicator *indicator : indicators )
96  {
97  QRect rect( x + spacing, indRect.top() + spacing, h - spacing * 2, h - spacing * 2 );
98  x += h;
99 
100  QIcon::Mode mode = QIcon::Normal;
101  if ( !( opt.state & QStyle::State_Enabled ) )
102  mode = QIcon::Disabled;
103  else if ( opt.state & QStyle::State_Selected )
104  mode = QIcon::Selected;
105 
106  indicator->icon().paint( painter, rect, Qt::AlignCenter, mode );
107  }
108 }
109 
110 static void _fixStyleOption( QStyleOptionViewItem &opt )
111 {
112  // This makes sure our delegate behaves correctly across different styles. Unfortunately there is inconsistency
113  // in how QStyleOptionViewItem::showDecorationSelected is prepared for paint() vs what is returned from view's viewOptions():
114  // - viewOptions() returns it based on style's SH_ItemView_ShowDecorationSelected hint
115  // - for paint() there is extra call to QTreeViewPrivate::adjustViewOptionsForIndex() which makes it
116  // always true if view's selection behavior is SelectRows (which is the default and our case with layer tree view)
117  // So for consistency between different calls we override it to what we get in paint() method ... phew!
118  opt.showDecorationSelected = true;
119 }
120 
121 bool QgsLayerTreeViewItemDelegate::helpEvent( QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index )
122 {
123  if ( event && event->type() == QEvent::ToolTip )
124  {
125  QHelpEvent *he = static_cast<QHelpEvent *>( event );
126 
127  QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
128  if ( node )
129  {
130  const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
131  if ( !indicators.isEmpty() )
132  {
133  QStyleOptionViewItem opt = option;
134  initStyleOption( &opt, index );
135  _fixStyleOption( opt );
136 
137  QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
138 
139  if ( indRect.contains( he->pos() ) )
140  {
141  int indicatorIndex = ( he->pos().x() - indRect.left() ) / indRect.height();
142  if ( indicatorIndex >= 0 && indicatorIndex < indicators.count() )
143  {
144  const QString tooltip = indicators[indicatorIndex]->toolTip();
145  if ( !tooltip.isEmpty() )
146  {
147  QToolTip::showText( he->globalPos(), tooltip, view );
148  return true;
149  }
150  }
151  }
152  }
153  }
154  }
155  return QStyledItemDelegate::helpEvent( event, view, option, index );
156 }
157 
158 
159 void QgsLayerTreeViewItemDelegate::onClicked( const QModelIndex &index )
160 {
161  QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
162  if ( !node )
163  return;
164 
165  const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
166  if ( indicators.isEmpty() )
167  return;
168 
169  QStyleOptionViewItem opt( mLayerTreeView->viewOptions() );
170  opt.rect = mLayerTreeView->visualRect( index );
171  initStyleOption( &opt, index );
172  _fixStyleOption( opt );
173 
174  QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
175 
176  QPoint pos = mLayerTreeView->mLastReleaseMousePos;
177  if ( indRect.contains( pos ) )
178  {
179  int indicatorIndex = ( pos.x() - indRect.left() ) / indRect.height();
180  if ( indicatorIndex >= 0 && indicatorIndex < indicators.count() )
181  emit indicators[indicatorIndex]->clicked( index );
182  }
183 }
184 
The QgsLayerTreeView class extends QTreeView and provides some additional functionality when working ...
A QProxyStyle subclass which correctly sets the base style to match the QGIS application style...
Definition: qgsproxystyle.h:30
This class is a base class for nodes in a layer tree.
void clicked(const QModelIndex &index)
Signal that is emitted when user clicks on the indicator.
Indicator that can be used in a layer tree view to display icons next to items of the layer tree...