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