QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 <QBrush>
23 #include <QHelpEvent>
24 #include <QMenu>
25 #include <QPen>
26 #include <QToolTip>
27 
29 
30 QgsLayerTreeViewProxyStyle::QgsLayerTreeViewProxyStyle( QgsLayerTreeView *treeView )
31  : QgsProxyStyle( treeView )
32  , mLayerTreeView( treeView )
33 {
34 }
35 
36 
37 QRect QgsLayerTreeViewProxyStyle::subElementRect( QStyle::SubElement element, const QStyleOption *option, const QWidget *widget ) const
38 {
39  if ( element == SE_LayerTreeItemIndicator )
40  {
41  if ( const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>( option ) )
42  {
43  if ( QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( vopt->index ) )
44  {
45  int count = mLayerTreeView->indicators( node ).count();
46  if ( count )
47  {
48  QRect vpr = mLayerTreeView->viewport()->rect();
49  QRect r = QProxyStyle::subElementRect( SE_ItemViewItemText, option, widget );
50  int indiWidth = r.height() * count;
51  int spacing = r.height() / 10;
52  int vpIndiWidth = vpr.width() - indiWidth - spacing - mLayerTreeView->layerMarkWidth();
53  return QRect( vpIndiWidth, r.top(), indiWidth, r.height() );
54  }
55  }
56  }
57  }
58  return QProxyStyle::subElementRect( element, option, widget );
59 }
60 
61 
62 // -----
63 
64 
65 QgsLayerTreeViewItemDelegate::QgsLayerTreeViewItemDelegate( QgsLayerTreeView *parent )
66  : QStyledItemDelegate( parent )
67  , mLayerTreeView( parent )
68 {
69  connect( mLayerTreeView, &QgsLayerTreeView::clicked, this, &QgsLayerTreeViewItemDelegate::onClicked );
70 }
71 
72 
73 void QgsLayerTreeViewItemDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
74 {
75  QStyledItemDelegate::paint( painter, option, index );
76 
77  QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
78  if ( !node )
79  return;
80 
81  QStyleOptionViewItem opt = option;
82  initStyleOption( &opt, index );
83 
84  QRect tRect = mLayerTreeView->style()->subElementRect( QStyle::SE_ItemViewItemText, &opt, mLayerTreeView );
85  int tPadding = tRect.height() / 10;
86 
87  // Draw layer context menu mark
88  QRect mRect( mLayerTreeView->viewport()->rect().right() - mLayerTreeView->layerMarkWidth(), tRect.top() + tPadding, mLayerTreeView->layerMarkWidth(), tRect.height() - tPadding * 2 );
89  QBrush pb = painter->brush();
90  QPen pp = painter->pen();
91  painter->setPen( QPen( Qt::NoPen ) );
92  QBrush b = QBrush( opt.palette.mid() );
93  QColor bc = b.color();
94  // mix mid color with base color for a less dominant, yet still opaque, version of the color
95  const QColor baseColor = opt.palette.base().color();
96  bc.setRed( static_cast< int >( bc.red() * 0.3 + baseColor.red() * 0.7 ) );
97  bc.setGreen( static_cast< int >( bc.green() * 0.3 + baseColor.green() * 0.7 ) );
98  bc.setBlue( static_cast< int >( bc.blue() * 0.3 + baseColor.blue() * 0.7 ) );
99  b.setColor( bc );
100  painter->setBrush( b );
101  painter->drawRect( mRect );
102  painter->setBrush( pb );
103  painter->setPen( pp );
104 
105  const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
106  if ( indicators.isEmpty() )
107  return;
108 
109  QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
110  int spacing = indRect.height() / 10;
111  int h = indRect.height();
112  int x = indRect.left();
113 
114  for ( QgsLayerTreeViewIndicator *indicator : indicators )
115  {
116  QRect rect( x + spacing, indRect.top() + spacing, h - spacing * 2, h - spacing * 2 );
117  // Add a little more padding so the icon does not look misaligned to background
118  QRect iconRect( x + spacing * 2, indRect.top() + spacing * 2, h - spacing * 4, h - spacing * 4 );
119  x += h;
120 
121  QIcon::Mode mode = QIcon::Normal;
122  if ( !( opt.state & QStyle::State_Enabled ) )
123  mode = QIcon::Disabled;
124  else if ( opt.state & QStyle::State_Selected )
125  mode = QIcon::Selected;
126 
127  // Draw indicator background, for when floating over text content
128  qreal bradius = spacing;
129  QBrush pb = painter->brush();
130  QPen pp = painter->pen();
131  QBrush b = QBrush( opt.palette.midlight() );
132  QColor bc = b.color();
133  bc.setRed( static_cast< int >( bc.red() * 0.3 + baseColor.red() * 0.7 ) );
134  bc.setGreen( static_cast< int >( bc.green() * 0.3 + baseColor.green() * 0.7 ) );
135  bc.setBlue( static_cast< int >( bc.blue() * 0.3 + baseColor.blue() * 0.7 ) );
136  b.setColor( bc );
137  painter->setBrush( b );
138  painter->setPen( QPen( QBrush( opt.palette.mid() ), 0.25 ) );
139  painter->drawRoundedRect( rect, bradius, bradius );
140  painter->setBrush( pb );
141  painter->setPen( pp );
142 
143  indicator->icon().paint( painter, iconRect, Qt::AlignCenter, mode );
144  }
145 }
146 
147 static void _fixStyleOption( QStyleOptionViewItem &opt )
148 {
149  // This makes sure our delegate behaves correctly across different styles. Unfortunately there is inconsistency
150  // in how QStyleOptionViewItem::showDecorationSelected is prepared for paint() vs what is returned from view's viewOptions():
151  // - viewOptions() returns it based on style's SH_ItemView_ShowDecorationSelected hint
152  // - for paint() there is extra call to QTreeViewPrivate::adjustViewOptionsForIndex() which makes it
153  // always true if view's selection behavior is SelectRows (which is the default and our case with layer tree view)
154  // So for consistency between different calls we override it to what we get in paint() method ... phew!
155  opt.showDecorationSelected = true;
156 }
157 
158 bool QgsLayerTreeViewItemDelegate::helpEvent( QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index )
159 {
160  if ( event && event->type() == QEvent::ToolTip )
161  {
162  QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
163  if ( node )
164  {
165  const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
166  if ( !indicators.isEmpty() )
167  {
168  QStyleOptionViewItem opt = option;
169  initStyleOption( &opt, index );
170  _fixStyleOption( opt );
171 
172  QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
173 
174  if ( indRect.contains( event->pos() ) )
175  {
176  int indicatorIndex = ( event->pos().x() - indRect.left() ) / indRect.height();
177  if ( indicatorIndex >= 0 && indicatorIndex < indicators.count() )
178  {
179  const QString tooltip = indicators[indicatorIndex]->toolTip();
180  if ( !tooltip.isEmpty() )
181  {
182  QToolTip::showText( event->globalPos(), tooltip, view );
183  return true;
184  }
185  }
186  }
187  }
188  }
189  }
190  return QStyledItemDelegate::helpEvent( event, view, option, index );
191 }
192 
193 
194 void QgsLayerTreeViewItemDelegate::onClicked( const QModelIndex &index )
195 {
196  QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
197  if ( !node )
198  return;
199 
200  const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
201  if ( indicators.isEmpty() )
202  return;
203 
204  QStyleOptionViewItem opt( mLayerTreeView->viewOptions() );
205  opt.rect = mLayerTreeView->visualRect( index );
206  initStyleOption( &opt, index );
207  _fixStyleOption( opt );
208 
209  QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
210 
211  QPoint pos = mLayerTreeView->mLastReleaseMousePos;
212  if ( indRect.contains( pos ) )
213  {
214  int indicatorIndex = ( pos.x() - indRect.left() ) / indRect.height();
215  if ( indicatorIndex >= 0 && indicatorIndex < indicators.count() )
216  emit indicators[indicatorIndex]->clicked( index );
217  }
218 }
219 
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)
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...