QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
30QgsLayerTreeViewProxyStyle::QgsLayerTreeViewProxyStyle( QgsLayerTreeView *treeView )
31 : QgsProxyStyle( treeView )
32 , mLayerTreeView( treeView )
33{
34}
35
36
37QRect 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
65QgsLayerTreeViewItemDelegate::QgsLayerTreeViewItemDelegate( QgsLayerTreeView *parent )
66 : QStyledItemDelegate( parent )
67 , mLayerTreeView( parent )
68{
69 connect( mLayerTreeView, &QgsLayerTreeView::clicked, this, &QgsLayerTreeViewItemDelegate::onClicked );
70}
71
72
73void 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
150static 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
161bool 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
197void 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