QGIS API Documentation 3.99.0-Master (26c88405ac0)
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
18#include "qgsapplication.h"
19#include "qgscolorscheme.h"
21#include "qgscolorswatchgrid.h"
22#include "qgscolorwidgets.h"
23#include "qgsdoublespinbox.h"
25#include "qgsfontutils.h"
26#include "qgsmapcanvas.h"
27#include "qgsmenuheader.h"
28#include "qgsscreenhelper.h"
29#include "qgssymbollayerutils.h"
30#include "qgstextformatwidget.h"
31#include "qgstextrenderer.h"
32#include "qgsunittypes.h"
33#include "qgsvectorlayer.h"
34
35#include <QClipboard>
36#include <QDrag>
37#include <QMenu>
38#include <QToolTip>
39
40#include "moc_qgsfontbutton.cpp"
41
42QgsFontButton::QgsFontButton( QWidget *parent, const QString &dialogTitle )
43 : QToolButton( parent )
44 , mDialogTitle( dialogTitle.isEmpty() ? tr( "Text Format" ) : dialogTitle )
45 , mNullFormatString( tr( "No Format" ) )
46{
47 setText( tr( "Font" ) );
48
49 setAcceptDrops( true );
50 connect( this, &QAbstractButton::clicked, this, &QgsFontButton::showSettingsDialog );
51
52 //setup dropdown menu
53 mMenu = new QMenu( this );
54 connect( mMenu, &QMenu::aboutToShow, this, &QgsFontButton::prepareMenu );
55 setMenu( mMenu );
56 setPopupMode( QToolButton::MenuButtonPopup );
57
58 //make sure height of button looks good under different platforms
59 const QSize size = QToolButton::minimumSizeHint();
60 const int fontHeight = Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 1.4;
61 const int minWidth = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 20;
62 mSizeHint = QSize( std::max( minWidth, size.width() ), std::max( size.height(), fontHeight ) );
63
64 mScreenHelper = new QgsScreenHelper( this );
65 connect( mScreenHelper, &QgsScreenHelper::screenDpiChanged, this, [this] { updatePreview(); } );
66}
67
69{
70 return mSizeHint;
71}
72
74{
75 return mSizeHint;
76}
77
78void QgsFontButton::showSettingsDialog()
79{
80 switch ( mMode )
81 {
83 {
85 if ( mExpressionContextGenerator )
86 context = mExpressionContextGenerator->createExpressionContext();
87 else
88 {
90 }
91
92 QgsSymbolWidgetContext symbolContext;
93 symbolContext.setExpressionContext( &context );
94 symbolContext.setMapCanvas( mMapCanvas );
95 symbolContext.setMessageBar( mMessageBar );
96
97 QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this );
98 if ( panel && panel->dockMode() )
99 {
100 mActivePanel = new QgsTextFormatPanelWidget( mFormat, mMapCanvas, this, mLayer.data() );
101 mActivePanel->setPanelTitle( mDialogTitle );
102 mActivePanel->setContext( symbolContext );
103
104 connect( mActivePanel, &QgsTextFormatPanelWidget::widgetChanged, this, [this] {
105 setTextFormat( mActivePanel->format() );
106 QgsFontUtils::addRecentFontFamily( mActivePanel->format().font().family() );
107 } );
108 panel->openPanel( mActivePanel );
109 return;
110 }
111
112 QgsTextFormatDialog dialog( mFormat, mMapCanvas, this, QgsGuiUtils::ModalDialogFlags, mLayer.data() );
113 dialog.setWindowTitle( mDialogTitle );
114 dialog.setContext( symbolContext );
115 if ( dialog.exec() )
116 {
117 setTextFormat( dialog.format() );
118 QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
119 }
120 break;
121 }
122
123 case ModeQFont:
124 {
125 bool ok;
126 const QFont newFont = QgsGuiUtils::getFont( ok, mFont, mDialogTitle );
127 if ( ok )
128 {
129 QgsFontUtils::addRecentFontFamily( newFont.family() );
130 setCurrentFont( newFont );
131 }
132 break;
133 }
134 }
135
136 // reactivate button's window
137 activateWindow();
138 raise();
139}
140
142{
143 return mMapCanvas;
144}
145
147{
148 mMapCanvas = mapCanvas;
149}
150
152{
153 mMessageBar = bar;
154}
155
157{
158 return mMessageBar;
159}
160
162{
163 if ( mActivePanel && !format.isValid() )
164 mActivePanel->acceptPanel();
165
166 mFormat = format;
167 updatePreview();
168
169 if ( mActivePanel && format.isValid() )
170 mActivePanel->setFormat( format );
171 emit changed();
172}
173
175{
176 mFormat = QgsTextFormat();
177 updatePreview();
178 emit changed();
179}
180
181void QgsFontButton::setColor( const QColor &color )
182{
183 QColor opaque = color;
184 opaque.setAlphaF( 1.0 );
185
186 if ( mNullFormatAction )
187 mNullFormatAction->setChecked( false );
188
189 if ( mFormat.color() != opaque )
190 {
191 mFormat.setColor( opaque );
192 updatePreview();
193 emit changed();
194 }
195}
196
198{
199 switch ( mMode )
200 {
201 case ModeTextRenderer:
202 QApplication::clipboard()->setMimeData( mFormat.toMimeData() );
203 break;
204
205 case ModeQFont:
206 QApplication::clipboard()->setMimeData( QgsFontUtils::toMimeData( mFont ) );
207 break;
208 }
209}
210
212{
213 QgsTextFormat tempFormat;
214 QFont font;
215 if ( mMode == ModeTextRenderer && formatFromMimeData( QApplication::clipboard()->mimeData(), tempFormat ) )
216 {
217 setTextFormat( tempFormat );
218 QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
219 }
220 else if ( mMode == ModeQFont && fontFromMimeData( QApplication::clipboard()->mimeData(), font ) )
221 {
222 QgsFontUtils::addRecentFontFamily( font.family() );
223 setCurrentFont( font );
224 }
225}
226
227bool QgsFontButton::event( QEvent *e )
228{
229 if ( e->type() == QEvent::ToolTip )
230 {
231 QHelpEvent *helpEvent = static_cast<QHelpEvent *>( e );
232 QString toolTip;
233 double fontSize = 0.0;
234 switch ( mMode )
235 {
236 case ModeTextRenderer:
237 fontSize = mFormat.size();
238 break;
239
240 case ModeQFont:
241 fontSize = mFont.pointSizeF();
242 break;
243 }
244 toolTip = QStringLiteral( "<b>%1</b><br>%2<br>Size: %3" ).arg( text(), mMode == ModeTextRenderer ? mFormat.font().family() : mFont.family() ).arg( fontSize );
245 QToolTip::showText( helpEvent->globalPos(), toolTip );
246 }
247 return QToolButton::event( e );
248}
249
250void QgsFontButton::mousePressEvent( QMouseEvent *e )
251{
252 if ( e->button() == Qt::RightButton )
253 {
254 QToolButton::showMenu();
255 return;
256 }
257 else if ( e->button() == Qt::LeftButton )
258 {
259 mDragStartPosition = e->pos();
260 }
261 QToolButton::mousePressEvent( e );
262}
263
264void QgsFontButton::mouseMoveEvent( QMouseEvent *e )
265{
266 //handle dragging fonts from button
267
268 if ( !( e->buttons() & Qt::LeftButton ) )
269 {
270 //left button not depressed, so not a drag
271 QToolButton::mouseMoveEvent( e );
272 return;
273 }
274
275 if ( ( e->pos() - mDragStartPosition ).manhattanLength() < QApplication::startDragDistance() )
276 {
277 //mouse not moved, so not a drag
278 QToolButton::mouseMoveEvent( e );
279 return;
280 }
281
282 //user is dragging font
283 QDrag *drag = new QDrag( this );
284 switch ( mMode )
285 {
286 case ModeTextRenderer:
287 drag->setMimeData( mFormat.toMimeData() );
288 break;
289
290 case ModeQFont:
291 drag->setMimeData( QgsFontUtils::toMimeData( mFont ) );
292 break;
293 }
294 const int iconSize = QgsGuiUtils::scaleIconSize( 50 );
295 drag->setPixmap( createDragIcon( QSize( iconSize, iconSize ) ) );
296 drag->exec( Qt::CopyAction );
297 setDown( false );
298}
299
300bool QgsFontButton::colorFromMimeData( const QMimeData *mimeData, QColor &resultColor, bool &hasAlpha )
301{
302 hasAlpha = false;
303 const QColor mimeColor = QgsSymbolLayerUtils::colorFromMimeData( mimeData, hasAlpha );
304
305 if ( mimeColor.isValid() )
306 {
307 resultColor = mimeColor;
308 return true;
309 }
310
311 //could not get color from mime data
312 return false;
313}
314
315void QgsFontButton::dragEnterEvent( QDragEnterEvent *e )
316{
317 //is dragged data valid font data?
318 QColor mimeColor;
319 QgsTextFormat format;
320 QFont font;
321 bool hasAlpha = false;
322
323 if ( mMode == ModeTextRenderer && formatFromMimeData( e->mimeData(), format ) )
324 {
325 e->acceptProposedAction();
326 updatePreview( QColor(), &format );
327 }
328 else if ( mMode == ModeQFont && fontFromMimeData( e->mimeData(), font ) )
329 {
330 e->acceptProposedAction();
331 updatePreview( QColor(), nullptr, &font );
332 }
333 else if ( mMode == ModeTextRenderer && colorFromMimeData( e->mimeData(), mimeColor, hasAlpha ) )
334 {
335 //if so, we accept the drag, and temporarily change the button's color
336 //to match the dragged color. This gives immediate feedback to the user
337 //that colors can be dropped here
338 e->acceptProposedAction();
339 updatePreview( mimeColor );
340 }
341}
342
343void QgsFontButton::dragLeaveEvent( QDragLeaveEvent *e )
344{
345 Q_UNUSED( e )
346 //reset button color
347 updatePreview();
348}
349
350void QgsFontButton::dropEvent( QDropEvent *e )
351{
352 //is dropped data valid format data?
353 QColor mimeColor;
354 QgsTextFormat format;
355 QFont font;
356 bool hasAlpha = false;
357 if ( mMode == ModeTextRenderer && formatFromMimeData( e->mimeData(), format ) )
358 {
359 setTextFormat( format );
360 QgsFontUtils::addRecentFontFamily( mFormat.font().family() );
361 return;
362 }
363 else if ( mMode == ModeQFont && fontFromMimeData( e->mimeData(), font ) )
364 {
365 QgsFontUtils::addRecentFontFamily( font.family() );
366 setCurrentFont( font );
367 return;
368 }
369 else if ( mMode == ModeTextRenderer && colorFromMimeData( e->mimeData(), mimeColor, hasAlpha ) )
370 {
371 //accept drop and set new color
372 e->acceptProposedAction();
373
374 if ( hasAlpha )
375 {
376 mFormat.setOpacity( mimeColor.alphaF() );
377 }
378 mimeColor.setAlphaF( 1.0 );
379 mFormat.setColor( mimeColor );
381 updatePreview();
382 emit changed();
383 }
384 updatePreview();
385}
386
388{
389 double size = 0;
390 switch ( mMode )
391 {
392 case ModeTextRenderer:
393 size = mFormat.size();
394 break;
395
396 case ModeQFont:
397 size = mFont.pointSizeF();
398 break;
399 }
400
401 const double increment = ( event->modifiers() & Qt::ControlModifier ) ? 0.1 : 1;
402 if ( event->angleDelta().y() > 0 )
403 {
404 size += increment;
405 }
406 else
407 {
408 size -= increment;
409 }
410 size = std::max( size, 1.0 );
411
412 switch ( mMode )
413 {
414 case ModeTextRenderer:
415 {
416 QgsTextFormat newFormat = mFormat;
417 newFormat.setSize( size );
418 setTextFormat( newFormat );
419 break;
420 }
421
422 case ModeQFont:
423 {
424 QFont newFont = mFont;
425 newFont.setPointSizeF( size );
426 setCurrentFont( newFont );
427 break;
428 }
429 }
430
431 event->accept();
432}
433
434QPixmap QgsFontButton::createColorIcon( const QColor &color ) const
435{
436 //create an icon pixmap
437 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
438 QPixmap pixmap( iconSize, iconSize );
439 pixmap.fill( Qt::transparent );
440
441 QPainter p;
442 p.begin( &pixmap );
443
444 //draw color over pattern
445 p.setBrush( QBrush( color ) );
446
447 //draw border
448 p.setPen( QColor( 197, 197, 197 ) );
449 p.drawRect( 0, 0, iconSize - 1, iconSize - 1 );
450 p.end();
451 return pixmap;
452}
453
454QPixmap QgsFontButton::createDragIcon( QSize size, const QgsTextFormat *tempFormat, const QFont *tempFont ) const
455{
456 if ( !tempFormat )
457 tempFormat = &mFormat;
458 if ( !tempFont )
459 tempFont = &mFont;
460
461 //create an icon pixmap
462 QPixmap pixmap( size.width(), size.height() );
463 pixmap.fill( Qt::transparent );
464 QPainter p;
465 p.begin( &pixmap );
466 p.setRenderHint( QPainter::Antialiasing );
467 const QRect rect( 0, 0, size.width(), size.height() );
468
469 if ( mMode == ModeQFont || tempFormat->color().lightnessF() < 0.7 )
470 {
471 p.setBrush( QBrush( QColor( 255, 255, 255 ) ) );
472 p.setPen( QPen( QColor( 150, 150, 150 ), 0 ) );
473 }
474 else
475 {
476 p.setBrush( QBrush( QColor( 0, 0, 0 ) ) );
477 p.setPen( QPen( QColor( 100, 100, 100 ), 0 ) );
478 }
479 p.drawRect( rect );
480 p.setBrush( Qt::NoBrush );
481 p.setPen( Qt::NoPen );
482
483 switch ( mMode )
484 {
485 case ModeTextRenderer:
486 {
487 QgsRenderContext context;
488 QgsMapToPixel newCoordXForm;
489 newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
490 context.setMapToPixel( newCoordXForm );
491
492 context.setScaleFactor( mScreenHelper->screenDpi() / 25.4 );
494 context.setPainter( &p );
495
496 // slightly inset text to account for buffer/background
497 const double fontSize = context.convertToPainterUnits( tempFormat->size(), tempFormat->sizeUnit(), tempFormat->sizeMapUnitScale() );
498 double xtrans = 0;
499 if ( tempFormat->buffer().enabled() )
500 xtrans = tempFormat->buffer().sizeUnit() == Qgis::RenderUnit::Percentage
501 ? fontSize * tempFormat->buffer().size() / 100
502 : context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() );
503 if ( tempFormat->background().enabled() && tempFormat->background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
504 xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat->background().size().width(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) );
505
506 double ytrans = 0.0;
507 if ( tempFormat->buffer().enabled() )
508 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() ) );
509 if ( tempFormat->background().enabled() )
510 ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat->background().size().height(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) );
511
512 QRectF textRect = rect;
513 textRect.setLeft( xtrans );
514 textRect.setWidth( textRect.width() - xtrans );
515 textRect.setTop( ytrans );
516 if ( textRect.height() > 300 )
517 textRect.setHeight( 300 );
518 if ( textRect.width() > 2000 )
519 textRect.setWidth( 2000 );
520
521 QgsTextRenderer::drawText( textRect, 0, Qgis::TextHorizontalAlignment::Center, QStringList() << tr( "Aa" ), context, *tempFormat );
522 break;
523 }
524 case ModeQFont:
525 {
526 p.setBrush( Qt::NoBrush );
527 p.setPen( QColor( 0, 0, 0 ) );
528 p.setFont( *tempFont );
529 QRectF textRect = rect;
530 textRect.setLeft( 2 );
531 p.drawText( textRect, Qt::AlignVCenter, tr( "Aa" ) );
532 break;
533 }
534 }
535
536 p.end();
537 return pixmap;
538}
539
540void QgsFontButton::prepareMenu()
541{
542 //we need to tear down and rebuild this menu every time it is shown. Otherwise the space allocated to any
543 //QgsColorSwatchGridAction is not recalculated by Qt and the swatch grid may not be the correct size
544 //for the number of colors shown in the grid. Note that we MUST refresh color swatch grids every time this
545 //menu is opened, otherwise color schemes like the recent color scheme grid are meaningless
546 mMenu->clear();
547
548 if ( mMode == ModeTextRenderer && mShowNoFormat )
549 {
550 mNullFormatAction = new QAction( mNullFormatString, this );
551 mMenu->addAction( mNullFormatAction );
552 connect( mNullFormatAction, &QAction::triggered, this, &QgsFontButton::setToNullFormat );
553 if ( !mFormat.isValid() )
554 {
555 mNullFormatAction->setCheckable( true );
556 mNullFormatAction->setChecked( true );
557 }
558 }
559
560 QWidgetAction *sizeAction = new QWidgetAction( mMenu );
561 QWidget *sizeWidget = new QWidget();
562 QVBoxLayout *sizeLayout = new QVBoxLayout();
563 sizeLayout->setContentsMargins( 0, 0, 0, 3 );
564 sizeLayout->setSpacing( 2 );
565
566 QString fontHeaderLabel;
567 switch ( mMode )
568 {
569 case ModeTextRenderer:
570 fontHeaderLabel = tr( "Font size (%1)" ).arg( QgsUnitTypes::toString( mFormat.sizeUnit() ) );
571 break;
572
573 case ModeQFont:
574 fontHeaderLabel = tr( "Font size (pt)" );
575 break;
576 }
577
578 QgsMenuHeader *sizeLabel = new QgsMenuHeader( fontHeaderLabel );
579 sizeLayout->addWidget( sizeLabel );
580
581 QgsDoubleSpinBox *sizeSpin = new QgsDoubleSpinBox( nullptr );
582 sizeSpin->setDecimals( 4 );
583 sizeSpin->setMaximum( 1e+9 );
584 sizeSpin->setShowClearButton( false );
585 sizeSpin->setValue( mMode == ModeTextRenderer ? mFormat.size() : mFont.pointSizeF() );
586 connect( sizeSpin, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double value ) {
587 switch ( mMode )
588 {
589 case ModeTextRenderer:
590 if ( mNullFormatAction )
591 mNullFormatAction->setChecked( false );
592 mFormat.setSize( value );
593 break;
594 case ModeQFont:
595 mFont.setPointSizeF( value );
596 break;
597 }
598 updatePreview();
599 emit changed();
600 } );
601 QHBoxLayout *spinLayout = new QHBoxLayout();
602 spinLayout->setContentsMargins( 4, 0, 4, 0 );
603 spinLayout->addWidget( sizeSpin );
604 sizeLayout->addLayout( spinLayout );
605 sizeWidget->setLayout( sizeLayout );
606 sizeAction->setDefaultWidget( sizeWidget );
607 sizeWidget->setFocusProxy( sizeSpin );
608 sizeWidget->setFocusPolicy( Qt::StrongFocus );
609 mMenu->addAction( sizeAction );
610
611 QMenu *recentFontMenu = new QMenu( tr( "Recent Fonts" ), mMenu );
612 const auto recentFontFamilies { QgsFontUtils::recentFontFamilies() };
613 for ( const QString &family : recentFontFamilies )
614 {
615 QAction *fontAction = new QAction( family, recentFontMenu );
616 QFont f = fontAction->font();
617 QgsFontUtils::setFontFamily( f, family );
618 fontAction->setFont( f );
619 fontAction->setToolTip( family );
620 recentFontMenu->addAction( fontAction );
621 if ( ( mMode == ModeTextRenderer && family == mFormat.font().family() )
622 || ( mMode == ModeQFont && family == mFont.family() ) )
623 {
624 fontAction->setCheckable( true );
625 fontAction->setChecked( true );
626 }
627 auto setFont = [this, family] {
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
696 QgsColorRampWidget *alphaRamp = new QgsColorRampWidget( mMenu, QgsColorWidget::Alpha, QgsColorRampWidget::Horizontal );
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, [this]( const QColor &color ) {
703 const double opacity = color.alphaF();
704 mFormat.setOpacity( opacity );
705 updatePreview();
706 if ( mNullFormatAction )
707 mNullFormatAction->setChecked( false );
708 emit changed();
709 } );
710 connect( colorAction, &QgsColorWidgetAction::colorChanged, alphaRamp, [alphaRamp]( const QColor &color ) { alphaRamp->setColor( color, false ); } );
711 mMenu->addAction( alphaAction );
712
713 //get schemes with ShowInColorButtonMenu flag set
715 QList<QgsColorScheme *>::iterator it = schemeList.begin();
716 for ( ; it != schemeList.end(); ++it )
717 {
718 QgsColorSwatchGridAction *colorAction = new QgsColorSwatchGridAction( *it, mMenu, QStringLiteral( "labeling" ), this );
719 colorAction->setBaseColor( mFormat.color() );
720 mMenu->addAction( colorAction );
722 connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsFontButton::addRecentColor );
723 }
724
725 mMenu->addSeparator();
726
727 QAction *copyColorAction = new QAction( tr( "Copy Color" ), this );
728 mMenu->addAction( copyColorAction );
729 connect( copyColorAction, &QAction::triggered, this, &QgsFontButton::copyColor );
730
731 QAction *pasteColorAction = new QAction( tr( "Paste Color" ), this );
732 //enable or disable paste action based on current clipboard contents. We always show the paste
733 //action, even if it's disabled, to give hint to the user that pasting colors is possible
734 QColor clipColor;
735 bool hasAlpha = false;
736 if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
737 {
738 pasteColorAction->setIcon( createColorIcon( clipColor ) );
739 }
740 else
741 {
742 pasteColorAction->setEnabled( false );
743 }
744 mMenu->addAction( pasteColorAction );
745 connect( pasteColorAction, &QAction::triggered, this, &QgsFontButton::pasteColor );
746 }
747}
748
749void QgsFontButton::addRecentColor( const QColor &color )
750{
752}
753
755{
756 return mFont;
757}
758
760{
761 return mLayer;
762}
763
765{
766 mLayer = layer;
767}
768
770{
771 mExpressionContextGenerator = generator;
772}
773
774void QgsFontButton::setCurrentFont( const QFont &font )
775{
776 mFont = font;
777 updatePreview();
778 emit changed();
779}
780
782{
783 return mMode;
784}
785
787{
788 mMode = mode;
789 updatePreview();
790}
791
792bool QgsFontButton::formatFromMimeData( const QMimeData *mimeData, QgsTextFormat &resultFormat ) const
793{
794 if ( !mimeData )
795 return false;
796
797 bool ok = false;
798 resultFormat = QgsTextFormat::fromMimeData( mimeData, &ok );
799 return ok;
800}
801
802bool QgsFontButton::fontFromMimeData( const QMimeData *mimeData, QFont &resultFont ) const
803{
804 if ( !mimeData )
805 return false;
806
807 bool ok = false;
808 resultFont = QgsFontUtils::fromMimeData( mimeData, &ok );
809 return ok;
810}
811
813{
814 if ( e->type() == QEvent::EnabledChange )
815 {
816 updatePreview();
817 }
818 QToolButton::changeEvent( e );
819}
820
821void QgsFontButton::showEvent( QShowEvent *e )
822{
823 updatePreview();
824 QToolButton::showEvent( e );
825}
826
828{
829 QToolButton::resizeEvent( event );
830 //recalculate icon size and redraw icon
831 mIconSize = QSize();
832 updatePreview();
833}
834
835void QgsFontButton::updatePreview( const QColor &color, QgsTextFormat *format, QFont *font )
836{
837 if ( mShowNoFormat && !mFormat.isValid() )
838 {
839 setIcon( QPixmap() );
840 return;
841 }
842
843 QgsTextFormat tempFormat;
844 QFont tempFont;
845
846 if ( format )
847 tempFormat = *format;
848 else
849 tempFormat = mFormat;
850 if ( font )
851 tempFont = *font;
852 else
853 tempFont = mFont;
854
855 if ( color.isValid() )
856 tempFormat.setColor( color );
857
858 // always show font previews based on the standard font size for buttons. Otherwise large/small text
859 // will be unreadable, making the button very non-user-friendly.
860 // Note that we take away a few points here, as the text in these buttons is rendered with a fairly large
861 // margin and we'd like to avoid cropping the text.
862 tempFormat.setSize( QToolButton::font().pointSizeF() - 2 );
864
865 QSize currentIconSize;
866 //icon size is button size with a small margin
867 if ( menu() )
868 {
869 if ( !mIconSize.isValid() )
870 {
871 //calculate size of push button part of widget (ie, without the menu dropdown button part)
872 QStyleOptionToolButton opt;
873 initStyleOption( &opt );
874 const QRect buttonSize = QApplication::style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton, this );
875 //make sure height of icon looks good under different platforms
876#ifdef Q_OS_WIN
877 mIconSize = QSize( buttonSize.width() - 10, height() - 6 );
878#elif defined( Q_OS_MAC )
879 mIconSize = QSize( buttonSize.width() - 10, height() - 2 );
880#else
881 mIconSize = QSize( buttonSize.width() - 10, height() - 12 );
882#endif
883 }
884 currentIconSize = mIconSize;
885 }
886 else
887 {
888 //no menu
889#ifdef Q_OS_WIN
890 currentIconSize = QSize( width() - 10, height() - 6 );
891#else
892 currentIconSize = QSize( width() - 10, height() - 12 );
893#endif
894 }
895
896 if ( !currentIconSize.isValid() || currentIconSize.width() <= 0 || currentIconSize.height() <= 0 )
897 {
898 return;
899 }
900
901 //create an icon pixmap
902 QPixmap pixmap( currentIconSize * devicePixelRatioF() );
903 pixmap.fill( Qt::transparent );
904 pixmap.setDevicePixelRatio( devicePixelRatioF() );
905 QPainter p;
906 p.begin( &pixmap );
907 p.setRenderHint( QPainter::Antialiasing );
908 const QRect rect( 0, 0, currentIconSize.width(), currentIconSize.height() );
909
910 switch ( mMode )
911 {
912 case ModeTextRenderer:
913 {
914 QgsRenderContext context;
915 QgsMapToPixel newCoordXForm;
916 newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
917 context.setMapToPixel( newCoordXForm );
918
919 context.setScaleFactor( mScreenHelper->screenDpi() / 25.4 );
920 context.setDevicePixelRatio( devicePixelRatioF() );
923 context.setPainter( &p );
924
925 // slightly inset text to account for buffer/background
926 const double fontSize = context.convertToPainterUnits( tempFormat.size(), tempFormat.sizeUnit(), tempFormat.sizeMapUnitScale() );
927 double xtrans = 0;
928 if ( tempFormat.buffer().enabled() )
929 xtrans = tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
930 ? fontSize * tempFormat.buffer().size() / 100
931 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
932 if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
933 xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
934
935 double ytrans = 0.0;
936 if ( tempFormat.buffer().enabled() )
937 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() ) );
938 if ( tempFormat.background().enabled() )
939 ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
940
941 QRectF textRect = rect;
942 textRect.setLeft( xtrans );
943 textRect.setWidth( textRect.width() - xtrans );
944 textRect.setTop( ytrans );
945 if ( textRect.height() > 300 )
946 textRect.setHeight( 300 );
947 if ( textRect.width() > 2000 )
948 textRect.setWidth( 2000 );
949
950 QgsTextRenderer::drawText( textRect, 0, Qgis::TextHorizontalAlignment::Left, QStringList() << text(), 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 p.end();
965 setIconSize( currentIconSize );
966 setIcon( pixmap );
967}
968
970{
971 //copy color
972 QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mFormat.color() ) );
973}
974
976{
977 QColor clipColor;
978 bool hasAlpha = false;
979 if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) )
980 {
981 //paste color
982 setColor( clipColor );
984 }
985}
986
987void QgsFontButton::setDialogTitle( const QString &title )
988{
989 mDialogTitle = title;
990}
991
993{
994 return mDialogTitle;
995}
@ PreferVector
Prefer vector-based rendering, when the result will still be visually near-identical to a raster-base...
Definition qgis.h:2705
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5187
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5188
@ Pixels
Pixels.
Definition qgis.h:5186
@ Antialiasing
Use antialiasing while drawing.
Definition qgis.h:2756
@ Center
Center align.
Definition qgis.h:2944
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:6222
static QgsColorSchemeRegistry * colorSchemeRegistry()
Returns the application's color scheme registry, used for managing color schemes.
@ Horizontal
Horizontal ramp.
QList< QgsColorScheme * > schemes() const
Returns all color schemes in the registry.
@ ShowInColorButtonMenu
Show scheme in color button drop-down 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.
void setColor(const QColor &color, bool emitSignals=false) override
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.
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.
void setParameters(double mapUnitsPerPixel, double centerX, double centerY, int widthPixels, int heightPixels, double rotation)
Sets parameters for use in transforming coordinates.
A bar for displaying non-blocking messages to the user.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
bool dockMode() const
Returns the dock mode state.
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.
static void addRecentColor(const QColor &color)
Adds a color to the list of recent colors.
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.
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 setRasterizedRenderingPolicy(Qgis::RasterizedRenderingPolicy policy)
Sets the policy controlling when rasterisation of content during renders is permitted.
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.
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.
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.
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.
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 size() const
Returns the size for rendered text.
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 dataset.
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.