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