QGIS API Documentation  3.27.0-Master (597e8eebd4)
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->index2node( vopt->index ) )
44  {
45  const int count = mLayerTreeView->indicators( node ).count();
46  if ( count )
47  {
48  const QRect vpr = mLayerTreeView->viewport()->rect();
49  const QRect r = QProxyStyle::subElementRect( SE_ItemViewItemText, option, widget );
50  const int indiWidth = r.height() * count;
51  const int spacing = r.height() / 10;
52  const 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->index2node( index );
78  if ( !node )
79  return;
80 
81  QStyleOptionViewItem opt = option;
82  initStyleOption( &opt, index );
83 
84  const QColor baseColor = opt.palette.base().color();
85  const QRect tRect = mLayerTreeView->style()->subElementRect( QStyle::SE_ItemViewItemText, &opt, mLayerTreeView );
86 
87  const bool shouldShowLayerMark = tRect.left() < 0; // Layer/group node icon not visible anymore?
88  if ( shouldShowLayerMark )
89  {
90  const int tPadding = tRect.height() / 10;
91  const QRect mRect( mLayerTreeView->viewport()->rect().right() - mLayerTreeView->layerMarkWidth(), tRect.top() + tPadding, mLayerTreeView->layerMarkWidth(), tRect.height() - tPadding * 2 );
92  const QBrush pb = painter->brush();
93  const QPen pp = painter->pen();
94  painter->setPen( QPen( Qt::NoPen ) );
95  QBrush b = QBrush( opt.palette.mid() );
96  QColor bc = b.color();
97  // mix mid color with base color for a less dominant, yet still opaque, version of the color
98  bc.setRed( static_cast< int >( bc.red() * 0.3 + baseColor.red() * 0.7 ) );
99  bc.setGreen( static_cast< int >( bc.green() * 0.3 + baseColor.green() * 0.7 ) );
100  bc.setBlue( static_cast< int >( bc.blue() * 0.3 + baseColor.blue() * 0.7 ) );
101  b.setColor( bc );
102  painter->setBrush( b );
103  painter->drawRect( mRect );
104  painter->setBrush( pb );
105  painter->setPen( pp );
106  }
107 
108  const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
109  if ( indicators.isEmpty() )
110  return;
111 
112  const QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
113  const int spacing = indRect.height() / 10;
114  const int h = indRect.height();
115  int x = indRect.left();
116 
117  for ( QgsLayerTreeViewIndicator *indicator : indicators )
118  {
119  const QRect rect( x + spacing, indRect.top() + spacing, h - spacing * 2, h - spacing * 2 );
120  // Add a little more padding so the icon does not look misaligned to background
121  const QRect iconRect( x + spacing * 2, indRect.top() + spacing * 2, h - spacing * 4, h - spacing * 4 );
122  x += h;
123 
124  QIcon::Mode mode = QIcon::Normal;
125  if ( !( opt.state & QStyle::State_Enabled ) )
126  mode = QIcon::Disabled;
127  else if ( opt.state & QStyle::State_Selected )
128  mode = QIcon::Selected;
129 
130  // Draw indicator background, for when floating over text content
131  const qreal bradius = spacing;
132  const QBrush pb = painter->brush();
133  const QPen pp = painter->pen();
134  QBrush b = QBrush( opt.palette.midlight() );
135  QColor bc = b.color();
136  bc.setRed( static_cast< int >( bc.red() * 0.3 + baseColor.red() * 0.7 ) );
137  bc.setGreen( static_cast< int >( bc.green() * 0.3 + baseColor.green() * 0.7 ) );
138  bc.setBlue( static_cast< int >( bc.blue() * 0.3 + baseColor.blue() * 0.7 ) );
139  b.setColor( bc );
140  painter->setBrush( b );
141  painter->setPen( QPen( QBrush( opt.palette.mid() ), 0.25 ) );
142  painter->drawRoundedRect( rect, bradius, bradius );
143  painter->setBrush( pb );
144  painter->setPen( pp );
145 
146  indicator->icon().paint( painter, iconRect, Qt::AlignCenter, mode );
147  }
148 }
149 
150 static void _fixStyleOption( QStyleOptionViewItem &opt )
151 {
152  // This makes sure our delegate behaves correctly across different styles. Unfortunately there is inconsistency
153  // in how QStyleOptionViewItem::showDecorationSelected is prepared for paint() vs what is returned from view's viewOptions():
154  // - viewOptions() returns it based on style's SH_ItemView_ShowDecorationSelected hint
155  // - for paint() there is extra call to QTreeViewPrivate::adjustViewOptionsForIndex() which makes it
156  // always true if view's selection behavior is SelectRows (which is the default and our case with layer tree view)
157  // So for consistency between different calls we override it to what we get in paint() method ... phew!
158  opt.showDecorationSelected = true;
159 }
160 
161 bool QgsLayerTreeViewItemDelegate::helpEvent( QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index )
162 {
163  if ( event && event->type() == QEvent::ToolTip )
164  {
165  QgsLayerTreeNode *node = mLayerTreeView->index2node( index );
166  if ( node )
167  {
168  const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
169  if ( !indicators.isEmpty() )
170  {
171  QStyleOptionViewItem opt = option;
172  initStyleOption( &opt, index );
173  _fixStyleOption( opt );
174 
175  const QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
176 
177  if ( indRect.contains( event->pos() ) )
178  {
179  const int indicatorIndex = ( event->pos().x() - indRect.left() ) / indRect.height();
180  if ( indicatorIndex >= 0 && indicatorIndex < indicators.count() )
181  {
182  const QString tooltip = indicators[indicatorIndex]->toolTip();
183  if ( !tooltip.isEmpty() )
184  {
185  QToolTip::showText( event->globalPos(), tooltip, view );
186  return true;
187  }
188  }
189  }
190  }
191  }
192  }
193  return QStyledItemDelegate::helpEvent( event, view, option, index );
194 }
195 
196 
197 void QgsLayerTreeViewItemDelegate::onClicked( const QModelIndex &index )
198 {
199  QgsLayerTreeNode *node = mLayerTreeView->index2node( index );
200  if ( !node )
201  return;
202 
203  const QList<QgsLayerTreeViewIndicator *> indicators = mLayerTreeView->indicators( node );
204  if ( indicators.isEmpty() )
205  return;
206 
207 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
208  QStyleOptionViewItem opt( mLayerTreeView->viewOptions() );
209 #else
210  QStyleOptionViewItem opt;
211  mLayerTreeView->initViewItemOption( &opt );
212 #endif
213  opt.rect = mLayerTreeView->visualRect( index );
214  initStyleOption( &opt, index );
215  _fixStyleOption( opt );
216 
217  const QRect indRect = mLayerTreeView->style()->subElementRect( static_cast<QStyle::SubElement>( QgsLayerTreeViewProxyStyle::SE_LayerTreeItemIndicator ), &opt, mLayerTreeView );
218 
219  const QPoint pos = mLayerTreeView->mLastReleaseMousePos;
220  if ( indRect.contains( pos ) )
221  {
222  const int indicatorIndex = ( pos.x() - indRect.left() ) / indRect.height();
223  if ( indicatorIndex >= 0 && indicatorIndex < indicators.count() )
224  emit indicators[indicatorIndex]->clicked( index );
225  }
226 }
227 
This class is a base class for nodes in a layer tree.
Indicator that can be used in a layer tree view to display icons next to items of the layer tree.
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:31