mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-18 00:03:05 -04:00
[FEATURE] New reusable interactive widget for editing gradient ramps
Supports: - drag to move color stops - double clicking to add a new stop - pressing delete will remove the selected stop - pressing arrow keys will move the selected stop, shift+arrow= larger move - drag and drop a color onto the widget to add a new stop Sponsored by North Road
This commit is contained in:
parent
79f3d4221b
commit
881be0f365
@ -82,6 +82,7 @@
|
|||||||
%Include qgsformannotationitem.sip
|
%Include qgsformannotationitem.sip
|
||||||
%Include qgsgenericprojectionselector.sip
|
%Include qgsgenericprojectionselector.sip
|
||||||
%Include qgsgeometryrubberband.sip
|
%Include qgsgeometryrubberband.sip
|
||||||
|
%Include qgsgradientstopeditor.sip
|
||||||
%Include qgsgroupwmsdatadialog.sip
|
%Include qgsgroupwmsdatadialog.sip
|
||||||
%Include qgshighlight.sip
|
%Include qgshighlight.sip
|
||||||
%Include qgshistogramwidget.sip
|
%Include qgshistogramwidget.sip
|
||||||
|
115
python/gui/qgsgradientstopeditor.sip
Normal file
115
python/gui/qgsgradientstopeditor.sip
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/** \ingroup gui
|
||||||
|
* \class QgsGradientStopEditor
|
||||||
|
* An interactive editor for previewing a gradient color ramp and modifying the position of color
|
||||||
|
* stops along the gradient.
|
||||||
|
* \note Added in version 2.16
|
||||||
|
*/
|
||||||
|
|
||||||
|
class QgsGradientStopEditor : QWidget
|
||||||
|
{
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include <qgsgradientstopeditor.h>
|
||||||
|
%End
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** Constructor for QgsGradientStopEditor.
|
||||||
|
* @param parent parent widget
|
||||||
|
* @param ramp optional initial gradient ramp
|
||||||
|
*/
|
||||||
|
QgsGradientStopEditor( QWidget* parent /TransferThis/ = nullptr, QgsVectorGradientColorRampV2* ramp = nullptr );
|
||||||
|
|
||||||
|
/** Sets the current ramp shown in the editor.
|
||||||
|
* @param ramp color ramp
|
||||||
|
* @see gradientRamp()
|
||||||
|
*/
|
||||||
|
void setGradientRamp( const QgsVectorGradientColorRampV2& ramp );
|
||||||
|
|
||||||
|
/** Returns the current ramp created by the editor.
|
||||||
|
* @see setGradientRamp()
|
||||||
|
*/
|
||||||
|
QgsVectorGradientColorRampV2 gradientRamp() const;
|
||||||
|
|
||||||
|
/** Sets the currently selected stop.
|
||||||
|
* @param index index of stop, where 0 corresponds to the first stop
|
||||||
|
* @see selectedStop()
|
||||||
|
*/
|
||||||
|
void selectStop( int index );
|
||||||
|
|
||||||
|
/** Returns details about the currently selected stop.
|
||||||
|
* @see selectStop()
|
||||||
|
*/
|
||||||
|
QgsGradientStop selectedStop() const;
|
||||||
|
|
||||||
|
virtual QSize sizeHint() const;
|
||||||
|
void paintEvent( QPaintEvent* event );
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
/** Sets the color for the current selected stop.
|
||||||
|
* @param color new stop color
|
||||||
|
* @see setSelectedStopOffset()
|
||||||
|
* @see setSelectedStopDetails()
|
||||||
|
* @see setColor1()
|
||||||
|
* @see setColor2()
|
||||||
|
*/
|
||||||
|
void setSelectedStopColor( const QColor& color );
|
||||||
|
|
||||||
|
/** Sets the offset for the current selected stop. This slot has no effect if either the
|
||||||
|
* first or last stop is selected, as they cannot be repositioned.
|
||||||
|
* @param offset new stop offset
|
||||||
|
* @see setSelectedStopColor()
|
||||||
|
* @see setSelectedStopDetails()
|
||||||
|
*/
|
||||||
|
void setSelectedStopOffset( double offset );
|
||||||
|
|
||||||
|
/** Sets the color and offset for the current selected stop.
|
||||||
|
* @param color new stop color
|
||||||
|
* @param offset new stop offset
|
||||||
|
* @see setSelectedStopColor()
|
||||||
|
* @see setSelectedStopOffset()
|
||||||
|
*/
|
||||||
|
void setSelectedStopDetails( const QColor& color, double offset );
|
||||||
|
|
||||||
|
/** Deletes the current selected stop. This slot has no effect if either the
|
||||||
|
* first or last stop is selected, as they cannot be deleted.
|
||||||
|
*/
|
||||||
|
void deleteSelectedStop();
|
||||||
|
|
||||||
|
/** Sets the color for the first stop.
|
||||||
|
* @param color new stop color
|
||||||
|
* @see setColor2()
|
||||||
|
* @see setSelectedStopColor()
|
||||||
|
*/
|
||||||
|
void setColor1( const QColor& color );
|
||||||
|
|
||||||
|
/** Sets the color for the last stop.
|
||||||
|
* @param color new stop color
|
||||||
|
* @see setColor1()
|
||||||
|
* @see setSelectedStopColor()
|
||||||
|
*/
|
||||||
|
void setColor2( const QColor& color );
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
//! Emmitted when the gradient ramp is changed by a user
|
||||||
|
void changed();
|
||||||
|
|
||||||
|
/** Emmitted when the current selected stop changes.
|
||||||
|
* @param stop details about newly selected stop
|
||||||
|
*/
|
||||||
|
void selectedStopChanged( const QgsGradientStop& stop );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual void mouseMoveEvent( QMouseEvent *event );
|
||||||
|
virtual void mousePressEvent( QMouseEvent *event );
|
||||||
|
virtual void mouseDoubleClickEvent( QMouseEvent * event );
|
||||||
|
virtual void keyPressEvent( QKeyEvent * event );
|
||||||
|
|
||||||
|
//Reimplemented to accept dragged colors
|
||||||
|
void dragEnterEvent( QDragEnterEvent * e );
|
||||||
|
|
||||||
|
//Reimplemented to accept dropped colors
|
||||||
|
void dropEvent( QDropEvent *e );
|
||||||
|
};
|
@ -212,6 +212,7 @@ SET(QGIS_GUI_SRCS
|
|||||||
qgsformannotationitem.cpp
|
qgsformannotationitem.cpp
|
||||||
qgsgenericprojectionselector.cpp
|
qgsgenericprojectionselector.cpp
|
||||||
qgsgeometryrubberband.cpp
|
qgsgeometryrubberband.cpp
|
||||||
|
qgsgradientstopeditor.cpp
|
||||||
qgsgroupwmsdatadialog.cpp
|
qgsgroupwmsdatadialog.cpp
|
||||||
qgshighlight.cpp
|
qgshighlight.cpp
|
||||||
qgshistogramwidget.cpp
|
qgshistogramwidget.cpp
|
||||||
@ -357,6 +358,7 @@ SET(QGIS_GUI_MOC_HDRS
|
|||||||
qgsfocuswatcher.h
|
qgsfocuswatcher.h
|
||||||
qgsformannotationitem.h
|
qgsformannotationitem.h
|
||||||
qgsgenericprojectionselector.h
|
qgsgenericprojectionselector.h
|
||||||
|
qgsgradientstopeditor.h
|
||||||
qgsgroupwmsdatadialog.h
|
qgsgroupwmsdatadialog.h
|
||||||
qgshistogramwidget.h
|
qgshistogramwidget.h
|
||||||
qgshtmlannotationitem.h
|
qgshtmlannotationitem.h
|
||||||
|
477
src/gui/qgsgradientstopeditor.cpp
Normal file
477
src/gui/qgsgradientstopeditor.cpp
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsgradientstopeditor.cpp
|
||||||
|
-------------------------
|
||||||
|
begin : April 2016
|
||||||
|
copyright : (C) 2016 by Nyall Dawson
|
||||||
|
email : nyall dot dawson at gmail dot com
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "qgsgradientstopeditor.h"
|
||||||
|
#include "qgsapplication.h"
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QStyleOptionFrameV3>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
#define MARKER_WIDTH 11
|
||||||
|
#define MARKER_HEIGHT 14
|
||||||
|
#define MARKER_GAP 1.5
|
||||||
|
#define MARGIN_BOTTOM ( MARKER_HEIGHT + 2 )
|
||||||
|
#define MARGIN_X ( MARKER_WIDTH / 2 )
|
||||||
|
#define FRAME_MARGIN 2
|
||||||
|
#define CLICK_THRESHOLD ( MARKER_WIDTH / 2 + 3 )
|
||||||
|
|
||||||
|
QgsGradientStopEditor::QgsGradientStopEditor( QWidget *parent, QgsVectorGradientColorRampV2 *ramp )
|
||||||
|
: QWidget( parent )
|
||||||
|
, mSelectedStop( 0 )
|
||||||
|
{
|
||||||
|
if ( ramp )
|
||||||
|
mGradient = *ramp;
|
||||||
|
mStops = mGradient.stops();
|
||||||
|
|
||||||
|
if ( sOuterTriangle.isEmpty() )
|
||||||
|
{
|
||||||
|
sOuterTriangle << QPointF( 0, MARKER_HEIGHT ) << QPointF( MARKER_WIDTH, MARKER_HEIGHT )
|
||||||
|
<< QPointF( MARKER_WIDTH, MARKER_WIDTH / 2.0 )
|
||||||
|
<< QPointF( MARKER_WIDTH / 2.0, 0 )
|
||||||
|
<< QPointF( 0, MARKER_WIDTH / 2.0 )
|
||||||
|
<< QPointF( 0, MARKER_HEIGHT );
|
||||||
|
}
|
||||||
|
if ( sInnerTriangle.isEmpty() )
|
||||||
|
{
|
||||||
|
sInnerTriangle << QPointF( MARKER_GAP, MARKER_HEIGHT - MARKER_GAP ) << QPointF( MARKER_WIDTH - MARKER_GAP, MARKER_HEIGHT - MARKER_GAP )
|
||||||
|
<< QPointF( MARKER_WIDTH - MARKER_GAP, MARKER_WIDTH / 2.0 + 1 )
|
||||||
|
<< QPointF( MARKER_WIDTH / 2.0, MARKER_GAP )
|
||||||
|
<< QPointF( MARKER_GAP, MARKER_WIDTH / 2.0 + 1 )
|
||||||
|
<< QPointF( MARKER_GAP, MARKER_HEIGHT - MARKER_GAP );
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocusPolicy( Qt::StrongFocus );
|
||||||
|
setAcceptDrops( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::setGradientRamp( const QgsVectorGradientColorRampV2 &ramp )
|
||||||
|
{
|
||||||
|
mGradient = ramp;
|
||||||
|
mStops = mGradient.stops();
|
||||||
|
mSelectedStop = 0;
|
||||||
|
update();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize QgsGradientStopEditor::sizeHint() const
|
||||||
|
{
|
||||||
|
//horizontal
|
||||||
|
return QSize( 200, 80 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::paintEvent( QPaintEvent *event )
|
||||||
|
{
|
||||||
|
Q_UNUSED( event );
|
||||||
|
QPainter painter( this );
|
||||||
|
|
||||||
|
QRect frameRect( rect().x() + MARGIN_X, rect().y(),
|
||||||
|
rect().width() - 2 * MARGIN_X,
|
||||||
|
rect().height() - MARGIN_BOTTOM );
|
||||||
|
|
||||||
|
//draw frame
|
||||||
|
QStyleOptionFrameV3 option;
|
||||||
|
option.initFrom( this );
|
||||||
|
option.state = hasFocus() ? QStyle::State_KeyboardFocusChange : QStyle::State_None;
|
||||||
|
option.rect = frameRect;
|
||||||
|
style()->drawPrimitive( QStyle::PE_Frame, &option, &painter );
|
||||||
|
|
||||||
|
if ( hasFocus() )
|
||||||
|
{
|
||||||
|
//draw focus rect
|
||||||
|
QStyleOptionFocusRect option;
|
||||||
|
option.initFrom( this );
|
||||||
|
option.state = QStyle::State_KeyboardFocusChange;
|
||||||
|
option.rect = frameRect;
|
||||||
|
style()->drawPrimitive( QStyle::PE_FrameFocusRect, &option, &painter );
|
||||||
|
}
|
||||||
|
|
||||||
|
//start with the checkboard pattern
|
||||||
|
QBrush checkBrush = QBrush( transparentBackground() );
|
||||||
|
painter.setBrush( checkBrush );
|
||||||
|
painter.setPen( Qt::NoPen );
|
||||||
|
|
||||||
|
QRect box( frameRect.x() + FRAME_MARGIN, frameRect.y() + FRAME_MARGIN,
|
||||||
|
frameRect.width() - 2 * FRAME_MARGIN,
|
||||||
|
frameRect.height() - 2 * FRAME_MARGIN );
|
||||||
|
|
||||||
|
painter.drawRect( box );
|
||||||
|
|
||||||
|
// draw gradient preview on top of checkerboard
|
||||||
|
for ( int i = 0; i < box.width() + 1; ++i )
|
||||||
|
{
|
||||||
|
QPen pen( mGradient.color( static_cast< double >( i ) / box.width() ) );
|
||||||
|
painter.setPen( pen );
|
||||||
|
painter.drawLine( box.left() + i, box.top(), box.left() + i, box.height() + 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw stop markers
|
||||||
|
int markerTop = frameRect.bottom() + 1;
|
||||||
|
drawStopMarker( painter, QPoint( box.left(), markerTop ), mGradient.color1(), mSelectedStop == 0 );
|
||||||
|
drawStopMarker( painter, QPoint( box.right(), markerTop ), mGradient.color2(), mSelectedStop == mGradient.count() - 1 );
|
||||||
|
int i = 1;
|
||||||
|
Q_FOREACH ( const QgsGradientStop& stop, mStops )
|
||||||
|
{
|
||||||
|
int x = stop.offset * box.width() + box.left();
|
||||||
|
drawStopMarker( painter, QPoint( x, markerTop ), stop.color, mSelectedStop == i );
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::selectStop( int index )
|
||||||
|
{
|
||||||
|
if ( index > 0 && index < mGradient.count() - 1 )
|
||||||
|
{
|
||||||
|
// need to map original stop index across to cached, possibly out of order stop index
|
||||||
|
QgsGradientStop selectedStop = mGradient.stops().at( index - 1 );
|
||||||
|
index = 1;
|
||||||
|
Q_FOREACH ( const QgsGradientStop& stop, mStops )
|
||||||
|
{
|
||||||
|
if ( stop == selectedStop )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mSelectedStop = index;
|
||||||
|
emit selectedStopChanged( selectedStop() );
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsGradientStop QgsGradientStopEditor::selectedStop() const
|
||||||
|
{
|
||||||
|
if ( mSelectedStop > 0 && mSelectedStop < mGradient.count() - 1 )
|
||||||
|
{
|
||||||
|
return mStops.at( mSelectedStop - 1 );
|
||||||
|
}
|
||||||
|
else if ( mSelectedStop == 0 )
|
||||||
|
{
|
||||||
|
return QgsGradientStop( 0.0, mGradient.color1() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return QgsGradientStop( 1.0, mGradient.color2() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::setSelectedStopColor( const QColor &color )
|
||||||
|
{
|
||||||
|
if ( mSelectedStop > 0 && mSelectedStop < mGradient.count() - 1 )
|
||||||
|
{
|
||||||
|
mStops[ mSelectedStop - 1 ].color = color;
|
||||||
|
mGradient.setStops( mStops );
|
||||||
|
}
|
||||||
|
else if ( mSelectedStop == 0 )
|
||||||
|
{
|
||||||
|
mGradient.setColor1( color );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mGradient.setColor2( color );
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::setSelectedStopOffset( double offset )
|
||||||
|
{
|
||||||
|
if ( mSelectedStop > 0 && mSelectedStop < mGradient.count() - 1 )
|
||||||
|
{
|
||||||
|
mStops[ mSelectedStop - 1 ].offset = offset;
|
||||||
|
mGradient.setStops( mStops );
|
||||||
|
update();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::setSelectedStopDetails( const QColor &color, double offset )
|
||||||
|
{
|
||||||
|
if ( mSelectedStop > 0 && mSelectedStop < mGradient.count() - 1 )
|
||||||
|
{
|
||||||
|
mStops[ mSelectedStop - 1 ].color = color;
|
||||||
|
mStops[ mSelectedStop - 1 ].offset = offset;
|
||||||
|
mGradient.setStops( mStops );
|
||||||
|
}
|
||||||
|
else if ( mSelectedStop == 0 )
|
||||||
|
{
|
||||||
|
mGradient.setColor1( color );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mGradient.setColor2( color );
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::deleteSelectedStop()
|
||||||
|
{
|
||||||
|
if ( selectedStopIsMovable() )
|
||||||
|
{
|
||||||
|
//delete stop
|
||||||
|
double stopOffset = mStops.at( mSelectedStop - 1 ).offset;
|
||||||
|
mStops.removeAt( mSelectedStop - 1 );
|
||||||
|
mGradient.setStops( mStops );
|
||||||
|
|
||||||
|
int closest = findClosestStop( relativePositionToPoint( stopOffset ) );
|
||||||
|
if ( closest >= 0 )
|
||||||
|
selectStop( closest );
|
||||||
|
update();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::setColor1( const QColor &color )
|
||||||
|
{
|
||||||
|
mGradient.setColor1( color );
|
||||||
|
update();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::setColor2( const QColor &color )
|
||||||
|
{
|
||||||
|
mGradient.setColor2( color );
|
||||||
|
update();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::mouseMoveEvent( QMouseEvent *e )
|
||||||
|
{
|
||||||
|
if ( e->buttons() & Qt::LeftButton )
|
||||||
|
{
|
||||||
|
if ( selectedStopIsMovable() )
|
||||||
|
{
|
||||||
|
double offset = pointToRelativePosition( e->pos().x() );
|
||||||
|
|
||||||
|
// have to edit the temporary stop list, as setting stops on the gradient will reorder them
|
||||||
|
// and change which stop corresponds to the selected one;
|
||||||
|
mStops[ mSelectedStop - 1 ].offset = offset;
|
||||||
|
|
||||||
|
mGradient.setStops( mStops );
|
||||||
|
update();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e->accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
int QgsGradientStopEditor::findClosestStop( int x, int threshold ) const
|
||||||
|
{
|
||||||
|
int closestStop = -1;
|
||||||
|
int closestDiff = INT_MAX;
|
||||||
|
int currentDiff = INT_MAX;
|
||||||
|
|
||||||
|
// check for matching stops first, so that they take precedence
|
||||||
|
// otherwise it's impossible to select a stop which sits above the first/last stop, making
|
||||||
|
// it impossible to move or delete these
|
||||||
|
int i = 1;
|
||||||
|
Q_FOREACH ( const QgsGradientStop& stop, mGradient.stops() )
|
||||||
|
{
|
||||||
|
currentDiff = qAbs( relativePositionToPoint( stop.offset ) + 1 - x );
|
||||||
|
if (( threshold < 0 || currentDiff < threshold ) && currentDiff < closestDiff )
|
||||||
|
{
|
||||||
|
closestStop = i;
|
||||||
|
closestDiff = currentDiff;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//first stop
|
||||||
|
currentDiff = qAbs( relativePositionToPoint( 0.0 ) + 1 - x );
|
||||||
|
if (( threshold < 0 || currentDiff < threshold ) && currentDiff < closestDiff )
|
||||||
|
{
|
||||||
|
closestStop = 0;
|
||||||
|
closestDiff = currentDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
//last stop
|
||||||
|
currentDiff = qAbs( relativePositionToPoint( 1.0 ) + 1 - x );
|
||||||
|
if (( threshold < 0 || currentDiff < threshold ) && currentDiff < closestDiff )
|
||||||
|
{
|
||||||
|
closestStop = mGradient.count() - 1;
|
||||||
|
closestDiff = currentDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return closestStop;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::mousePressEvent( QMouseEvent *e )
|
||||||
|
{
|
||||||
|
if ( e->pos().y() >= rect().height() - MARGIN_BOTTOM - 1 )
|
||||||
|
{
|
||||||
|
// find closest point
|
||||||
|
int closestStop = findClosestStop( e->pos().x(), CLICK_THRESHOLD );
|
||||||
|
if ( closestStop >= 0 )
|
||||||
|
{
|
||||||
|
selectStop( closestStop );
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
e->accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::mouseDoubleClickEvent( QMouseEvent *e )
|
||||||
|
{
|
||||||
|
if ( e->buttons() & Qt::LeftButton )
|
||||||
|
{
|
||||||
|
// add a new stop
|
||||||
|
double offset = pointToRelativePosition( e->pos().x() );
|
||||||
|
mStops << QgsGradientStop( offset, mGradient.color( offset ) );
|
||||||
|
mSelectedStop = mStops.length();
|
||||||
|
mGradient.setStops( mStops );
|
||||||
|
update();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
e->accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::keyPressEvent( QKeyEvent *e )
|
||||||
|
{
|
||||||
|
if (( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete ) )
|
||||||
|
{
|
||||||
|
deleteSelectedStop();
|
||||||
|
e->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( e->key() == Qt::Key_Left || e->key() == Qt::Key_Right )
|
||||||
|
{
|
||||||
|
if ( selectedStopIsMovable() )
|
||||||
|
{
|
||||||
|
// calculate offset corresponding to 1 px
|
||||||
|
double offsetDiff = pointToRelativePosition( rect().x() + MARGIN_X + FRAME_MARGIN + 2 ) - pointToRelativePosition( rect().x() + MARGIN_X + FRAME_MARGIN + 1 );
|
||||||
|
|
||||||
|
if ( e->modifiers() & Qt::ShiftModifier )
|
||||||
|
offsetDiff *= 10.0;
|
||||||
|
|
||||||
|
if ( e->key() == Qt::Key_Left )
|
||||||
|
offsetDiff *= -1;
|
||||||
|
|
||||||
|
mStops[ mSelectedStop - 1 ].offset = qBound( 0.0, mStops[ mSelectedStop - 1 ].offset + offsetDiff, 1.0 );
|
||||||
|
mGradient.setStops( mStops );
|
||||||
|
update();
|
||||||
|
e->accept();
|
||||||
|
emit changed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget::keyPressEvent( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
const QPixmap& QgsGradientStopEditor::transparentBackground()
|
||||||
|
{
|
||||||
|
static QPixmap transpBkgrd;
|
||||||
|
|
||||||
|
if ( transpBkgrd.isNull() )
|
||||||
|
transpBkgrd = QgsApplication::getThemePixmap( "/transp-background_8x8.png" );
|
||||||
|
|
||||||
|
return transpBkgrd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::drawStopMarker( QPainter& painter, QPoint topMiddle, const QColor &color, bool selected )
|
||||||
|
{
|
||||||
|
painter.save();
|
||||||
|
painter.setRenderHint( QPainter::Antialiasing );
|
||||||
|
painter.setBrush( selected ? QColor( 150, 150, 150 ) : Qt::white );
|
||||||
|
painter.setPen( selected ? Qt::black : QColor( 150, 150, 150 ) );
|
||||||
|
// 0.5 offsets to make edges pixel grid aligned
|
||||||
|
painter.translate( qRound( topMiddle.x() - MARKER_WIDTH / 2.0 ) + 0.5, topMiddle.y() + 0.5 );
|
||||||
|
painter.drawPolygon( sOuterTriangle );
|
||||||
|
|
||||||
|
// draw the checkerboard background for marker
|
||||||
|
painter.setBrush( QBrush( transparentBackground() ) );
|
||||||
|
painter.setPen( Qt::NoPen );
|
||||||
|
painter.drawPolygon( sInnerTriangle );
|
||||||
|
|
||||||
|
// draw color on top
|
||||||
|
painter.setBrush( color );
|
||||||
|
painter.drawPolygon( sInnerTriangle );
|
||||||
|
painter.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
double QgsGradientStopEditor::pointToRelativePosition( int x ) const
|
||||||
|
{
|
||||||
|
int left = rect().x() + MARGIN_X + FRAME_MARGIN;
|
||||||
|
int right = left + rect().width() - 2 * MARGIN_X - 2 * FRAME_MARGIN;
|
||||||
|
|
||||||
|
if ( x <= left )
|
||||||
|
return 0;
|
||||||
|
else if ( x >= right )
|
||||||
|
return 1.0;
|
||||||
|
|
||||||
|
return static_cast< double >( x - left ) / ( right - left );
|
||||||
|
}
|
||||||
|
|
||||||
|
int QgsGradientStopEditor::relativePositionToPoint( double position ) const
|
||||||
|
{
|
||||||
|
int left = rect().x() + MARGIN_X + FRAME_MARGIN;
|
||||||
|
int right = left + rect().width() - 2 * MARGIN_X - 2 * FRAME_MARGIN;
|
||||||
|
|
||||||
|
if ( position <= 0 )
|
||||||
|
return left;
|
||||||
|
else if ( position >= 1.0 )
|
||||||
|
return right;
|
||||||
|
|
||||||
|
return left + ( right - left ) * position;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QgsGradientStopEditor::selectedStopIsMovable() const
|
||||||
|
{
|
||||||
|
// first and last stop can't be moved or deleted
|
||||||
|
return mSelectedStop > 0 && mSelectedStop < mGradient.count() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::dragEnterEvent( QDragEnterEvent *e )
|
||||||
|
{
|
||||||
|
//is dragged data valid color data?
|
||||||
|
bool hasAlpha;
|
||||||
|
QColor mimeColor = QgsSymbolLayerV2Utils::colorFromMimeData( e->mimeData(), hasAlpha );
|
||||||
|
|
||||||
|
if ( mimeColor.isValid() )
|
||||||
|
{
|
||||||
|
//if so, we accept the drag
|
||||||
|
e->acceptProposedAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsGradientStopEditor::dropEvent( QDropEvent *e )
|
||||||
|
{
|
||||||
|
//is dropped data valid color data?
|
||||||
|
bool hasAlpha = false;
|
||||||
|
QColor mimeColor = QgsSymbolLayerV2Utils::colorFromMimeData( e->mimeData(), hasAlpha );
|
||||||
|
|
||||||
|
if ( mimeColor.isValid() )
|
||||||
|
{
|
||||||
|
//accept drop and set new color
|
||||||
|
e->acceptProposedAction();
|
||||||
|
|
||||||
|
// add a new stop here
|
||||||
|
double offset = pointToRelativePosition( e->pos().x() );
|
||||||
|
mStops << QgsGradientStop( offset, mimeColor );
|
||||||
|
mSelectedStop = mStops.length();
|
||||||
|
mGradient.setStops( mStops );
|
||||||
|
update();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
//could not get color from mime data
|
||||||
|
}
|
||||||
|
|
||||||
|
|
179
src/gui/qgsgradientstopeditor.h
Normal file
179
src/gui/qgsgradientstopeditor.h
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsgradientstopeditor.h
|
||||||
|
-----------------------
|
||||||
|
begin : April 2016
|
||||||
|
copyright : (C) 2016 by Nyall Dawson
|
||||||
|
email : nyall dot dawson at gmail dot com
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGSGRADIENTSTOPEDITOR_H
|
||||||
|
#define QGSGRADIENTSTOPEDITOR_H
|
||||||
|
|
||||||
|
#include "qgsvectorcolorrampv2.h"
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
|
||||||
|
/** \ingroup gui
|
||||||
|
* \class QgsGradientStopEditor
|
||||||
|
* An interactive editor for previewing a gradient color ramp and modifying the position of color
|
||||||
|
* stops along the gradient.
|
||||||
|
* \note Added in version 2.16
|
||||||
|
*/
|
||||||
|
|
||||||
|
class GUI_EXPORT QgsGradientStopEditor : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** Constructor for QgsGradientStopEditor.
|
||||||
|
* @param parent parent widget
|
||||||
|
* @param ramp optional initial gradient ramp
|
||||||
|
*/
|
||||||
|
QgsGradientStopEditor( QWidget* parent = nullptr, QgsVectorGradientColorRampV2* ramp = nullptr );
|
||||||
|
|
||||||
|
/** Sets the current ramp shown in the editor.
|
||||||
|
* @param ramp color ramp
|
||||||
|
* @see gradientRamp()
|
||||||
|
*/
|
||||||
|
void setGradientRamp( const QgsVectorGradientColorRampV2& ramp );
|
||||||
|
|
||||||
|
/** Returns the current ramp created by the editor.
|
||||||
|
* @see setGradientRamp()
|
||||||
|
*/
|
||||||
|
QgsVectorGradientColorRampV2 gradientRamp() const { return mGradient; }
|
||||||
|
|
||||||
|
/** Sets the currently selected stop.
|
||||||
|
* @param index index of stop, where 0 corresponds to the first stop
|
||||||
|
* @see selectedStop()
|
||||||
|
*/
|
||||||
|
void selectStop( int index );
|
||||||
|
|
||||||
|
/** Returns details about the currently selected stop.
|
||||||
|
* @see selectStop()
|
||||||
|
*/
|
||||||
|
QgsGradientStop selectedStop() const;
|
||||||
|
|
||||||
|
virtual QSize sizeHint() const override;
|
||||||
|
void paintEvent( QPaintEvent* event ) override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
/** Sets the color for the current selected stop.
|
||||||
|
* @param color new stop color
|
||||||
|
* @see setSelectedStopOffset()
|
||||||
|
* @see setSelectedStopDetails()
|
||||||
|
* @see setColor1()
|
||||||
|
* @see setColor2()
|
||||||
|
*/
|
||||||
|
void setSelectedStopColor( const QColor& color );
|
||||||
|
|
||||||
|
/** Sets the offset for the current selected stop. This slot has no effect if either the
|
||||||
|
* first or last stop is selected, as they cannot be repositioned.
|
||||||
|
* @param offset new stop offset
|
||||||
|
* @see setSelectedStopColor()
|
||||||
|
* @see setSelectedStopDetails()
|
||||||
|
*/
|
||||||
|
void setSelectedStopOffset( double offset );
|
||||||
|
|
||||||
|
/** Sets the color and offset for the current selected stop.
|
||||||
|
* @param color new stop color
|
||||||
|
* @param offset new stop offset
|
||||||
|
* @see setSelectedStopColor()
|
||||||
|
* @see setSelectedStopOffset()
|
||||||
|
*/
|
||||||
|
void setSelectedStopDetails( const QColor& color, double offset );
|
||||||
|
|
||||||
|
/** Deletes the current selected stop. This slot has no effect if either the
|
||||||
|
* first or last stop is selected, as they cannot be deleted.
|
||||||
|
*/
|
||||||
|
void deleteSelectedStop();
|
||||||
|
|
||||||
|
/** Sets the color for the first stop.
|
||||||
|
* @param color new stop color
|
||||||
|
* @see setColor2()
|
||||||
|
* @see setSelectedStopColor()
|
||||||
|
*/
|
||||||
|
void setColor1( const QColor& color );
|
||||||
|
|
||||||
|
/** Sets the color for the last stop.
|
||||||
|
* @param color new stop color
|
||||||
|
* @see setColor1()
|
||||||
|
* @see setSelectedStopColor()
|
||||||
|
*/
|
||||||
|
void setColor2( const QColor& color );
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
//! Emmitted when the gradient ramp is changed by a user
|
||||||
|
void changed();
|
||||||
|
|
||||||
|
/** Emmitted when the current selected stop changes.
|
||||||
|
* @param stop details about newly selected stop
|
||||||
|
*/
|
||||||
|
void selectedStopChanged( const QgsGradientStop& stop );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual void mouseMoveEvent( QMouseEvent *event ) override;
|
||||||
|
virtual void mousePressEvent( QMouseEvent *event ) override;
|
||||||
|
virtual void mouseDoubleClickEvent( QMouseEvent * event ) override;
|
||||||
|
virtual void keyPressEvent( QKeyEvent * event ) override;
|
||||||
|
|
||||||
|
//Reimplemented to accept dragged colors
|
||||||
|
void dragEnterEvent( QDragEnterEvent * e ) override;
|
||||||
|
|
||||||
|
//Reimplemented to accept dropped colors
|
||||||
|
void dropEvent( QDropEvent *e ) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** Generates a checkboard pattern pixmap for use as a background to transparent colors
|
||||||
|
* @returns checkerboard pixmap
|
||||||
|
*/
|
||||||
|
const QPixmap& transparentBackground();
|
||||||
|
|
||||||
|
/** Draws a stop marker on the specified painter.
|
||||||
|
* @param painter destination painter
|
||||||
|
* @param topMiddle coordinate corresponding to top middle point of desired marker
|
||||||
|
* @param color color of marker
|
||||||
|
* @param selected set to true to draw the marker in a selected state
|
||||||
|
*/
|
||||||
|
void drawStopMarker( QPainter& painter, QPoint topMiddle, const QColor& color, bool selected = false );
|
||||||
|
|
||||||
|
//! Converts an x-coordinate in the widget's coordinate system to a relative ramp position
|
||||||
|
double pointToRelativePosition( int x ) const;
|
||||||
|
|
||||||
|
//! Converts a relative ramp position to a x-coordinate in the widget's coordinate system
|
||||||
|
int relativePositionToPoint( double position ) const;
|
||||||
|
|
||||||
|
//! Returns true if the selected stop is movable and deletable
|
||||||
|
bool selectedStopIsMovable() const;
|
||||||
|
|
||||||
|
//! Returns the closest stop to a mouse x position, or -1 if no stops within tolerance
|
||||||
|
int findClosestStop( int x, int threshold = -1 ) const;
|
||||||
|
|
||||||
|
QgsVectorGradientColorRampV2 mGradient;
|
||||||
|
|
||||||
|
//! We keep a separate, unordered copy of the gradient stops so that the selected stop is not changed.
|
||||||
|
QgsGradientStopsList mStops;
|
||||||
|
|
||||||
|
//! Stop number of selected stop, where 0 = first stop
|
||||||
|
int mSelectedStop;
|
||||||
|
|
||||||
|
//! Polygon for stop triangle marker outer
|
||||||
|
QPolygonF sOuterTriangle;
|
||||||
|
|
||||||
|
//! Polygon for stop triangle marker inner
|
||||||
|
QPolygonF sInnerTriangle;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QGSGRADIENTSTOPEDITOR_H
|
Loading…
x
Reference in New Issue
Block a user