QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
qgsdetaileditemdelegate.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsdetailedlistwidget.cpp - A rich QItemDelegate subclass
3 -------------------
4 begin : Sat May 17 2008
5 copyright : (C) 2008 Tim Sutton
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgsdetaileditemdata.h"
22#include "qgsrendercontext.h"
23
24#include <QCheckBox>
25#include <QFont>
26#include <QFontMetrics>
27#include <QLinearGradient>
28#include <QModelIndex>
29#include <QPainter>
30#include <QStyleOptionViewItem>
31
32#include "moc_qgsdetaileditemdelegate.cpp"
33
35 : QAbstractItemDelegate( parent )
36 , mpWidget( new QgsDetailedItemWidget() )
37 , mpCheckBox( new QCheckBox() )
38
39{
40 //mpWidget->setFixedHeight(80);
41 mpCheckBox->resize( mpCheckBox->sizeHint().height(), mpCheckBox->sizeHint().height() );
44}
45
47{
48 delete mpCheckBox;
49 delete mpWidget;
50}
51
52void QgsDetailedItemDelegate::paint( QPainter *thepPainter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
53{
54 // After painting we need to restore the painter to its original state
55 const QgsScopedQPainterState painterState( thepPainter );
56 if ( index.data( Qt::UserRole ).userType() == qMetaTypeId<QgsDetailedItemData>() )
57 {
58 const QgsDetailedItemData myData = index.data( Qt::UserRole ).value<QgsDetailedItemData>();
59 if ( myData.isRenderedAsWidget() )
60 {
61 paintAsWidget( thepPainter, option, myData );
62 }
63 else //render by manually painting
64 {
65 paintManually( thepPainter, option, myData );
66 }
67 } //can convert item data
68}
69
70
71QSize QgsDetailedItemDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
72{
73 if ( index.data( Qt::UserRole ).userType() == qMetaTypeId<QgsDetailedItemData>() )
74 {
75 const QgsDetailedItemData myData = index.data( Qt::UserRole ).value<QgsDetailedItemData>();
76 if ( myData.isRenderedAsWidget() )
77 {
78 return QSize( 378, mpWidget->height() );
79 }
80 else // fall back to hand calculated & hand drawn item
81 {
82 //for some reason itmes are non selectable if using rect.width() on osx and win
83 return QSize( 50, height( option, myData ) );
84 //return QSize(theOption.rect.width(), myHeight + myVerticalSpacer);
85 }
86 }
87 else //can't convert to qgsdetaileditemdata
88 {
89 return QSize( 50, 50 ); //fallback
90 }
91}
92
93void QgsDetailedItemDelegate::paintManually( QPainter *thepPainter, const QStyleOptionViewItem &option, const QgsDetailedItemData &data ) const
94{
95 //
96 // Get the strings and checkbox properties
97 //
98 //bool myCheckState = index.model()->data(theIndex, Qt::CheckStateRole).toBool();
99 mpCheckBox->setChecked( data.isChecked() );
100 mpCheckBox->setEnabled( data.isEnabled() );
101 QPixmap myCbxPixmap( mpCheckBox->size() );
102 mpCheckBox->render( &myCbxPixmap ); //we will draw this onto the widget further down
103
104 //
105 // Calculate the widget height and other metrics
106 //
107
108 const QFontMetrics myTitleMetrics( titleFont( option ) );
109 const QFontMetrics myDetailMetrics( detailFont( option ) );
110 int myTextStartX = option.rect.x() + horizontalSpacing();
111 int myTextStartY = option.rect.y() + verticalSpacing();
112 const int myHeight = myTitleMetrics.height() + verticalSpacing();
113
114 //
115 // Draw the item background with a gradient if its highlighted
116 //
117 if ( option.state & QStyle::State_Selected )
118 {
119 drawHighlight( option, thepPainter, height( option, data ) );
120 thepPainter->setPen( option.palette.highlightedText().color() );
121 }
122 else
123 {
124 thepPainter->setPen( option.palette.text().color() );
125 }
126
127
128 //
129 // Draw the checkbox
130 //
131 if ( data.isCheckable() )
132 {
133 thepPainter->drawPixmap( option.rect.x(), option.rect.y() + mpCheckBox->height(), myCbxPixmap );
134 myTextStartX = option.rect.x() + myCbxPixmap.width() + horizontalSpacing();
135 }
136 //
137 // Draw the decoration (pixmap)
138 //
139 bool myIconFlag = false;
140 const QPixmap myDecoPixmap = data.icon();
141 if ( !myDecoPixmap.isNull() )
142 {
143 myIconFlag = true;
144 int iconWidth = 32, iconHeight = 32;
145
146 if ( myDecoPixmap.width() <= iconWidth && myDecoPixmap.height() <= iconHeight )
147 {
148 // the pixmap has reasonable size
149 int offsetX = 0, offsetY = 0;
150 if ( myDecoPixmap.width() < iconWidth )
151 offsetX = ( iconWidth - myDecoPixmap.width() ) / 2;
152 if ( myDecoPixmap.height() < iconHeight )
153 offsetY = ( iconHeight - myDecoPixmap.height() ) / 2;
154
155 thepPainter->drawPixmap( myTextStartX + offsetX, myTextStartY + offsetY, myDecoPixmap );
156 }
157 else
158 {
159 // shrink the pixmap, it's too big
160 thepPainter->drawPixmap( myTextStartX, myTextStartY, iconWidth, iconHeight, myDecoPixmap );
161 }
162
163 myTextStartX += iconWidth + horizontalSpacing();
164 }
165 //
166 // Draw the title
167 //
168 myTextStartY += myHeight / 2;
169 thepPainter->setFont( titleFont( option ) );
170 thepPainter->drawText( myTextStartX, myTextStartY, data.title() );
171 //
172 // Draw the description with word wrapping if needed
173 //
174 thepPainter->setFont( detailFont( option ) ); //return to original font set by client
175 if ( myIconFlag )
176 {
177 myTextStartY += verticalSpacing();
178 }
179 else
180 {
181 myTextStartY += myDetailMetrics.height() + verticalSpacing();
182 }
183 const QStringList myList = wordWrap( data.detail(), myDetailMetrics, option.rect.width() - myTextStartX );
184 QStringListIterator myLineWrapIterator( myList );
185 while ( myLineWrapIterator.hasNext() )
186 {
187 const QString myLine = myLineWrapIterator.next();
188 thepPainter->drawText( myTextStartX, myTextStartY, myLine );
189 myTextStartY += myDetailMetrics.height() - verticalSpacing();
190 }
191
192 //
193 // Draw the category. Not sure if we need word wrapping for it.
194 //
195 thepPainter->setFont( categoryFont( option ) ); //return to original font set by client
196 thepPainter->drawText( myTextStartX, myTextStartY, data.category() );
197
198 //
199 // Draw the category with word wrapping if needed
200 //
201 /*
202 myTextStartY += verticalSpacing();
203 if ( myIconFlag )
204 {
205 myTextStartY += verticalSpacing();
206 }
207 else
208 {
209 myTextStartY += myCategoryMetrics.height() + verticalSpacing();
210 }
211 myList =
212 wordWrap( data.category(), myCategoryMetrics, option.rect.width() - myTextStartX );
213 QStringListIterator myLineWrapIter( myList );
214 while ( myLineWrapIter.hasNext() )
215 {
216 QString myLine = myLineWrapIter.next();
217 thepPainter->drawText( myTextStartX,
218 myTextStartY,
219 myLine );
220 myTextStartY += myCategoryMetrics.height() - verticalSpacing();
221 }
222 */
223} //render by manual painting
224
225
226void QgsDetailedItemDelegate::paintAsWidget( QPainter *thepPainter, const QStyleOptionViewItem &option, const QgsDetailedItemData &data ) const
227{
228 mpWidget->setChecked( data.isChecked() );
229 mpWidget->setData( data );
230 mpWidget->resize( option.rect.width(), mpWidget->height() );
231 mpWidget->setAutoFillBackground( true );
232 //mpWidget->setAttribute(Qt::WA_OpaquePaintEvent);
233 mpWidget->repaint();
234 if ( option.state & QStyle::State_Selected )
235 {
236 drawHighlight( option, thepPainter, height( option, data ) );
237 }
238 const QPixmap myPixmap = mpWidget->grab();
239 thepPainter->drawPixmap( option.rect.x(), option.rect.y(), myPixmap );
240} //render as widget
241
242void QgsDetailedItemDelegate::drawHighlight( const QStyleOptionViewItem &option, QPainter *thepPainter, int height ) const
243{
244 const QColor myColor1 = option.palette.highlight().color();
245 QColor myColor2 = myColor1;
246 myColor2 = myColor2.lighter( 110 ); //10% lighter
247 QLinearGradient myGradient( QPointF( 0, option.rect.y() ), QPointF( 0, option.rect.y() + height ) );
248 myGradient.setColorAt( 0, myColor1 );
249 myGradient.setColorAt( 0.1, myColor2 );
250 myGradient.setColorAt( 0.5, myColor1 );
251 myGradient.setColorAt( 0.9, myColor2 );
252 myGradient.setColorAt( 1, myColor2 );
253 thepPainter->fillRect( option.rect, QBrush( myGradient ) );
254}
255
256int QgsDetailedItemDelegate::height( const QStyleOptionViewItem &option, const QgsDetailedItemData &data ) const
257{
258 const QFontMetrics myTitleMetrics( titleFont( option ) );
259 const QFontMetrics myDetailMetrics( detailFont( option ) );
260 const QFontMetrics myCategoryMetrics( categoryFont( option ) );
261 //we don't word wrap the title so its easy to measure
262 int myHeight = myTitleMetrics.height() + verticalSpacing();
263 //the detail needs to be measured though
264 const QStringList myList = wordWrap( data.detail(), myDetailMetrics, option.rect.width() - ( mpCheckBox->width() + horizontalSpacing() ) );
265 myHeight += ( myList.count() + 1 ) * ( myDetailMetrics.height() - verticalSpacing() );
266 //we don't word wrap the category so its easy to measure
267 myHeight += myCategoryMetrics.height() + verticalSpacing();
268#if 0
269 // if category should be wrapped use this code
270 myList = wordWrap( data.category(),
271 myCategoryMetrics,
272 option.rect.width() - ( mpCheckBox->width() + horizontalSpacing() ) );
273 myHeight += ( myList.count() + 1 ) * ( myCategoryMetrics.height() - verticalSpacing() );
274#endif
275 return myHeight;
276}
277
278
279QFont QgsDetailedItemDelegate::detailFont( const QStyleOptionViewItem &option ) const
280{
281 const QFont myFont = option.font;
282 return myFont;
283}
284
285QFont QgsDetailedItemDelegate::categoryFont( const QStyleOptionViewItem &option ) const
286{
287 QFont myFont = option.font;
288 myFont.setBold( true );
289 return myFont;
290}
291
292QFont QgsDetailedItemDelegate::titleFont( const QStyleOptionViewItem &option ) const
293{
294 QFont myTitleFont = detailFont( option );
295 myTitleFont.setBold( true );
296 myTitleFont.setPointSize( myTitleFont.pointSize() );
297 return myTitleFont;
298}
299
300
301QStringList QgsDetailedItemDelegate::wordWrap( const QString &string, const QFontMetrics &metrics, int width ) const
302{
303 if ( string.isEmpty() )
304 return QStringList();
305 if ( 50 >= width )
306 return QStringList() << string;
307 //QString myDebug = QString("Word wrapping: %1 into %2 pixels").arg(theString).arg(theWidth);
308 //qDebug(myDebug.toLocal8Bit());
309 //iterate the string
310 QStringList myList;
311 QString myCumulativeLine;
312 QString myStringToPreviousSpace;
313 int myPreviousSpacePos = 0;
314 for ( int i = 0; i < string.count(); ++i )
315 {
316 const QChar myChar = string.at( i );
317 if ( myChar == QChar( ' ' ) )
318 {
319 myStringToPreviousSpace = myCumulativeLine;
320 myPreviousSpacePos = i;
321 }
322 myCumulativeLine += myChar;
323 if ( metrics.boundingRect( myCumulativeLine ).width() >= width )
324 {
325 //time to wrap
326 //TODO deal with long strings that have no spaces
327 //forcing a break at current pos...
328 myList << myStringToPreviousSpace.trimmed();
329 i = myPreviousSpacePos;
330 myStringToPreviousSpace.clear();
331 myCumulativeLine.clear();
332 }
333 } //end of i loop
334 //add whatever is left in the string to the list
335 if ( !myCumulativeLine.trimmed().isEmpty() )
336 {
337 myList << myCumulativeLine.trimmed();
338 }
339
340 //qDebug("Wrapped legend entry:");
341 //qDebug(theString);
342 //qDebug(myList.join("\n").toLocal8Bit());
343 return myList;
344}
345
346
348{
349 return mVerticalSpacing;
350}
351
352
354{
355 mVerticalSpacing = value;
356}
357
358
360{
361 return mHorizontalSpacing;
362}
363
364
366{
367 mHorizontalSpacing = value;
368}
The data only representation of a QgsDetailedItemWidget, designed to be used in custom views.
QString category() const
Returns the item's category.
bool isCheckable() const
Returns true if the item is checkable.
bool isEnabled() const
Returns true if the item is enabled.
QString title() const
Returns the item's title.
bool isRenderedAsWidget() const
Returns true if the item will be rendered using a widget.
QPixmap icon() const
Returns the item's icon.
QString detail() const
Returns the detailed description for the item.
bool isChecked() const
Returns true if the item is checked.
QgsDetailedItemDelegate(QObject *parent=nullptr)
Constructor for QgsDetailedItemDelegate.
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
Reimplement for parent class.
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Reimplement for parent class.
A widget renderer for detailed item views.
Scoped object for saving and restoring a QPainter object's state.