QGIS API Documentation  3.13.0-Master (740be229cb)
qgsfontbutton.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfontbutton.h
3  ---------------
4  Date : May 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 "qgsfontbutton.h"
17 #include "qgstextformatwidget.h"
18 #include "qgssymbollayerutils.h"
19 #include "qgscolorscheme.h"
20 #include "qgsmapcanvas.h"
21 #include "qgscolorwidgets.h"
22 #include "qgscolorschemeregistry.h"
23 #include "qgscolorswatchgrid.h"
24 #include "qgsdoublespinbox.h"
25 #include "qgsunittypes.h"
26 #include "qgsmenuheader.h"
27 #include "qgsfontutils.h"
28 #include "qgsapplication.h"
30 #include "qgsvectorlayer.h"
31 #include <QMenu>
32 #include <QClipboard>
33 #include <QDrag>
34 #include <QDesktopWidget>
35 #include <QToolTip>
36 
37 QgsFontButton::QgsFontButton( QWidget *parent, const QString &dialogTitle )
38  : QToolButton( parent )
39  , mDialogTitle( dialogTitle.isEmpty() ? tr( "Text Format" ) : dialogTitle )
40 
41 {
42  setText( tr( "Font" ) );
43 
44  setAcceptDrops( true );
45  connect( this, &QAbstractButton::clicked, this, &QgsFontButton::showSettingsDialog );
46 
47  //setup dropdown menu
48  mMenu = new QMenu( this );
49  connect( mMenu, &QMenu::aboutToShow, this, &QgsFontButton::prepareMenu );
50  setMenu( mMenu );
51  setPopupMode( QToolButton::MenuButtonPopup );
52 
53  //make sure height of button looks good under different platforms
54  QSize size = QToolButton::minimumSizeHint();
55  int fontHeight = Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 1.4;
56 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
57  int minWidth = Qgis::UI_SCALE_FACTOR * fontMetrics().width( 'X' ) * 20;
58 #else
59  int minWidth = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 20;
60 #endif
61  mSizeHint = QSize( std::max( minWidth, size.width() ), std::max( size.height(), fontHeight ) );
62 }
63 
65 {
66  return mSizeHint;
67 }
68 
70 {
71  return mSizeHint;
72 }
73 
74 void QgsFontButton::showSettingsDialog()
75 {
76  switch ( mMode )
77  {
78  case ModeTextRenderer:
79  {
80  QgsExpressionContext context;
81  if ( mExpressionContextGenerator )
82  context = mExpressionContextGenerator->createExpressionContext();
83  else
84  {
86  }
87 
88  QgsSymbolWidgetContext symbolContext;
89  symbolContext.setExpressionContext( &context );
90  symbolContext.setMapCanvas( mMapCanvas );
91  symbolContext.setMessageBar( mMessageBar );
92 
94  if ( panel && panel->dockMode() )
95  {
96  QgsTextFormatPanelWidget *formatWidget = new QgsTextFormatPanelWidget( mFormat, mMapCanvas, this, mLayer.data() );
97  formatWidget->setPanelTitle( mDialogTitle );
98  formatWidget->setContext( symbolContext );
99 
100  connect( formatWidget, &QgsTextFormatPanelWidget::widgetChanged, this, [ this, formatWidget ] { this->setTextFormat( formatWidget->format() ); } );
101  panel->openPanel( formatWidget );
102  return;
103  }
104 
105  QgsTextFormatDialog dialog( mFormat, mMapCanvas, this, QgsGuiUtils::ModalDialogFlags, mLayer.data() );
106  dialog.setWindowTitle( mDialogTitle );
107  dialog.setContext( symbolContext );
108  if ( dialog.exec() )
109  {
110  setTextFormat( dialog.format() );
111  QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
112  }
113  break;
114  }
115 
116  case ModeQFont:
117  {
118  bool ok;
119  QFont newFont = QgsGuiUtils::getFont( ok, mFont, mDialogTitle );
120  if ( ok )
121  {
122  QgsFontUtils::addRecentFontFamily( newFont.family() );
123  setCurrentFont( newFont );
124  }
125  break;
126  }
127  }
128 
129  // reactivate button's window
130  activateWindow();
131  raise();
132 }
133 
135 {
136  return mMapCanvas;
137 }
138 
140 {
141  mMapCanvas = mapCanvas;
142 }
143 
145 {
146  mMessageBar = bar;
147 }
148 
150 {
151  return mMessageBar;
152 }
153 
155 {
156  mFormat = format;
157  updatePreview();
158  emit changed();
159 }
160 
161 void QgsFontButton::setColor( const QColor &color )
162 {
163  QColor opaque = color;
164  opaque.setAlphaF( 1.0 );
165 
166  if ( mFormat.color() != opaque )
167  {
168  mFormat.setColor( opaque );
169  updatePreview();
170  emit changed();
171  }
172 }
173 
175 {
176  switch ( mMode )
177  {
178  case ModeTextRenderer:
179  QApplication::clipboard()->setMimeData( mFormat.toMimeData() );
180  break;
181 
182  case ModeQFont:
183  QApplication::clipboard()->setMimeData( QgsFontUtils::toMimeData( mFont ) );
184  break;
185  }
186 }
187 
189 {
190  QgsTextFormat tempFormat;
191  QFont font;
192  if ( mMode == ModeTextRenderer && formatFromMimeData( QApplication::clipboard()->mimeData(), tempFormat ) )
193  {
194  setTextFormat( tempFormat );
195  QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
196  }
197  else if ( mMode == ModeQFont && fontFromMimeData( QApplication::clipboard()->mimeData(), font ) )
198  {
199  QgsFontUtils::addRecentFontFamily( font.family() );
200  setCurrentFont( font );
201  }
202 }
203 
204 bool QgsFontButton::event( QEvent *e )
205 {
206  if ( e->type() == QEvent::ToolTip )
207  {
208  QHelpEvent *helpEvent = static_cast< QHelpEvent *>( e );
209  QString toolTip;
210  double fontSize = 0.0;
211  switch ( mMode )
212  {
213  case ModeTextRenderer:
214  fontSize = mFormat.size();
215  break;
216 
217  case ModeQFont:
218  fontSize = mFont.pointSizeF();
219  break;
220  }
221  toolTip = QStringLiteral( "<b>%1</b><br>%2<br>Size: %3" ).arg( text(), mMode == ModeTextRenderer ? mFormat.font().family() : mFont.family() ).arg( fontSize );
222  QToolTip::showText( helpEvent->globalPos(), toolTip );
223  }
224  return QToolButton::event( e );
225 }
226 
227 void QgsFontButton::mousePressEvent( QMouseEvent *e )
228 {
229  if ( e->button() == Qt::RightButton )
230  {
231  QToolButton::showMenu();
232  return;
233  }
234  else if ( e->button() == Qt::LeftButton )
235  {
236  mDragStartPosition = e->pos();
237  }
238  QToolButton::mousePressEvent( e );
239 }
240 
241 void QgsFontButton::mouseMoveEvent( QMouseEvent *e )
242 {
243  //handle dragging fonts from button
244 
245  if ( !( e->buttons() & Qt::LeftButton ) )
246  {
247  //left button not depressed, so not a drag
248  QToolButton::mouseMoveEvent( e );
249  return;
250  }
251 
252  if ( ( e->pos() - mDragStartPosition ).manhattanLength() < QApplication::startDragDistance() )
253  {
254  //mouse not moved, so not a drag
255  QToolButton::mouseMoveEvent( e );
256  return;
257  }
258 
259  //user is dragging font
260  QDrag *drag = new QDrag( this );
261  switch ( mMode )
262  {
263  case ModeTextRenderer:
264  drag->setMimeData( mFormat.toMimeData() );
265  break;
266 
267  case ModeQFont:
268  drag->setMimeData( QgsFontUtils::toMimeData( mFont ) );
269  break;
270  }
271  const int iconSize = QgsGuiUtils::scaleIconSize( 50 );
272  drag->setPixmap( createDragIcon( QSize( iconSize, iconSize ) ) );
273  drag->exec( Qt::CopyAction );
274  setDown( false );
275 }
276 
277 bool QgsFontButton::colorFromMimeData( const QMimeData *mimeData, QColor &resultColor, bool &hasAlpha )
278 {
279  hasAlpha = false;
280  QColor mimeColor = QgsSymbolLayerUtils::colorFromMimeData( mimeData, hasAlpha );
281 
282  if ( mimeColor.isValid() )
283  {
284  resultColor = mimeColor;
285  return true;
286  }
287 
288  //could not get color from mime data
289  return false;
290 }
291 
292 void QgsFontButton::dragEnterEvent( QDragEnterEvent *e )
293 {
294  //is dragged data valid font data?
295  QColor mimeColor;
296  QgsTextFormat format;
297  QFont font;
298  bool hasAlpha = false;
299 
300  if ( mMode == ModeTextRenderer && formatFromMimeData( e->mimeData(), format ) )
301  {
302  e->acceptProposedAction();
303  updatePreview( QColor(), &format );
304  }
305  else if ( mMode == ModeQFont && fontFromMimeData( e->mimeData(), font ) )
306  {
307  e->acceptProposedAction();
308  updatePreview( QColor(), nullptr, &font );
309  }
310  else if ( mMode == ModeTextRenderer && colorFromMimeData( e->mimeData(), mimeColor, hasAlpha ) )
311  {
312  //if so, we accept the drag, and temporarily change the button's color
313  //to match the dragged color. This gives immediate feedback to the user
314  //that colors can be dropped here
315  e->acceptProposedAction();
316  updatePreview( mimeColor );
317  }
318 }
319 
320 void QgsFontButton::dragLeaveEvent( QDragLeaveEvent *e )
321 {
322  Q_UNUSED( e )
323  //reset button color
324  updatePreview();
325 }
326 
327 void QgsFontButton::dropEvent( QDropEvent *e )
328 {
329  //is dropped data valid format data?
330  QColor mimeColor;
331  QgsTextFormat format;
332  QFont font;
333  bool hasAlpha = false;
334  if ( mMode == ModeTextRenderer && formatFromMimeData( e->mimeData(), format ) )
335  {
336  setTextFormat( format );
337  QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
338  return;
339  }
340  else if ( mMode == ModeQFont && fontFromMimeData( e->mimeData(), font ) )
341  {
342  QgsFontUtils::addRecentFontFamily( font.family() );
343  setCurrentFont( font );
344  return;
345  }
346  else if ( mMode == ModeTextRenderer && colorFromMimeData( e->mimeData(), mimeColor, hasAlpha ) )
347  {
348  //accept drop and set new color
349  e->acceptProposedAction();
350 
351  if ( hasAlpha )
352  {
353  mFormat.setOpacity( mimeColor.alphaF() );
354  }
355  mimeColor.setAlphaF( 1.0 );
356  mFormat.setColor( mimeColor );
358  updatePreview();
359  emit changed();
360  }
361  updatePreview();
362 }
363 
364 void QgsFontButton::wheelEvent( QWheelEvent *event )
365 {
366  double size = 0;
367  switch ( mMode )
368  {
369  case ModeTextRenderer:
370  size = mFormat.size();
371  break;
372 
373  case ModeQFont:
374  size = mFont.pointSizeF();
375  break;
376  }
377 
378  double increment = event->modifiers() & Qt::ControlModifier ? 0.1 : 1;
379  if ( event->delta() > 0 )
380  {
381  size += increment;
382  }
383  else
384  {
385  size -= increment;
386  }
387  size = std::max( size, 1.0 );
388 
389  switch ( mMode )
390  {
391  case ModeTextRenderer:
392  {
393  QgsTextFormat newFormat = mFormat;
394  newFormat.setSize( size );
395  setTextFormat( newFormat );
396  break;
397  }
398 
399  case ModeQFont:
400  {
401  QFont newFont = mFont;
402  newFont.setPointSizeF( size );
403  setCurrentFont( newFont );
404  break;
405  }
406  }
407 
408  event->accept();
409 }
410 
411 QPixmap QgsFontButton::createColorIcon( const QColor &color ) const
412 {
413  //create an icon pixmap
414  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
415  QPixmap pixmap( iconSize, iconSize );
416  pixmap.fill( Qt::transparent );
417 
418  QPainter p;
419  p.begin( &pixmap );
420 
421  //draw color over pattern
422  p.setBrush( QBrush( color ) );
423 
424  //draw border
425  p.setPen( QColor( 197, 197, 197 ) );
426  p.drawRect( 0, 0, iconSize - 1, iconSize - 1 );
427  p.end();
428  return pixmap;
429 }
430 
431 QPixmap QgsFontButton::createDragIcon( QSize size, const QgsTextFormat *tempFormat, const QFont *tempFont ) const
432 {
433  if ( !tempFormat )
434  tempFormat = &mFormat;
435  if ( !tempFont )
436  tempFont = &mFont;
437 
438  //create an icon pixmap
439  QPixmap pixmap( size.width(), size.height() );
440  pixmap.fill( Qt::transparent );
441  QPainter p;
442  p.begin( &pixmap );
443  p.setRenderHint( QPainter::Antialiasing );
444  QRect rect( 0, 0, size.width(), size.height() );
445 
446  if ( mMode == ModeQFont || tempFormat->color().lightnessF() < 0.7 )
447  {
448  p.setBrush( QBrush( QColor( 255, 255, 255 ) ) );
449  p.setPen( QPen( QColor( 150, 150, 150 ), 0 ) );
450  }
451  else
452  {
453  p.setBrush( QBrush( QColor( 0, 0, 0 ) ) );
454  p.setPen( QPen( QColor( 100, 100, 100 ), 0 ) );
455  }
456  p.drawRect( rect );
457  p.setBrush( Qt::NoBrush );
458  p.setPen( Qt::NoPen );
459 
460  switch ( mMode )
461  {
462  case ModeTextRenderer:
463  {
464  QgsRenderContext context;
465  QgsMapToPixel newCoordXForm;
466  newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
467  context.setMapToPixel( newCoordXForm );
468 
469  context.setScaleFactor( QgsApplication::desktop()->logicalDpiX() / 25.4 );
470  context.setUseAdvancedEffects( true );
471  context.setPainter( &p );
472 
473  // slightly inset text to account for buffer/background
474  double xtrans = 0;
475  if ( tempFormat->buffer().enabled() )
476  xtrans = context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() );
477  if ( tempFormat->background().enabled() && tempFormat->background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
478  xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat->background().size().width(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) );
479 
480  double ytrans = 0.0;
481  if ( tempFormat->buffer().enabled() )
482  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() ) );
483  if ( tempFormat->background().enabled() )
484  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat->background().size().height(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) );
485 
486  QRectF textRect = rect;
487  textRect.setLeft( xtrans );
488  textRect.setWidth( textRect.width() - xtrans );
489  textRect.setTop( ytrans );
490  if ( textRect.height() > 300 )
491  textRect.setHeight( 300 );
492  if ( textRect.width() > 2000 )
493  textRect.setWidth( 2000 );
494 
495  QgsTextRenderer::drawText( textRect, 0, QgsTextRenderer::AlignCenter, QStringList() << tr( "Aa" ),
496  context, *tempFormat );
497  break;
498  }
499  case ModeQFont:
500  {
501  p.setBrush( Qt::NoBrush );
502  p.setPen( QColor( 0, 0, 0 ) );
503  p.setFont( *tempFont );
504  QRectF textRect = rect;
505  textRect.setLeft( 2 );
506  p.drawText( textRect, Qt::AlignVCenter, tr( "Aa" ) );
507  break;
508  }
509  }
510 
511  p.end();
512  return pixmap;
513 }
514 
515 void QgsFontButton::prepareMenu()
516 {
517  //we need to tear down and rebuild this menu every time it is shown. Otherwise the space allocated to any
518  //QgsColorSwatchGridAction is not recalculated by Qt and the swatch grid may not be the correct size
519  //for the number of colors shown in the grid. Note that we MUST refresh color swatch grids every time this
520  //menu is opened, otherwise color schemes like the recent color scheme grid are meaningless
521  mMenu->clear();
522 
523 
524  QWidgetAction *sizeAction = new QWidgetAction( mMenu );
525  QWidget *sizeWidget = new QWidget();
526  QVBoxLayout *sizeLayout = new QVBoxLayout();
527  sizeLayout->setMargin( 0 );
528  sizeLayout->setContentsMargins( 0, 0, 0, 3 );
529  sizeLayout->setSpacing( 2 );
530 
531  QString fontHeaderLabel;
532  switch ( mMode )
533  {
534  case ModeTextRenderer:
535  fontHeaderLabel = tr( "Font size (%1)" ).arg( QgsUnitTypes::toString( mFormat.sizeUnit() ) );
536  break;
537 
538  case ModeQFont:
539  fontHeaderLabel = tr( "Font size (pt)" );
540  break;
541  }
542 
543  QgsMenuHeader *sizeLabel = new QgsMenuHeader( fontHeaderLabel );
544  sizeLayout->addWidget( sizeLabel );
545 
546  QgsDoubleSpinBox *sizeSpin = new QgsDoubleSpinBox( nullptr );
547  sizeSpin->setDecimals( 4 );
548  sizeSpin->setMaximum( 1e+9 );
549  sizeSpin->setShowClearButton( false );
550  sizeSpin->setValue( mMode == ModeTextRenderer ? mFormat.size() : mFont.pointSizeF() );
551  connect( sizeSpin, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ),
552  this, [ = ]( double value )
553  {
554  switch ( mMode )
555  {
556  case ModeTextRenderer:
557  mFormat.setSize( value );
558  break;
559  case ModeQFont:
560  mFont.setPointSizeF( value );
561  break;
562  }
563  updatePreview();
564  emit changed();
565  } );
566  QHBoxLayout *spinLayout = new QHBoxLayout();
567  spinLayout->setMargin( 0 );
568  spinLayout->setContentsMargins( 4, 0, 4, 0 );
569  spinLayout->addWidget( sizeSpin );
570  sizeLayout->addLayout( spinLayout );
571  sizeWidget->setLayout( sizeLayout );
572  sizeAction->setDefaultWidget( sizeWidget );
573  sizeWidget->setFocusProxy( sizeSpin );
574  sizeWidget->setFocusPolicy( Qt::StrongFocus );
575  mMenu->addAction( sizeAction );
576 
577  QMenu *recentFontMenu = new QMenu( tr( "Recent Fonts" ), mMenu );
578  const auto recentFontFamilies { QgsFontUtils::recentFontFamilies() };
579  for ( const QString &family : recentFontFamilies )
580  {
581  QAction *fontAction = new QAction( family, recentFontMenu );
582  QFont f = fontAction->font();
583  f.setFamily( family );
584  fontAction->setFont( f );
585  fontAction->setToolTip( family );
586  recentFontMenu->addAction( fontAction );
587  if ( ( mMode == ModeTextRenderer && family == mFormat.font().family() )
588  || ( mMode == ModeQFont && family == mFont.family() ) )
589  {
590  fontAction->setCheckable( true );
591  fontAction->setChecked( true );
592  }
593  auto setFont = [this, family]
594  {
595  switch ( mMode )
596  {
597  case ModeTextRenderer:
598  {
599  QgsTextFormat newFormat = mFormat;
600  QFont f = newFormat.font();
601  f.setFamily( family );
602  newFormat.setFont( f );
603  setTextFormat( newFormat );
604  QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
605  break;
606  }
607  case ModeQFont:
608  {
609  QFont font = mFont;
610  font.setFamily( family );
611  setCurrentFont( font );
613  break;
614  }
615  }
616  };
617  connect( fontAction, &QAction::triggered, this, setFont );
618  }
619  mMenu->addMenu( recentFontMenu );
620 
621  QAction *configureAction = new QAction( tr( "Configure Format…" ), this );
622  mMenu->addAction( configureAction );
623  connect( configureAction, &QAction::triggered, this, &QgsFontButton::showSettingsDialog );
624 
625  QAction *copyFormatAction = new QAction( tr( "Copy Format" ), this );
626  mMenu->addAction( copyFormatAction );
627  connect( copyFormatAction, &QAction::triggered, this, &QgsFontButton::copyFormat );
628  QAction *pasteFormatAction = new QAction( tr( "Paste Format" ), this );
629  //enable or disable paste action based on current clipboard contents. We always show the paste
630  //action, even if it's disabled, to give hint to the user that pasting colors is possible
631  QgsTextFormat tempFormat;
632  QFont tempFont;
633  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
634  if ( mMode == ModeTextRenderer && formatFromMimeData( QApplication::clipboard()->mimeData(), tempFormat ) )
635  {
637  tempFormat.setSize( 14 );
638  pasteFormatAction->setIcon( createDragIcon( QSize( iconSize, iconSize ), &tempFormat ) );
639  }
640  else if ( mMode == ModeQFont && fontFromMimeData( QApplication::clipboard()->mimeData(), tempFont ) )
641  {
642  tempFont.setPointSize( 8 );
643  pasteFormatAction->setIcon( createDragIcon( QSize( iconSize, iconSize ), nullptr, &tempFont ) );
644  }
645  else
646  {
647  pasteFormatAction->setEnabled( false );
648  }
649  mMenu->addAction( pasteFormatAction );
650  connect( pasteFormatAction, &QAction::triggered, this, &QgsFontButton::pasteFormat );
651 
652  if ( mMode == ModeTextRenderer )
653  {
654  mMenu->addSeparator();
655 
656  QgsColorWheel *colorWheel = new QgsColorWheel( mMenu );
657  colorWheel->setColor( mFormat.color() );
658  QgsColorWidgetAction *colorAction = new QgsColorWidgetAction( colorWheel, mMenu, mMenu );
659  colorAction->setDismissOnColorSelection( false );
660  connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsFontButton::setColor );
661  mMenu->addAction( colorAction );
662 
664  QColor alphaColor = mFormat.color();
665  alphaColor.setAlphaF( mFormat.opacity() );
666  alphaRamp->setColor( alphaColor );
667  QgsColorWidgetAction *alphaAction = new QgsColorWidgetAction( alphaRamp, mMenu, mMenu );
668  alphaAction->setDismissOnColorSelection( false );
669  connect( alphaAction, &QgsColorWidgetAction::colorChanged, this, [ = ]( const QColor & color )
670  {
671  double opacity = color.alphaF();
672  mFormat.setOpacity( opacity );
673  updatePreview();
674  emit changed();
675  } );
676  connect( colorAction, &QgsColorWidgetAction::colorChanged, alphaRamp, [alphaRamp]( const QColor & color ) { alphaRamp->setColor( color, false ); }
677  );
678  mMenu->addAction( alphaAction );
679 
680  //get schemes with ShowInColorButtonMenu flag set
681  QList< QgsColorScheme * > schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorButtonMenu );
682  QList< QgsColorScheme * >::iterator it = schemeList.begin();
683  for ( ; it != schemeList.end(); ++it )
684  {
685  QgsColorSwatchGridAction *colorAction = new QgsColorSwatchGridAction( *it, mMenu, QStringLiteral( "labeling" ), this );
686  colorAction->setBaseColor( mFormat.color() );
687  mMenu->addAction( colorAction );
688  connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsFontButton::setColor );
689  connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsFontButton::addRecentColor );
690  }
691 
692  mMenu->addSeparator();
693 
694  QAction *copyColorAction = new QAction( tr( "Copy Color" ), this );
695  mMenu->addAction( copyColorAction );
696  connect( copyColorAction, &QAction::triggered, this, &QgsFontButton::copyColor );
697 
698  QAction *pasteColorAction = new QAction( tr( "Paste Color" ), this );
699  //enable or disable paste action based on current clipboard contents. We always show the paste
700  //action, even if it's disabled, to give hint to the user that pasting colors is possible
701  QColor clipColor;
702  bool hasAlpha = false;
703  if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
704  {
705  pasteColorAction->setIcon( createColorIcon( clipColor ) );
706  }
707  else
708  {
709  pasteColorAction->setEnabled( false );
710  }
711  mMenu->addAction( pasteColorAction );
712  connect( pasteColorAction, &QAction::triggered, this, &QgsFontButton::pasteColor );
713  }
714 }
715 
716 void QgsFontButton::addRecentColor( const QColor &color )
717 {
719 }
720 
721 QFont QgsFontButton::currentFont() const
722 {
723  return mFont;
724 }
725 
727 {
728  return mLayer;
729 }
730 
732 {
733  mLayer = layer;
734 }
735 
737 {
738  mExpressionContextGenerator = generator;
739 }
740 
741 void QgsFontButton::setCurrentFont( const QFont &font )
742 {
743  mFont = font;
744  updatePreview();
745  emit changed();
746 }
747 
749 {
750  return mMode;
751 }
752 
754 {
755  mMode = mode;
756  updatePreview();
757 }
758 
759 bool QgsFontButton::formatFromMimeData( const QMimeData *mimeData, QgsTextFormat &resultFormat ) const
760 {
761  bool ok = false;
762  resultFormat = QgsTextFormat::fromMimeData( mimeData, &ok );
763  return ok;
764 }
765 
766 bool QgsFontButton::fontFromMimeData( const QMimeData *mimeData, QFont &resultFont ) const
767 {
768  bool ok = false;
769  resultFont = QgsFontUtils::fromMimeData( mimeData, &ok );
770  return ok;
771 }
772 
773 void QgsFontButton::changeEvent( QEvent *e )
774 {
775  if ( e->type() == QEvent::EnabledChange )
776  {
777  updatePreview();
778  }
779  QToolButton::changeEvent( e );
780 }
781 
782 void QgsFontButton::showEvent( QShowEvent *e )
783 {
784  updatePreview();
785  QToolButton::showEvent( e );
786 }
787 
788 void QgsFontButton::resizeEvent( QResizeEvent *event )
789 {
790  QToolButton::resizeEvent( event );
791  //recalculate icon size and redraw icon
792  mIconSize = QSize();
793  updatePreview();
794 }
795 
796 void QgsFontButton::updatePreview( const QColor &color, QgsTextFormat *format, QFont *font )
797 {
798  QgsTextFormat tempFormat;
799  QFont tempFont;
800 
801  if ( format )
802  tempFormat = *format;
803  else
804  tempFormat = mFormat;
805  if ( font )
806  tempFont = *font;
807  else
808  tempFont = mFont;
809 
810  if ( color.isValid() )
811  tempFormat.setColor( color );
812 
813  QSize currentIconSize;
814  //icon size is button size with a small margin
815  if ( menu() )
816  {
817  if ( !mIconSize.isValid() )
818  {
819  //calculate size of push button part of widget (ie, without the menu dropdown button part)
820  QStyleOptionToolButton opt;
821  initStyleOption( &opt );
822  QRect buttonSize = QApplication::style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton,
823  this );
824  //make sure height of icon looks good under different platforms
825 #ifdef Q_OS_WIN
826  mIconSize = QSize( buttonSize.width() - 10, height() - 6 );
827 #elif defined(Q_OS_MAC)
828  mIconSize = QSize( buttonSize.width() - 10, height() - 2 );
829 #else
830  mIconSize = QSize( buttonSize.width() - 10, height() - 12 );
831 #endif
832  }
833  currentIconSize = mIconSize;
834  }
835  else
836  {
837  //no menu
838 #ifdef Q_OS_WIN
839  currentIconSize = QSize( width() - 10, height() - 6 );
840 #else
841  currentIconSize = QSize( width() - 10, height() - 12 );
842 #endif
843  }
844 
845  if ( !currentIconSize.isValid() || currentIconSize.width() <= 0 || currentIconSize.height() <= 0 )
846  {
847  return;
848  }
849 
850  //create an icon pixmap
851  QPixmap pixmap( currentIconSize );
852  pixmap.fill( Qt::transparent );
853  QPainter p;
854  p.begin( &pixmap );
855  p.setRenderHint( QPainter::Antialiasing );
856  QRect rect( 0, 0, currentIconSize.width(), currentIconSize.height() );
857 
858  switch ( mMode )
859  {
860  case ModeTextRenderer:
861  {
862  QgsRenderContext context;
863  QgsMapToPixel newCoordXForm;
864  newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
865  context.setMapToPixel( newCoordXForm );
866 
867  context.setScaleFactor( QgsApplication::desktop()->logicalDpiX() / 25.4 );
868  context.setUseAdvancedEffects( true );
869  context.setPainter( &p );
870 
871  // slightly inset text to account for buffer/background
872  double xtrans = 0;
873  if ( tempFormat.buffer().enabled() )
874  xtrans = context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
875  if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
876  xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
877 
878  double ytrans = 0.0;
879  if ( tempFormat.buffer().enabled() )
880  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) );
881  if ( tempFormat.background().enabled() )
882  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
883 
884  QRectF textRect = rect;
885  textRect.setLeft( xtrans );
886  textRect.setWidth( textRect.width() - xtrans );
887  textRect.setTop( ytrans );
888  if ( textRect.height() > 300 )
889  textRect.setHeight( 300 );
890  if ( textRect.width() > 2000 )
891  textRect.setWidth( 2000 );
892 
893  QgsTextRenderer::drawText( textRect, 0, QgsTextRenderer::AlignLeft, QStringList() << text(),
894  context, tempFormat );
895  break;
896  }
897  case ModeQFont:
898  {
899  p.setBrush( Qt::NoBrush );
900  p.setPen( QColor( 0, 0, 0 ) );
901  p.setFont( tempFont );
902  QRectF textRect = rect;
903  textRect.setLeft( 2 );
904  p.drawText( textRect, Qt::AlignVCenter, text() );
905  break;
906  }
907 
908  }
909  p.end();
910  setIconSize( currentIconSize );
911  setIcon( pixmap );
912 }
913 
915 {
916  //copy color
917  QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mFormat.color() ) );
918 }
919 
921 {
922  QColor clipColor;
923  bool hasAlpha = false;
924  if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
925  {
926  //paste color
927  setColor( clipColor );
929  }
930 }
931 
932 void QgsFontButton::setDialogTitle( const QString &title )
933 {
934  mDialogTitle = title;
935 }
936 
937 QString QgsFontButton::dialogTitle() const
938 {
939  return mDialogTitle;
940 }
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value...
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
QSize minimumSizeHint() const override
A color swatch grid which can be embedded into a menu.
static QFont fromMimeData(const QMimeData *data, bool *ok=nullptr)
Attempts to parse the provided mime data as a QFont.
void dragEnterEvent(QDragEnterEvent *e) override
void setOpacity(double opacity)
Sets the text&#39;s opacity.
QSizeF size() const
Returns the size of the background shape.
double opacity() const
Returns the text&#39;s opacity.
bool dockMode()
Returns the dock mode state.
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
void setCurrentFont(const QFont &font)
Sets the current text font to show in the widget.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:182
QgsFontButton(QWidget *parent=nullptr, const QString &dialogTitle=QString())
Construct a new font button.
virtual void setColor(const QColor &color, bool emitSignals=false)
Sets the color for the widget.
Mode
Available button modes.
Definition: qgsfontbutton.h:56
void copyFormat()
Copies the current text format to the clipboard.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly...
void setShowClearButton(bool showClearButton)
Sets whether the widget will show a clear button.
void copyColor()
Copies the current text color to the clipboard.
void setFont(const QFont &font)
Sets the font used for rendering text.
void resizeEvent(QResizeEvent *event) override
QColor color() const
Returns the color that text will be rendered in.
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:45
void colorChanged(const QColor &color)
Emitted when a color has been selected from the widget.
QgsFontButton::Mode mode() const
Returns the current button mode.
Base class for any widget that can be shown as a inline panel.
void mousePressEvent(QMouseEvent *e) override
Show scheme in color button drop-down menu.
QgsVectorLayer * layer() const
Returns the layer associated with the widget.
void pasteFormat()
Pastes a format from the clipboard.
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the widget.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:78
void setLayer(QgsVectorLayer *layer)
Sets a layer to associate with the widget.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
void setMessageBar(QgsMessageBar *bar)
Sets the message bar associated with the widget.
static QgsTextFormat fromMimeData(const QMimeData *data, bool *ok=nullptr)
Attempts to parse the provided mime data as a QgsTextFormat.
Custom widget for displaying subheaders within a QMenu in a standard style.
Definition: qgsmenuheader.h:32
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
QList< QgsColorScheme * > schemes() const
Returns all color schemes in the registry.
virtual QgsExpressionContext createExpressionContext() const =0
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void mouseMoveEvent(QMouseEvent *e) override
void setUseAdvancedEffects(bool enabled)
Used to enable or disable advanced effects such as blend modes.
static QStringList recentFontFamilies()
Returns a list of recently used font families.
void setSize(double size)
Sets the size for rendered text.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
A color wheel widget.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget...
Contains settings which reflect the context in which a symbol (or renderer) widget is shown...
Alpha component (opacity) of color.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas to associate with the widget.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setTextFormat(const QgsTextFormat &format)
Sets the current text format to show in the widget.
A color ramp widget.
bool event(QEvent *e) override
QSize sizeHint() const override
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
QString dialogTitle() const
Returns the title for the text settings dialog window.
QgsTextBackgroundSettings & background()
Returns a reference to the text background settings.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the size of rendered text.
void changed()
Emitted when the widget&#39;s text format settings are changed.
void setColor(const QColor &color, bool emitSignals=false) override
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer&#39;s project and layer.
Configure font settings for use with QgsTextRenderer.
Definition: qgsfontbutton.h:58
QMimeData * toMimeData() const
Returns new mime data representing the text format settings.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window&#39;s toolbar icons.
static void addRecentColor(const QColor &color)
Adds a color to the list of recent colors.
void widgetChanged()
Emitted when the widget state changes.
void setExpressionContext(QgsExpressionContext *context)
Sets the optional expression context used for the widget.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the widget is shown, e.g., the associated map canvas and expression context...
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
Abstract interface for generating an expression context.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the widget is shown, e.g., the associated map canvas and expression context...
static void drawText(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true)
Draws text within a rectangle using the specified settings.
void setParameters(double mapUnitsPerPixel, double centerX, double centerY, int widthPixels, int heightPixels, double rotation)
Set parameters for use in transforming coordinates.
static void addRecentFontFamily(const QString &family)
Adds a font family to the list of recently used font families.
static QgsColorSchemeRegistry * colorSchemeRegistry()
Returns the application&#39;s color scheme registry, used for managing color schemes. ...
QgsTextFormat format() const
Returns the current formatting settings defined by the widget.
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the buffer size.
double size() const
Returns the size of the buffer.
An action containing a color widget, which can be embedded into a menu.
static QMimeData * colorToMimeData(const QColor &color)
Creates mime data from a color.
void setMode(Mode mode)
Sets the current button mode.
double size() const
Returns the size for rendered text.
bool enabled() const
Returns whether the background is enabled.
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context&#39;s map to pixel transform, which transforms between map coordinates and device coordi...
A panel widget for customizing text formatting settings.
SizeType sizeType() const
Returns the method used to determine the size of the background shape (e.g., fixed size or buffer aro...
bool enabled() const
Returns whether the buffer is enabled.
void setMessageBar(QgsMessageBar *bar)
Sets the message bar associated with the widget.
Configure font settings for use with QFont objects.
Definition: qgsfontbutton.h:59
QFont currentFont() const
Returns the current QFont set by the widget.
void appendScopes(const QList< QgsExpressionContextScope *> &scopes)
Appends a list of scopes to the end of the context.
void dragLeaveEvent(QDragLeaveEvent *e) override
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the shape size.
static QMimeData * toMimeData(const QFont &font)
Returns new mime data representing the specified font settings.
void wheelEvent(QWheelEvent *event) override
Container for all settings relating to text rendering.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the size of rendered text.
void setDialogTitle(const QString &title)
Sets the title for the text settings dialog window.
A simple dialog for customizing text formatting settings.
void setDismissOnColorSelection(bool dismiss)
Sets whether the parent menu should be dismissed and closed when a color is selected from the action&#39;...
void dropEvent(QDropEvent *e) override
Represents a vector layer which manages a vector based data sets.
void showEvent(QShowEvent *e) override
void changeEvent(QEvent *e) override
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
void registerExpressionContextGenerator(QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
QFont font() const
Returns the font used for rendering text.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units used for the shape&#39;s size.
QFont getFont(bool &ok, const QFont &initial, const QString &title)
Show font selection dialog.
void setColor(const QColor &color)
Sets the current color for the text.
static QColor colorFromMimeData(const QMimeData *data, bool &hasAlpha)
Attempts to parse mime data as a color.
void colorChanged(const QColor &color)
Emitted when a color has been selected from the widget.
void setBaseColor(const QColor &baseColor)
Sets the base color for the color grid.
void pasteColor()
Pastes a color from the clipboard to the text format.