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