mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
[FEATURE] Add right click menu to color buttons, allowing copying and pasting colors. Pasting colors accepts clipboard text in a variety of common formats, including hex and css style rgb and rgba strings.
This commit is contained in:
parent
edbfb57ca1
commit
f31e637ed1
@ -189,9 +189,25 @@ class QgsSymbolLayerV2Utils
|
||||
static QgsVectorColorRampV2* loadColorRamp( QDomElement& element ) /Factory/;
|
||||
static QDomElement saveColorRamp( QString name, QgsVectorColorRampV2* ramp, QDomDocument& doc );
|
||||
|
||||
/** parse color definition with format "rgb(0,0,0)" or "0,0,0" */
|
||||
/**
|
||||
* Attempts to parse a string as a color using a variety of common formats, including hex
|
||||
* codes, rgb and rgba strings.
|
||||
* @param colorStr string representing the color
|
||||
* @returns parsed color
|
||||
* @note added in 2.3
|
||||
*/
|
||||
static QColor parseColor( QString colorStr );
|
||||
|
||||
/**
|
||||
* Attempts to parse a string as a color using a variety of common formats, including hex
|
||||
* codes, rgb and rgba strings.
|
||||
* @param colorStr string representing the color
|
||||
* @param containsAlpha if colorStr contains an explicit alpha value then containsAlpha will be set to true
|
||||
* @returns parsed color
|
||||
* @note added in 2.3
|
||||
*/
|
||||
static QColor parseColorWithAlpha( const QString colorStr, bool &containsAlpha );
|
||||
|
||||
/**Returns the line width scale factor depending on the unit and the paint device*/
|
||||
static double lineWidthScaleFactor( const QgsRenderContext& c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale& scale = QgsMapUnitScale() );
|
||||
/**Returns scale factor painter units -> pixel dimensions*/
|
||||
|
@ -111,4 +111,9 @@ class QgsColorButton: QPushButton
|
||||
void changeEvent( QEvent* e );
|
||||
void showEvent( QShowEvent* e );
|
||||
static const QPixmap& transpBkgrd();
|
||||
|
||||
/**
|
||||
* Reimplemented to detect right mouse button clicks on the color button.
|
||||
*/
|
||||
void mousePressEvent( QMouseEvent* e );
|
||||
};
|
||||
|
@ -2657,18 +2657,105 @@ QDomElement QgsSymbolLayerV2Utils::saveColorRamp( QString name, QgsVectorColorRa
|
||||
return rampEl;
|
||||
}
|
||||
|
||||
// parse color definition with format "rgb(0,0,0)" or "0,0,0"
|
||||
// should support other formats like FFFFFF
|
||||
QColor QgsSymbolLayerV2Utils::parseColor( QString colorStr )
|
||||
{
|
||||
if ( colorStr.startsWith( "rgb(" ) )
|
||||
bool hasAlpha;
|
||||
return parseColorWithAlpha( colorStr, hasAlpha );
|
||||
}
|
||||
|
||||
QColor QgsSymbolLayerV2Utils::parseColorWithAlpha( const QString colorStr, bool &containsAlpha )
|
||||
{
|
||||
QColor parsedColor;
|
||||
|
||||
//color in hex format "#aabbcc"
|
||||
if ( QColor::isValidColor( colorStr ) )
|
||||
{
|
||||
colorStr = colorStr.mid( 4, colorStr.size() - 5 ).trimmed();
|
||||
//string is a valid hex color string
|
||||
parsedColor.setNamedColor( colorStr );
|
||||
if ( parsedColor.isValid() )
|
||||
{
|
||||
containsAlpha = false;
|
||||
return parsedColor;
|
||||
}
|
||||
}
|
||||
QStringList p = colorStr.split( QChar( ',' ) );
|
||||
if ( p.count() != 3 )
|
||||
return QColor();
|
||||
return QColor( p[0].toInt(), p[1].toInt(), p[2].toInt() );
|
||||
|
||||
//color in hex format, without #
|
||||
QRegExp hexColorRx2( "^\\s*(?:[0-9a-fA-F]{3}){1,2}\\s*$" );
|
||||
if ( hexColorRx2.indexIn( colorStr ) != -1 )
|
||||
{
|
||||
//add "#" and parse
|
||||
parsedColor.setNamedColor( QString( "#" ) + colorStr );
|
||||
if ( parsedColor.isValid() )
|
||||
{
|
||||
containsAlpha = false;
|
||||
return parsedColor;
|
||||
}
|
||||
}
|
||||
|
||||
//color in (rrr,ggg,bbb) format, brackets and rgb prefix optional
|
||||
QRegExp rgbFormatRx( "^\\s*(?:rgb)?\\(?\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*\\)?\\s*;?\\s*$" );
|
||||
if ( rgbFormatRx.indexIn( colorStr ) != -1 )
|
||||
{
|
||||
int r = rgbFormatRx.cap( 1 ).toInt();
|
||||
int g = rgbFormatRx.cap( 2 ).toInt();
|
||||
int b = rgbFormatRx.cap( 3 ).toInt();
|
||||
parsedColor.setRgb( r, g, b );
|
||||
if ( parsedColor.isValid() )
|
||||
{
|
||||
containsAlpha = false;
|
||||
return parsedColor;
|
||||
}
|
||||
}
|
||||
|
||||
//color in (r%,g%,b%) format, brackets and rgb prefix optional
|
||||
QRegExp rgbPercentFormatRx( "^\\s*(?:rgb)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*\\)?\\s*;?\\s*$" );
|
||||
if ( rgbPercentFormatRx.indexIn( colorStr ) != -1 )
|
||||
{
|
||||
int r = qRound( rgbPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
|
||||
int g = qRound( rgbPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
|
||||
int b = qRound( rgbPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
|
||||
parsedColor.setRgb( r, g, b );
|
||||
if ( parsedColor.isValid() )
|
||||
{
|
||||
containsAlpha = false;
|
||||
return parsedColor;
|
||||
}
|
||||
}
|
||||
|
||||
//color in (r,g,b,a) format, brackets and rgba prefix optional
|
||||
QRegExp rgbaFormatRx( "^\\s*(?:rgba)?\\(?\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" );
|
||||
if ( rgbaFormatRx.indexIn( colorStr ) != -1 )
|
||||
{
|
||||
int r = rgbaFormatRx.cap( 1 ).toInt();
|
||||
int g = rgbaFormatRx.cap( 2 ).toInt();
|
||||
int b = rgbaFormatRx.cap( 3 ).toInt();
|
||||
int a = qRound( rgbaFormatRx.cap( 4 ).toDouble() * 255.0 );
|
||||
parsedColor.setRgb( r, g, b, a );
|
||||
if ( parsedColor.isValid() )
|
||||
{
|
||||
containsAlpha = true;
|
||||
return parsedColor;
|
||||
}
|
||||
}
|
||||
|
||||
//color in (r%,g%,b%,a) format, brackets and rgba prefix optional
|
||||
QRegExp rgbaPercentFormatRx( "^\\s*(?:rgba)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" );
|
||||
if ( rgbaPercentFormatRx.indexIn( colorStr ) != -1 )
|
||||
{
|
||||
int r = qRound( rgbaPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
|
||||
int g = qRound( rgbaPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
|
||||
int b = qRound( rgbaPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
|
||||
int a = qRound( rgbaPercentFormatRx.cap( 4 ).toDouble() * 255.0 );
|
||||
parsedColor.setRgb( r, g, b, a );
|
||||
if ( parsedColor.isValid() )
|
||||
{
|
||||
containsAlpha = true;
|
||||
return parsedColor;
|
||||
}
|
||||
}
|
||||
|
||||
//couldn't parse string as color
|
||||
return QColor();
|
||||
}
|
||||
|
||||
double QgsSymbolLayerV2Utils::lineWidthScaleFactor( const QgsRenderContext& c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale& scale )
|
||||
|
@ -225,9 +225,25 @@ class CORE_EXPORT QgsSymbolLayerV2Utils
|
||||
static QgsVectorColorRampV2* loadColorRamp( QDomElement& element );
|
||||
static QDomElement saveColorRamp( QString name, QgsVectorColorRampV2* ramp, QDomDocument& doc );
|
||||
|
||||
/** parse color definition with format "rgb(0,0,0)" or "0,0,0" */
|
||||
/**
|
||||
* Attempts to parse a string as a color using a variety of common formats, including hex
|
||||
* codes, rgb and rgba strings.
|
||||
* @param colorStr string representing the color
|
||||
* @returns parsed color
|
||||
* @note added in 2.3
|
||||
*/
|
||||
static QColor parseColor( QString colorStr );
|
||||
|
||||
/**
|
||||
* Attempts to parse a string as a color using a variety of common formats, including hex
|
||||
* codes, rgb and rgba strings.
|
||||
* @param colorStr string representing the color
|
||||
* @param containsAlpha if colorStr contains an explicit alpha value then containsAlpha will be set to true
|
||||
* @returns parsed color
|
||||
* @note added in 2.3
|
||||
*/
|
||||
static QColor parseColorWithAlpha( const QString colorStr, bool &containsAlpha );
|
||||
|
||||
/**Returns the line width scale factor depending on the unit and the paint device*/
|
||||
static double lineWidthScaleFactor( const QgsRenderContext& c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale& scale = QgsMapUnitScale() );
|
||||
/**Returns scale factor painter units -> pixel dimensions*/
|
||||
|
@ -17,10 +17,14 @@
|
||||
#include "qgscolordialog.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgssymbollayerv2utils.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QSettings>
|
||||
#include <QTemporaryFile>
|
||||
#include <QMouseEvent>
|
||||
#include <QMenu>
|
||||
#include <QClipboard>
|
||||
|
||||
/*!
|
||||
\class QgsColorButton
|
||||
@ -87,6 +91,86 @@ void QgsColorButton::onButtonClicked()
|
||||
activateWindow();
|
||||
}
|
||||
|
||||
void QgsColorButton::mousePressEvent( QMouseEvent *e )
|
||||
{
|
||||
if ( e->button() == Qt::RightButton )
|
||||
{
|
||||
showContextMenu( e );
|
||||
}
|
||||
else
|
||||
{
|
||||
QPushButton::mousePressEvent( e );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsColorButton::showContextMenu( QMouseEvent *event )
|
||||
{
|
||||
QMenu colorContextMenu;
|
||||
|
||||
QAction* copyAsHexAction = new QAction( tr( "Copy color" ), 0 );
|
||||
colorContextMenu.addAction( copyAsHexAction );
|
||||
QAction* copyAsRgbAction = new QAction( tr( "Copy as rgb" ), 0 );
|
||||
colorContextMenu.addAction( copyAsRgbAction );
|
||||
QAction* copyAsRgbaAction = new QAction( tr( "Copy as rgba" ), 0 );
|
||||
if ( mColorDialogOptions & QColorDialog::ShowAlphaChannel )
|
||||
{
|
||||
//alpha enabled, so add rgba action
|
||||
colorContextMenu.addAction( copyAsRgbaAction );
|
||||
}
|
||||
|
||||
QString clipboardText = QApplication::clipboard()->text();
|
||||
QAction* pasteColorAction = new QAction( tr( "Paste color" ), 0 );
|
||||
pasteColorAction->setEnabled( false );
|
||||
colorContextMenu.addSeparator();
|
||||
colorContextMenu.addAction( pasteColorAction );
|
||||
QColor clipColor;
|
||||
if ( !( clipboardText.isEmpty() ) )
|
||||
{
|
||||
bool hasAlpha = false;
|
||||
clipColor = QgsSymbolLayerV2Utils::parseColorWithAlpha( clipboardText, hasAlpha );
|
||||
|
||||
if ( clipColor.isValid() )
|
||||
{
|
||||
if ( !hasAlpha )
|
||||
{
|
||||
//clipboard color has no explicit alpha component, so keep existing alpha
|
||||
clipColor.setAlpha( mColor.alpha() );
|
||||
}
|
||||
pasteColorAction->setEnabled( true );
|
||||
}
|
||||
}
|
||||
|
||||
QAction* selectedAction = colorContextMenu.exec( event->globalPos( ) );
|
||||
if ( selectedAction == copyAsHexAction )
|
||||
{
|
||||
//copy color as hex code
|
||||
QString colorString = mColor.name();
|
||||
QApplication::clipboard()->setText( colorString );
|
||||
}
|
||||
else if ( selectedAction == copyAsRgbAction )
|
||||
{
|
||||
//copy color as rgb
|
||||
QString colorString = QString( "rgb(%1,%2,%3)" ).arg( mColor.red() ). arg( mColor.green() ).arg( mColor.blue() );
|
||||
QApplication::clipboard()->setText( colorString );
|
||||
}
|
||||
else if ( selectedAction == copyAsRgbaAction )
|
||||
{
|
||||
//copy color as rgba
|
||||
QString colorString = QString( "rgba(%1,%2,%3,%4)" ).arg( mColor.red() ).arg( mColor.green() ).arg( mColor.blue() ).arg( QString::number( mColor.alphaF(), 'f', 2 ) );
|
||||
QApplication::clipboard()->setText( colorString );
|
||||
}
|
||||
else if ( selectedAction == pasteColorAction )
|
||||
{
|
||||
//paste color
|
||||
setColor( clipColor );
|
||||
}
|
||||
|
||||
delete copyAsHexAction;
|
||||
delete copyAsRgbAction;
|
||||
delete copyAsRgbaAction;
|
||||
delete pasteColorAction;
|
||||
}
|
||||
|
||||
void QgsColorButton::setValidColor( const QColor& newColor )
|
||||
{
|
||||
if ( newColor.isValid() )
|
||||
|
10
src/gui/qgscolorbutton.h
Normal file → Executable file
10
src/gui/qgscolorbutton.h
Normal file → Executable file
@ -139,6 +139,11 @@ class GUI_EXPORT QgsColorButton: public QPushButton
|
||||
void showEvent( QShowEvent* e );
|
||||
static const QPixmap& transpBkgrd();
|
||||
|
||||
/**
|
||||
* Reimplemented to detect right mouse button clicks on the color button.
|
||||
*/
|
||||
void mousePressEvent( QMouseEvent* e );
|
||||
|
||||
private:
|
||||
QString mColorDialogTitle;
|
||||
QColor mColor;
|
||||
@ -147,6 +152,11 @@ class GUI_EXPORT QgsColorButton: public QPushButton
|
||||
QTemporaryFile mTempPNG;
|
||||
bool mColorSet; // added in QGIS 2.1
|
||||
|
||||
/**
|
||||
* Shows the color button context menu and handles copying and pasting color values.
|
||||
*/
|
||||
void showContextMenu( QMouseEvent* event );
|
||||
|
||||
private slots:
|
||||
void onButtonClicked();
|
||||
|
||||
|
@ -54,6 +54,7 @@ class TestStyleV2: public QObject
|
||||
void testCreateColorRamps();
|
||||
void testLoadColorRamps();
|
||||
void testSaveLoad();
|
||||
void testParseColor();
|
||||
};
|
||||
|
||||
|
||||
@ -213,6 +214,76 @@ void TestStyleV2::testSaveLoad()
|
||||
testLoadColorRamps();
|
||||
}
|
||||
|
||||
void TestStyleV2::testParseColor()
|
||||
{
|
||||
// values for color tests
|
||||
QMap< QString, QPair< QColor, bool> > colorTests;
|
||||
colorTests.insert( "bad color", qMakePair( QColor( ), false ) );
|
||||
colorTests.insert( "red", qMakePair( QColor( 255, 0, 0 ), false ) );
|
||||
colorTests.insert( "#ff00ff", qMakePair( QColor( 255, 0, 255 ), false ) );
|
||||
colorTests.insert( "#99AA00", qMakePair( QColor( 153, 170, 0 ), false ) );
|
||||
colorTests.insert( "#GG0000", qMakePair( QColor(), false ) );
|
||||
colorTests.insert( "000000", qMakePair( QColor( 0, 0, 0 ), false ) );
|
||||
colorTests.insert( "00ff00", qMakePair( QColor( 0, 255, 0 ), false ) );
|
||||
colorTests.insert( "00gg00", qMakePair( QColor( ), false ) );
|
||||
colorTests.insert( "00ff000", qMakePair( QColor(), false ) );
|
||||
colorTests.insert( "fff", qMakePair( QColor( 255, 255, 255 ), false ) );
|
||||
colorTests.insert( "fff0", qMakePair( QColor(), false ) );
|
||||
colorTests.insert( "0,0,0", qMakePair( QColor( 0, 0, 0 ), false ) );
|
||||
colorTests.insert( "127,60,0", qMakePair( QColor( 127, 60, 0 ), false ) );
|
||||
colorTests.insert( "255,255,255", qMakePair( QColor( 255, 255, 255 ), false ) );
|
||||
colorTests.insert( "256,60,0", qMakePair( QColor(), false ) );
|
||||
colorTests.insert( "rgb(127,60,0)", qMakePair( QColor( 127, 60, 0 ), false ) );
|
||||
colorTests.insert( "rgb(255,255,255)", qMakePair( QColor( 255, 255, 255 ), false ) );
|
||||
colorTests.insert( "rgb(256,60,0)", qMakePair( QColor(), false ) );
|
||||
colorTests.insert( " rgb( 127, 60 , 0 ) ", qMakePair( QColor( 127, 60, 0 ), false ) );
|
||||
colorTests.insert( "rgb(127,60,0);", qMakePair( QColor( 127, 60, 0 ), false ) );
|
||||
colorTests.insert( "(127,60,0);", qMakePair( QColor( 127, 60, 0 ), false ) );
|
||||
colorTests.insert( "(127,60,0)", qMakePair( QColor( 127, 60, 0 ), false ) );
|
||||
colorTests.insert( "127,060,000", qMakePair( QColor( 127, 60, 0 ), false ) );
|
||||
colorTests.insert( "0,0,0,0", qMakePair( QColor( 0, 0, 0, 0 ), true ) );
|
||||
colorTests.insert( "127,60,0,0.5", qMakePair( QColor( 127, 60, 0, 128 ), true ) );
|
||||
colorTests.insert( "255,255,255,0.1", qMakePair( QColor( 255, 255, 255, 26 ), true ) );
|
||||
colorTests.insert( "rgba(127,60,0,1.0)", qMakePair( QColor( 127, 60, 0, 255 ), true ) );
|
||||
colorTests.insert( "rgba(255,255,255,0.0)", qMakePair( QColor( 255, 255, 255, 0 ), true ) );
|
||||
colorTests.insert( " rgba( 127, 60 , 0 , 0.2 ) ", qMakePair( QColor( 127, 60, 0, 51 ), true ) );
|
||||
colorTests.insert( "rgba(127,60,0,0.1);", qMakePair( QColor( 127, 60, 0, 26 ), true ) );
|
||||
colorTests.insert( "(127,60,0,1);", qMakePair( QColor( 127, 60, 0, 255 ), true ) );
|
||||
colorTests.insert( "(127,60,0,1.0)", qMakePair( QColor( 127, 60, 0, 255 ), true ) );
|
||||
colorTests.insert( "127,060,000,1", qMakePair( QColor( 127, 60, 0, 255 ), true ) );
|
||||
colorTests.insert( "0%,0%,0%", qMakePair( QColor( 0, 0, 0 ), false ) );
|
||||
colorTests.insert( "50 %,60 %,0 %", qMakePair( QColor( 127, 153, 0 ), false ) );
|
||||
colorTests.insert( "100%, 100%, 100%", qMakePair( QColor( 255, 255, 255 ), false ) );
|
||||
colorTests.insert( "rgb(50%,60%,0%)", qMakePair( QColor( 127, 153, 0 ), false ) );
|
||||
colorTests.insert( "rgb(100%, 100%, 100%)", qMakePair( QColor( 255, 255, 255 ), false ) );
|
||||
colorTests.insert( " rgb( 50 % , 60 % , 0 % ) ", qMakePair( QColor( 127, 153, 0 ), false ) );
|
||||
colorTests.insert( "rgb(50%,60%,0%);", qMakePair( QColor( 127, 153, 0 ), false ) );
|
||||
colorTests.insert( "(50%,60%,0%);", qMakePair( QColor( 127, 153, 0 ), false ) );
|
||||
colorTests.insert( "(50%,60%,0%)", qMakePair( QColor( 127, 153, 0 ), false ) );
|
||||
colorTests.insert( "050%,060%,000%", qMakePair( QColor( 127, 153, 0 ), false ) );
|
||||
colorTests.insert( "0%,0%,0%,0", qMakePair( QColor( 0, 0, 0, 0 ), true ) );
|
||||
colorTests.insert( "50 %,60 %,0 %,0.5", qMakePair( QColor( 127, 153, 0, 128 ), true ) );
|
||||
colorTests.insert( "100%, 100%, 100%, 1.0", qMakePair( QColor( 255, 255, 255, 255 ), true ) );
|
||||
colorTests.insert( "rgba(50%,60%,0%, 1.0)", qMakePair( QColor( 127, 153, 0, 255 ), true ) );
|
||||
colorTests.insert( "rgba(100%, 100%, 100%, 0.0)", qMakePair( QColor( 255, 255, 255, 0 ), true ) );
|
||||
colorTests.insert( " rgba( 50 % , 60 % , 0 %, 0.5 ) ", qMakePair( QColor( 127, 153, 0, 128 ), true ) );
|
||||
colorTests.insert( "rgba(50%,60%,0%,0);", qMakePair( QColor( 127, 153, 0, 0 ), true ) );
|
||||
colorTests.insert( "(50%,60%,0%,1);", qMakePair( QColor( 127, 153, 0, 255 ), true ) );
|
||||
colorTests.insert( "(50%,60%,0%,1.0)", qMakePair( QColor( 127, 153, 0, 255 ), true ) );
|
||||
colorTests.insert( "050%,060%,000%,0", qMakePair( QColor( 127, 153, 0, 0 ), true ) );
|
||||
|
||||
QMap<QString, QPair< QColor, bool> >::const_iterator i = colorTests.constBegin();
|
||||
while ( i != colorTests.constEnd() )
|
||||
{
|
||||
QgsDebugMsg( "color string: " + i.key() );
|
||||
bool hasAlpha = false;
|
||||
QColor result = QgsSymbolLayerV2Utils::parseColorWithAlpha( i.key(), hasAlpha );
|
||||
QVERIFY( result == i.value().first );
|
||||
QVERIFY( hasAlpha == i.value().second );
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN( TestStyleV2 )
|
||||
#include "moc_testqgsstylev2.cxx"
|
||||
|
Loading…
x
Reference in New Issue
Block a user