QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  const auto recentFontFamilies { QgsFontUtils::recentFontFamilies() };
548  for ( const QString &family : recentFontFamilies )
549  {
550  QAction *fontAction = new QAction( family, recentFontMenu );
551  QFont f = fontAction->font();
552  f.setFamily( family );
553  fontAction->setFont( f );
554  fontAction->setToolTip( family );
555  recentFontMenu->addAction( fontAction );
556  if ( ( mMode == ModeTextRenderer && family == mFormat.font().family() )
557  || ( mMode == ModeQFont && family == mFont.family() ) )
558  {
559  fontAction->setCheckable( true );
560  fontAction->setChecked( true );
561  }
562  auto setFont = [this, family]
563  {
564  switch ( mMode )
565  {
566  case ModeTextRenderer:
567  {
568  QgsTextFormat newFormat = mFormat;
569  QFont f = newFormat.font();
570  f.setFamily( family );
571  newFormat.setFont( f );
572  setTextFormat( newFormat );
573  QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
574  break;
575  }
576  case ModeQFont:
577  {
578  QFont font = mFont;
579  font.setFamily( family );
580  setCurrentFont( font );
582  break;
583  }
584  }
585  };
586  connect( fontAction, &QAction::triggered, this, setFont );
587  }
588  mMenu->addMenu( recentFontMenu );
589 
590  QAction *configureAction = new QAction( tr( "Configure Format…" ), this );
591  mMenu->addAction( configureAction );
592  connect( configureAction, &QAction::triggered, this, &QgsFontButton::showSettingsDialog );
593 
594  QAction *copyFormatAction = new QAction( tr( "Copy Format" ), this );
595  mMenu->addAction( copyFormatAction );
596  connect( copyFormatAction, &QAction::triggered, this, &QgsFontButton::copyFormat );
597  QAction *pasteFormatAction = new QAction( tr( "Paste Format" ), this );
598  //enable or disable paste action based on current clipboard contents. We always show the paste
599  //action, even if it's disabled, to give hint to the user that pasting colors is possible
600  QgsTextFormat tempFormat;
601  QFont tempFont;
602  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
603  if ( mMode == ModeTextRenderer && formatFromMimeData( QApplication::clipboard()->mimeData(), tempFormat ) )
604  {
606  tempFormat.setSize( 14 );
607  pasteFormatAction->setIcon( createDragIcon( QSize( iconSize, iconSize ), &tempFormat ) );
608  }
609  else if ( mMode == ModeQFont && fontFromMimeData( QApplication::clipboard()->mimeData(), tempFont ) )
610  {
611  tempFont.setPointSize( 8 );
612  pasteFormatAction->setIcon( createDragIcon( QSize( iconSize, iconSize ), nullptr, &tempFont ) );
613  }
614  else
615  {
616  pasteFormatAction->setEnabled( false );
617  }
618  mMenu->addAction( pasteFormatAction );
619  connect( pasteFormatAction, &QAction::triggered, this, &QgsFontButton::pasteFormat );
620 
621  if ( mMode == ModeTextRenderer )
622  {
623  mMenu->addSeparator();
624 
625  QgsColorWheel *colorWheel = new QgsColorWheel( mMenu );
626  colorWheel->setColor( mFormat.color() );
627  QgsColorWidgetAction *colorAction = new QgsColorWidgetAction( colorWheel, mMenu, mMenu );
628  colorAction->setDismissOnColorSelection( false );
629  connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsFontButton::setColor );
630  mMenu->addAction( colorAction );
631 
633  QColor alphaColor = mFormat.color();
634  alphaColor.setAlphaF( mFormat.opacity() );
635  alphaRamp->setColor( alphaColor );
636  QgsColorWidgetAction *alphaAction = new QgsColorWidgetAction( alphaRamp, mMenu, mMenu );
637  alphaAction->setDismissOnColorSelection( false );
638  connect( alphaAction, &QgsColorWidgetAction::colorChanged, this, [ = ]( const QColor & color )
639  {
640  double opacity = color.alphaF();
641  mFormat.setOpacity( opacity );
642  updatePreview();
643  emit changed();
644  } );
645  connect( colorAction, &QgsColorWidgetAction::colorChanged, alphaRamp, [alphaRamp]( const QColor & color ) { alphaRamp->setColor( color, false ); }
646  );
647  mMenu->addAction( alphaAction );
648 
649  //get schemes with ShowInColorButtonMenu flag set
650  QList< QgsColorScheme * > schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorButtonMenu );
651  QList< QgsColorScheme * >::iterator it = schemeList.begin();
652  for ( ; it != schemeList.end(); ++it )
653  {
654  QgsColorSwatchGridAction *colorAction = new QgsColorSwatchGridAction( *it, mMenu, QStringLiteral( "labeling" ), this );
655  colorAction->setBaseColor( mFormat.color() );
656  mMenu->addAction( colorAction );
657  connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsFontButton::setColor );
658  connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsFontButton::addRecentColor );
659  }
660 
661  mMenu->addSeparator();
662 
663  QAction *copyColorAction = new QAction( tr( "Copy Color" ), this );
664  mMenu->addAction( copyColorAction );
665  connect( copyColorAction, &QAction::triggered, this, &QgsFontButton::copyColor );
666 
667  QAction *pasteColorAction = new QAction( tr( "Paste Color" ), this );
668  //enable or disable paste action based on current clipboard contents. We always show the paste
669  //action, even if it's disabled, to give hint to the user that pasting colors is possible
670  QColor clipColor;
671  bool hasAlpha = false;
672  if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
673  {
674  pasteColorAction->setIcon( createColorIcon( clipColor ) );
675  }
676  else
677  {
678  pasteColorAction->setEnabled( false );
679  }
680  mMenu->addAction( pasteColorAction );
681  connect( pasteColorAction, &QAction::triggered, this, &QgsFontButton::pasteColor );
682  }
683 }
684 
685 void QgsFontButton::addRecentColor( const QColor &color )
686 {
688 }
689 
690 QFont QgsFontButton::currentFont() const
691 {
692  return mFont;
693 }
694 
695 void QgsFontButton::setCurrentFont( const QFont &font )
696 {
697  mFont = font;
698  updatePreview();
699  emit changed();
700 }
701 
703 {
704  return mMode;
705 }
706 
708 {
709  mMode = mode;
710  updatePreview();
711 }
712 
713 bool QgsFontButton::formatFromMimeData( const QMimeData *mimeData, QgsTextFormat &resultFormat ) const
714 {
715  bool ok = false;
716  resultFormat = QgsTextFormat::fromMimeData( mimeData, &ok );
717  return ok;
718 }
719 
720 bool QgsFontButton::fontFromMimeData( const QMimeData *mimeData, QFont &resultFont ) const
721 {
722  bool ok = false;
723  resultFont = QgsFontUtils::fromMimeData( mimeData, &ok );
724  return ok;
725 }
726 
727 void QgsFontButton::changeEvent( QEvent *e )
728 {
729  if ( e->type() == QEvent::EnabledChange )
730  {
731  updatePreview();
732  }
733  QToolButton::changeEvent( e );
734 }
735 
736 void QgsFontButton::showEvent( QShowEvent *e )
737 {
738  updatePreview();
739  QToolButton::showEvent( e );
740 }
741 
742 void QgsFontButton::resizeEvent( QResizeEvent *event )
743 {
744  QToolButton::resizeEvent( event );
745  //recalculate icon size and redraw icon
746  mIconSize = QSize();
747  updatePreview();
748 }
749 
750 void QgsFontButton::updatePreview( const QColor &color, QgsTextFormat *format, QFont *font )
751 {
752  QgsTextFormat tempFormat;
753  QFont tempFont;
754 
755  if ( format )
756  tempFormat = *format;
757  else
758  tempFormat = mFormat;
759  if ( font )
760  tempFont = *font;
761  else
762  tempFont = mFont;
763 
764  if ( color.isValid() )
765  tempFormat.setColor( color );
766 
767  QSize currentIconSize;
768  //icon size is button size with a small margin
769  if ( menu() )
770  {
771  if ( !mIconSize.isValid() )
772  {
773  //calculate size of push button part of widget (ie, without the menu dropdown button part)
774  QStyleOptionToolButton opt;
775  initStyleOption( &opt );
776  QRect buttonSize = QApplication::style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton,
777  this );
778  //make sure height of icon looks good under different platforms
779 #ifdef Q_OS_WIN
780  mIconSize = QSize( buttonSize.width() - 10, height() - 6 );
781 #elif defined(Q_OS_MAC)
782  mIconSize = QSize( buttonSize.width() - 10, height() - 2 );
783 #else
784  mIconSize = QSize( buttonSize.width() - 10, height() - 12 );
785 #endif
786  }
787  currentIconSize = mIconSize;
788  }
789  else
790  {
791  //no menu
792 #ifdef Q_OS_WIN
793  currentIconSize = QSize( width() - 10, height() - 6 );
794 #else
795  currentIconSize = QSize( width() - 10, height() - 12 );
796 #endif
797  }
798 
799  if ( !currentIconSize.isValid() || currentIconSize.width() <= 0 || currentIconSize.height() <= 0 )
800  {
801  return;
802  }
803 
804  //create an icon pixmap
805  QPixmap pixmap( currentIconSize );
806  pixmap.fill( Qt::transparent );
807  QPainter p;
808  p.begin( &pixmap );
809  p.setRenderHint( QPainter::Antialiasing );
810  QRect rect( 0, 0, currentIconSize.width(), currentIconSize.height() );
811 
812  switch ( mMode )
813  {
814  case ModeTextRenderer:
815  {
816  QgsRenderContext context;
817  QgsMapToPixel newCoordXForm;
818  newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
819  context.setMapToPixel( newCoordXForm );
820 
821  context.setScaleFactor( QgsApplication::desktop()->logicalDpiX() / 25.4 );
822  context.setUseAdvancedEffects( true );
823  context.setPainter( &p );
824 
825  // slightly inset text to account for buffer/background
826  double xtrans = 0;
827  if ( tempFormat.buffer().enabled() )
828  xtrans = context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
829  if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
830  xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
831 
832  double ytrans = 0.0;
833  if ( tempFormat.buffer().enabled() )
834  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) );
835  if ( tempFormat.background().enabled() )
836  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
837 
838  QRectF textRect = rect;
839  textRect.setLeft( xtrans );
840  textRect.setWidth( textRect.width() - xtrans );
841  textRect.setTop( ytrans );
842  if ( textRect.height() > 300 )
843  textRect.setHeight( 300 );
844  if ( textRect.width() > 2000 )
845  textRect.setWidth( 2000 );
846 
847  QgsTextRenderer::drawText( textRect, 0, QgsTextRenderer::AlignLeft, QStringList() << text(),
848  context, tempFormat );
849  break;
850  }
851  case ModeQFont:
852  {
853  p.setBrush( Qt::NoBrush );
854  p.setPen( QColor( 0, 0, 0 ) );
855  p.setFont( tempFont );
856  QRectF textRect = rect;
857  textRect.setLeft( 2 );
858  p.drawText( textRect, Qt::AlignVCenter, text() );
859  break;
860  }
861 
862  }
863  p.end();
864  setIconSize( currentIconSize );
865  setIcon( pixmap );
866 }
867 
869 {
870  //copy color
871  QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mFormat.color() ) );
872 }
873 
875 {
876  QColor clipColor;
877  bool hasAlpha = false;
878  if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
879  {
880  //paste color
881  setColor( clipColor );
883  }
884 }
885 
886 void QgsFontButton::setDialogTitle( const QString &title )
887 {
888  mDialogTitle = title;
889 }
890 
891 QString QgsFontButton::dialogTitle() const
892 {
893  return mDialogTitle;
894 }
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.
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.
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.
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.