QGIS API Documentation  2.12.0-Lyon
qgscolorwidgets.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscolorwidgets.cpp - color selection widgets
3  ---------------------
4  begin : September 2014
5  copyright : (C) 2014 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 "qgscolorwidgets.h"
17 #include "qgsapplication.h"
18 #include "qgssymbollayerv2utils.h"
19 #include <QResizeEvent>
20 #include <QStyleOptionFrameV3>
21 #include <QPainter>
22 #include <QHBoxLayout>
23 #include <QSpinBox>
24 #include <QLineEdit>
25 #include <QFontMetrics>
26 #include <QToolButton>
27 #include <QMenu>
28 #include <QSettings>
29 #include <QDrag>
30 #include <cmath>
31 
32 
33 //
34 // QgsColorWidget
35 //
36 
38  : QWidget( parent )
39  , mCurrentColor( Qt::red )
40  , mComponent( component )
41  , mExplicitHue( 0 )
42 {
43  setAcceptDrops( true );
44 }
45 
47 {
48 
49 }
50 
52 {
53  return componentValue( mComponent );
54 }
55 
57 {
58  //craft a pixmap for the drag icon
59  QPixmap pixmap( 50, 50 );
60  pixmap.fill( Qt::transparent );
61  QPainter painter;
62  painter.begin( &pixmap );
63  //start with a light gray background
64  painter.fillRect( QRect( 0, 0, 50, 50 ), QBrush( QColor( 200, 200, 200 ) ) );
65  //draw rect with white border, filled with current color
66  QColor pixmapColor = color;
67  pixmapColor.setAlpha( 255 );
68  painter.setBrush( QBrush( pixmapColor ) );
69  painter.setPen( QPen( Qt::white ) );
70  painter.drawRect( QRect( 1, 1, 47, 47 ) );
71  painter.end();
72  return pixmap;
73 }
74 
76 {
77  if ( !mCurrentColor.isValid() )
78  {
79  return -1;
80  }
81 
82  switch ( component )
83  {
85  return mCurrentColor.red();
87  return mCurrentColor.green();
89  return mCurrentColor.blue();
91  //hue is treated specially, to avoid -1 hues values from QColor for ambiguous hues
92  return hue();
96  return mCurrentColor.value();
98  return mCurrentColor.alpha();
99  default:
100  return -1;
101  }
102 }
103 
105 {
106  return componentRange( mComponent );
107 }
108 
110 {
111  if ( component == QgsColorWidget::Multiple )
112  {
113  //no component
114  return -1;
115  }
116 
117  if ( component == QgsColorWidget::Hue )
118  {
119  //hue ranges to 359
120  return 359;
121  }
122  else
123  {
124  //all other components range to 255
125  return 255;
126  }
127 }
128 
130 {
131  if ( mCurrentColor.hue() >= 0 )
132  {
133  return mCurrentColor.hue();
134  }
135  else
136  {
137  return mExplicitHue;
138  }
139 }
140 
141 void QgsColorWidget::alterColor( QColor& color, const QgsColorWidget::ColorComponent component, const int newValue ) const
142 {
143  int h, s, v, a;
144  color.getHsv( &h, &s, &v, &a );
145 
146  //clip value to sensible range
147  int clippedValue = qMin( qMax( 0, newValue ), componentRange( component ) );
148 
149  switch ( component )
150  {
151  case QgsColorWidget::Red:
152  color.setRed( clippedValue );
153  return;
155  color.setGreen( clippedValue );
156  return;
158  color.setBlue( clippedValue );
159  return;
160  case QgsColorWidget::Hue:
161  color.setHsv( clippedValue, s, v, a );
162  return;
164  color.setHsv( h, clippedValue, v, a );
165  return;
167  color.setHsv( h, s, clippedValue, a );
168  return;
170  color.setAlpha( clippedValue );
171  return;
172  default:
173  return;
174  }
175 }
176 
178 {
179  static QPixmap transpBkgrd;
180 
181  if ( transpBkgrd.isNull() )
182  transpBkgrd = QgsApplication::getThemePixmap( "/transp-background_8x8.png" );
183 
184  return transpBkgrd;
185 }
186 
188 {
189  //is dragged data valid color data?
190  bool hasAlpha;
191  QColor mimeColor = QgsSymbolLayerV2Utils::colorFromMimeData( e->mimeData(), hasAlpha );
192 
193  if ( mimeColor.isValid() )
194  {
195  //if so, we accept the drag
197  }
198 }
199 
201 {
202  //is dropped data valid color data?
203  bool hasAlpha = false;
204  QColor mimeColor = QgsSymbolLayerV2Utils::colorFromMimeData( e->mimeData(), hasAlpha );
205 
206  if ( mimeColor.isValid() )
207  {
208  //accept drop and set new color
210 
211  if ( !hasAlpha )
212  {
213  //mime color has no explicit alpha component, so keep existing alpha
214  mimeColor.setAlpha( mCurrentColor.alpha() );
215  }
216 
217  setColor( mimeColor );
218  emit colorChanged( mCurrentColor );
219  }
220 
221  //could not get color from mime data
222 }
223 
225 {
226  return mCurrentColor;
227 }
228 
230 {
231  if ( component == mComponent )
232  {
233  return;
234  }
235 
237  update();
238 }
239 
240 void QgsColorWidget::setComponentValue( const int value )
241 {
243  {
244  return;
245  }
246 
247  //clip value to valid range
248  int valueClipped = qMin( value, componentRange() );
249  valueClipped = qMax( valueClipped, 0 );
250 
251  int r, g, b, a;
252  mCurrentColor.getRgb( &r, &g, &b, &a );
253  int h, s, v;
254  mCurrentColor.getHsv( &h, &s, &v );
255  //overwrite hue with explicit hue if required
256  h = hue();
257 
258  switch ( mComponent )
259  {
260  case QgsColorWidget::Red:
261  if ( r == valueClipped )
262  {
263  return;
264  }
265  mCurrentColor.setRed( valueClipped );
266  break;
268  if ( g == valueClipped )
269  {
270  return;
271  }
272  mCurrentColor.setGreen( valueClipped );
273  break;
275  if ( b == valueClipped )
276  {
277  return;
278  }
279  mCurrentColor.setBlue( valueClipped );
280  break;
281  case QgsColorWidget::Hue:
282  if ( h == valueClipped )
283  {
284  return;
285  }
286  mCurrentColor.setHsv( valueClipped, s, v, a );
287  break;
289  if ( s == valueClipped )
290  {
291  return;
292  }
293  mCurrentColor.setHsv( h, valueClipped, v, a );
294  break;
296  if ( v == valueClipped )
297  {
298  return;
299  }
300  mCurrentColor.setHsv( h, s, valueClipped, a );
301  break;
303  if ( a == valueClipped )
304  {
305  return;
306  }
307  mCurrentColor.setAlpha( valueClipped );
308  break;
309  default:
310  return;
311  }
312 
313  //update recorded hue
314  if ( mCurrentColor.hue() >= 0 )
315  {
317  }
318 
319  update();
320 }
321 
322 void QgsColorWidget::setColor( const QColor &color, const bool emitSignals )
323 {
324  if ( color == mCurrentColor )
325  {
326  return;
327  }
328 
330 
331  //update recorded hue
332  if ( color.hue() >= 0 )
333  {
334  mExplicitHue = color.hue();
335  }
336 
337  if ( emitSignals )
338  {
339  emit colorChanged( mCurrentColor );
340  }
341 
342  update();
343 }
344 
345 
346 //
347 // QgsColorWheel
348 //
349 
351  : QgsColorWidget( parent )
352  , mMargin( 4 )
353  , mWheelThickness( 18 )
354  , mClickedPart( QgsColorWheel::None )
355  , mWheelImage( 0 )
356  , mTriangleImage( 0 )
357  , mWidgetImage( 0 )
358  , mWheelDirty( true )
359  , mTriangleDirty( true )
360 {
361  //create wheel hue brush - only do this once
362  QConicalGradient wheelGradient = QConicalGradient( 0, 0, 0 );
363  int wheelStops = 20;
364  QColor gradColor = QColor::fromHsvF( 1.0, 1.0, 1.0 );
365  for ( int pos = 0; pos <= wheelStops; ++pos )
366  {
367  double relativePos = ( double )pos / wheelStops;
368  gradColor.setHsvF( relativePos, 1, 1 );
369  wheelGradient.setColorAt( relativePos, gradColor );
370  }
371  mWheelBrush = QBrush( wheelGradient );
372 }
373 
375 {
376  delete mWheelImage;
377  delete mTriangleImage;
378  delete mWidgetImage;
379 }
380 
382 {
383  Q_UNUSED( event );
384  QPainter painter( this );
385 
386  //draw a frame
388  option.initFrom( this );
389  option.state = this->hasFocus() ? QStyle::State_Active : QStyle::State_None;
390  style()->drawPrimitive( QStyle::PE_Frame, &option, &painter );
391 
392  if ( !mWidgetImage || !mWheelImage || !mTriangleImage )
393  {
394  createImages( size() );
395  }
396 
397  //draw everything in an image
398  mWidgetImage->fill( Qt::transparent );
399  QPainter imagePainter( mWidgetImage );
400  imagePainter.setRenderHint( QPainter::Antialiasing );
401 
402  if ( mWheelDirty )
403  {
404  //need to redraw the wheel image
405  createWheel();
406  }
407 
408  //draw wheel centered on widget
409  QPointF center = QPointF( width() / 2.0, height() / 2.0 );
410  imagePainter.drawImage( QPointF( center.x() - ( mWheelImage->width() / 2.0 ), center.y() - ( mWheelImage->height() / 2.0 ) ), *mWheelImage );
411 
412  //draw hue marker
413  int h = hue();
414  double length = mWheelImage->width() / 2.0;
415  QLineF hueMarkerLine = QLineF( center.x(), center.y(), center.x() + length, center.y() );
416  hueMarkerLine.setAngle( h );
417  imagePainter.save();
418  //use sourceIn mode for nicer antialiasing
419  imagePainter.setCompositionMode( QPainter::CompositionMode_SourceIn );
420  QPen pen;
421  pen.setWidth( 2 );
422  //adapt pen color for hue
423  pen.setColor( h > 20 && h < 200 ? Qt::black : Qt::white );
424  imagePainter.setPen( pen );
425  imagePainter.drawLine( hueMarkerLine );
426  imagePainter.restore();
427 
428  //draw triangle
429  if ( mTriangleDirty )
430  {
431  createTriangle();
432  }
433  imagePainter.drawImage( QPointF( center.x() - ( mWheelImage->width() / 2.0 ), center.y() - ( mWheelImage->height() / 2.0 ) ), *mTriangleImage );
434 
435  //draw current color marker
436  double triangleRadius = length - mWheelThickness - 1;
437 
438  //adapted from equations at https://github.com/timjb/colortriangle/blob/master/colortriangle.js by Tim Baumann
439  double lightness = mCurrentColor.lightnessF();
440  double hueRadians = ( h * M_PI / 180.0 );
441  double hx = cos( hueRadians ) * triangleRadius;
442  double hy = -sin( hueRadians ) * triangleRadius;
443  double sx = -cos( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius;
444  double sy = -sin( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius;
445  double vx = -cos( hueRadians + ( M_PI / 3.0 ) ) * triangleRadius;
446  double vy = sin( hueRadians + ( M_PI / 3.0 ) ) * triangleRadius;
447  double mx = ( sx + vx ) / 2.0;
448  double my = ( sy + vy ) / 2.0;
449 
450  double a = ( 1 - 2.0 * fabs( lightness - 0.5 ) ) * mCurrentColor.hslSaturationF();
451  double x = sx + ( vx - sx ) * lightness + ( hx - mx ) * a;
452  double y = sy + ( vy - sy ) * lightness + ( hy - my ) * a;
453 
454  //adapt pen color for lightness
455  pen.setColor( lightness > 0.7 ? Qt::black : Qt::white );
456  imagePainter.setPen( pen );
457  imagePainter.setBrush( Qt::NoBrush );
458  imagePainter.drawEllipse( QPointF( x + center.x(), y + center.y() ), 4.0, 4.0 );
459  imagePainter.end();
460 
461  //draw image onto widget
462  painter.drawImage( QPoint( 0, 0 ), *mWidgetImage );
463  painter.end();
464 }
465 
466 void QgsColorWheel::setColor( const QColor &color, const bool emitSignals )
467 {
468  if ( color.hue() >= 0 && color.hue() != hue() )
469  {
470  //hue has changed, need to redraw the triangle
471  mTriangleDirty = true;
472  }
473 
474  QgsColorWidget::setColor( color, emitSignals );
475 }
476 
477 void QgsColorWheel::createImages( const QSizeF size )
478 {
479  double wheelSize = qMin( size.width(), size.height() ) - mMargin * 2.0;
480  mWheelThickness = wheelSize / 15.0;
481 
482  //recreate cache images at correct size
483  delete mWheelImage;
484  mWheelImage = new QImage( wheelSize, wheelSize, QImage::Format_ARGB32 );
485  delete mTriangleImage;
486  mTriangleImage = new QImage( wheelSize, wheelSize, QImage::Format_ARGB32 );
487  delete mWidgetImage;
488  mWidgetImage = new QImage( size.width(), size.height(), QImage::Format_ARGB32 );
489 
490  //trigger a redraw for the images
491  mWheelDirty = true;
492  mTriangleDirty = true;
493 }
494 
496 {
497  //recreate images for new size
498  createImages( event->size() );
500 }
501 
502 void QgsColorWheel::setColorFromPos( const QPointF pos )
503 {
504  QPointF center = QPointF( width() / 2.0, height() / 2.0 );
505  //line from center to mouse position
506  QLineF line = QLineF( center.x(), center.y(), pos.x(), pos.y() );
507 
508  QColor newColor = QColor();
509 
510  int h, s, l, alpha;
511  mCurrentColor.getHsl( &h, &s, &l, &alpha );
512  //override hue with explicit hue, so we don't get -1 values from QColor for hue
513  h = hue();
514 
515  if ( mClickedPart == QgsColorWheel::Triangle )
516  {
517  //adapted from equations at https://github.com/timjb/colortriangle/blob/master/colortriangle.js by Tim Baumann
518 
519  //position of event relative to triangle center
520  double x = pos.x() - center.x();
521  double y = pos.y() - center.y();
522 
523  double eventAngleRadians = line.angle() * M_PI / 180.0;
524  double hueRadians = h * M_PI / 180.0;
525  double rad0 = fmod( eventAngleRadians + 2.0 * M_PI - hueRadians, 2.0 * M_PI );
526  double rad1 = fmod( rad0, (( 2.0 / 3.0 ) * M_PI ) ) - ( M_PI / 3.0 );
527  double length = mWheelImage->width() / 2.0;
528  double triangleLength = length - mWheelThickness - 1;
529 
530  double a = 0.5 * triangleLength;
531  double b = tan( rad1 ) * a;
532  double r = sqrt( x * x + y * y );
533  double maxR = sqrt( a * a + b * b );
534 
535  if ( r > maxR )
536  {
537  double dx = tan( rad1 ) * r;
538  double rad2 = atan( dx / maxR );
539  rad2 = qMin( rad2, M_PI / 3.0 );
540  rad2 = qMax( rad2, -M_PI / 3.0 );
541  eventAngleRadians += rad2 - rad1;
542  rad0 = fmod( eventAngleRadians + 2.0 * M_PI - hueRadians, 2.0 * M_PI );
543  rad1 = fmod( rad0, (( 2.0 / 3.0 ) * M_PI ) ) - ( M_PI / 3.0 );
544  b = tan( rad1 ) * a;
545  r = sqrt( a * a + b * b );
546  }
547 
548  double triangleSideLength = sqrt( 3.0 ) * triangleLength;
549  double newL = (( -sin( rad0 ) * r ) / triangleSideLength ) + 0.5;
550  double widthShare = 1.0 - ( fabs( newL - 0.5 ) * 2.0 );
551  double newS = ((( cos( rad0 ) * r ) + ( triangleLength / 2.0 ) ) / ( 1.5 * triangleLength ) ) / widthShare;
552  s = qMin( qRound( qMax( 0.0, newS ) * 255.0 ), 255 );
553  l = qMin( qRound( qMax( 0.0, newL ) * 255.0 ), 255 );
554  newColor = QColor::fromHsl( h, s, l );
555  //explicitly set the hue again, so that it's exact
556  newColor.setHsv( h, newColor.hsvSaturation(), newColor.value(), alpha );
557  }
558  else if ( mClickedPart == QgsColorWheel::Wheel )
559  {
560  //use hue angle
562  int v = mCurrentColor.value();
563  int newHue = line.angle();
564  newColor = QColor::fromHsv( newHue, s, v, alpha );
565  //hue has changed, need to redraw triangle
566  mTriangleDirty = true;
567  }
568 
569  if ( newColor.isValid() && newColor != mCurrentColor )
570  {
571  //color has changed
572  mCurrentColor = QColor( newColor );
573 
574  if ( mCurrentColor.hue() >= 0 )
575  {
576  //color has a valid hue, so update the QgsColorWidget's explicit hue
578  }
579 
580  update();
581  emit colorChanged( mCurrentColor );
582  }
583 }
584 
586 {
587  setColorFromPos( event->posF() );
588 }
589 
591 {
592  //calculate where the event occurred -- on the wheel or inside the triangle?
593 
594  //create a line from the widget's center to the event
595  QLineF line = QLineF( width() / 2.0, height() / 2.0, event->pos().x(), event->pos().y() );
596 
597  double innerLength = mWheelImage->width() / 2.0 - mWheelThickness;
598  if ( line.length() < innerLength )
599  {
600  mClickedPart = QgsColorWheel::Triangle;
601  }
602  else
603  {
604  mClickedPart = QgsColorWheel::Wheel;
605  }
606  setColorFromPos( event->posF() );
607 }
608 
610 {
611  Q_UNUSED( event );
612  mClickedPart = QgsColorWheel::None;
613 }
614 
615 void QgsColorWheel::createWheel()
616 {
617  if ( !mWheelImage )
618  {
619  return;
620  }
621 
622  int maxSize = qMin( mWheelImage->width(), mWheelImage->height() );
623  double wheelRadius = maxSize / 2.0;
624 
625  mWheelImage->fill( Qt::transparent );
626  QPainter p( mWheelImage );
627  p.setRenderHint( QPainter::Antialiasing );
628  p.setBrush( mWheelBrush );
629  p.setPen( Qt::NoPen );
630 
631  //draw hue wheel as a circle
632  p.translate( wheelRadius, wheelRadius );
633  p.drawEllipse( QPointF( 0.0, 0.0 ), wheelRadius, wheelRadius );
634 
635  //cut hole in center of circle to make a ring
636  p.setCompositionMode( QPainter::CompositionMode_DestinationOut );
637  p.setBrush( QBrush( Qt::black ) );
638  p.drawEllipse( QPointF( 0.0, 0.0 ), wheelRadius - mWheelThickness, wheelRadius - mWheelThickness );
639  p.end();
640 
641  mWheelDirty = false;
642 }
643 
644 void QgsColorWheel::createTriangle()
645 {
646  if ( !mWheelImage || !mTriangleImage )
647  {
648  return;
649  }
650 
651  QPointF center = QPointF( mWheelImage->width() / 2.0, mWheelImage->height() / 2.0 );
652  mTriangleImage->fill( Qt::transparent );
653 
654  QPainter imagePainter( mTriangleImage );
655  imagePainter.setRenderHint( QPainter::Antialiasing );
656 
657  int angle = hue();
658  double wheelRadius = mWheelImage->width() / 2.0;
659  double triangleRadius = wheelRadius - mWheelThickness - 1;
660 
661  //pure version of hue (at full saturation and value)
662  QColor pureColor = QColor::fromHsv( angle, 255, 255 );
663  //create copy of color but with 0 alpha
664  QColor alphaColor = QColor( pureColor );
665  alphaColor.setAlpha( 0 );
666 
667  //some rather ugly shortcuts to obtain corners and midpoints of triangle
668  QLineF line1 = QLineF( center.x(), center.y(), center.x() - triangleRadius * cos( M_PI / 3.0 ), center.y() - triangleRadius * sin( M_PI / 3.0 ) );
669  QLineF line2 = QLineF( center.x(), center.y(), center.x() + triangleRadius, center.y() );
670  QLineF line3 = QLineF( center.x(), center.y(), center.x() - triangleRadius * cos( M_PI / 3.0 ), center.y() + triangleRadius * sin( M_PI / 3.0 ) );
671  QLineF line4 = QLineF( center.x(), center.y(), center.x() - triangleRadius * cos( M_PI / 3.0 ), center.y() );
672  QLineF line5 = QLineF( center.x(), center.y(), ( line2.p2().x() + line1.p2().x() ) / 2.0, ( line2.p2().y() + line1.p2().y() ) / 2.0 );
673  line1.setAngle( line1.angle() + angle );
674  line2.setAngle( line2.angle() + angle );
675  line3.setAngle( line3.angle() + angle );
676  line4.setAngle( line4.angle() + angle );
677  line5.setAngle( line5.angle() + angle );
678  QPointF p1 = line1.p2();
679  QPointF p2 = line2.p2();
680  QPointF p3 = line3.p2();
681  QPointF p4 = line4.p2();
682  QPointF p5 = line5.p2();
683 
684  //inspired by Tim Baumann's work at https://github.com/timjb/colortriangle/blob/master/colortriangle.js
685  QLinearGradient colorGrad = QLinearGradient( p4.x(), p4.y(), p2.x(), p2.y() );
686  colorGrad.setColorAt( 0, alphaColor );
687  colorGrad.setColorAt( 1, pureColor );
688  QLinearGradient whiteGrad = QLinearGradient( p3.x(), p3.y(), p5.x(), p5.y() );
689  whiteGrad.setColorAt( 0, QColor( 255, 255, 255, 255 ) );
690  whiteGrad.setColorAt( 1, QColor( 255, 255, 255, 0 ) );
691 
692  QPolygonF triangle;
693  triangle << p2 << p1 << p3 << p2;
694  imagePainter.setPen( Qt::NoPen );
695  //start with a black triangle
696  imagePainter.setBrush( QBrush( Qt::black ) );
697  imagePainter.drawPolygon( triangle );
698  //draw a gradient from transparent to the pure color at the triangle's tip
699  imagePainter.setBrush( QBrush( colorGrad ) );
700  imagePainter.drawPolygon( triangle );
701  //draw a white gradient using additive composition mode
702  imagePainter.setCompositionMode( QPainter::CompositionMode_Plus );
703  imagePainter.setBrush( QBrush( whiteGrad ) );
704  imagePainter.drawPolygon( triangle );
705 
706  //above process results in some small artifacts on the edge of the triangle. Let's clear these up
707  //use source composition mode and draw an outline using a transparent pen
708  //this clears the edge pixels and leaves a nice smooth image
709  imagePainter.setCompositionMode( QPainter::CompositionMode_Source );
710  imagePainter.setBrush( Qt::NoBrush );
711  imagePainter.setPen( QPen( Qt::transparent ) );
712  imagePainter.drawPolygon( triangle );
713 
714  imagePainter.end();
715  mTriangleDirty = false;
716 }
717 
718 
719 
720 //
721 // QgsColorBox
722 //
723 
725  : QgsColorWidget( parent, component )
726  , mMargin( 2 )
727  , mBoxImage( 0 )
728  , mDirty( true )
729 {
730  setFocusPolicy( Qt::StrongFocus );
731  setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );
732 
733  mBoxImage = new QImage( width() - mMargin * 2, height() - mMargin * 2, QImage::Format_RGB32 );
734 }
735 
737 {
738  delete mBoxImage;
739 }
740 
742 {
743  return QSize( 200, 200 );
744 }
745 
747 {
748  Q_UNUSED( event );
749  QPainter painter( this );
750 
751  QStyleOptionFrameV3 option;
752  option.initFrom( this );
753  option.state = hasFocus() ? QStyle::State_Active : QStyle::State_None;
754  style()->drawPrimitive( QStyle::PE_Frame, &option, &painter );
755 
756  if ( mDirty )
757  {
758  createBox();
759  }
760 
761  //draw background image
762  painter.drawImage( QPoint( mMargin, mMargin ), *mBoxImage );
763 
764  //draw cross lines
765  double xPos = mMargin + ( width() - 2 * mMargin - 1 ) * ( double )xComponentValue() / ( double )valueRangeX();
766  double yPos = mMargin + ( height() - 2 * mMargin - 1 ) - ( height() - 2 * mMargin - 1 ) * ( double )yComponentValue() / ( double )valueRangeY();
767 
768  painter.setBrush( Qt::white );
769  painter.setPen( Qt::NoPen );
770 
771  painter.drawRect( xPos - 1, mMargin, 3, height() - 2 * mMargin - 1 );
772  painter.drawRect( mMargin, yPos - 1, width() - 2 * mMargin - 1, 3 );
773  painter.setPen( Qt::black );
774  painter.drawLine( xPos, mMargin, xPos, height() - mMargin - 1 );
775  painter.drawLine( mMargin, yPos, width() - mMargin - 1, yPos );
776 
777  painter.end();
778 }
779 
781 {
782  if ( component != mComponent )
783  {
784  //need to redraw
785  mDirty = true;
786  }
787  QgsColorWidget::setComponent( component );
788 }
789 
790 void QgsColorBox::setColor( const QColor &color, const bool emitSignals )
791 {
792  //check if we need to redraw the box image
793  if ( mComponent == QgsColorWidget::Red && mCurrentColor.red() != color.red() )
794  {
795  mDirty = true;
796  }
797  else if ( mComponent == QgsColorWidget::Green && mCurrentColor.green() != color.green() )
798  {
799  mDirty = true;
800  }
801  else if ( mComponent == QgsColorWidget::Blue && mCurrentColor.blue() != color.blue() )
802  {
803  mDirty = true;
804  }
805  else if ( mComponent == QgsColorWidget::Hue && color.hsvHue() >= 0 && hue() != color.hsvHue() )
806  {
807  mDirty = true;
808  }
810  {
811  mDirty = true;
812  }
813  else if ( mComponent == QgsColorWidget::Value && mCurrentColor.value() != color.value() )
814  {
815  mDirty = true;
816  }
817  QgsColorWidget::setColor( color, emitSignals );
818 }
819 
821 {
822  mDirty = true;
823  delete mBoxImage;
824  mBoxImage = new QImage( event->size().width() - mMargin * 2, event->size().height() - mMargin * 2, QImage::Format_RGB32 );
826 }
827 
829 {
830  setColorFromPoint( event->pos() );
831 }
832 
834 {
835  setColorFromPoint( event->pos() );
836 }
837 
838 void QgsColorBox::createBox()
839 {
840  int maxValueX = mBoxImage->width();
841  int maxValueY = mBoxImage->height();
842 
843  //create a temporary color object
844  QColor currentColor = QColor( mCurrentColor );
845  int colorComponentValue;
846 
847  for ( int y = 0; y < maxValueY; ++y )
848  {
849  QRgb* scanLine = ( QRgb* )mBoxImage->scanLine( y );
850 
851  colorComponentValue = int( valueRangeY() - valueRangeY() * ( double( y ) / maxValueY ) );
852  alterColor( currentColor, yComponent(), colorComponentValue );
853  for ( int x = 0; x < maxValueX; ++x )
854  {
855  colorComponentValue = int( valueRangeX() * ( double( x ) / maxValueX ) );
856  alterColor( currentColor, xComponent(), colorComponentValue );
857  scanLine[x] = currentColor.rgb();
858  }
859  }
860  mDirty = false;
861 }
862 
863 int QgsColorBox::valueRangeX() const
864 {
865  return componentRange( xComponent() );
866 }
867 
868 int QgsColorBox::valueRangeY() const
869 {
870  return componentRange( yComponent() );
871 }
872 
873 QgsColorWidget::ColorComponent QgsColorBox::yComponent() const
874 {
875  switch ( mComponent )
876  {
877  case QgsColorWidget::Red:
878  return QgsColorWidget::Green;
881  return QgsColorWidget::Red;
882  case QgsColorWidget::Hue:
886  return QgsColorWidget::Hue;
887  default:
888  //should not occur
889  return QgsColorWidget::Red;
890  }
891 }
892 
893 int QgsColorBox::yComponentValue() const
894 {
895  return componentValue( yComponent() );
896 }
897 
898 QgsColorWidget::ColorComponent QgsColorBox::xComponent() const
899 {
900  switch ( mComponent )
901  {
902  case QgsColorWidget::Red:
904  return QgsColorWidget::Blue;
906  return QgsColorWidget::Green;
907  case QgsColorWidget::Hue:
909  return QgsColorWidget:: Value;
912  default:
913  //should not occur
914  return QgsColorWidget::Red;
915  }
916 }
917 
918 int QgsColorBox::xComponentValue() const
919 {
920  return componentValue( xComponent() );
921 }
922 
923 void QgsColorBox::setColorFromPoint( const QPoint &point )
924 {
925  int valX = valueRangeX() * ( point.x() - mMargin ) / ( width() - 2 * mMargin - 1 );
926  valX = qMin( qMax( valX, 0 ), valueRangeX() );
927 
928  int valY = valueRangeY() - valueRangeY() * ( point.y() - mMargin ) / ( height() - 2 * mMargin - 1 );
929  valY = qMin( qMax( valY, 0 ), valueRangeY() );
930 
932  alterColor( color, xComponent(), valX );
933  alterColor( color, yComponent(), valY );
934 
935  if ( color == mCurrentColor )
936  {
937  return;
938  }
939 
940  if ( color.hue() >= 0 )
941  {
942  mExplicitHue = color.hue();
943  }
944 
946  update();
947  emit colorChanged( color );
948 }
949 
950 
951 //
952 // QgsColorRampWidget
953 //
954 
956  const QgsColorWidget::ColorComponent component,
957  const Orientation orientation )
958  : QgsColorWidget( parent, component )
959  , mMargin( 4 )
960  , mShowFrame( false )
961 {
962  setFocusPolicy( Qt::StrongFocus );
963  setOrientation( orientation );
964 
965  //create triangle polygons
966  setMarkerSize( 5 );
967 }
968 
970 {
971 
972 }
973 
975 {
976  if ( mOrientation == QgsColorRampWidget::Horizontal )
977  {
978  //horizontal
979  return QSize( 200, 28 );
980  }
981  else
982  {
983  //vertical
984  return QSize( 18, 200 );
985  }
986 }
987 
989 {
990  Q_UNUSED( event );
991  QPainter painter( this );
992 
993  if ( mShowFrame )
994  {
995  //draw frame
996  QStyleOptionFrameV3 option;
997  option.initFrom( this );
998  option.state = hasFocus() ? QStyle::State_KeyboardFocusChange : QStyle::State_None;
999  style()->drawPrimitive( QStyle::PE_Frame, &option, &painter );
1000  }
1001 
1002  if ( hasFocus() )
1003  {
1004  //draw focus rect
1005  QStyleOptionFocusRect option;
1006  option.initFrom( this );
1007  option.state = QStyle::State_KeyboardFocusChange;
1008  style()->drawPrimitive( QStyle::PE_FrameFocusRect, &option, &painter );
1009  }
1010 
1012  {
1013  int maxValue = ( mOrientation == QgsColorRampWidget::Horizontal ? width() : height() ) - 1 - 2 * mMargin;
1014  QColor color = QColor( mCurrentColor );
1015  color.setAlpha( 255 );
1016  QPen pen;
1017  pen.setWidth( 0 );
1018  painter.setPen( pen );
1019  painter.setBrush( Qt::NoBrush );
1020 
1021  //draw background ramp
1022  for ( int c = 0; c <= maxValue; ++c )
1023  {
1024  int colorVal = componentRange() * ( double )c / maxValue;
1025  //vertical sliders are reversed
1026  if ( mOrientation == QgsColorRampWidget::Vertical )
1027  {
1028  colorVal = componentRange() - colorVal;
1029  }
1030  alterColor( color, mComponent, colorVal );
1031  if ( color.hue() < 0 )
1032  {
1033  color.setHsv( hue(), color.saturation(), color.value() );
1034  }
1035  pen.setColor( color );
1036  painter.setPen( pen );
1037  if ( mOrientation == QgsColorRampWidget::Horizontal )
1038  {
1039  //horizontal
1040  painter.drawLine( c + mMargin, mMargin, c + mMargin, height() - mMargin - 1 );
1041  }
1042  else
1043  {
1044  //vertical
1045  painter.drawLine( mMargin, c + mMargin, width() - mMargin - 1, c + mMargin );
1046  }
1047  }
1048  }
1049  else if ( mComponent == QgsColorWidget::Alpha )
1050  {
1051  //alpha ramps are drawn differently
1052  //start with the checkboard pattern
1053  QBrush checkBrush = QBrush( transparentBackground() );
1054  painter.setBrush( checkBrush );
1055  painter.setPen( Qt::NoPen );
1056  painter.drawRect( mMargin, mMargin, width() - 2 * mMargin - 1, height() - 2 * mMargin - 1 );
1057  QLinearGradient colorGrad;
1058  if ( mOrientation == QgsColorRampWidget::Horizontal )
1059  {
1060  //horizontal
1061  colorGrad = QLinearGradient( mMargin, 0, width() - mMargin - 1, 0 );
1062  }
1063  else
1064  {
1065  //vertical
1066  colorGrad = QLinearGradient( 0, mMargin, 0, height() - mMargin - 1 );
1067  }
1068  QColor transparent = QColor( mCurrentColor );
1069  transparent.setAlpha( 0 );
1070  colorGrad.setColorAt( 0, transparent );
1071  QColor opaque = QColor( mCurrentColor );
1072  opaque.setAlpha( 255 );
1073  colorGrad.setColorAt( 1, opaque );
1074  QBrush colorBrush = QBrush( colorGrad );
1075  painter.setBrush( colorBrush );
1076  painter.drawRect( mMargin, mMargin, width() - 2 * mMargin - 1, height() - 2 * mMargin - 1 );
1077  }
1078 
1079  if ( mOrientation == QgsColorRampWidget::Horizontal )
1080  {
1081  //draw marker triangles for horizontal ramps
1082  painter.setRenderHint( QPainter::Antialiasing );
1083  painter.setBrush( QBrush( Qt::black ) );
1084  painter.setPen( Qt::NoPen );
1085  painter.translate( mMargin + ( width() - 2 * mMargin ) * ( double )componentValue() / componentRange(), mMargin - 1 );
1086  painter.drawPolygon( mTopTriangle );
1087  painter.translate( 0, height() - mMargin - 2 );
1088  painter.setBrush( QBrush( Qt::white ) );
1089  painter.drawPolygon( mBottomTriangle );
1090  painter.end();
1091  }
1092  else
1093  {
1094  //draw cross lines for vertical ramps
1095  int ypos = mMargin + ( height() - 2 * mMargin - 1 ) - ( height() - 2 * mMargin - 1 ) * ( double )componentValue() / componentRange();
1096  painter.setBrush( Qt::white );
1097  painter.setPen( Qt::NoPen );
1098  painter.drawRect( mMargin, ypos - 1, width() - 2 * mMargin - 1, 3 );
1099  painter.setPen( Qt::black );
1100  painter.drawLine( mMargin, ypos, width() - mMargin - 1, ypos );
1101  }
1102 }
1103 
1105 {
1106  mOrientation = orientation;
1107  if ( orientation == QgsColorRampWidget::Horizontal )
1108  {
1109  //horizontal
1110  setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1111  }
1112  else
1113  {
1114  //vertical
1115  setSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding );
1116  }
1117  updateGeometry();
1118 }
1119 
1121 {
1122  if ( margin == mMargin )
1123  {
1124  return;
1125  }
1126  mMargin = margin;
1127  update();
1128 }
1129 
1130 void QgsColorRampWidget::setShowFrame( const bool showFrame )
1131 {
1132  if ( showFrame == mShowFrame )
1133  {
1134  return;
1135  }
1136  mShowFrame = showFrame;
1137  update();
1138 }
1139 
1140 void QgsColorRampWidget::setMarkerSize( const int markerSize )
1141 {
1142  //create triangle polygons
1143  mTopTriangle << QPoint( -markerSize, 0 ) << QPoint( markerSize, 0 ) << QPoint( 0, markerSize );
1144  mBottomTriangle << QPoint( -markerSize, 0 ) << QPoint( markerSize, 0 ) << QPoint( 0, -markerSize );
1145  update();
1146 }
1147 
1149 {
1150  setColorFromPoint( event->posF() );
1151 }
1152 
1154 {
1155  setColorFromPoint( event->posF() );
1156 }
1157 
1159 {
1160  int oldValue = componentValue();
1161  if (( mOrientation == QgsColorRampWidget::Horizontal && ( event->key() == Qt::Key_Right || event->key() == Qt::Key_Up ) )
1162  || ( mOrientation == QgsColorRampWidget::Vertical && ( event->key() == Qt::Key_Left || event->key() == Qt::Key_Up ) ) )
1163  {
1165  }
1166  else if (( mOrientation == QgsColorRampWidget::Horizontal && ( event->key() == Qt::Key_Left || event->key() == Qt::Key_Down ) )
1167  || ( mOrientation == QgsColorRampWidget::Vertical && ( event->key() == Qt::Key_Right || event->key() == Qt::Key_Down ) ) )
1168  {
1170  }
1171  else if (( mOrientation == QgsColorRampWidget::Horizontal && event->key() == Qt::Key_PageDown )
1172  || ( mOrientation == QgsColorRampWidget::Vertical && event->key() == Qt::Key_PageUp ) )
1173  {
1175  }
1176  else if (( mOrientation == QgsColorRampWidget::Horizontal && event->key() == Qt::Key_PageUp )
1177  || ( mOrientation == QgsColorRampWidget::Vertical && event->key() == Qt::Key_PageDown ) )
1178  {
1180  }
1181  else if (( mOrientation == QgsColorRampWidget::Horizontal && event->key() == Qt::Key_Home )
1182  || ( mOrientation == QgsColorRampWidget::Vertical && event->key() == Qt::Key_End ) )
1183  {
1184  setComponentValue( 0 );
1185  }
1186  else if (( mOrientation == QgsColorRampWidget::Horizontal && event->key() == Qt::Key_End )
1187  || ( mOrientation == QgsColorRampWidget::Vertical && event->key() == Qt::Key_Home ) )
1188  {
1189  //set to maximum value
1191  }
1192  else
1193  {
1195  return;
1196  }
1197 
1198  if ( componentValue() != oldValue )
1199  {
1200  //value has changed
1201  emit colorChanged( mCurrentColor );
1202  emit valueChanged( componentValue() );
1203  }
1204 }
1205 
1206 void QgsColorRampWidget::setColorFromPoint( const QPointF &point )
1207 {
1208  int oldValue = componentValue();
1209  int val;
1210  if ( mOrientation == QgsColorRampWidget::Horizontal )
1211  {
1212  val = componentRange() * ( point.x() - mMargin ) / ( width() - 2 * mMargin );
1213  }
1214  else
1215  {
1216  val = componentRange() - componentRange() * ( point.y() - mMargin ) / ( height() - 2 * mMargin );
1217  }
1218  val = qMax( 0, qMin( val, componentRange() ) );
1219  setComponentValue( val );
1220 
1221  if ( componentValue() != oldValue )
1222  {
1223  //value has changed
1224  emit colorChanged( mCurrentColor );
1225  emit valueChanged( componentValue() );
1226  }
1227 }
1228 
1229 //
1230 // QgsColorSliderWidget
1231 //
1232 
1234  : QgsColorWidget( parent, component )
1235  , mRampWidget( 0 )
1236  , mSpinBox( 0 )
1237 {
1238  QHBoxLayout* hLayout = new QHBoxLayout();
1239  hLayout->setMargin( 0 );
1240  hLayout->setSpacing( 5 );
1241 
1242  mRampWidget = new QgsColorRampWidget( 0, component );
1243  mRampWidget->setColor( mCurrentColor );
1244  hLayout->addWidget( mRampWidget, 1 );
1245 
1246  mSpinBox = new QSpinBox();
1247  //set spinbox to a reasonable width
1248  int largestCharWidth = mSpinBox->fontMetrics().width( "888%" );
1249  mSpinBox->setMinimumWidth( largestCharWidth + 35 );
1250  mSpinBox->setMinimum( 0 );
1251  mSpinBox->setMaximum( convertRealToDisplay( componentRange() ) );
1252  mSpinBox->setValue( convertRealToDisplay( componentValue() ) );
1253  if ( component == QgsColorWidget::Hue )
1254  {
1255  //degrees suffix for hue
1256  mSpinBox->setSuffix( QChar( 176 ) );
1257  }
1258  else if ( component == QgsColorWidget::Saturation || component == QgsColorWidget::Value || component == QgsColorWidget::Alpha )
1259  {
1260  mSpinBox->setSuffix( tr( "%" ) );
1261  }
1262  hLayout->addWidget( mSpinBox );
1263  setLayout( hLayout );
1264 
1265  connect( mRampWidget, SIGNAL( valueChanged( int ) ), this, SLOT( rampChanged( int ) ) );
1266  connect( mRampWidget, SIGNAL( colorChanged( const QColor ) ), this, SLOT( rampColorChanged( const QColor ) ) );
1267  connect( mSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( spinChanged( int ) ) );
1268 }
1269 
1271 {
1272 }
1273 
1275 {
1276  QgsColorWidget::setComponent( component );
1277  mRampWidget->setComponent( component );
1278  mSpinBox->setMaximum( convertRealToDisplay( componentRange() ) );
1279  if ( component == QgsColorWidget::Hue )
1280  {
1281  //degrees suffix for hue
1282  mSpinBox->setSuffix( QChar( 176 ) );
1283  }
1284  else if ( component == QgsColorWidget::Saturation || component == QgsColorWidget::Value || component == QgsColorWidget::Alpha )
1285  {
1286  //saturation, value and alpha are in %
1287  mSpinBox->setSuffix( tr( "%" ) );
1288  }
1289  else
1290  {
1291  //clear suffix
1292  mSpinBox->setSuffix( QString() );
1293  }
1294 }
1295 
1297 {
1299  mRampWidget->blockSignals( true );
1300  mRampWidget->setComponentValue( value );
1301  mRampWidget->blockSignals( false );
1302  mSpinBox->blockSignals( true );
1303  mSpinBox->setValue( convertRealToDisplay( value ) );
1304  mSpinBox->blockSignals( false );
1305 }
1306 
1307 void QgsColorSliderWidget::setColor( const QColor &color, bool emitSignals )
1308 {
1309  QgsColorWidget::setColor( color, emitSignals );
1310  mRampWidget->setColor( color );
1311  mSpinBox->blockSignals( true );
1312  mSpinBox->setValue( convertRealToDisplay( componentValue() ) );
1313  mSpinBox->blockSignals( false );
1314 }
1315 
1316 void QgsColorSliderWidget::rampColorChanged( const QColor &color )
1317 {
1318  emit colorChanged( color );
1319 }
1320 
1321 void QgsColorSliderWidget::spinChanged( int value )
1322 {
1323  int convertedValue = convertDisplayToReal( value );
1324  QgsColorWidget::setComponentValue( convertedValue );
1325  mRampWidget->setComponentValue( convertedValue );
1326  emit colorChanged( mCurrentColor );
1327 }
1328 
1329 void QgsColorSliderWidget::rampChanged( int value )
1330 {
1331  mSpinBox->blockSignals( true );
1332  mSpinBox->setValue( convertRealToDisplay( value ) );
1333  mSpinBox->blockSignals( false );
1334 }
1335 
1336 
1337 int QgsColorSliderWidget::convertRealToDisplay( const int realValue ) const
1338 {
1339  //scale saturation, value or alpha to 0->100 range. This makes more sense for users
1340  //for whom "255" is a totally arbitrary value!
1342  {
1343  return qRound( 100.0 * realValue / 255.0 );
1344  }
1345 
1346  //leave all other values intact
1347  return realValue;
1348 }
1349 
1350 int QgsColorSliderWidget::convertDisplayToReal( const int displayValue ) const
1351 {
1352  //scale saturation, value or alpha from 0->100 range (see note in convertRealToDisplay)
1354  {
1355  return qRound( 255.0 * displayValue / 100.0 );
1356  }
1357 
1358  //leave all other values intact
1359  return displayValue;
1360 }
1361 
1362 //
1363 // QgsColorTextWidget
1364 //
1365 
1367  : QgsColorWidget( parent )
1368  , mLineEdit( 0 )
1369  , mMenuButton( 0 )
1370  , mFormat( QgsColorTextWidget::HexRgb )
1371 {
1372  QHBoxLayout* hLayout = new QHBoxLayout();
1373  hLayout->setMargin( 0 );
1374  hLayout->setSpacing( 0 );
1375 
1376  mLineEdit = new QLineEdit( 0 );
1377  hLayout->addWidget( mLineEdit );
1378 
1379  mMenuButton = new QToolButton( mLineEdit );
1380  mMenuButton->setIcon( QgsApplication::getThemeIcon( "/mIconDropDownMenu.svg" ) );
1381  mMenuButton->setCursor( Qt::ArrowCursor );
1382  mMenuButton->setFocusPolicy( Qt::NoFocus );
1383  mMenuButton->setStyleSheet( "QToolButton { border: none; padding: 0px; }" );
1384 
1385  setLayout( hLayout );
1386 
1387  int frameWidth = mLineEdit->style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
1388  mLineEdit->setStyleSheet( QString( "QLineEdit { padding-right: %1px; } " )
1389  .arg( mMenuButton->sizeHint().width() + frameWidth + 1 ) );
1390 
1391  connect( mLineEdit, SIGNAL( editingFinished() ), this, SLOT( textChanged() ) );
1392  connect( mMenuButton, SIGNAL( clicked() ), this, SLOT( showMenu() ) );
1393 
1394  //restore format setting
1395  QSettings settings;
1396  mFormat = ( ColorTextFormat )settings.value( "/ColorWidgets/textWidgetFormat", 0 ).toInt();
1397 
1398  updateText();
1399 }
1400 
1402 {
1403 
1404 }
1405 
1406 void QgsColorTextWidget::setColor( const QColor &color, const bool emitSignals )
1407 {
1408  QgsColorWidget::setColor( color, emitSignals );
1409  updateText();
1410 }
1411 
1413 {
1414  Q_UNUSED( event );
1415  QSize sz = mMenuButton->sizeHint();
1416  int frameWidth = style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
1417  mMenuButton->move( mLineEdit->rect().right() - frameWidth - sz.width(),
1418  ( mLineEdit->rect().bottom() + 1 - sz.height() ) / 2 );
1419 }
1420 
1421 void QgsColorTextWidget::updateText()
1422 {
1423  switch ( mFormat )
1424  {
1425  case HexRgb:
1426  mLineEdit->setText( mCurrentColor.name() );
1427  break;
1428  case HexRgbA:
1429  mLineEdit->setText( mCurrentColor.name() + QString( "%1" ).arg( mCurrentColor.alpha(), 2, 16, QChar( '0' ) ) );
1430  break;
1431  case Rgb:
1432  mLineEdit->setText( QString( tr( "rgb( %1, %2, %3 )" ) ).arg( mCurrentColor.red() ).arg( mCurrentColor.green() ).arg( mCurrentColor.blue() ) );
1433  break;
1434  case Rgba:
1435  mLineEdit->setText( QString( tr( "rgba( %1, %2, %3, %4 )" ) ).arg( mCurrentColor.red() ).arg( mCurrentColor.green() ).arg( mCurrentColor.blue() ).arg( QString::number( mCurrentColor.alphaF(), 'f', 2 ) ) );
1436  break;
1437  }
1438 }
1439 
1440 void QgsColorTextWidget::textChanged()
1441 {
1442  QString testString = mLineEdit->text();
1443  bool containsAlpha;
1444  QColor color = QgsSymbolLayerV2Utils::parseColorWithAlpha( testString, containsAlpha );
1445  if ( !color.isValid() )
1446  {
1447  //bad color string
1448  updateText();
1449  return;
1450  }
1451 
1452  //good color string
1453  if ( color != mCurrentColor )
1454  {
1455  //retain alpha if no explicit alpha set
1456  if ( !containsAlpha )
1457  {
1458  color.setAlpha( mCurrentColor.alpha() );
1459  }
1460  //color has changed
1461  mCurrentColor = color;
1462  emit colorChanged( mCurrentColor );
1463  }
1464  updateText();
1465 }
1466 
1467 void QgsColorTextWidget::showMenu()
1468 {
1469  QMenu colorContextMenu;
1470 
1471  QAction* hexRgbAction = new QAction( tr( "#RRGGBB" ), 0 );
1472  colorContextMenu.addAction( hexRgbAction );
1473  QAction* hexRgbaAction = new QAction( tr( "#RRGGBBAA" ), 0 );
1474  colorContextMenu.addAction( hexRgbaAction );
1475  QAction* rgbAction = new QAction( tr( "rgb( r, g, b )" ), 0 );
1476  colorContextMenu.addAction( rgbAction );
1477  QAction* rgbaAction = new QAction( tr( "rgba( r, g, b, a )" ), 0 );
1478  colorContextMenu.addAction( rgbaAction );
1479 
1480  QAction* selectedAction = colorContextMenu.exec( QCursor::pos() );
1481  if ( selectedAction == hexRgbAction )
1482  {
1483  mFormat = QgsColorTextWidget::HexRgb;
1484  }
1485  else if ( selectedAction == hexRgbaAction )
1486  {
1487  mFormat = QgsColorTextWidget::HexRgbA;
1488  }
1489  else if ( selectedAction == rgbAction )
1490  {
1491  mFormat = QgsColorTextWidget::Rgb;
1492  }
1493  else if ( selectedAction == rgbaAction )
1494  {
1495  mFormat = QgsColorTextWidget::Rgba;
1496  }
1497 
1498  //save format setting
1499  QSettings settings;
1500  settings.setValue( "/ColorWidgets/textWidgetFormat", ( int )mFormat );
1501 
1502  updateText();
1503 }
1504 
1505 
1506 //
1507 // QgsColorPreviewWidget
1508 //
1509 
1511  : QgsColorWidget( parent )
1512  , mColor2( QColor() )
1513 {
1514 
1515 }
1516 
1518 {
1519 
1520 }
1521 
1522 void QgsColorPreviewWidget::drawColor( const QColor &color, const QRect &rect, QPainter& painter )
1523 {
1524  painter.setPen( Qt::NoPen );
1525  //if color has an alpha, start with a checkboard pattern
1526  if ( color.alpha() < 255 )
1527  {
1528  QBrush checkBrush = QBrush( transparentBackground() );
1529  painter.setBrush( checkBrush );
1530  painter.drawRect( rect );
1531 
1532  //draw half of widget showing solid color, the other half showing color with alpha
1533 
1534  //ensure at least a 1px overlap to avoid artefacts
1535  QBrush colorBrush = QBrush( color );
1536  painter.setBrush( colorBrush );
1537  painter.drawRect( floor( rect.width() / 2.0 ) + rect.left(), rect.top(), rect.width() - floor( rect.width() / 2.0 ), rect.height() );
1538 
1539  QColor opaqueColor = QColor( color );
1540  opaqueColor.setAlpha( 255 );
1541  QBrush opaqueBrush = QBrush( opaqueColor );
1542  painter.setBrush( opaqueBrush );
1543  painter.drawRect( rect.left(), rect.top(), ceil( rect.width() / 2.0 ), rect.height() );
1544  }
1545  else
1546  {
1547  //no alpha component, just draw a solid rectangle
1548  QBrush brush = QBrush( color );
1549  painter.setBrush( brush );
1550  painter.drawRect( rect );
1551  }
1552 }
1553 
1555 {
1556  Q_UNUSED( event );
1557  QPainter painter( this );
1558 
1559  if ( mColor2.isValid() )
1560  {
1561  //drawing with two color sections
1562  int verticalSplit = qRound( height() / 2.0 );
1563  drawColor( mCurrentColor, QRect( 0, 0, width(), verticalSplit ), painter );
1564  drawColor( mColor2, QRect( 0, verticalSplit, width(), height() - verticalSplit ), painter );
1565  }
1566  else if ( mCurrentColor.isValid() )
1567  {
1568  drawColor( mCurrentColor, QRect( 0, 0, width(), height() ), painter );
1569  }
1570 
1571  painter.end();
1572 }
1573 
1575 {
1576  if ( color == mColor2 )
1577  {
1578  return;
1579  }
1580  mColor2 = color;
1581  update();
1582 }
1583 
1585 {
1586  if ( e->button() == Qt::LeftButton )
1587  {
1588  mDragStartPosition = e->pos();
1589  }
1591 }
1592 
1594 {
1595  if (( e->pos() - mDragStartPosition ).manhattanLength() >= QApplication::startDragDistance() )
1596  {
1597  //mouse moved, so a drag. nothing to do here
1599  return;
1600  }
1601 
1602  //work out which color was clicked
1603  QColor clickedColor = mCurrentColor;
1604  if ( mColor2.isValid() )
1605  {
1606  //two color sections, check if dragged color was the second color
1607  int verticalSplit = qRound( height() / 2.0 );
1608  if ( mDragStartPosition.y() >= verticalSplit )
1609  {
1610  clickedColor = mColor2;
1611  }
1612  }
1613  emit colorChanged( clickedColor );
1614 
1615 }
1616 
1618 {
1619  //handle dragging colors from button
1620 
1621  if ( !( e->buttons() & Qt::LeftButton ) )
1622  {
1623  //left button not depressed, so not a drag
1625  return;
1626  }
1627 
1628  if (( e->pos() - mDragStartPosition ).manhattanLength() < QApplication::startDragDistance() )
1629  {
1630  //mouse not moved, so not a drag
1632  return;
1633  }
1634 
1635  //user is dragging color
1636 
1637  //work out which color is being dragged
1638  QColor dragColor = mCurrentColor;
1639  if ( mColor2.isValid() )
1640  {
1641  //two color sections, check if dragged color was the second color
1642  int verticalSplit = qRound( height() / 2.0 );
1643  if ( mDragStartPosition.y() >= verticalSplit )
1644  {
1645  dragColor = mColor2;
1646  }
1647  }
1648 
1649  QDrag *drag = new QDrag( this );
1651  drag->setPixmap( createDragIcon( dragColor ) );
1652  drag->exec( Qt::CopyAction );
1653 }
int hue() const
Returns the hue for the widget.
uchar * scanLine(int i)
Orientation orientation() const
Fetches the orientation for the color ramp.
void paintEvent(QPaintEvent *event) override
bool showFrame() const
Fetches whether the ramp is drawn within a frame.
void setStyleSheet(const QString &styleSheet)
void setMinimum(int min)
A base class for interactive color widgets.
Orientation
Specifies the orientation of a color ramp.
void mouseReleaseEvent(QMouseEvent *e) override
int width() const
int componentRange() const
Returns the range of valid values for the color widget's component.
bool end()
void setCursor(const QCursor &)
virtual QSize sizeHint() const
const QMimeData * mimeData() const
static QMimeData * colorToMimeData(const QColor &color)
Creates mime data from a color.
void fillRect(const QRectF &rectangle, const QBrush &brush)
void setCompositionMode(CompositionMode mode)
qreal alphaF() const
void setRenderHint(RenderHint hint, bool on)
QgsColorSliderWidget(QWidget *parent=0, const ColorComponent component=QgsColorWidget::Red)
Construct a new color slider widget.
void updateGeometry()
void fill(const QColor &color)
void setSuffix(const QString &suffix)
void setMimeData(QMimeData *data)
QString name() const
void setText(const QString &)
QgsColorWidget(QWidget *parent=0, const ColorComponent component=Multiple)
Construct a new color widget.
void setFocusPolicy(Qt::FocusPolicy policy)
void setColorAt(qreal position, const QColor &color)
void valueChanged(const int value)
Emitted when the widget's color component value changes.
int value() const
QStyle * style() const
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
virtual int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const =0
void setPixmap(const QPixmap &pixmap)
void alterColor(QColor &color, const QgsColorWidget::ColorComponent component, const int newValue) const
Alters a color by modifiying the value of a specific color component.
void setMinimumWidth(int minw)
void addAction(QAction *action)
void setMarkerSize(const int markerSize)
Sets the size for drawing the triangular markers on the ramp.
static QColor colorFromMimeData(const QMimeData *data, bool &hasAlpha)
Attempts to parse mime data as a color.
int y() const
void save()
void paintEvent(QPaintEvent *event) override
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
QgsColorPreviewWidget(QWidget *parent=0)
Construct a new color preview widget.
void setBlue(int blue)
QgsColorRampWidget(QWidget *parent=0, const ColorComponent component=QgsColorWidget::Red, const Orientation orientation=QgsColorRampWidget::Horizontal)
Construct a new color ramp widget.
void setAlpha(int alpha)
virtual void mouseReleaseEvent(QMouseEvent *event) override
virtual QSize sizeHint() const override
int height() const
bool hasFocus() const
static const QPixmap & transparentBackground()
Generates a checkboard pattern pixmap for use as a background to transparent colors.
Qt::MouseButtons buttons() const
virtual void mousePressEvent(QMouseEvent *event) override
void drawLine(const QLineF &line)
void acceptProposedAction()
void setRed(int red)
virtual void mouseReleaseEvent(QMouseEvent *event)
int hue() const
void setIcon(const QIcon &icon)
QString tr(const char *sourceText, const char *disambiguation, int n)
static QPixmap getThemePixmap(const QString &theName)
Helper to get a theme icon as a pixmap.
void getHsl(int *h, int *s, int *l, int *a) const
void getHsv(int *h, int *s, int *v, int *a) const
void update()
int x() const
int y() const
virtual void setComponentValue(const int value) override
Alters the widget's color by setting the value for the widget's color component.
QColor fromHsl(int h, int s, int l, int a)
QColor fromHsv(int h, int s, int v, int a)
virtual void setColor(const QColor &color, const bool emitSignals=false) override
Sets the color for the widget.
virtual void mousePressEvent(QMouseEvent *event) override
void initFrom(const QWidget *widget)
int width() const
Qt::DropAction exec(QFlags< Qt::DropAction > supportedActions)
void setValue(const QString &key, const QVariant &value)
QSize size() const
void drawRect(const QRectF &rectangle)
virtual void setComponent(const ColorComponent component) override
Sets the color component which the widget controls.
qreal length() const
void addWidget(QWidget *widget, int stretch, QFlags< Qt::AlignmentFlag > alignment)
virtual void mousePressEvent(QMouseEvent *event) override
QString number(int n, int base)
qreal x() const
qreal y() const
void setGreen(int green)
QPointF p2() const
void setLayout(QLayout *layout)
A color wheel widget.
virtual void mousePressEvent(QMouseEvent *event)
int toInt(bool *ok) const
int x() const
void fill(uint pixelValue)
void setOrientation(const Orientation orientation)
Sets the orientation for the color ramp.
int top() const
QRgb rgb() const
int red() const
void setPen(const QColor &color)
int width() const
void drawEllipse(const QRectF &rectangle)
int left() const
Qt::MouseButton button() const
QPointF posF() const
void setAngle(qreal angle)
virtual void mouseMoveEvent(QMouseEvent *event)
int componentValue() const
Returns the current value of the widget's color component.
virtual void setColor(const QColor &color, const bool emitSignals=false) override
void resizeEvent(QResizeEvent *event) override
#define M_PI
QColor color() const
Returns the current color for the widget.
qreal lightnessF() const
A color ramp widget.
QPoint pos() const
void setBrush(const QBrush &brush)
void mouseMoveEvent(QMouseEvent *e) override
void dragEnterEvent(QDragEnterEvent *e) override
QgsColorTextWidget(QWidget *parent=0)
Construct a new color line edit widget.
void setColor(const QColor &color)
void paintEvent(QPaintEvent *event) override
virtual void setComponent(const ColorComponent component) override
Sets the color component which the widget controls.
void setMargin(int margin)
QAction * exec()
void setSizePolicy(QSizePolicy)
int alpha() const
void setAcceptDrops(bool on)
int green() const
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
int key() const
virtual void resizeEvent(QResizeEvent *event) override
bool blockSignals(bool block)
const QSize & size() const
bool isNull() const
void setHsvF(qreal h, qreal s, qreal v, qreal a)
int width(const QString &text, int len) const
virtual void setComponentValue(const int value)
Alters the widget's color by setting the value for the widget's color component.
void restore()
void setMaximum(int max)
void setInteriorMargin(const int margin)
Sets the margin between the edge of the widget and the ramp.
ColorComponent
Specifies the color component which the widget alters.
int blue() const
QVariant value(const QString &key, const QVariant &defaultValue) const
int width() const
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, QFlags< Qt::ImageConversionFlag > flags)
QFontMetrics fontMetrics() const
QPoint pos()
static QColor parseColorWithAlpha(const QString &colorStr, bool &containsAlpha, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes...
void setWidth(int width)
void dropEvent(QDropEvent *e) override
void setValue(int val)
virtual void mouseMoveEvent(QMouseEvent *event) override
void colorChanged(const QColor &color)
Emitted when the widget's color changes.
void getRgb(int *r, int *g, int *b, int *a) const
virtual void setColor(const QColor &color, const bool emitSignals=false) override
Sets the color for the widget.
int height() const
virtual void keyPressEvent(QKeyEvent *event)
QgsColorWheel(QWidget *parent=0)
Constructs a new color wheel widget.
void translate(const QPointF &offset)
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const =0
qreal hslSaturationF() const
qreal angle() const
void setHsv(int h, int s, int v, int a)
virtual void setColor(const QColor &color, const bool emitSignals=false) override
void paintEvent(QPaintEvent *event) override
virtual ~QgsColorWidget()
int height() const
int mExplicitHue
QColor wipes the hue information when it is ambiguous (eg, for saturation = 0).
virtual void mouseMoveEvent(QMouseEvent *event) override
int hsvHue() const
int saturation() const
ColorComponent mComponent
const QPoint & pos() const
virtual void resizeEvent(QResizeEvent *event)
qreal height() const
virtual void mouseMoveEvent(QMouseEvent *event) override
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
virtual void resizeEvent(QResizeEvent *event) override
bool begin(QPaintDevice *device)
static QPixmap createDragIcon(const QColor &color)
Create an icon for dragging colors.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
A line edit widget which displays colors as text and accepts string representations of colors...
virtual void setColor2(const QColor &color)
Sets the second color for the widget.
void mousePressEvent(QMouseEvent *e) override
qreal width() const
void setSpacing(int spacing)
virtual ~QgsColorWheel()
int height() const
virtual QSize sizeHint() const override
ColorComponent component() const
Returns the color component which the widget controls.
virtual void setComponent(const ColorComponent component)
Sets the color component which the widget controls.
QColor fromHsvF(qreal h, qreal s, qreal v, qreal a)
int hsvSaturation() const
virtual void setColor(const QColor &color, const bool emitSignals=false)
Sets the color for the widget.
int startDragDistance()
bool isValid() const
virtual void keyPressEvent(QKeyEvent *event) override
virtual ~QgsColorBox()
QgsColorBox(QWidget *parent=0, const ColorComponent component=Value)
Construct a new color box widget.
void setShowFrame(const bool showFrame)
Sets whether the ramp should be drawn within a frame.