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