QGIS API Documentation 3.99.0-Master (8e76e220402)
Loading...
Searching...
No Matches
qgscompoundcolorwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscompoundcolorwidget.cpp
3 --------------------------
4 begin : April 2016
5 copyright : (C) 2016 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
17
18#include "qgsapplication.h"
19#include "qgscolorscheme.h"
21#include "qgsguiutils.h"
22#include "qgsscreenhelper.h"
23#include "qgssettings.h"
24#include "qgssymbollayerutils.h"
25
26#include <QButtonGroup>
27#include <QFileDialog>
28#include <QHeaderView>
29#include <QInputDialog>
30#include <QMenu>
31#include <QMessageBox>
32#include <QMouseEvent>
33#include <QPushButton>
34#include <QRegularExpression>
35#include <QScreen>
36#include <QString>
37#include <QToolButton>
38#include <QVBoxLayout>
39
40#include "moc_qgscompoundcolorwidget.cpp"
41
42using namespace Qt::StringLiterals;
43
44QgsCompoundColorWidget::QgsCompoundColorWidget( QWidget *parent, const QColor &color, Layout widgetLayout )
45 : QgsPanelWidget( parent )
46{
47 setupUi( this );
48
49 mScreenHelper = new QgsScreenHelper( this );
50
51 mRgbRadios = {
58 };
59
60 mCmykRadios = {
65 };
66
67 mRgbGroup = new QButtonGroup( this );
68 int i = 0;
69 for ( auto colorRadio : mRgbRadios )
70 mRgbGroup->addButton( colorRadio.first, i++ );
71
72 mCmykGroup = new QButtonGroup( this );
73 i = 0;
74 for ( auto colorRadio : mCmykRadios )
75 mCmykGroup->addButton( colorRadio.first, i++ );
76
77 connect( mRgbGroup, &QButtonGroup::idToggled, this, &QgsCompoundColorWidget::onColorButtonGroupToggled );
78 connect( mCmykGroup, &QButtonGroup::idToggled, this, &QgsCompoundColorWidget::onColorButtonGroupToggled );
79 connect( mAddColorToSchemeButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mAddColorToSchemeButton_clicked );
80 connect( mAddCustomColorButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mAddCustomColorButton_clicked );
81 connect( mSampleButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mSampleButton_clicked );
82 connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsCompoundColorWidget::mTabWidget_currentChanged );
83 connect( mActionShowInButtons, &QAction::toggled, this, &QgsCompoundColorWidget::mActionShowInButtons_toggled );
84
85 mColorModel->addItem( tr( "RGB" ), QColor::Spec::Rgb );
86 mColorModel->addItem( tr( "CMYK" ), QColor::Spec::Cmyk );
87 connect( mColorModel, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [this]( int ) {
88 const QColor::Spec spec = static_cast<QColor::Spec>( mColorModel->currentData().toInt() );
89 if ( spec == QColor::Spec::Cmyk )
90 setColor( this->color().toCmyk() );
91 else
92 setColor( this->color().toRgb() );
93
94 updateComponent();
95 } );
96
97 if ( widgetLayout == LayoutVertical )
98 {
99 // shuffle stuff around
100 QVBoxLayout *newLayout = new QVBoxLayout();
101 newLayout->setContentsMargins( 0, 0, 0, 0 );
102 newLayout->addWidget( mTabWidget );
103 newLayout->addWidget( mSlidersWidget );
104 newLayout->addWidget( mPreviewWidget );
105 newLayout->addWidget( mSwatchesWidget );
106 delete layout();
107 setLayout( newLayout );
108 }
109
110 const QgsSettings settings;
111
112 mSchemeList->header()->hide();
113 mSchemeList->setColumnWidth( 0, static_cast<int>( Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 6 ) );
114
115 //get schemes with ShowInColorDialog set
116 refreshSchemeComboBox();
117 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
118
119 //choose a reasonable starting scheme
120 int activeScheme = settings.value( u"Windows/ColorDialog/activeScheme"_s, 0 ).toInt();
121 activeScheme = activeScheme >= mSchemeComboBox->count() ? 0 : activeScheme;
122
123 mSchemeList->setScheme( schemeList.at( activeScheme ) );
124
125 mSchemeComboBox->setCurrentIndex( activeScheme );
126 updateActionsForCurrentScheme();
127
128 //listen out for selection changes in list, so we can enable/disable the copy colors option
129 connect( mSchemeList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsCompoundColorWidget::listSelectionChanged );
130 //copy action defaults to disabled
131 mActionCopyColors->setEnabled( false );
132
133 connect( mActionCopyColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::copyColors );
134 connect( mActionPasteColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::pasteColors );
135 connect( mActionExportColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::showExportColorsDialog );
136 connect( mActionImportColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::showImportColorsDialog );
137 connect( mActionImportPalette, &QAction::triggered, this, &QgsCompoundColorWidget::importPalette );
138 connect( mActionRemovePalette, &QAction::triggered, this, &QgsCompoundColorWidget::removePalette );
139 connect( mActionNewPalette, &QAction::triggered, this, &QgsCompoundColorWidget::newPalette );
140 connect( mRemoveColorsFromSchemeButton, &QAbstractButton::clicked, mSchemeList, &QgsColorSchemeList::removeSelection );
141
142 QMenu *schemeMenu = new QMenu( mSchemeToolButton );
143 schemeMenu->addAction( mActionCopyColors );
144 schemeMenu->addAction( mActionPasteColors );
145 schemeMenu->addSeparator();
146 schemeMenu->addAction( mActionImportColors );
147 schemeMenu->addAction( mActionExportColors );
148 schemeMenu->addSeparator();
149 schemeMenu->addAction( mActionNewPalette );
150 schemeMenu->addAction( mActionImportPalette );
151 schemeMenu->addAction( mActionRemovePalette );
152 schemeMenu->addAction( mActionShowInButtons );
153 mSchemeToolButton->setMenu( schemeMenu );
154
155 connect( mSchemeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsCompoundColorWidget::schemeIndexChanged );
156 connect( mSchemeList, &QgsColorSchemeList::colorSelected, this, &QgsCompoundColorWidget::_setColor );
157
158 mOldColorLabel->hide();
159
160 mVerticalRamp->setOrientation( QgsColorRampWidget::Vertical );
161 mVerticalRamp->setInteriorMargin( 2 );
162 mVerticalRamp->setShowFrame( true );
163
164 mRedSlider->setComponent( QgsColorWidget::Red );
165 mGreenSlider->setComponent( QgsColorWidget::Green );
166 mBlueSlider->setComponent( QgsColorWidget::Blue );
167 mHueSlider->setComponent( QgsColorWidget::Hue );
168 mSaturationSlider->setComponent( QgsColorWidget::Saturation );
169 mValueSlider->setComponent( QgsColorWidget::Value );
170 mAlphaSlider->setComponent( QgsColorWidget::Alpha );
171 mCyanSlider->setComponent( QgsColorWidget::Cyan );
172 mMagentaSlider->setComponent( QgsColorWidget::Magenta );
173 mYellowSlider->setComponent( QgsColorWidget::Yellow );
174 mBlackSlider->setComponent( QgsColorWidget::Black );
175
176 mSwatchButton1->setShowMenu( false );
177 mSwatchButton1->setBehavior( QgsColorButton::SignalOnly );
178 mSwatchButton2->setShowMenu( false );
179 mSwatchButton2->setBehavior( QgsColorButton::SignalOnly );
180 mSwatchButton3->setShowMenu( false );
181 mSwatchButton3->setBehavior( QgsColorButton::SignalOnly );
182 mSwatchButton4->setShowMenu( false );
183 mSwatchButton4->setBehavior( QgsColorButton::SignalOnly );
184 mSwatchButton5->setShowMenu( false );
185 mSwatchButton5->setBehavior( QgsColorButton::SignalOnly );
186 mSwatchButton6->setShowMenu( false );
187 mSwatchButton6->setBehavior( QgsColorButton::SignalOnly );
188 mSwatchButton7->setShowMenu( false );
189 mSwatchButton7->setBehavior( QgsColorButton::SignalOnly );
190 mSwatchButton8->setShowMenu( false );
191 mSwatchButton8->setBehavior( QgsColorButton::SignalOnly );
192 mSwatchButton9->setShowMenu( false );
193 mSwatchButton9->setBehavior( QgsColorButton::SignalOnly );
194 mSwatchButton10->setShowMenu( false );
195 mSwatchButton10->setBehavior( QgsColorButton::SignalOnly );
196 mSwatchButton11->setShowMenu( false );
197 mSwatchButton11->setBehavior( QgsColorButton::SignalOnly );
198 mSwatchButton12->setShowMenu( false );
199 mSwatchButton12->setBehavior( QgsColorButton::SignalOnly );
200 mSwatchButton13->setShowMenu( false );
201 mSwatchButton13->setBehavior( QgsColorButton::SignalOnly );
202 mSwatchButton14->setShowMenu( false );
203 mSwatchButton14->setBehavior( QgsColorButton::SignalOnly );
204 mSwatchButton15->setShowMenu( false );
205 mSwatchButton15->setBehavior( QgsColorButton::SignalOnly );
206 mSwatchButton16->setShowMenu( false );
207 mSwatchButton16->setBehavior( QgsColorButton::SignalOnly );
208 //restore custom colors
209 mSwatchButton1->setColor( settings.value( u"Windows/ColorDialog/customColor1"_s, QVariant( QColor() ) ).value<QColor>() );
210 mSwatchButton2->setColor( settings.value( u"Windows/ColorDialog/customColor2"_s, QVariant( QColor() ) ).value<QColor>() );
211 mSwatchButton3->setColor( settings.value( u"Windows/ColorDialog/customColor3"_s, QVariant( QColor() ) ).value<QColor>() );
212 mSwatchButton4->setColor( settings.value( u"Windows/ColorDialog/customColor4"_s, QVariant( QColor() ) ).value<QColor>() );
213 mSwatchButton5->setColor( settings.value( u"Windows/ColorDialog/customColor5"_s, QVariant( QColor() ) ).value<QColor>() );
214 mSwatchButton6->setColor( settings.value( u"Windows/ColorDialog/customColor6"_s, QVariant( QColor() ) ).value<QColor>() );
215 mSwatchButton7->setColor( settings.value( u"Windows/ColorDialog/customColor7"_s, QVariant( QColor() ) ).value<QColor>() );
216 mSwatchButton8->setColor( settings.value( u"Windows/ColorDialog/customColor8"_s, QVariant( QColor() ) ).value<QColor>() );
217 mSwatchButton9->setColor( settings.value( u"Windows/ColorDialog/customColor9"_s, QVariant( QColor() ) ).value<QColor>() );
218 mSwatchButton10->setColor( settings.value( u"Windows/ColorDialog/customColor10"_s, QVariant( QColor() ) ).value<QColor>() );
219 mSwatchButton11->setColor( settings.value( u"Windows/ColorDialog/customColor11"_s, QVariant( QColor() ) ).value<QColor>() );
220 mSwatchButton12->setColor( settings.value( u"Windows/ColorDialog/customColor12"_s, QVariant( QColor() ) ).value<QColor>() );
221 mSwatchButton13->setColor( settings.value( u"Windows/ColorDialog/customColor13"_s, QVariant( QColor() ) ).value<QColor>() );
222 mSwatchButton14->setColor( settings.value( u"Windows/ColorDialog/customColor14"_s, QVariant( QColor() ) ).value<QColor>() );
223 mSwatchButton15->setColor( settings.value( u"Windows/ColorDialog/customColor15"_s, QVariant( QColor() ) ).value<QColor>() );
224 mSwatchButton16->setColor( settings.value( u"Windows/ColorDialog/customColor16"_s, QVariant( QColor() ) ).value<QColor>() );
225
226 //restore sample radius
227 mSpinBoxRadius->setValue( settings.value( u"Windows/ColorDialog/sampleRadius"_s, 1 ).toInt() );
228 mSamplePreview->setColor( QColor() );
229
230 // hidpi friendly sizes
231 const int swatchWidth = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.9 * mSwatchButton1->fontMetrics().height(), 38.0 ) ) );
232 const int swatchHeight = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.5 * mSwatchButton1->fontMetrics().height(), 30.0 ) ) );
233 mSwatchButton1->setMinimumSize( swatchWidth, swatchHeight );
234 mSwatchButton1->setMaximumSize( swatchWidth, swatchHeight );
235 mSwatchButton2->setMinimumSize( swatchWidth, swatchHeight );
236 mSwatchButton2->setMaximumSize( swatchWidth, swatchHeight );
237 mSwatchButton3->setMinimumSize( swatchWidth, swatchHeight );
238 mSwatchButton3->setMaximumSize( swatchWidth, swatchHeight );
239 mSwatchButton4->setMinimumSize( swatchWidth, swatchHeight );
240 mSwatchButton4->setMaximumSize( swatchWidth, swatchHeight );
241 mSwatchButton5->setMinimumSize( swatchWidth, swatchHeight );
242 mSwatchButton5->setMaximumSize( swatchWidth, swatchHeight );
243 mSwatchButton6->setMinimumSize( swatchWidth, swatchHeight );
244 mSwatchButton6->setMaximumSize( swatchWidth, swatchHeight );
245 mSwatchButton7->setMinimumSize( swatchWidth, swatchHeight );
246 mSwatchButton7->setMaximumSize( swatchWidth, swatchHeight );
247 mSwatchButton8->setMinimumSize( swatchWidth, swatchHeight );
248 mSwatchButton8->setMaximumSize( swatchWidth, swatchHeight );
249 mSwatchButton9->setMinimumSize( swatchWidth, swatchHeight );
250 mSwatchButton9->setMaximumSize( swatchWidth, swatchHeight );
251 mSwatchButton10->setMinimumSize( swatchWidth, swatchHeight );
252 mSwatchButton10->setMaximumSize( swatchWidth, swatchHeight );
253 mSwatchButton11->setMinimumSize( swatchWidth, swatchHeight );
254 mSwatchButton11->setMaximumSize( swatchWidth, swatchHeight );
255 mSwatchButton12->setMinimumSize( swatchWidth, swatchHeight );
256 mSwatchButton12->setMaximumSize( swatchWidth, swatchHeight );
257 mSwatchButton13->setMinimumSize( swatchWidth, swatchHeight );
258 mSwatchButton13->setMaximumSize( swatchWidth, swatchHeight );
259 mSwatchButton14->setMinimumSize( swatchWidth, swatchHeight );
260 mSwatchButton14->setMaximumSize( swatchWidth, swatchHeight );
261 mSwatchButton15->setMinimumSize( swatchWidth, swatchHeight );
262 mSwatchButton15->setMaximumSize( swatchWidth, swatchHeight );
263 mSwatchButton16->setMinimumSize( swatchWidth, swatchHeight );
264 mSwatchButton16->setMaximumSize( swatchWidth, swatchHeight );
265 const int previewHeight = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 2.0 * mSwatchButton1->fontMetrics().height(), 40.0 ) ) );
266 mColorPreview->setMinimumSize( 0, previewHeight );
267 mPreviewWidget->setMaximumHeight( previewHeight * 2 );
268 const int swatchAddSize = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.4 * mSwatchButton1->fontMetrics().height(), 28.0 ) ) );
269 mAddCustomColorButton->setMinimumWidth( swatchAddSize );
270 mAddCustomColorButton->setMaximumWidth( swatchAddSize );
271
272 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
273 mTabWidget->setIconSize( QSize( iconSize, iconSize ) );
274
275 setColor( color );
276
277 // restore active Rgb/Cmyk component radio button
278 const int activeRgbRadio = settings.value( u"Windows/ColorDialog/activeComponent"_s, 2 ).toInt();
279 if ( QAbstractButton *rgbRadio = mRgbGroup->button( activeRgbRadio ) )
280 rgbRadio->setChecked( true );
281
282 const int activeCmykRadio = settings.value( u"Windows/ColorDialog/activeCmykComponent"_s, 0 ).toInt();
283 if ( QAbstractButton *cmykRadio = mCmykGroup->button( activeCmykRadio ) )
284 cmykRadio->setChecked( true );
285
286 const int currentTab = settings.value( u"Windows/ColorDialog/activeTab"_s, 0 ).toInt();
287 mTabWidget->setCurrentIndex( currentTab );
288
289 //setup connections
290 connect( mColorBox, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
291 connect( mColorWheel, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
292 connect( mColorText, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
293 connect( mVerticalRamp, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
294 connect( mRedSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
295 connect( mGreenSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
296 connect( mBlueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
297 connect( mHueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
298 connect( mValueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
299 connect( mCyanSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
300 connect( mMagentaSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
301 connect( mYellowSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
302 connect( mBlackSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
303 connect( mSaturationSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
304 connect( mAlphaSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
305 connect( mColorPreview, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
306 connect( mSwatchButton1, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
307 connect( mSwatchButton2, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
308 connect( mSwatchButton3, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
309 connect( mSwatchButton4, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
310 connect( mSwatchButton5, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
311 connect( mSwatchButton6, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
312 connect( mSwatchButton7, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
313 connect( mSwatchButton8, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
314 connect( mSwatchButton9, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
315 connect( mSwatchButton10, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
316 connect( mSwatchButton11, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
317 connect( mSwatchButton12, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
318 connect( mSwatchButton13, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
319 connect( mSwatchButton14, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
320 connect( mSwatchButton15, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
321 connect( mSwatchButton16, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
322}
323
331
333{
334 //all widgets should have the same color, so it shouldn't matter
335 //which we fetch it from
336 return mColorPreview->color();
337}
338
339void QgsCompoundColorWidget::setAllowOpacity( const bool allowOpacity )
340{
341 mAllowAlpha = allowOpacity;
342 mAlphaLabel->setVisible( allowOpacity );
343 mAlphaSlider->setVisible( allowOpacity );
344 mColorText->setAllowOpacity( allowOpacity );
345 if ( !allowOpacity )
346 {
347 mAlphaLayout->setContentsMargins( 0, 0, 0, 0 );
348 mAlphaLayout->setSpacing( 0 );
349 }
350}
351
353{
354 mColorModel->setVisible( colorModelEditable );
355}
356
357void QgsCompoundColorWidget::refreshSchemeComboBox()
358{
359 mSchemeComboBox->blockSignals( true );
360 mSchemeComboBox->clear();
361 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
362 QList<QgsColorScheme *>::const_iterator schemeIt = schemeList.constBegin();
363 for ( ; schemeIt != schemeList.constEnd(); ++schemeIt )
364 {
365 mSchemeComboBox->addItem( ( *schemeIt )->schemeName() );
366 }
367 mSchemeComboBox->blockSignals( false );
368}
369
370
372{
373 QgsSettings s;
374 const QString lastDir = s.value( u"/UI/lastGplPaletteDir"_s, QDir::homePath() ).toString();
375 const QString filePath = QFileDialog::getOpenFileName( parent, tr( "Select Palette File" ), lastDir, u"GPL (*.gpl);;All files (*.*)"_s );
376 if ( parent )
377 parent->activateWindow();
378 if ( filePath.isEmpty() )
379 {
380 return nullptr;
381 }
382
383 //check if file exists
384 const QFileInfo fileInfo( filePath );
385 if ( !fileInfo.exists() || !fileInfo.isReadable() )
386 {
387 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Error, file does not exist or is not readable." ) );
388 return nullptr;
389 }
390
391 s.setValue( u"/UI/lastGplPaletteDir"_s, fileInfo.absolutePath() );
392 QFile file( filePath );
393
394 QgsNamedColorList importedColors;
395 bool ok = false;
396 QString paletteName;
397 importedColors = QgsSymbolLayerUtils::importColorsFromGpl( file, ok, paletteName );
398 if ( !ok )
399 {
400 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Palette file is not readable." ) );
401 return nullptr;
402 }
403
404 if ( importedColors.length() == 0 )
405 {
406 //no imported colors
407 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "No colors found in palette file." ) );
408 return nullptr;
409 }
410
411 //TODO - handle conflicting file names, name for new palette
412 QgsUserColorScheme *importedScheme = new QgsUserColorScheme( fileInfo.fileName() );
413 importedScheme->setName( paletteName );
414 importedScheme->setColors( importedColors );
415
417 return importedScheme;
418}
419
420void QgsCompoundColorWidget::importPalette()
421{
422 if ( importUserPaletteFromFile( this ) )
423 {
424 //refresh combobox
425 refreshSchemeComboBox();
426 mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
427 }
428}
429
430
432{
433 if ( QMessageBox::question( parent, tr( "Remove Color Palette" ), tr( "Are you sure you want to remove %1?" ).arg( scheme->schemeName() ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
434 {
435 //user canceled
436 return false;
437 }
438
439 //remove palette and associated gpl file
440 if ( !scheme->erase() )
441 {
442 //something went wrong
443 return false;
444 }
445
446 //remove scheme from registry
448 return true;
449}
450
451void QgsCompoundColorWidget::removePalette()
452{
453 //get current scheme
454 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
455 int prevIndex = mSchemeComboBox->currentIndex();
456 if ( prevIndex >= schemeList.length() )
457 {
458 return;
459 }
460
461 //make user scheme is a user removable scheme
462 QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( schemeList.at( prevIndex ) );
463 if ( !userScheme )
464 {
465 return;
466 }
467
468 if ( removeUserPalette( userScheme, this ) )
469 {
470 refreshSchemeComboBox();
471 prevIndex = std::max( std::min( prevIndex, mSchemeComboBox->count() - 1 ), 0 );
472 mSchemeComboBox->setCurrentIndex( prevIndex );
473 }
474}
475
477{
478 bool ok = false;
479 const QString name = QInputDialog::getText( parent, tr( "Create New Palette" ), tr( "Enter a name for the new palette:" ), QLineEdit::Normal, tr( "New palette" ), &ok );
480
481 if ( !ok || name.isEmpty() )
482 {
483 //user canceled
484 return nullptr;
485 }
486
487 //generate file name for new palette
488 const QDir palettePath( gplFilePath() );
489 const thread_local QRegularExpression badChars( "[,^@={}\\[\\]~!?:&*\"|#%<>$\"'();`' /\\\\]" );
490 QString filename = name.simplified().toLower().replace( badChars, u"_"_s );
491 if ( filename.isEmpty() )
492 {
493 filename = tr( "new_palette" );
494 }
495 QFileInfo destFileInfo( palettePath.filePath( filename + ".gpl" ) );
496 int fileNumber = 1;
497 while ( destFileInfo.exists() )
498 {
499 //try to generate a unique file name
500 destFileInfo = QFileInfo( palettePath.filePath( filename + u"%1.gpl"_s.arg( fileNumber ) ) );
501 fileNumber++;
502 }
503
504 QgsUserColorScheme *newScheme = new QgsUserColorScheme( destFileInfo.fileName() );
505 newScheme->setName( name );
506
508 return newScheme;
509}
510
511void QgsCompoundColorWidget::newPalette()
512{
513 if ( createNewUserPalette( this ) )
514 {
515 //refresh combobox and set new scheme as active
516 refreshSchemeComboBox();
517 mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
518 }
519}
520
521QString QgsCompoundColorWidget::gplFilePath()
522{
523 QString palettesDir = QgsApplication::qgisSettingsDirPath() + "palettes";
524
525 const QDir localDir;
526 if ( !localDir.mkpath( palettesDir ) )
527 {
528 return QString();
529 }
530
531 return palettesDir;
532}
533
534void QgsCompoundColorWidget::schemeIndexChanged( int index )
535{
536 //save changes to scheme
537 if ( mSchemeList->isDirty() )
538 {
539 mSchemeList->saveColorsToScheme();
540 }
541
542 //get schemes with ShowInColorDialog set
543 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
544 if ( index >= schemeList.length() )
545 {
546 return;
547 }
548
549 QgsColorScheme *scheme = schemeList.at( index );
550 mSchemeList->setScheme( scheme );
551
552 updateActionsForCurrentScheme();
553
554 //copy action defaults to disabled
555 mActionCopyColors->setEnabled( false );
556}
557
558void QgsCompoundColorWidget::listSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
559{
560 Q_UNUSED( deselected )
561 mActionCopyColors->setEnabled( selected.length() > 0 );
562}
563
564void QgsCompoundColorWidget::mAddCustomColorButton_clicked()
565{
566 switch ( mLastCustomColorIndex )
567 {
568 case 0:
569 mSwatchButton1->setColor( mColorPreview->color() );
570 break;
571 case 1:
572 mSwatchButton2->setColor( mColorPreview->color() );
573 break;
574 case 2:
575 mSwatchButton3->setColor( mColorPreview->color() );
576 break;
577 case 3:
578 mSwatchButton4->setColor( mColorPreview->color() );
579 break;
580 case 4:
581 mSwatchButton5->setColor( mColorPreview->color() );
582 break;
583 case 5:
584 mSwatchButton6->setColor( mColorPreview->color() );
585 break;
586 case 6:
587 mSwatchButton7->setColor( mColorPreview->color() );
588 break;
589 case 7:
590 mSwatchButton8->setColor( mColorPreview->color() );
591 break;
592 case 8:
593 mSwatchButton9->setColor( mColorPreview->color() );
594 break;
595 case 9:
596 mSwatchButton10->setColor( mColorPreview->color() );
597 break;
598 case 10:
599 mSwatchButton11->setColor( mColorPreview->color() );
600 break;
601 case 11:
602 mSwatchButton12->setColor( mColorPreview->color() );
603 break;
604 case 12:
605 mSwatchButton13->setColor( mColorPreview->color() );
606 break;
607 case 13:
608 mSwatchButton14->setColor( mColorPreview->color() );
609 break;
610 case 14:
611 mSwatchButton15->setColor( mColorPreview->color() );
612 break;
613 case 15:
614 mSwatchButton16->setColor( mColorPreview->color() );
615 break;
616 }
617 mLastCustomColorIndex++;
618 if ( mLastCustomColorIndex >= 16 )
619 {
620 mLastCustomColorIndex = 0;
621 }
622}
623
624void QgsCompoundColorWidget::mSampleButton_clicked()
625{
626 //activate picker color
628 grabMouse();
629 grabKeyboard();
630 mPickingColor = true;
631 setMouseTracking( true );
632}
633
634void QgsCompoundColorWidget::mTabWidget_currentChanged( int index )
635{
636 //disable radio buttons if not using the first tab, as they have no meaning for other tabs
637 const bool enabled = index == 0;
638 const QList<QRadioButton *> colorRadios { mHueRadio, mSaturationRadio, mValueRadio, mRedRadio, mGreenRadio, mBlueRadio, mCyanRadio, mMagentaRadio, mYellowRadio, mBlackRadio };
639 for ( QRadioButton *colorRadio : colorRadios )
640 colorRadio->setEnabled( enabled );
641}
642
643void QgsCompoundColorWidget::mActionShowInButtons_toggled( bool state )
644{
645 QgsUserColorScheme *scheme = dynamic_cast<QgsUserColorScheme *>( mSchemeList->scheme() );
646 if ( scheme )
647 {
648 scheme->setShowSchemeInMenu( state );
649 }
650}
651
652QScreen *QgsCompoundColorWidget::findScreenAt( QPoint pos )
653{
654 const QList<QScreen *> screens = QGuiApplication::screens();
655 for ( QScreen *screen : screens )
656 {
657 if ( screen->geometry().contains( pos ) )
658 {
659 return screen;
660 }
661 }
662 return nullptr;
663}
664
665void QgsCompoundColorWidget::saveSettings()
666{
667 //save changes to scheme
668 if ( mSchemeList->isDirty() )
669 {
670 mSchemeList->saveColorsToScheme();
671 }
672
673 QgsSettings settings;
674
675 // record active component
676 settings.setValue( u"Windows/ColorDialog/activeComponent"_s, mRgbGroup->checkedId() );
677 settings.setValue( u"Windows/ColorDialog/activeCmykComponent"_s, mCmykGroup->checkedId() );
678
679 //record current scheme
680 settings.setValue( u"Windows/ColorDialog/activeScheme"_s, mSchemeComboBox->currentIndex() );
681
682 //record current tab
683 settings.setValue( u"Windows/ColorDialog/activeTab"_s, mTabWidget->currentIndex() );
684
685 //record custom colors
686 settings.setValue( u"Windows/ColorDialog/customColor1"_s, QVariant( mSwatchButton1->color() ) );
687 settings.setValue( u"Windows/ColorDialog/customColor2"_s, QVariant( mSwatchButton2->color() ) );
688 settings.setValue( u"Windows/ColorDialog/customColor3"_s, QVariant( mSwatchButton3->color() ) );
689 settings.setValue( u"Windows/ColorDialog/customColor4"_s, QVariant( mSwatchButton4->color() ) );
690 settings.setValue( u"Windows/ColorDialog/customColor5"_s, QVariant( mSwatchButton5->color() ) );
691 settings.setValue( u"Windows/ColorDialog/customColor6"_s, QVariant( mSwatchButton6->color() ) );
692 settings.setValue( u"Windows/ColorDialog/customColor7"_s, QVariant( mSwatchButton7->color() ) );
693 settings.setValue( u"Windows/ColorDialog/customColor8"_s, QVariant( mSwatchButton8->color() ) );
694 settings.setValue( u"Windows/ColorDialog/customColor9"_s, QVariant( mSwatchButton9->color() ) );
695 settings.setValue( u"Windows/ColorDialog/customColor10"_s, QVariant( mSwatchButton10->color() ) );
696 settings.setValue( u"Windows/ColorDialog/customColor11"_s, QVariant( mSwatchButton11->color() ) );
697 settings.setValue( u"Windows/ColorDialog/customColor12"_s, QVariant( mSwatchButton12->color() ) );
698 settings.setValue( u"Windows/ColorDialog/customColor13"_s, QVariant( mSwatchButton13->color() ) );
699 settings.setValue( u"Windows/ColorDialog/customColor14"_s, QVariant( mSwatchButton14->color() ) );
700 settings.setValue( u"Windows/ColorDialog/customColor15"_s, QVariant( mSwatchButton15->color() ) );
701 settings.setValue( u"Windows/ColorDialog/customColor16"_s, QVariant( mSwatchButton16->color() ) );
702
703 //sample radius
704 settings.setValue( u"Windows/ColorDialog/sampleRadius"_s, mSpinBoxRadius->value() );
705}
706
707void QgsCompoundColorWidget::stopPicking( QPoint eventPos, const bool takeSample )
708{
709 //release mouse and keyboard, and reset cursor
710 releaseMouse();
711 releaseKeyboard();
712 unsetCursor();
713 setMouseTracking( false );
714 mPickingColor = false;
715
716 if ( !takeSample )
717 {
718 //not sampling color, nothing more to do
719 return;
720 }
721
722 //grab snapshot of pixel under mouse cursor
723 const QColor snappedColor = sampleColor( eventPos );
724 mSamplePreview->setColor( snappedColor );
725 mColorPreview->setColor( snappedColor, true );
726}
727
729{
730 const QColor::Spec colorSpec = color.spec() == QColor::Cmyk ? QColor::Cmyk : QColor::Rgb;
731 mColorModel->setCurrentIndex( mColorModel->findData( colorSpec ) );
732 mRGB->setVisible( colorSpec != QColor::Cmyk );
733 mCMYK->setVisible( colorSpec == QColor::Cmyk );
734 _setColor( color );
735}
736
737void QgsCompoundColorWidget::_setColor( const QColor &color )
738{
739 if ( !color.isValid() )
740 {
741 return;
742 }
743
744 QColor fixedColor = QColor( color );
745 if ( !mAllowAlpha )
746 {
747 //opacity disallowed, so don't permit transparent colors
748 fixedColor.setAlpha( 255 );
749 }
750
751 if ( mColorModel->currentIndex() && fixedColor.spec() != QColor::Cmyk )
752 {
753 fixedColor = fixedColor.toCmyk();
754 }
755
756 const QList<QgsColorWidget *> colorWidgets = this->findChildren<QgsColorWidget *>();
757 const auto constColorWidgets = colorWidgets;
758 for ( QgsColorWidget *widget : constColorWidgets )
759 {
760 if ( widget == mSamplePreview )
761 {
762 continue;
763 }
764 widget->blockSignals( true );
765 widget->setColor( fixedColor );
766 widget->blockSignals( false );
767 }
768
769
770 emit currentColorChanged( fixedColor );
771}
772
774{
775 mOldColorLabel->setVisible( color.isValid() );
776 mColorPreview->setColor2( color );
777}
778
780{
781 saveSettings();
782 QWidget::hideEvent( e );
783}
784
786{
787 if ( mPickingColor )
788 {
789 //don't show dialog if in color picker mode
790 e->accept();
791 return;
792 }
793
794 QWidget::mousePressEvent( e );
795}
796
797QColor QgsCompoundColorWidget::averageColor( const QImage &image ) const
798{
799 QRgb tmpRgb;
800 int colorCount = 0;
801 int sumRed = 0;
802 int sumBlue = 0;
803 int sumGreen = 0;
804 //scan through image and sum rgb components
805 for ( int heightIndex = 0; heightIndex < image.height(); ++heightIndex )
806 {
807 const QRgb *scanLine = reinterpret_cast<const QRgb *>( image.constScanLine( heightIndex ) );
808 for ( int widthIndex = 0; widthIndex < image.width(); ++widthIndex )
809 {
810 tmpRgb = scanLine[widthIndex];
811 sumRed += qRed( tmpRgb );
812 sumBlue += qBlue( tmpRgb );
813 sumGreen += qGreen( tmpRgb );
814 colorCount++;
815 }
816 }
817 //calculate average components as floats
818 const double avgRed = static_cast<double>( sumRed ) / ( 255.0 * colorCount );
819 const double avgGreen = static_cast<double>( sumGreen ) / ( 255.0 * colorCount );
820 const double avgBlue = static_cast<double>( sumBlue ) / ( 255.0 * colorCount );
821
822 //create a new color representing the average
823 return QColor::fromRgbF( avgRed, avgGreen, avgBlue );
824}
825
826QColor QgsCompoundColorWidget::sampleColor( QPoint point ) const
827{
828 const int sampleRadius = mSpinBoxRadius->value() - 1;
829 QScreen *screen = findScreenAt( point );
830 if ( !screen )
831 {
832 return QColor();
833 }
834
835 const int x = point.x() - screen->geometry().left();
836 const int y = point.y() - screen->geometry().top();
837 const QPixmap snappedPixmap = screen->grabWindow( 0, x - sampleRadius, y - sampleRadius, 1 + sampleRadius * 2, 1 + sampleRadius * 2 );
838 const QImage snappedImage = snappedPixmap.toImage();
839 //scan all pixels and take average color
840 return averageColor( snappedImage );
841}
842
844{
845 if ( mPickingColor )
846 {
847 //currently in color picker mode
848 //sample color under cursor update preview widget to give feedback to user
849 const QColor hoverColor = sampleColor( e->globalPos() );
850 mSamplePreview->setColor( hoverColor );
851
852 e->accept();
853 return;
854 }
855
856 QWidget::mouseMoveEvent( e );
857}
858
860{
861 if ( mPickingColor )
862 {
863 //end color picking operation by sampling the color under cursor
864 stopPicking( e->globalPos() );
865 e->accept();
866 return;
867 }
868
869 QWidget::mouseReleaseEvent( e );
870}
871
873{
874 if ( !mPickingColor )
875 {
876 //if not picking a color, use default tool button behavior
878 return;
879 }
880
881 //cancel picking, sampling the color if space was pressed
882 stopPicking( QCursor::pos(), e->key() == Qt::Key_Space );
883}
884
885
886void QgsCompoundColorWidget::updateComponent()
887{
888 const bool isCmyk = mColorModel->currentData().toInt() == QColor::Spec::Cmyk;
889 const auto radios = isCmyk ? mCmykRadios : mRgbRadios;
890 const QButtonGroup *group = isCmyk ? mCmykGroup : mRgbGroup;
891
892 const int id = group->checkedId();
893 if ( id >= 0 && id < radios.count() )
894 {
895 const QgsColorWidget::ColorComponent component = radios.at( group->checkedId() ).second;
896 mColorBox->setComponent( component );
897 mVerticalRamp->setComponent( component );
898 }
899}
900
901void QgsCompoundColorWidget::onColorButtonGroupToggled( int, bool checked )
902{
903 if ( checked )
904 updateComponent();
905}
906
907void QgsCompoundColorWidget::mAddColorToSchemeButton_clicked()
908{
909 mSchemeList->addColor( mColorPreview->color(), QgsSymbolLayerUtils::colorToName( mColorPreview->color() ) );
910}
911
912void QgsCompoundColorWidget::updateActionsForCurrentScheme()
913{
914 QgsColorScheme *scheme = mSchemeList->scheme();
915
916 mActionImportColors->setEnabled( scheme->isEditable() );
917 mActionPasteColors->setEnabled( scheme->isEditable() );
918 mAddColorToSchemeButton->setEnabled( scheme->isEditable() );
919 mRemoveColorsFromSchemeButton->setEnabled( scheme->isEditable() );
920
921 QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( scheme );
922 mActionRemovePalette->setEnabled( static_cast<bool>( userScheme ) );
923 if ( userScheme )
924 {
925 mActionShowInButtons->setEnabled( true );
926 whileBlocking( mActionShowInButtons )->setChecked( userScheme->flags() & QgsColorScheme::ShowInColorButtonMenu );
927 }
928 else
929 {
930 whileBlocking( mActionShowInButtons )->setChecked( false );
931 mActionShowInButtons->setEnabled( false );
932 }
933}
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:6534
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
static QgsColorSchemeRegistry * colorSchemeRegistry()
Returns the application's color scheme registry, used for managing color schemes.
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
@ Sampler
Color/Value picker.
@ SignalOnly
Emit colorClicked signal only, no dialog.
void colorClicked(const QColor &color)
Emitted when the button is clicked, if the button's behavior is set to SignalOnly.
@ Vertical
Vertical ramp.
void pasteColors()
Pastes colors from clipboard to the list.
void removeSelection()
Removes any selected colors from the list.
void copyColors()
Copies colors from the list to the clipboard.
void showExportColorsDialog()
Displays a file picker dialog allowing users to export colors from the list into a file.
void colorSelected(const QColor &color)
Emitted when a color is selected from the list.
void showImportColorsDialog()
Displays a file picker dialog allowing users to import colors into the list from a file.
void addColorScheme(QgsColorScheme *scheme)
Adds a color scheme to the registry.
QList< QgsColorScheme * > schemes() const
Returns all color schemes in the registry.
bool removeColorScheme(QgsColorScheme *scheme)
Removes all matching color schemes from the registry.
@ ShowInColorButtonMenu
Show scheme in color button drop-down menu.
@ ShowInColorDialog
Show scheme in color picker dialog.
virtual bool isEditable() const
Returns whether the color scheme is editable.
void colorChanged(const QColor &color)
Emitted when the widget's color changes.
ColorComponent
Specifies the color component which the widget alters.
@ Hue
Hue component of color (based on HSV model).
@ Alpha
Alpha component (opacity) of color.
@ Green
Green component of color.
@ Red
Red component of color.
@ Saturation
Saturation component of color (based on HSV model).
@ Magenta
Magenta component (based on CMYK model) of color.
@ Yellow
Yellow component (based on CMYK model) of color.
@ Black
Black component (based on CMYK model) of color.
@ Cyan
Cyan component (based on CMYK model) of color.
@ Blue
Blue component of color.
@ Value
Value component of color (based on HSV model).
@ LayoutVertical
Use a narrower, vertically stacked layout.
void currentColorChanged(const QColor &color)
Emitted when the dialog's color changes.
void hideEvent(QHideEvent *e) override
QgsCompoundColorWidget(QWidget *parent=nullptr, const QColor &color=QColor(), Layout layout=LayoutDefault)
Constructor for QgsCompoundColorWidget.
void mousePressEvent(QMouseEvent *e) override
void setColorModelEditable(bool colorModelEditable)
Sets whether color model is editable or not.
static QgsUserColorScheme * importUserPaletteFromFile(QWidget *parent)
Triggers a user prompt for importing a new color scheme from an existing GPL file.
void setPreviousColor(const QColor &color)
Sets the color to show in an optional "previous color" section.
static bool removeUserPalette(QgsUserColorScheme *scheme, QWidget *parent)
Triggers a user prompt for removing an existing user color scheme.
static QgsUserColorScheme * createNewUserPalette(QWidget *parent)
Triggers a user prompt for creating a new user color scheme.
void mouseMoveEvent(QMouseEvent *e) override
void setAllowOpacity(bool allowOpacity)
Sets whether opacity modification (transparency) is permitted for the color dialog.
void setColor(const QColor &color)
Sets the current color for the dialog.
void keyPressEvent(QKeyEvent *e) override
QColor color() const
Returns the current color for the dialog.
void mouseReleaseEvent(QMouseEvent *e) override
bool setColors(const QgsNamedColorList &colors, const QString &context=QString(), const QColor &baseColor=QColor()) override
Sets the colors for the scheme.
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an inline panel.
void keyPressEvent(QKeyEvent *event) override
Overridden key press event to handle the esc event on the widget.
static void addRecentColor(const QColor &color)
Adds a color to the list of recent colors.
A utility class for dynamic handling of changes to screen properties.
Stores settings for use within QGIS.
Definition qgssettings.h:68
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static QgsNamedColorList importColorsFromGpl(QFile &file, bool &ok, QString &name)
Imports colors from a gpl GIMP palette file.
static QString colorToName(const QColor &color)
Returns a friendly display name for a color.
A color scheme which stores its colors in a gpl palette file within the "palettes" subfolder off the ...
QString schemeName() const override
Gets the name for the color scheme.
void setName(const QString &name)
Sets the name for the scheme.
QgsColorScheme::SchemeFlags flags() const override
Returns the current flags for the color scheme.
bool erase()
Erases the associated gpl palette file from the users "palettes" folder.
void setShowSchemeInMenu(bool show)
Sets whether a this scheme should be shown in color button menus.
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:6839