QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
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
503 ? fontSize * tempFormat->buffer().size() / 100
504 : context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() ) );
505 if ( tempFormat->background().enabled() )
506 ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat->background().size().height(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) );
507
508 QRectF textRect = rect;
509 textRect.setLeft( xtrans );
510 textRect.setWidth( textRect.width() - xtrans );
511 textRect.setTop( ytrans );
512 if ( textRect.height() > 300 )
513 textRect.setHeight( 300 );
514 if ( textRect.width() > 2000 )
515 textRect.setWidth( 2000 );
516
517 QgsTextRenderer::drawText( textRect, 0, Qgis::TextHorizontalAlignment::Center, QStringList() << tr( "Aa" ),
518 context, *tempFormat );
519 break;
520 }
521 case ModeQFont:
522 {
523 p.setBrush( Qt::NoBrush );
524 p.setPen( QColor( 0, 0, 0 ) );
525 p.setFont( *tempFont );
526 QRectF textRect = rect;
527 textRect.setLeft( 2 );
528 p.drawText( textRect, Qt::AlignVCenter, tr( "Aa" ) );
529 break;
530 }
531 }
532
533 p.end();
534 return pixmap;
535}
536
537void QgsFontButton::prepareMenu()
538{
539 //we need to tear down and rebuild this menu every time it is shown. Otherwise the space allocated to any
540 //QgsColorSwatchGridAction is not recalculated by Qt and the swatch grid may not be the correct size
541 //for the number of colors shown in the grid. Note that we MUST refresh color swatch grids every time this
542 //menu is opened, otherwise color schemes like the recent color scheme grid are meaningless
543 mMenu->clear();
544
545 if ( mMode == ModeTextRenderer && mShowNoFormat )
546 {
547 mNullFormatAction = new QAction( mNullFormatString, this );
548 mMenu->addAction( mNullFormatAction );
549 connect( mNullFormatAction, &QAction::triggered, this, &QgsFontButton::setToNullFormat );
550 if ( !mFormat.isValid() )
551 {
552 mNullFormatAction->setCheckable( true );
553 mNullFormatAction->setChecked( true );
554 }
555 }
556
557 QWidgetAction *sizeAction = new QWidgetAction( mMenu );
558 QWidget *sizeWidget = new QWidget();
559 QVBoxLayout *sizeLayout = new QVBoxLayout();
560 sizeLayout->setContentsMargins( 0, 0, 0, 3 );
561 sizeLayout->setSpacing( 2 );
562
563 QString fontHeaderLabel;
564 switch ( mMode )
565 {
566 case ModeTextRenderer:
567 fontHeaderLabel = tr( "Font size (%1)" ).arg( QgsUnitTypes::toString( mFormat.sizeUnit() ) );
568 break;
569
570 case ModeQFont:
571 fontHeaderLabel = tr( "Font size (pt)" );
572 break;
573 }
574
575 QgsMenuHeader *sizeLabel = new QgsMenuHeader( fontHeaderLabel );
576 sizeLayout->addWidget( sizeLabel );
577
578 QgsDoubleSpinBox *sizeSpin = new QgsDoubleSpinBox( nullptr );
579 sizeSpin->setDecimals( 4 );
580 sizeSpin->setMaximum( 1e+9 );
581 sizeSpin->setShowClearButton( false );
582 sizeSpin->setValue( mMode == ModeTextRenderer ? mFormat.size() : mFont.pointSizeF() );
583 connect( sizeSpin, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ),
584 this, [ = ]( double value )
585 {
586 switch ( mMode )
587 {
588 case ModeTextRenderer:
589 if ( mNullFormatAction )
590 mNullFormatAction->setChecked( false );
591 mFormat.setSize( value );
592 break;
593 case ModeQFont:
594 mFont.setPointSizeF( value );
595 break;
596 }
597 updatePreview();
598 emit changed();
599 } );
600 QHBoxLayout *spinLayout = new QHBoxLayout();
601 spinLayout->setContentsMargins( 4, 0, 4, 0 );
602 spinLayout->addWidget( sizeSpin );
603 sizeLayout->addLayout( spinLayout );
604 sizeWidget->setLayout( sizeLayout );
605 sizeAction->setDefaultWidget( sizeWidget );
606 sizeWidget->setFocusProxy( sizeSpin );
607 sizeWidget->setFocusPolicy( Qt::StrongFocus );
608 mMenu->addAction( sizeAction );
609
610 QMenu *recentFontMenu = new QMenu( tr( "Recent Fonts" ), mMenu );
611 const auto recentFontFamilies { QgsFontUtils::recentFontFamilies() };
612 for ( const QString &family : recentFontFamilies )
613 {
614 QAction *fontAction = new QAction( family, recentFontMenu );
615 QFont f = fontAction->font();
616 QgsFontUtils::setFontFamily( f, family );
617 fontAction->setFont( f );
618 fontAction->setToolTip( family );
619 recentFontMenu->addAction( fontAction );
620 if ( ( mMode == ModeTextRenderer && family == mFormat.font().family() )
621 || ( mMode == ModeQFont && family == mFont.family() ) )
622 {
623 fontAction->setCheckable( true );
624 fontAction->setChecked( true );
625 }
626 auto setFont = [this, family]
627 {
628 switch ( mMode )
629 {
630 case ModeTextRenderer:
631 {
632 QgsTextFormat newFormat = mFormat;
633 QFont f = newFormat.font();
634 QgsFontUtils::setFontFamily( f, family );
635 newFormat.setFont( f );
636 setTextFormat( newFormat );
637 QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
638 break;
639 }
640 case ModeQFont:
641 {
642 QFont font = mFont;
643 QgsFontUtils::setFontFamily( font, family );
644 setCurrentFont( font );
646 break;
647 }
648 }
649 };
650 connect( fontAction, &QAction::triggered, this, setFont );
651 }
652 mMenu->addMenu( recentFontMenu );
653
654 QAction *configureAction = new QAction( tr( "Configure Format…" ), this );
655 mMenu->addAction( configureAction );
656 connect( configureAction, &QAction::triggered, this, &QgsFontButton::showSettingsDialog );
657
658 QAction *copyFormatAction = new QAction( tr( "Copy Format" ), this );
659 mMenu->addAction( copyFormatAction );
660 connect( copyFormatAction, &QAction::triggered, this, &QgsFontButton::copyFormat );
661 QAction *pasteFormatAction = new QAction( tr( "Paste Format" ), this );
662 //enable or disable paste action based on current clipboard contents. We always show the paste
663 //action, even if it's disabled, to give hint to the user that pasting colors is possible
664 QgsTextFormat tempFormat;
665 QFont tempFont;
666 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
667 if ( mMode == ModeTextRenderer && formatFromMimeData( QApplication::clipboard()->mimeData(), tempFormat ) )
668 {
670 tempFormat.setSize( 14 );
671 pasteFormatAction->setIcon( createDragIcon( QSize( iconSize, iconSize ), &tempFormat ) );
672 }
673 else if ( mMode == ModeQFont && fontFromMimeData( QApplication::clipboard()->mimeData(), tempFont ) )
674 {
675 tempFont.setPointSize( 8 );
676 pasteFormatAction->setIcon( createDragIcon( QSize( iconSize, iconSize ), nullptr, &tempFont ) );
677 }
678 else
679 {
680 pasteFormatAction->setEnabled( false );
681 }
682 mMenu->addAction( pasteFormatAction );
683 connect( pasteFormatAction, &QAction::triggered, this, &QgsFontButton::pasteFormat );
684
685 if ( mMode == ModeTextRenderer )
686 {
687 mMenu->addSeparator();
688
689 QgsColorWheel *colorWheel = new QgsColorWheel( mMenu );
690 colorWheel->setColor( mFormat.color() );
691 QgsColorWidgetAction *colorAction = new QgsColorWidgetAction( colorWheel, mMenu, mMenu );
692 colorAction->setDismissOnColorSelection( false );
693 connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsFontButton::setColor );
694 mMenu->addAction( colorAction );
695
697 QColor alphaColor = mFormat.color();
698 alphaColor.setAlphaF( mFormat.opacity() );
699 alphaRamp->setColor( alphaColor );
700 QgsColorWidgetAction *alphaAction = new QgsColorWidgetAction( alphaRamp, mMenu, mMenu );
701 alphaAction->setDismissOnColorSelection( false );
702 connect( alphaAction, &QgsColorWidgetAction::colorChanged, this, [ = ]( const QColor & color )
703 {
704 const double opacity = color.alphaF();
705 mFormat.setOpacity( opacity );
706 updatePreview();
707 if ( mNullFormatAction )
708 mNullFormatAction->setChecked( false );
709 emit changed();
710 } );
711 connect( colorAction, &QgsColorWidgetAction::colorChanged, alphaRamp, [alphaRamp]( const QColor & color ) { alphaRamp->setColor( color, false ); }
712 );
713 mMenu->addAction( alphaAction );
714
715 //get schemes with ShowInColorButtonMenu flag set
716 QList< QgsColorScheme * > schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorButtonMenu );
717 QList< QgsColorScheme * >::iterator it = schemeList.begin();
718 for ( ; it != schemeList.end(); ++it )
719 {
720 QgsColorSwatchGridAction *colorAction = new QgsColorSwatchGridAction( *it, mMenu, QStringLiteral( "labeling" ), this );
721 colorAction->setBaseColor( mFormat.color() );
722 mMenu->addAction( colorAction );
724 connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsFontButton::addRecentColor );
725 }
726
727 mMenu->addSeparator();
728
729 QAction *copyColorAction = new QAction( tr( "Copy Color" ), this );
730 mMenu->addAction( copyColorAction );
731 connect( copyColorAction, &QAction::triggered, this, &QgsFontButton::copyColor );
732
733 QAction *pasteColorAction = new QAction( tr( "Paste Color" ), this );
734 //enable or disable paste action based on current clipboard contents. We always show the paste
735 //action, even if it's disabled, to give hint to the user that pasting colors is possible
736 QColor clipColor;
737 bool hasAlpha = false;
738 if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
739 {
740 pasteColorAction->setIcon( createColorIcon( clipColor ) );
741 }
742 else
743 {
744 pasteColorAction->setEnabled( false );
745 }
746 mMenu->addAction( pasteColorAction );
747 connect( pasteColorAction, &QAction::triggered, this, &QgsFontButton::pasteColor );
748 }
749}
750
751void QgsFontButton::addRecentColor( const QColor &color )
752{
754}
755
757{
758 return mFont;
759}
760
762{
763 return mLayer;
764}
765
767{
768 mLayer = layer;
769}
770
772{
773 mExpressionContextGenerator = generator;
774}
775
776void QgsFontButton::setCurrentFont( const QFont &font )
777{
778 mFont = font;
779 updatePreview();
780 emit changed();
781}
782
784{
785 return mMode;
786}
787
789{
790 mMode = mode;
791 updatePreview();
792}
793
794bool QgsFontButton::formatFromMimeData( const QMimeData *mimeData, QgsTextFormat &resultFormat ) const
795{
796 bool ok = false;
797 resultFormat = QgsTextFormat::fromMimeData( mimeData, &ok );
798 return ok;
799}
800
801bool QgsFontButton::fontFromMimeData( const QMimeData *mimeData, QFont &resultFont ) const
802{
803 bool ok = false;
804 resultFont = QgsFontUtils::fromMimeData( mimeData, &ok );
805 return ok;
806}
807
809{
810 if ( e->type() == QEvent::EnabledChange )
811 {
812 updatePreview();
813 }
814 QToolButton::changeEvent( e );
815}
816
817void QgsFontButton::showEvent( QShowEvent *e )
818{
819 updatePreview();
820 QToolButton::showEvent( e );
821}
822
823void QgsFontButton::resizeEvent( QResizeEvent *event )
824{
825 QToolButton::resizeEvent( event );
826 //recalculate icon size and redraw icon
827 mIconSize = QSize();
828 updatePreview();
829}
830
831void QgsFontButton::updatePreview( const QColor &color, QgsTextFormat *format, QFont *font )
832{
833 if ( mShowNoFormat && !mFormat.isValid() )
834 {
835 setIcon( QPixmap() );
836 return;
837 }
838
839 QgsTextFormat tempFormat;
840 QFont tempFont;
841
842 if ( format )
843 tempFormat = *format;
844 else
845 tempFormat = mFormat;
846 if ( font )
847 tempFont = *font;
848 else
849 tempFont = mFont;
850
851 if ( color.isValid() )
852 tempFormat.setColor( color );
853
854 // always show font previews based on the standard font size for buttons. Otherwise large/small text
855 // will be unreadable, making the button very non-user-friendly.
856 // Note that we take away a few points here, as the text in these buttons is rendered with a fairly large
857 // margin and we'd like to avoid cropping the text.
858 tempFormat.setSize( QToolButton::font().pointSizeF() - 2 );
860
861 QSize currentIconSize;
862 //icon size is button size with a small margin
863 if ( menu() )
864 {
865 if ( !mIconSize.isValid() )
866 {
867 //calculate size of push button part of widget (ie, without the menu dropdown button part)
868 QStyleOptionToolButton opt;
869 initStyleOption( &opt );
870 const QRect buttonSize = QApplication::style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton,
871 this );
872 //make sure height of icon looks good under different platforms
873#ifdef Q_OS_WIN
874 mIconSize = QSize( buttonSize.width() - 10, height() - 6 );
875#elif defined(Q_OS_MAC)
876 mIconSize = QSize( buttonSize.width() - 10, height() - 2 );
877#else
878 mIconSize = QSize( buttonSize.width() - 10, height() - 12 );
879#endif
880 }
881 currentIconSize = mIconSize;
882 }
883 else
884 {
885 //no menu
886#ifdef Q_OS_WIN
887 currentIconSize = QSize( width() - 10, height() - 6 );
888#else
889 currentIconSize = QSize( width() - 10, height() - 12 );
890#endif
891 }
892
893 if ( !currentIconSize.isValid() || currentIconSize.width() <= 0 || currentIconSize.height() <= 0 )
894 {
895 return;
896 }
897
898 //create an icon pixmap
899 QPixmap pixmap( currentIconSize * devicePixelRatioF() );
900 pixmap.fill( Qt::transparent );
901 pixmap.setDevicePixelRatio( devicePixelRatioF() );
902 QPainter p;
903 p.begin( &pixmap );
904 p.setRenderHint( QPainter::Antialiasing );
905 const QRect rect( 0, 0, currentIconSize.width(), currentIconSize.height() );
906
907 switch ( mMode )
908 {
909 case ModeTextRenderer:
910 {
911 QgsRenderContext context;
912 QgsMapToPixel newCoordXForm;
913 newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
914 context.setMapToPixel( newCoordXForm );
915
916 context.setScaleFactor( mScreenHelper->screenDpi() / 25.4 );
917 context.setDevicePixelRatio( devicePixelRatioF() );
918 context.setUseAdvancedEffects( true );
920 context.setPainter( &p );
921
922 // slightly inset text to account for buffer/background
923 const double fontSize = context.convertToPainterUnits( tempFormat.size(), tempFormat.sizeUnit(), tempFormat.sizeMapUnitScale() );
924 double xtrans = 0;
925 if ( tempFormat.buffer().enabled() )
926 xtrans = tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
927 ? fontSize * tempFormat.buffer().size() / 100
928 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
929 if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
930 xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
931
932 double ytrans = 0.0;
933 if ( tempFormat.buffer().enabled() )
934 ytrans = std::max( ytrans, tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
935 ? fontSize * tempFormat.buffer().size() / 100
936 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) );
937 if ( tempFormat.background().enabled() )
938 ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
939
940 QRectF textRect = rect;
941 textRect.setLeft( xtrans );
942 textRect.setWidth( textRect.width() - xtrans );
943 textRect.setTop( ytrans );
944 if ( textRect.height() > 300 )
945 textRect.setHeight( 300 );
946 if ( textRect.width() > 2000 )
947 textRect.setWidth( 2000 );
948
949 QgsTextRenderer::drawText( textRect, 0, Qgis::TextHorizontalAlignment::Left, QStringList() << text(),
950 context, tempFormat );
951 break;
952 }
953 case ModeQFont:
954 {
955 p.setBrush( Qt::NoBrush );
956 p.setPen( QColor( 0, 0, 0 ) );
957 p.setFont( tempFont );
958 QRectF textRect = rect;
959 textRect.setLeft( 2 );
960 p.drawText( textRect, Qt::AlignVCenter, text() );
961 break;
962 }
963
964 }
965 p.end();
966 setIconSize( currentIconSize );
967 setIcon( pixmap );
968}
969
971{
972 //copy color
973 QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mFormat.color() ) );
974}
975
977{
978 QColor clipColor;
979 bool hasAlpha = false;
980 if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
981 {
982 //paste color
983 setColor( clipColor );
985 }
986}
987
988void QgsFontButton::setDialogTitle( const QString &title )
989{
990 mDialogTitle = title;
991}
992
994{
995 return mDialogTitle;
996}
@ 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:5627
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.