QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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  int minWidth = Qgis::UI_SCALE_FACTOR * fontMetrics().width( 'X' ) * 20;
57  mSizeHint = QSize( std::max( minWidth, size.width() ), std::max( size.height(), fontHeight ) );
58 }
59 
61 {
62  return mSizeHint;
63 }
64 
66 {
67  return mSizeHint;
68 }
69 
70 void QgsFontButton::showSettingsDialog()
71 {
72  switch ( mMode )
73  {
74  case ModeTextRenderer:
75  {
76  QgsExpressionContext context;
77  if ( mExpressionContextGenerator )
78  context = mExpressionContextGenerator->createExpressionContext();
79  else
80  {
82  }
83 
84  QgsSymbolWidgetContext symbolContext;
85  symbolContext.setExpressionContext( &context );
86  symbolContext.setMapCanvas( mMapCanvas );
87  symbolContext.setMessageBar( mMessageBar );
88 
90  if ( panel && panel->dockMode() )
91  {
92  QgsTextFormatPanelWidget *formatWidget = new QgsTextFormatPanelWidget( mFormat, mMapCanvas, this, mLayer.data() );
93  formatWidget->setPanelTitle( mDialogTitle );
94  formatWidget->setContext( symbolContext );
95 
96  connect( formatWidget, &QgsTextFormatPanelWidget::widgetChanged, this, [ this, formatWidget ] { this->setTextFormat( formatWidget->format() ); } );
97  panel->openPanel( formatWidget );
98  return;
99  }
100 
101  QgsTextFormatDialog dialog( mFormat, mMapCanvas, this, QgsGuiUtils::ModalDialogFlags, mLayer.data() );
102  dialog.setWindowTitle( mDialogTitle );
103  dialog.setContext( symbolContext );
104  if ( dialog.exec() )
105  {
106  setTextFormat( dialog.format() );
107  QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
108  }
109  break;
110  }
111 
112  case ModeQFont:
113  {
114  bool ok;
115  QFont newFont = QgsGuiUtils::getFont( ok, mFont, mDialogTitle );
116  if ( ok )
117  {
118  QgsFontUtils::addRecentFontFamily( newFont.family() );
119  setCurrentFont( newFont );
120  }
121  break;
122  }
123  }
124 
125  // reactivate button's window
126  activateWindow();
127  raise();
128 }
129 
131 {
132  return mMapCanvas;
133 }
134 
136 {
137  mMapCanvas = mapCanvas;
138 }
139 
141 {
142  mMessageBar = bar;
143 }
144 
146 {
147  return mMessageBar;
148 }
149 
151 {
152  mFormat = format;
153  updatePreview();
154  emit changed();
155 }
156 
157 void QgsFontButton::setColor( const QColor &color )
158 {
159  QColor opaque = color;
160  opaque.setAlphaF( 1.0 );
161 
162  if ( mFormat.color() != opaque )
163  {
164  mFormat.setColor( opaque );
165  updatePreview();
166  emit changed();
167  }
168 }
169 
171 {
172  switch ( mMode )
173  {
174  case ModeTextRenderer:
175  QApplication::clipboard()->setMimeData( mFormat.toMimeData() );
176  break;
177 
178  case ModeQFont:
179  QApplication::clipboard()->setMimeData( QgsFontUtils::toMimeData( mFont ) );
180  break;
181  }
182 }
183 
185 {
186  QgsTextFormat tempFormat;
187  QFont font;
188  if ( mMode == ModeTextRenderer && formatFromMimeData( QApplication::clipboard()->mimeData(), tempFormat ) )
189  {
190  setTextFormat( tempFormat );
191  QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
192  }
193  else if ( mMode == ModeQFont && fontFromMimeData( QApplication::clipboard()->mimeData(), font ) )
194  {
195  QgsFontUtils::addRecentFontFamily( font.family() );
196  setCurrentFont( font );
197  }
198 }
199 
200 bool QgsFontButton::event( QEvent *e )
201 {
202  if ( e->type() == QEvent::ToolTip )
203  {
204  QHelpEvent *helpEvent = static_cast< QHelpEvent *>( e );
205  QString toolTip;
206  double fontSize = 0.0;
207  switch ( mMode )
208  {
209  case ModeTextRenderer:
210  fontSize = mFormat.size();
211  break;
212 
213  case ModeQFont:
214  fontSize = mFont.pointSizeF();
215  break;
216  }
217  toolTip = QStringLiteral( "<b>%1</b><br>%2<br>Size: %3" ).arg( text(), mMode == ModeTextRenderer ? mFormat.font().family() : mFont.family() ).arg( fontSize );
218  QToolTip::showText( helpEvent->globalPos(), toolTip );
219  }
220  return QToolButton::event( e );
221 }
222 
223 void QgsFontButton::mousePressEvent( QMouseEvent *e )
224 {
225  if ( e->button() == Qt::RightButton )
226  {
227  QToolButton::showMenu();
228  return;
229  }
230  else if ( e->button() == Qt::LeftButton )
231  {
232  mDragStartPosition = e->pos();
233  }
234  QToolButton::mousePressEvent( e );
235 }
236 
237 void QgsFontButton::mouseMoveEvent( QMouseEvent *e )
238 {
239  //handle dragging fonts from button
240 
241  if ( !( e->buttons() & Qt::LeftButton ) )
242  {
243  //left button not depressed, so not a drag
244  QToolButton::mouseMoveEvent( e );
245  return;
246  }
247 
248  if ( ( e->pos() - mDragStartPosition ).manhattanLength() < QApplication::startDragDistance() )
249  {
250  //mouse not moved, so not a drag
251  QToolButton::mouseMoveEvent( e );
252  return;
253  }
254 
255  //user is dragging font
256  QDrag *drag = new QDrag( this );
257  switch ( mMode )
258  {
259  case ModeTextRenderer:
260  drag->setMimeData( mFormat.toMimeData() );
261  break;
262 
263  case ModeQFont:
264  drag->setMimeData( QgsFontUtils::toMimeData( mFont ) );
265  break;
266  }
267  const int iconSize = QgsGuiUtils::scaleIconSize( 50 );
268  drag->setPixmap( createDragIcon( QSize( iconSize, iconSize ) ) );
269  drag->exec( Qt::CopyAction );
270  setDown( false );
271 }
272 
273 bool QgsFontButton::colorFromMimeData( const QMimeData *mimeData, QColor &resultColor, bool &hasAlpha )
274 {
275  hasAlpha = false;
276  QColor mimeColor = QgsSymbolLayerUtils::colorFromMimeData( mimeData, hasAlpha );
277 
278  if ( mimeColor.isValid() )
279  {
280  resultColor = mimeColor;
281  return true;
282  }
283 
284  //could not get color from mime data
285  return false;
286 }
287 
288 void QgsFontButton::dragEnterEvent( QDragEnterEvent *e )
289 {
290  //is dragged data valid font data?
291  QColor mimeColor;
292  QgsTextFormat format;
293  QFont font;
294  bool hasAlpha = false;
295 
296  if ( mMode == ModeTextRenderer && formatFromMimeData( e->mimeData(), format ) )
297  {
298  e->acceptProposedAction();
299  updatePreview( QColor(), &format );
300  }
301  else if ( mMode == ModeQFont && fontFromMimeData( e->mimeData(), font ) )
302  {
303  e->acceptProposedAction();
304  updatePreview( QColor(), nullptr, &font );
305  }
306  else if ( mMode == ModeTextRenderer && colorFromMimeData( e->mimeData(), mimeColor, hasAlpha ) )
307  {
308  //if so, we accept the drag, and temporarily change the button's color
309  //to match the dragged color. This gives immediate feedback to the user
310  //that colors can be dropped here
311  e->acceptProposedAction();
312  updatePreview( mimeColor );
313  }
314 }
315 
316 void QgsFontButton::dragLeaveEvent( QDragLeaveEvent *e )
317 {
318  Q_UNUSED( e )
319  //reset button color
320  updatePreview();
321 }
322 
323 void QgsFontButton::dropEvent( QDropEvent *e )
324 {
325  //is dropped data valid format data?
326  QColor mimeColor;
327  QgsTextFormat format;
328  QFont font;
329  bool hasAlpha = false;
330  if ( mMode == ModeTextRenderer && formatFromMimeData( e->mimeData(), format ) )
331  {
332  setTextFormat( format );
333  QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
334  return;
335  }
336  else if ( mMode == ModeQFont && fontFromMimeData( e->mimeData(), font ) )
337  {
338  QgsFontUtils::addRecentFontFamily( font.family() );
339  setCurrentFont( font );
340  return;
341  }
342  else if ( mMode == ModeTextRenderer && colorFromMimeData( e->mimeData(), mimeColor, hasAlpha ) )
343  {
344  //accept drop and set new color
345  e->acceptProposedAction();
346 
347  if ( hasAlpha )
348  {
349  mFormat.setOpacity( mimeColor.alphaF() );
350  }
351  mimeColor.setAlphaF( 1.0 );
352  mFormat.setColor( mimeColor );
354  updatePreview();
355  emit changed();
356  }
357  updatePreview();
358 }
359 
360 void QgsFontButton::wheelEvent( QWheelEvent *event )
361 {
362  double size = 0;
363  switch ( mMode )
364  {
365  case ModeTextRenderer:
366  size = mFormat.size();
367  break;
368 
369  case ModeQFont:
370  size = mFont.pointSizeF();
371  break;
372  }
373 
374  double increment = event->modifiers() & Qt::ControlModifier ? 0.1 : 1;
375  if ( event->delta() > 0 )
376  {
377  size += increment;
378  }
379  else
380  {
381  size -= increment;
382  }
383  size = std::max( size, 1.0 );
384 
385  switch ( mMode )
386  {
387  case ModeTextRenderer:
388  {
389  QgsTextFormat newFormat = mFormat;
390  newFormat.setSize( size );
391  setTextFormat( newFormat );
392  break;
393  }
394 
395  case ModeQFont:
396  {
397  QFont newFont = mFont;
398  newFont.setPointSizeF( size );
399  setCurrentFont( newFont );
400  break;
401  }
402  }
403 
404  event->accept();
405 }
406 
407 QPixmap QgsFontButton::createColorIcon( const QColor &color ) const
408 {
409  //create an icon pixmap
410  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
411  QPixmap pixmap( iconSize, iconSize );
412  pixmap.fill( Qt::transparent );
413 
414  QPainter p;
415  p.begin( &pixmap );
416 
417  //draw color over pattern
418  p.setBrush( QBrush( color ) );
419 
420  //draw border
421  p.setPen( QColor( 197, 197, 197 ) );
422  p.drawRect( 0, 0, iconSize - 1, iconSize - 1 );
423  p.end();
424  return pixmap;
425 }
426 
427 QPixmap QgsFontButton::createDragIcon( QSize size, const QgsTextFormat *tempFormat, const QFont *tempFont ) const
428 {
429  if ( !tempFormat )
430  tempFormat = &mFormat;
431  if ( !tempFont )
432  tempFont = &mFont;
433 
434  //create an icon pixmap
435  QPixmap pixmap( size.width(), size.height() );
436  pixmap.fill( Qt::transparent );
437  QPainter p;
438  p.begin( &pixmap );
439  p.setRenderHint( QPainter::Antialiasing );
440  QRect rect( 0, 0, size.width(), size.height() );
441 
442  if ( mMode == ModeQFont || tempFormat->color().lightnessF() < 0.7 )
443  {
444  p.setBrush( QBrush( QColor( 255, 255, 255 ) ) );
445  p.setPen( QPen( QColor( 150, 150, 150 ), 0 ) );
446  }
447  else
448  {
449  p.setBrush( QBrush( QColor( 0, 0, 0 ) ) );
450  p.setPen( QPen( QColor( 100, 100, 100 ), 0 ) );
451  }
452  p.drawRect( rect );
453  p.setBrush( Qt::NoBrush );
454  p.setPen( Qt::NoPen );
455 
456  switch ( mMode )
457  {
458  case ModeTextRenderer:
459  {
460  QgsRenderContext context;
461  QgsMapToPixel newCoordXForm;
462  newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
463  context.setMapToPixel( newCoordXForm );
464 
465  context.setScaleFactor( QgsApplication::desktop()->logicalDpiX() / 25.4 );
466  context.setUseAdvancedEffects( true );
467  context.setPainter( &p );
468 
469  // slightly inset text to account for buffer/background
470  double xtrans = 0;
471  if ( tempFormat->buffer().enabled() )
472  xtrans = context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() );
473  if ( tempFormat->background().enabled() && tempFormat->background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
474  xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat->background().size().width(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) );
475 
476  double ytrans = 0.0;
477  if ( tempFormat->buffer().enabled() )
478  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() ) );
479  if ( tempFormat->background().enabled() )
480  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat->background().size().height(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) );
481 
482  QRectF textRect = rect;
483  textRect.setLeft( xtrans );
484  textRect.setWidth( textRect.width() - xtrans );
485  textRect.setTop( ytrans );
486  if ( textRect.height() > 300 )
487  textRect.setHeight( 300 );
488  if ( textRect.width() > 2000 )
489  textRect.setWidth( 2000 );
490 
491  QgsTextRenderer::drawText( textRect, 0, QgsTextRenderer::AlignCenter, QStringList() << tr( "Aa" ),
492  context, *tempFormat );
493  break;
494  }
495  case ModeQFont:
496  {
497  p.setBrush( Qt::NoBrush );
498  p.setPen( QColor( 0, 0, 0 ) );
499  p.setFont( *tempFont );
500  QRectF textRect = rect;
501  textRect.setLeft( 2 );
502  p.drawText( textRect, Qt::AlignVCenter, tr( "Aa" ) );
503  break;
504  }
505  }
506 
507  p.end();
508  return pixmap;
509 }
510 
511 void QgsFontButton::prepareMenu()
512 {
513  //we need to tear down and rebuild this menu every time it is shown. Otherwise the space allocated to any
514  //QgsColorSwatchGridAction is not recalculated by Qt and the swatch grid may not be the correct size
515  //for the number of colors shown in the grid. Note that we MUST refresh color swatch grids every time this
516  //menu is opened, otherwise color schemes like the recent color scheme grid are meaningless
517  mMenu->clear();
518 
519 
520  QWidgetAction *sizeAction = new QWidgetAction( mMenu );
521  QWidget *sizeWidget = new QWidget();
522  QVBoxLayout *sizeLayout = new QVBoxLayout();
523  sizeLayout->setMargin( 0 );
524  sizeLayout->setContentsMargins( 0, 0, 0, 3 );
525  sizeLayout->setSpacing( 2 );
526 
527  QString fontHeaderLabel;
528  switch ( mMode )
529  {
530  case ModeTextRenderer:
531  fontHeaderLabel = tr( "Font size (%1)" ).arg( QgsUnitTypes::toString( mFormat.sizeUnit() ) );
532  break;
533 
534  case ModeQFont:
535  fontHeaderLabel = tr( "Font size (pt)" );
536  break;
537  }
538 
539  QgsMenuHeader *sizeLabel = new QgsMenuHeader( fontHeaderLabel );
540  sizeLayout->addWidget( sizeLabel );
541 
542  QgsDoubleSpinBox *sizeSpin = new QgsDoubleSpinBox( nullptr );
543  sizeSpin->setDecimals( 4 );
544  sizeSpin->setMaximum( 1e+9 );
545  sizeSpin->setShowClearButton( false );
546  sizeSpin->setValue( mMode == ModeTextRenderer ? mFormat.size() : mFont.pointSizeF() );
547  connect( sizeSpin, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ),
548  this, [ = ]( double value )
549  {
550  switch ( mMode )
551  {
552  case ModeTextRenderer:
553  mFormat.setSize( value );
554  break;
555  case ModeQFont:
556  mFont.setPointSizeF( value );
557  break;
558  }
559  updatePreview();
560  emit changed();
561  } );
562  QHBoxLayout *spinLayout = new QHBoxLayout();
563  spinLayout->setMargin( 0 );
564  spinLayout->setContentsMargins( 4, 0, 4, 0 );
565  spinLayout->addWidget( sizeSpin );
566  sizeLayout->addLayout( spinLayout );
567  sizeWidget->setLayout( sizeLayout );
568  sizeAction->setDefaultWidget( sizeWidget );
569  sizeWidget->setFocusProxy( sizeSpin );
570  sizeWidget->setFocusPolicy( Qt::StrongFocus );
571  mMenu->addAction( sizeAction );
572 
573  QMenu *recentFontMenu = new QMenu( tr( "Recent Fonts" ), mMenu );
574  const auto recentFontFamilies { QgsFontUtils::recentFontFamilies() };
575  for ( const QString &family : recentFontFamilies )
576  {
577  QAction *fontAction = new QAction( family, recentFontMenu );
578  QFont f = fontAction->font();
579  f.setFamily( family );
580  fontAction->setFont( f );
581  fontAction->setToolTip( family );
582  recentFontMenu->addAction( fontAction );
583  if ( ( mMode == ModeTextRenderer && family == mFormat.font().family() )
584  || ( mMode == ModeQFont && family == mFont.family() ) )
585  {
586  fontAction->setCheckable( true );
587  fontAction->setChecked( true );
588  }
589  auto setFont = [this, family]
590  {
591  switch ( mMode )
592  {
593  case ModeTextRenderer:
594  {
595  QgsTextFormat newFormat = mFormat;
596  QFont f = newFormat.font();
597  f.setFamily( family );
598  newFormat.setFont( f );
599  setTextFormat( newFormat );
600  QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
601  break;
602  }
603  case ModeQFont:
604  {
605  QFont font = mFont;
606  font.setFamily( family );
607  setCurrentFont( font );
609  break;
610  }
611  }
612  };
613  connect( fontAction, &QAction::triggered, this, setFont );
614  }
615  mMenu->addMenu( recentFontMenu );
616 
617  QAction *configureAction = new QAction( tr( "Configure Format…" ), this );
618  mMenu->addAction( configureAction );
619  connect( configureAction, &QAction::triggered, this, &QgsFontButton::showSettingsDialog );
620 
621  QAction *copyFormatAction = new QAction( tr( "Copy Format" ), this );
622  mMenu->addAction( copyFormatAction );
623  connect( copyFormatAction, &QAction::triggered, this, &QgsFontButton::copyFormat );
624  QAction *pasteFormatAction = new QAction( tr( "Paste Format" ), this );
625  //enable or disable paste action based on current clipboard contents. We always show the paste
626  //action, even if it's disabled, to give hint to the user that pasting colors is possible
627  QgsTextFormat tempFormat;
628  QFont tempFont;
629  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
630  if ( mMode == ModeTextRenderer && formatFromMimeData( QApplication::clipboard()->mimeData(), tempFormat ) )
631  {
633  tempFormat.setSize( 14 );
634  pasteFormatAction->setIcon( createDragIcon( QSize( iconSize, iconSize ), &tempFormat ) );
635  }
636  else if ( mMode == ModeQFont && fontFromMimeData( QApplication::clipboard()->mimeData(), tempFont ) )
637  {
638  tempFont.setPointSize( 8 );
639  pasteFormatAction->setIcon( createDragIcon( QSize( iconSize, iconSize ), nullptr, &tempFont ) );
640  }
641  else
642  {
643  pasteFormatAction->setEnabled( false );
644  }
645  mMenu->addAction( pasteFormatAction );
646  connect( pasteFormatAction, &QAction::triggered, this, &QgsFontButton::pasteFormat );
647 
648  if ( mMode == ModeTextRenderer )
649  {
650  mMenu->addSeparator();
651 
652  QgsColorWheel *colorWheel = new QgsColorWheel( mMenu );
653  colorWheel->setColor( mFormat.color() );
654  QgsColorWidgetAction *colorAction = new QgsColorWidgetAction( colorWheel, mMenu, mMenu );
655  colorAction->setDismissOnColorSelection( false );
656  connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsFontButton::setColor );
657  mMenu->addAction( colorAction );
658 
660  QColor alphaColor = mFormat.color();
661  alphaColor.setAlphaF( mFormat.opacity() );
662  alphaRamp->setColor( alphaColor );
663  QgsColorWidgetAction *alphaAction = new QgsColorWidgetAction( alphaRamp, mMenu, mMenu );
664  alphaAction->setDismissOnColorSelection( false );
665  connect( alphaAction, &QgsColorWidgetAction::colorChanged, this, [ = ]( const QColor & color )
666  {
667  double opacity = color.alphaF();
668  mFormat.setOpacity( opacity );
669  updatePreview();
670  emit changed();
671  } );
672  connect( colorAction, &QgsColorWidgetAction::colorChanged, alphaRamp, [alphaRamp]( const QColor & color ) { alphaRamp->setColor( color, false ); }
673  );
674  mMenu->addAction( alphaAction );
675 
676  //get schemes with ShowInColorButtonMenu flag set
677  QList< QgsColorScheme * > schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorButtonMenu );
678  QList< QgsColorScheme * >::iterator it = schemeList.begin();
679  for ( ; it != schemeList.end(); ++it )
680  {
681  QgsColorSwatchGridAction *colorAction = new QgsColorSwatchGridAction( *it, mMenu, QStringLiteral( "labeling" ), this );
682  colorAction->setBaseColor( mFormat.color() );
683  mMenu->addAction( colorAction );
684  connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsFontButton::setColor );
685  connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsFontButton::addRecentColor );
686  }
687 
688  mMenu->addSeparator();
689 
690  QAction *copyColorAction = new QAction( tr( "Copy Color" ), this );
691  mMenu->addAction( copyColorAction );
692  connect( copyColorAction, &QAction::triggered, this, &QgsFontButton::copyColor );
693 
694  QAction *pasteColorAction = new QAction( tr( "Paste Color" ), this );
695  //enable or disable paste action based on current clipboard contents. We always show the paste
696  //action, even if it's disabled, to give hint to the user that pasting colors is possible
697  QColor clipColor;
698  bool hasAlpha = false;
699  if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
700  {
701  pasteColorAction->setIcon( createColorIcon( clipColor ) );
702  }
703  else
704  {
705  pasteColorAction->setEnabled( false );
706  }
707  mMenu->addAction( pasteColorAction );
708  connect( pasteColorAction, &QAction::triggered, this, &QgsFontButton::pasteColor );
709  }
710 }
711 
712 void QgsFontButton::addRecentColor( const QColor &color )
713 {
715 }
716 
717 QFont QgsFontButton::currentFont() const
718 {
719  return mFont;
720 }
721 
723 {
724  return mLayer;
725 }
726 
728 {
729  mLayer = layer;
730 }
731 
733 {
734  mExpressionContextGenerator = generator;
735 }
736 
737 void QgsFontButton::setCurrentFont( const QFont &font )
738 {
739  mFont = font;
740  updatePreview();
741  emit changed();
742 }
743 
745 {
746  return mMode;
747 }
748 
750 {
751  mMode = mode;
752  updatePreview();
753 }
754 
755 bool QgsFontButton::formatFromMimeData( const QMimeData *mimeData, QgsTextFormat &resultFormat ) const
756 {
757  bool ok = false;
758  resultFormat = QgsTextFormat::fromMimeData( mimeData, &ok );
759  return ok;
760 }
761 
762 bool QgsFontButton::fontFromMimeData( const QMimeData *mimeData, QFont &resultFont ) const
763 {
764  bool ok = false;
765  resultFont = QgsFontUtils::fromMimeData( mimeData, &ok );
766  return ok;
767 }
768 
769 void QgsFontButton::changeEvent( QEvent *e )
770 {
771  if ( e->type() == QEvent::EnabledChange )
772  {
773  updatePreview();
774  }
775  QToolButton::changeEvent( e );
776 }
777 
778 void QgsFontButton::showEvent( QShowEvent *e )
779 {
780  updatePreview();
781  QToolButton::showEvent( e );
782 }
783 
784 void QgsFontButton::resizeEvent( QResizeEvent *event )
785 {
786  QToolButton::resizeEvent( event );
787  //recalculate icon size and redraw icon
788  mIconSize = QSize();
789  updatePreview();
790 }
791 
792 void QgsFontButton::updatePreview( const QColor &color, QgsTextFormat *format, QFont *font )
793 {
794  QgsTextFormat tempFormat;
795  QFont tempFont;
796 
797  if ( format )
798  tempFormat = *format;
799  else
800  tempFormat = mFormat;
801  if ( font )
802  tempFont = *font;
803  else
804  tempFont = mFont;
805 
806  if ( color.isValid() )
807  tempFormat.setColor( color );
808 
809  QSize currentIconSize;
810  //icon size is button size with a small margin
811  if ( menu() )
812  {
813  if ( !mIconSize.isValid() )
814  {
815  //calculate size of push button part of widget (ie, without the menu dropdown button part)
816  QStyleOptionToolButton opt;
817  initStyleOption( &opt );
818  QRect buttonSize = QApplication::style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton,
819  this );
820  //make sure height of icon looks good under different platforms
821 #ifdef Q_OS_WIN
822  mIconSize = QSize( buttonSize.width() - 10, height() - 6 );
823 #elif defined(Q_OS_MAC)
824  mIconSize = QSize( buttonSize.width() - 10, height() - 2 );
825 #else
826  mIconSize = QSize( buttonSize.width() - 10, height() - 12 );
827 #endif
828  }
829  currentIconSize = mIconSize;
830  }
831  else
832  {
833  //no menu
834 #ifdef Q_OS_WIN
835  currentIconSize = QSize( width() - 10, height() - 6 );
836 #else
837  currentIconSize = QSize( width() - 10, height() - 12 );
838 #endif
839  }
840 
841  if ( !currentIconSize.isValid() || currentIconSize.width() <= 0 || currentIconSize.height() <= 0 )
842  {
843  return;
844  }
845 
846  //create an icon pixmap
847  QPixmap pixmap( currentIconSize );
848  pixmap.fill( Qt::transparent );
849  QPainter p;
850  p.begin( &pixmap );
851  p.setRenderHint( QPainter::Antialiasing );
852  QRect rect( 0, 0, currentIconSize.width(), currentIconSize.height() );
853 
854  switch ( mMode )
855  {
856  case ModeTextRenderer:
857  {
858  QgsRenderContext context;
859  QgsMapToPixel newCoordXForm;
860  newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
861  context.setMapToPixel( newCoordXForm );
862 
863  context.setScaleFactor( QgsApplication::desktop()->logicalDpiX() / 25.4 );
864  context.setUseAdvancedEffects( true );
865  context.setPainter( &p );
866 
867  // slightly inset text to account for buffer/background
868  double xtrans = 0;
869  if ( tempFormat.buffer().enabled() )
870  xtrans = context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
871  if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
872  xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
873 
874  double ytrans = 0.0;
875  if ( tempFormat.buffer().enabled() )
876  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) );
877  if ( tempFormat.background().enabled() )
878  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
879 
880  QRectF textRect = rect;
881  textRect.setLeft( xtrans );
882  textRect.setWidth( textRect.width() - xtrans );
883  textRect.setTop( ytrans );
884  if ( textRect.height() > 300 )
885  textRect.setHeight( 300 );
886  if ( textRect.width() > 2000 )
887  textRect.setWidth( 2000 );
888 
889  QgsTextRenderer::drawText( textRect, 0, QgsTextRenderer::AlignLeft, QStringList() << text(),
890  context, tempFormat );
891  break;
892  }
893  case ModeQFont:
894  {
895  p.setBrush( Qt::NoBrush );
896  p.setPen( QColor( 0, 0, 0 ) );
897  p.setFont( tempFont );
898  QRectF textRect = rect;
899  textRect.setLeft( 2 );
900  p.drawText( textRect, Qt::AlignVCenter, text() );
901  break;
902  }
903 
904  }
905  p.end();
906  setIconSize( currentIconSize );
907  setIcon( pixmap );
908 }
909 
911 {
912  //copy color
913  QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mFormat.color() ) );
914 }
915 
917 {
918  QColor clipColor;
919  bool hasAlpha = false;
920  if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
921  {
922  //paste color
923  setColor( clipColor );
925  }
926 }
927 
928 void QgsFontButton::setDialogTitle( const QString &title )
929 {
930  mDialogTitle = title;
931 }
932 
933 QString QgsFontButton::dialogTitle() const
934 {
935  return mDialogTitle;
936 }
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:154
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:75
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.