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