1 /***************************************************************************
2      qgssymbolbutton.h
3      -----------------
4     Date                 : July 2017
5     Copyright            : (C) 2017 by Nyall Dawson
6     Email                : nyall dot dawson 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 
16 #include "qgssymbolbutton.h"
17 #include "qgspanelwidget.h"
18 #include "qgsexpressioncontext.h"
19 #include "qgsexpressioncontextgenerator.h"
20 #include "qgsvectorlayer.h"
21 #include "qgssymbolselectordialog.h"
22 #include "qgsstyle.h"
23 #include "qgscolorwidgets.h"
24 #include "qgscolorschemeregistry.h"
25 #include "qgscolorswatchgrid.h"
26 #include "qgssymbollayerutils.h"
27 #include "qgsapplication.h"
28 #include "qgsguiutils.h"
29 #include "qgsexpressioncontextutils.h"
30 #include "qgsgui.h"
31 #include "qgscolordialog.h"
32 
33 #include <QMenu>
34 #include <QClipboard>
35 #include <QDrag>
36 
QgsSymbolButton(QWidget * parent,const QString & dialogTitle)37 QgsSymbolButton::QgsSymbolButton( QWidget *parent, const QString &dialogTitle )
38   : QToolButton( parent )
39   , mDialogTitle( dialogTitle.isEmpty() ? tr( "Symbol Settings" ) : dialogTitle )
40 {
41   mSymbol.reset( QgsFillSymbol::createSimple( QgsStringMap() ) );
42 
43   setAcceptDrops( true );
44   connect( this, &QAbstractButton::clicked, this, &QgsSymbolButton::showSettingsDialog );
45 
46   //setup dropdown menu
47   mMenu = new QMenu( this );
48   connect( mMenu, &QMenu::aboutToShow, this, &QgsSymbolButton::prepareMenu );
49   setMenu( mMenu );
50   setPopupMode( QToolButton::MenuButtonPopup );
51 
52   //make sure height of button looks good under different platforms
53   QSize size = QToolButton::minimumSizeHint();
54   int fontHeight = static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 1.4 );
55   mSizeHint = QSize( size.width(), std::max( size.height(), fontHeight ) );
56 }
57 
minimumSizeHint() const58 QSize QgsSymbolButton::minimumSizeHint() const
59 {
60 
61   return mSizeHint;
62 }
63 
sizeHint() const64 QSize QgsSymbolButton::sizeHint() const
65 {
66   return mSizeHint;
67 }
68 
setSymbolType(QgsSymbol::SymbolType type)69 void QgsSymbolButton::setSymbolType( QgsSymbol::SymbolType type )
70 {
71   if ( type != mType )
72   {
73     switch ( type )
74     {
75       case QgsSymbol::Marker:
76         mSymbol.reset( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
77         break;
78 
79       case QgsSymbol::Line:
80         mSymbol.reset( QgsLineSymbol::createSimple( QgsStringMap() ) );
81         break;
82 
83       case QgsSymbol::Fill:
84         mSymbol.reset( QgsFillSymbol::createSimple( QgsStringMap() ) );
85         break;
86 
87       case QgsSymbol::Hybrid:
88         break;
89     }
90   }
91   updatePreview();
92   mType = type;
93 }
94 
showSettingsDialog()95 void QgsSymbolButton::showSettingsDialog()
96 {
97   QgsExpressionContext context;
98   if ( mExpressionContextGenerator )
99     context  = mExpressionContextGenerator->createExpressionContext();
100   else
101   {
102     context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer.data() ) );
103   }
104   QgsSymbol *newSymbol = mSymbol->clone();
105 
106   QgsSymbolWidgetContext symbolContext;
107   symbolContext.setExpressionContext( &context );
108   symbolContext.setMapCanvas( mMapCanvas );
109   symbolContext.setMessageBar( mMessageBar );
110 
111   QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this );
112   if ( panel && panel->dockMode() )
113   {
114     QgsSymbolSelectorWidget *d = new QgsSymbolSelectorWidget( newSymbol, QgsStyle::defaultStyle(), mLayer, panel );
115     d->setPanelTitle( mDialogTitle );
116     d->setContext( symbolContext );
117     connect( d, &QgsPanelWidget::widgetChanged, this, &QgsSymbolButton::updateSymbolFromWidget );
118     connect( d, &QgsPanelWidget::panelAccepted, this, &QgsSymbolButton::cleanUpSymbolSelector );
119     panel->openPanel( d );
120   }
121   else
122   {
123     QgsSymbolSelectorDialog dialog( newSymbol, QgsStyle::defaultStyle(), mLayer, this );
124     dialog.setWindowTitle( mDialogTitle );
125     dialog.setContext( symbolContext );
126     if ( dialog.exec() )
127     {
128       setSymbol( newSymbol );
129     }
130     else
131     {
132       delete newSymbol;
133     }
134 
135     // reactivate button's window
136     activateWindow();
137   }
138 }
139 
updateSymbolFromWidget()140 void QgsSymbolButton::updateSymbolFromWidget()
141 {
142   if ( QgsSymbolSelectorWidget *w = qobject_cast<QgsSymbolSelectorWidget *>( sender() ) )
143     setSymbol( w->symbol()->clone() );
144 }
145 
cleanUpSymbolSelector(QgsPanelWidget * container)146 void QgsSymbolButton::cleanUpSymbolSelector( QgsPanelWidget *container )
147 {
148   QgsSymbolSelectorWidget *w = qobject_cast<QgsSymbolSelectorWidget *>( container );
149   if ( !w )
150     return;
151 
152   delete w->symbol();
153 }
154 
mapCanvas() const155 QgsMapCanvas *QgsSymbolButton::mapCanvas() const
156 {
157   return mMapCanvas;
158 }
159 
setMapCanvas(QgsMapCanvas * mapCanvas)160 void QgsSymbolButton::setMapCanvas( QgsMapCanvas *mapCanvas )
161 {
162   mMapCanvas = mapCanvas;
163 }
164 
setMessageBar(QgsMessageBar * bar)165 void QgsSymbolButton::setMessageBar( QgsMessageBar *bar )
166 {
167   mMessageBar = bar;
168 }
169 
messageBar() const170 QgsMessageBar *QgsSymbolButton::messageBar() const
171 {
172   return mMessageBar;
173 }
174 
layer() const175 QgsVectorLayer *QgsSymbolButton::layer() const
176 {
177   return mLayer;
178 }
179 
setLayer(QgsVectorLayer * layer)180 void QgsSymbolButton::setLayer( QgsVectorLayer *layer )
181 {
182   mLayer = layer;
183 }
184 
registerExpressionContextGenerator(QgsExpressionContextGenerator * generator)185 void QgsSymbolButton::registerExpressionContextGenerator( QgsExpressionContextGenerator *generator )
186 {
187   mExpressionContextGenerator = generator;
188 }
189 
setSymbol(QgsSymbol * symbol)190 void QgsSymbolButton::setSymbol( QgsSymbol *symbol )
191 {
192   mSymbol.reset( symbol );
193   updatePreview();
194   emit changed();
195 }
196 
setColor(const QColor & color)197 void QgsSymbolButton::setColor( const QColor &color )
198 {
199   QColor opaque = color;
200   opaque.setAlphaF( 1.0 );
201 
202   if ( opaque == mSymbol->color() )
203     return;
204 
205   mSymbol->setColor( opaque );
206   updatePreview();
207   emit changed();
208 }
209 
copySymbol()210 void QgsSymbolButton::copySymbol()
211 {
212   QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::symbolToMimeData( mSymbol.get() ) );
213 }
214 
pasteSymbol()215 void QgsSymbolButton::pasteSymbol()
216 {
217   std::unique_ptr< QgsSymbol > symbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
218   if ( symbol && symbol->type() == mType )
219     setSymbol( symbol.release() );
220 }
221 
copyColor()222 void QgsSymbolButton::copyColor()
223 {
224   QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mSymbol->color() ) );
225 }
226 
pasteColor()227 void QgsSymbolButton::pasteColor()
228 {
229   QColor clipColor;
230   bool hasAlpha = false;
231   if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
232   {
233     //paste color
234     setColor( clipColor );
235     QgsRecentColorScheme::addRecentColor( clipColor );
236   }
237 }
238 
mousePressEvent(QMouseEvent * e)239 void QgsSymbolButton::mousePressEvent( QMouseEvent *e )
240 {
241   if ( mPickingColor )
242   {
243     //don't show dialog if in color picker mode
244     e->accept();
245     return;
246   }
247 
248   if ( e->button() == Qt::RightButton )
249   {
250     QToolButton::showMenu();
251     return;
252   }
253   else if ( e->button() == Qt::LeftButton )
254   {
255     mDragStartPosition = e->pos();
256   }
257   QToolButton::mousePressEvent( e );
258 }
259 
mouseMoveEvent(QMouseEvent * e)260 void QgsSymbolButton::mouseMoveEvent( QMouseEvent *e )
261 {
262   if ( mPickingColor )
263   {
264     updatePreview( QgsGui::sampleColor( e->globalPos() ) );
265     e->accept();
266     return;
267   }
268 
269   //handle dragging colors/symbols from button
270 
271   if ( !( e->buttons() & Qt::LeftButton ) )
272   {
273     //left button not depressed, so not a drag
274     QToolButton::mouseMoveEvent( e );
275     return;
276   }
277 
278   if ( ( e->pos() - mDragStartPosition ).manhattanLength() < QApplication::startDragDistance() )
279   {
280     //mouse not moved, so not a drag
281     QToolButton::mouseMoveEvent( e );
282     return;
283   }
284 
285   //user is dragging
286   QDrag *drag = new QDrag( this );
287   drag->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mSymbol->color() ) );
288   drag->setPixmap( QgsColorWidget::createDragIcon( mSymbol->color() ) );
289   drag->exec( Qt::CopyAction );
290   setDown( false );
291 }
292 
mouseReleaseEvent(QMouseEvent * e)293 void QgsSymbolButton::mouseReleaseEvent( QMouseEvent *e )
294 {
295   if ( mPickingColor )
296   {
297     //end color picking operation by sampling the color under cursor
298     stopPicking( e->globalPos() );
299     e->accept();
300     return;
301   }
302 
303   QToolButton::mouseReleaseEvent( e );
304 }
305 
keyPressEvent(QKeyEvent * e)306 void QgsSymbolButton::keyPressEvent( QKeyEvent *e )
307 {
308   if ( !mPickingColor )
309   {
310     //if not picking a color, use default tool button behavior
311     QToolButton::keyPressEvent( e );
312     return;
313   }
314 
315   //cancel picking, sampling the color if space was pressed
316   stopPicking( QCursor::pos(), e->key() == Qt::Key_Space );
317 }
318 
dragEnterEvent(QDragEnterEvent * e)319 void QgsSymbolButton::dragEnterEvent( QDragEnterEvent *e )
320 {
321   //is dragged data valid color data?
322   QColor mimeColor;
323   bool hasAlpha = false;
324 
325   if ( colorFromMimeData( e->mimeData(), mimeColor, hasAlpha ) )
326   {
327     //if so, we accept the drag, and temporarily change the button's color
328     //to match the dragged color. This gives immediate feedback to the user
329     //that colors can be dropped here
330     e->acceptProposedAction();
331     updatePreview( mimeColor );
332   }
333 }
334 
dragLeaveEvent(QDragLeaveEvent * e)335 void QgsSymbolButton::dragLeaveEvent( QDragLeaveEvent *e )
336 {
337   Q_UNUSED( e )
338   //reset button color
339   updatePreview();
340 }
341 
dropEvent(QDropEvent * e)342 void QgsSymbolButton::dropEvent( QDropEvent *e )
343 {
344   //is dropped data valid format data?
345   QColor mimeColor;
346   bool hasAlpha = false;
347   if ( colorFromMimeData( e->mimeData(), mimeColor, hasAlpha ) )
348   {
349     //accept drop and set new color
350     e->acceptProposedAction();
351     mimeColor.setAlphaF( 1.0 );
352     mSymbol->setColor( mimeColor );
353     QgsRecentColorScheme::addRecentColor( mimeColor );
354     updatePreview();
355     emit changed();
356   }
357   updatePreview();
358 }
359 
prepareMenu()360 void QgsSymbolButton::prepareMenu()
361 {
362   //we need to tear down and rebuild this menu every time it is shown. Otherwise the space allocated to any
363   //QgsColorSwatchGridAction is not recalculated by Qt and the swatch grid may not be the correct size
364   //for the number of colors shown in the grid. Note that we MUST refresh color swatch grids every time this
365   //menu is opened, otherwise color schemes like the recent color scheme grid are meaningless
366   mMenu->clear();
367 
368   QAction *configureAction = new QAction( tr( "Configure Symbol…" ), this );
369   mMenu->addAction( configureAction );
370   connect( configureAction, &QAction::triggered, this, &QgsSymbolButton::showSettingsDialog );
371 
372   QAction *copySymbolAction = new QAction( tr( "Copy Symbol" ), this );
373   mMenu->addAction( copySymbolAction );
374   connect( copySymbolAction, &QAction::triggered, this, &QgsSymbolButton::copySymbol );
375   QAction *pasteSymbolAction = new QAction( tr( "Paste Symbol" ), this );
376   //enable or disable paste action based on current clipboard contents. We always show the paste
377   //action, even if it's disabled, to give hint to the user that pasting symbols is possible
378   std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
379   if ( tempSymbol && tempSymbol->type() == mType )
380   {
381     const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
382     pasteSymbolAction->setIcon( QgsSymbolLayerUtils::symbolPreviewIcon( tempSymbol.get(), QSize( iconSize, iconSize ), 1 ) );
383   }
384   else
385   {
386     pasteSymbolAction->setEnabled( false );
387   }
388   mMenu->addAction( pasteSymbolAction );
389   connect( pasteSymbolAction, &QAction::triggered, this, &QgsSymbolButton::pasteSymbol );
390 
391   mMenu->addSeparator();
392 
393   QgsColorWheel *colorWheel = new QgsColorWheel( mMenu );
394   colorWheel->setColor( mSymbol->color() );
395   QgsColorWidgetAction *colorAction = new QgsColorWidgetAction( colorWheel, mMenu, mMenu );
396   colorAction->setDismissOnColorSelection( false );
397   connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsSymbolButton::setColor );
398   mMenu->addAction( colorAction );
399 
400   QgsColorRampWidget *alphaRamp = new QgsColorRampWidget( mMenu, QgsColorWidget::Alpha, QgsColorRampWidget::Horizontal );
401   QColor alphaColor = mSymbol->color();
402   alphaColor.setAlphaF( mSymbol->opacity() );
403   alphaRamp->setColor( alphaColor );
404   QgsColorWidgetAction *alphaAction = new QgsColorWidgetAction( alphaRamp, mMenu, mMenu );
405   alphaAction->setDismissOnColorSelection( false );
406   connect( alphaAction, &QgsColorWidgetAction::colorChanged, this, [ = ]( const QColor & color )
407   {
408     double opacity = color.alphaF();
409     mSymbol->setOpacity( opacity );
410     updatePreview();
411     emit changed();
412   } );
413   connect( colorAction, &QgsColorWidgetAction::colorChanged, alphaRamp, [alphaRamp]( const QColor & color ) { alphaRamp->setColor( color, false ); }
414          );
415   mMenu->addAction( alphaAction );
416 
417   //get schemes with ShowInColorButtonMenu flag set
418   QList< QgsColorScheme * > schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorButtonMenu );
419   QList< QgsColorScheme * >::iterator it = schemeList.begin();
420   for ( ; it != schemeList.end(); ++it )
421   {
422     QgsColorSwatchGridAction *colorAction = new QgsColorSwatchGridAction( *it, mMenu, QStringLiteral( "symbology" ), this );
423     colorAction->setBaseColor( mSymbol->color() );
424     mMenu->addAction( colorAction );
425     connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsSymbolButton::setColor );
426     connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsSymbolButton::addRecentColor );
427   }
428 
429   mMenu->addSeparator();
430 
431   QAction *copyColorAction = new QAction( tr( "Copy Color" ), this );
432   mMenu->addAction( copyColorAction );
433   connect( copyColorAction, &QAction::triggered, this, &QgsSymbolButton::copyColor );
434 
435   QAction *pasteColorAction = new QAction( tr( "Paste Color" ), this );
436   //enable or disable paste action based on current clipboard contents. We always show the paste
437   //action, even if it's disabled, to give hint to the user that pasting colors is possible
438   QColor clipColor;
439   bool hasAlpha = false;
440   if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
441   {
442     pasteColorAction->setIcon( createColorIcon( clipColor ) );
443   }
444   else
445   {
446     pasteColorAction->setEnabled( false );
447   }
448   mMenu->addAction( pasteColorAction );
449   connect( pasteColorAction, &QAction::triggered, this, &QgsSymbolButton::pasteColor );
450 
451   QAction *pickColorAction = new QAction( tr( "Pick Color" ), this );
452   mMenu->addAction( pickColorAction );
453   connect( pickColorAction, &QAction::triggered, this, &QgsSymbolButton::activatePicker );
454 
455   QAction *chooseColorAction = new QAction( tr( "Choose Color…" ), this );
456   mMenu->addAction( chooseColorAction );
457   connect( chooseColorAction, &QAction::triggered, this, &QgsSymbolButton::showColorDialog );
458 }
459 
addRecentColor(const QColor & color)460 void QgsSymbolButton::addRecentColor( const QColor &color )
461 {
462   QgsRecentColorScheme::addRecentColor( color );
463 }
464 
activatePicker()465 void QgsSymbolButton::activatePicker()
466 {
467   //activate picker color
468   QApplication::setOverrideCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::Sampler ) );
469   grabMouse();
470   grabKeyboard();
471   mPickingColor = true;
472   setMouseTracking( true );
473 }
474 
475 
changeEvent(QEvent * e)476 void QgsSymbolButton::changeEvent( QEvent *e )
477 {
478   if ( e->type() == QEvent::EnabledChange )
479   {
480     updatePreview();
481   }
482   QToolButton::changeEvent( e );
483 }
484 
showEvent(QShowEvent * e)485 void QgsSymbolButton::showEvent( QShowEvent *e )
486 {
487   updatePreview();
488   QToolButton::showEvent( e );
489 }
490 
resizeEvent(QResizeEvent * event)491 void QgsSymbolButton::resizeEvent( QResizeEvent *event )
492 {
493   QToolButton::resizeEvent( event );
494   //recalculate icon size and redraw icon
495   mIconSize = QSize();
496   updatePreview();
497 }
498 
updatePreview(const QColor & color,QgsSymbol * tempSymbol)499 void QgsSymbolButton::updatePreview( const QColor &color, QgsSymbol *tempSymbol )
500 {
501   std::unique_ptr< QgsSymbol > previewSymbol;
502 
503   if ( tempSymbol )
504     previewSymbol.reset( tempSymbol->clone() );
505   else
506     previewSymbol.reset( mSymbol->clone() );
507 
508   if ( color.isValid() )
509     previewSymbol->setColor( color );
510 
511   QSize currentIconSize;
512   //icon size is button size with a small margin
513   if ( menu() )
514   {
515     if ( !mIconSize.isValid() )
516     {
517       //calculate size of push button part of widget (ie, without the menu dropdown button part)
518       QStyleOptionToolButton opt;
519       initStyleOption( &opt );
520       QRect buttonSize = QApplication::style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton,
521                          this );
522       //make sure height of icon looks good under different platforms
523 #ifdef Q_OS_WIN
524       mIconSize = QSize( buttonSize.width() - 10, height() - 6 );
525 #else
526       mIconSize = QSize( buttonSize.width() - 10, height() - 12 );
527 #endif
528     }
529     currentIconSize = mIconSize;
530   }
531   else
532   {
533     //no menu
534 #ifdef Q_OS_WIN
535     currentIconSize = QSize( width() - 10, height() - 6 );
536 #else
537     currentIconSize = QSize( width() - 10, height() - 12 );
538 #endif
539   }
540 
541   if ( !currentIconSize.isValid() || currentIconSize.width() <= 0 || currentIconSize.height() <= 0 )
542   {
543     return;
544   }
545 
546   //create an icon pixmap
547   QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( previewSymbol.get(), currentIconSize );
548   setIconSize( currentIconSize );
549   setIcon( icon );
550 
551   // set tooltip
552   // create very large preview image
553 
554 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
555   int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().width( 'X' ) * 23 );
556 #else
557   int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 23 );
558 #endif
559   int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
560 
561   QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( previewSymbol.get(), QSize( width, height ), height / 20 );
562   QByteArray data;
563   QBuffer buffer( &data );
564   pm.save( &buffer, "PNG", 100 );
565   setToolTip( QStringLiteral( "<img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) ) );
566 }
567 
colorFromMimeData(const QMimeData * mimeData,QColor & resultColor,bool & hasAlpha)568 bool QgsSymbolButton::colorFromMimeData( const QMimeData *mimeData, QColor &resultColor, bool &hasAlpha )
569 {
570   hasAlpha = false;
571   QColor mimeColor = QgsSymbolLayerUtils::colorFromMimeData( mimeData, hasAlpha );
572 
573   if ( mimeColor.isValid() )
574   {
575     resultColor = mimeColor;
576     return true;
577   }
578 
579   //could not get color from mime data
580   return false;
581 }
582 
createColorIcon(const QColor & color) const583 QPixmap QgsSymbolButton::createColorIcon( const QColor &color ) const
584 {
585   //create an icon pixmap
586   const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
587   QPixmap pixmap( iconSize, iconSize );
588   pixmap.fill( Qt::transparent );
589 
590   QPainter p;
591   p.begin( &pixmap );
592 
593   //draw color over pattern
594   p.setBrush( QBrush( color ) );
595 
596   //draw border
597   p.setPen( QColor( 197, 197, 197 ) );
598   p.drawRect( 0, 0, iconSize - 1, iconSize - 1 );
599   p.end();
600   return pixmap;
601 }
602 
stopPicking(QPoint eventPos,bool samplingColor)603 void QgsSymbolButton::stopPicking( QPoint eventPos, bool samplingColor )
604 {
605   //release mouse and keyboard, and reset cursor
606   releaseMouse();
607   releaseKeyboard();
608   QgsApplication::restoreOverrideCursor();
609   setMouseTracking( false );
610   mPickingColor = false;
611 
612   if ( !samplingColor )
613   {
614     //not sampling color, restore old icon
615     updatePreview();
616     return;
617   }
618 
619   const QColor newColor = QgsGui::sampleColor( eventPos );
620   setColor( newColor );
621   addRecentColor( newColor );
622 }
623 
showColorDialog()624 void QgsSymbolButton::showColorDialog()
625 {
626   QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this );
627   if ( panel && panel->dockMode() )
628   {
629     QColor currentColor = mSymbol->color();
630     QgsCompoundColorWidget *colorWidget = new QgsCompoundColorWidget( panel, currentColor, QgsCompoundColorWidget::LayoutVertical );
631     colorWidget->setPanelTitle( tr( "Symbol Color" ) );
632     colorWidget->setAllowOpacity( true );
633 
634     if ( currentColor.isValid() )
635     {
636       colorWidget->setPreviousColor( currentColor );
637     }
638 
639     connect( colorWidget, &QgsCompoundColorWidget::currentColorChanged, this, [ = ]( const QColor & newColor )
640     {
641       if ( newColor.isValid() )
642       {
643         setColor( newColor );
644       }
645     } );
646     panel->openPanel( colorWidget );
647     return;
648   }
649 
650   QgsColorDialog dialog( this, Qt::WindowFlags(), mSymbol->color() );
651   dialog.setTitle( tr( "Symbol Color" ) );
652   dialog.setAllowOpacity( true );
653 
654   if ( dialog.exec() && dialog.color().isValid() )
655   {
656     setColor( dialog.color() );
657   }
658 
659   // reactivate button's window
660   activateWindow();
661 }
662 
setDialogTitle(const QString & title)663 void QgsSymbolButton::setDialogTitle( const QString &title )
664 {
665   mDialogTitle = title;
666 }
667 
dialogTitle() const668 QString QgsSymbolButton::dialogTitle() const
669 {
670   return mDialogTitle;
671 }
672 
symbol()673 QgsSymbol *QgsSymbolButton::symbol()
674 {
675   return mSymbol.get();
676 }
677