32#include <QPainterPath> 
   38  QVBoxLayout *vl = 
new QVBoxLayout();
 
   39  vl->setContentsMargins( 0, 0, 0, 0 );
 
   41  mConfigureButton = 
new QToolButton();
 
   42  mConfigureButton->setPopupMode( QToolButton::InstantPopup );
 
   44  QHBoxLayout *hl = 
new QHBoxLayout();
 
   45  hl->setContentsMargins( 0, 0, 0, 0 );
 
   46  hl->addWidget( mConfigureButton );
 
   49  mMenu = 
new QMenu( 
this );
 
   50  mConfigureButton->setMenu( mMenu );
 
   52  mSettingsAction = 
new QgsElevationControllerSettingsAction( mMenu );
 
   53  mMenu->addAction( mSettingsAction );
 
   54  mInvertDirectionAction = 
new QAction( tr( 
"Invert Direction" ), 
this );
 
   55  mInvertDirectionAction->setCheckable( 
true );
 
   56  mMenu->addAction( mInvertDirectionAction );
 
   58  mSettingsAction->sizeSpin()->clear();
 
   59  connect( mSettingsAction->sizeSpin(), qOverload< double >( &QgsDoubleSpinBox::valueChanged ), 
this, [
this]( 
double size )
 
   61    setFixedRangeSize( size < 0 ? -1 : size );
 
   64  mMenu->addSeparator();
 
   69  mSliderLabels = 
new QgsElevationControllerLabels();
 
   71  QHBoxLayout *hlSlider = 
new QHBoxLayout();
 
   72  hlSlider->setContentsMargins( 0, 0, 0, 0 );
 
   73  hlSlider->setSpacing( 2 );
 
   74  hlSlider->addWidget( mSlider );
 
   75  hlSlider->addWidget( mSliderLabels, 1 );
 
   76  hlSlider->addStretch();
 
   77  vl->addLayout( hlSlider );
 
   79  setCursor( Qt::ArrowCursor );
 
   95    if ( mBlockSliderChanges )
 
   99    mSliderLabels->setRange( 
range() );
 
  102  connect( mInvertDirectionAction, &QAction::toggled, 
this, [
this]()
 
  105    mSliderLabels->setInverted( mInvertDirectionAction->isChecked() );
 
 
  117  QWidget::resizeEvent( event );
 
 
  124  const int snappedLower = 
static_cast< int >( std::floor( mCurrentRange.
lower() * mSliderPrecision ) );
 
  125  const int snappedUpper = 
static_cast< int >( std::ceil( mCurrentRange.
upper() * mSliderPrecision ) );
 
  127    return mCurrentRange;
 
  130  if ( mFixedRangeSize >= 0 )
 
  134    if ( sliderRange.
upper() + mFixedRangeSize <= mRangeLimits.
upper() )
 
 
  162  if ( 
range == mCurrentRange )
 
  165  mCurrentRange = 
range;
 
  166  mBlockSliderChanges = 
true;
 
  167  mSlider->
setRange( 
static_cast< int >( std::floor( 
range.
lower() * mSliderPrecision ) ),
 
  168                     static_cast< int >( std::ceil( 
range.
upper() * mSliderPrecision ) ) );
 
  169  mBlockSliderChanges = 
false;
 
  172  mSliderLabels->setRange( mCurrentRange );
 
 
  180  mRangeLimits = limits;
 
  182  const double limitRange = limits.
upper() - limits.
lower();
 
  185  mSliderPrecision = std::max( 1000, mSlider->height() ) / limitRange;
 
  187  mBlockSliderChanges = 
true;
 
  188  mSlider->
setRangeLimits( 
static_cast< int >( std::floor( limits.
lower() * mSliderPrecision ) ),
 
  189                           static_cast< int >( std::ceil( limits.
upper() * mSliderPrecision ) ) );
 
  192  const double newCurrentLower = std::max( mCurrentRange.
lower(), limits.
lower() );
 
  193  const double newCurrentUpper = std::min( mCurrentRange.
upper(), limits.
upper() );
 
  194  const bool rangeHasChanged = newCurrentLower != mCurrentRange.
lower() || newCurrentUpper != mCurrentRange.
upper();
 
  196  mSlider->
setRange( 
static_cast< int >( std::floor( newCurrentLower * mSliderPrecision ) ),
 
  197                     static_cast< int >( std::ceil( newCurrentUpper * mSliderPrecision ) ) );
 
  198  mCurrentRange = 
QgsDoubleRange( newCurrentLower, newCurrentUpper );
 
  199  mBlockSliderChanges = 
false;
 
  200  if ( rangeHasChanged )
 
  203  mSliderLabels->setLimits( mRangeLimits );
 
 
  206void QgsElevationControllerWidget::updateWidgetMask()
 
  214  QRegion reg( frameGeometry() );
 
  215  reg -= QRegion( geometry() );
 
  216  reg += childrenRegion();
 
  222  return mFixedRangeSize;
 
 
  227  if ( size == mFixedRangeSize )
 
  230  mFixedRangeSize = size;
 
  231  if ( mFixedRangeSize < 0 )
 
  237    mSlider->
setFixedRangeSize( 
static_cast< int >( std::round( mFixedRangeSize * mSliderPrecision ) ) );
 
  239  if ( mFixedRangeSize != mSettingsAction->sizeSpin()->value() )
 
  240    mSettingsAction->sizeSpin()->setValue( mFixedRangeSize );
 
 
  246  mInvertDirectionAction->setChecked( inverted );
 
 
  251  mSliderLabels->setSignificantElevations( elevations );
 
 
  258QgsElevationControllerLabels::QgsElevationControllerLabels( QWidget *parent )
 
  262  QFont smallerFont = font();
 
  263  int fontSize = smallerFont.pointSize();
 
  265  fontSize = std::max( fontSize - 1, 8 ); 
 
  267  fontSize = std::max( fontSize - 2, 7 );
 
  269  smallerFont.setPointSize( fontSize );
 
  270  setFont( smallerFont );
 
  272  const QFontMetrics fm( smallerFont );
 
  273  setMinimumWidth( fm.horizontalAdvance( 
'0' ) * 5 );
 
  274  setAttribute( Qt::WA_TransparentForMouseEvents );
 
  277void QgsElevationControllerLabels::paintEvent( QPaintEvent * )
 
  279  QStyleOptionSlider styleOption;
 
  280  styleOption.initFrom( 
this );
 
  282  const QRect sliderRect = style()->subControlRect( QStyle::CC_Slider, &styleOption,  QStyle::SC_SliderHandle, 
this );
 
  283  const int sliderHeight = sliderRect.height();
 
  286  const QFontMetrics fm( f );
 
  288  const int left = rect().left() + 2;
 
  290  const double limitRange = mLimits.upper() - mLimits.lower();
 
  291  const double lowerFraction = ( mRange.lower() - mLimits.lower() ) / limitRange;
 
  292  const double upperFraction = ( mRange.upper() - mLimits.lower() ) / limitRange;
 
  293  const int lowerY = !mInverted
 
  294                     ? ( std::min( 
static_cast< int >( std::round( rect().bottom() - sliderHeight * 0.5 - ( rect().height() - sliderHeight ) * lowerFraction + fm.ascent() ) ),
 
  295                                   rect().bottom() - fm.descent() ) )
 
  296                     : ( std::max( static_cast< int >( std::round( rect().top() + sliderHeight * 0.5 + ( rect().height() - sliderHeight ) * lowerFraction - fm.descent() ) ),
 
  297                                   rect().top() + fm.ascent() ) );
 
  298  const int upperY = !mInverted ?
 
  299                     ( std::max( 
static_cast< int >( std::round( rect().bottom() - sliderHeight * 0.5 - ( rect().height() - sliderHeight ) * upperFraction - fm.descent() ) ),
 
  300                                 rect().top() + fm.ascent() ) )
 
  301                     : ( std::min( static_cast< int >( std::round( rect().top() + sliderHeight * 0.5 + ( rect().height() - sliderHeight ) * upperFraction + fm.ascent() ) ),
 
  302                                   rect().bottom() - fm.descent() ) );
 
  304  const bool lowerIsCloseToLimit = !mInverted
 
  305                                   ? ( lowerY + fm.height() > rect().bottom() - fm.descent() )
 
  306                                   : ( lowerY - fm.height() < rect().top() + fm.ascent() ) ;
 
  307  const bool upperIsCloseToLimit = !mInverted
 
  308                                   ? ( upperY - fm.height() < rect().top() + fm.ascent() )
 
  309                                   : ( upperY + fm.height() > rect().bottom() - fm.descent() ) ;
 
  310  const bool lowerIsCloseToUpperLimit = !mInverted
 
  311                                        ? ( lowerY - fm.height() < rect().top() + fm.ascent() )
 
  312                                        : ( lowerY + fm.height() > rect().bottom() - fm.descent() );
 
  318  for ( 
double value : std::as_const( mSignificantElevations ) )
 
  320    const double valueFraction = ( value - mLimits.lower() ) / limitRange;
 
  321    const double verticalCenter = !mInverted
 
  322                                  ? ( std::min( 
static_cast< int >( std::round( rect().bottom() - sliderHeight * 0.5 - ( rect().height() - sliderHeight ) * valueFraction + fm.capHeight() * 0.5 ) ),
 
  323                                      rect().bottom() - fm.descent() ) )
 
  324                                  : ( std::max( static_cast< int >( std::round( rect().top() + sliderHeight * 0.5 + ( rect().height() - sliderHeight ) * valueFraction + fm.capHeight() * 0.5 ) ),
 
  325                                      rect().top() + fm.ascent() ) );
 
  327    const bool valueIsCloseToLower = verticalCenter + fm.height() > lowerY && verticalCenter - fm.height() < lowerY;
 
  328    if ( valueIsCloseToLower )
 
  331    const bool valueIsCloseToUpper = verticalCenter + fm.height() > upperY && verticalCenter - fm.height() < upperY;
 
  332    if ( valueIsCloseToUpper )
 
  335    const bool valueIsCloseToLowerLimit = !mInverted
 
  336                                          ? ( verticalCenter + fm.height() > rect().bottom() - fm.descent() )
 
  337                                          : ( verticalCenter - fm.height() < rect().top() + fm.ascent() ) ;
 
  338    if ( valueIsCloseToLowerLimit )
 
  341    const bool valueIsCloseToUpperLimit = !mInverted
 
  342                                          ? ( verticalCenter - fm.height() < rect().top() + fm.ascent() )
 
  343                                          : ( verticalCenter + fm.height() > rect().bottom() - fm.descent() ) ;
 
  344    if ( valueIsCloseToUpperLimit )
 
  347    path.addText( left, verticalCenter, f, locale.toString( value ) );
 
  350  if ( mLimits.lower() > std::numeric_limits< double >::lowest() )
 
  352    if ( lowerIsCloseToLimit )
 
  355      path.addText( left, lowerY, f, locale.toString( mRange.lower() ) );
 
  360      path.addText( left, lowerY, f, locale.toString( mRange.lower() ) );
 
  362      path.addText( left, !mInverted ? ( rect().bottom() - fm.descent() ) : ( rect().top() + fm.ascent() ), f, locale.toString( mLimits.
lower() ) );
 
  366  if ( mLimits.upper() < std::numeric_limits< double >::max() )
 
  370      if ( !lowerIsCloseToUpperLimit )
 
  373        path.addText( left, !mInverted ? ( rect().top() + fm.ascent() ) : ( rect().bottom() - fm.descent() ), f, locale.toString( mLimits.upper() ) );
 
  378      if ( upperIsCloseToLimit )
 
  381        path.addText( left, upperY, f, locale.toString( mRange.upper() ) );
 
  386        path.addText( left, upperY, f, locale.toString( mRange.upper() ) );
 
  388        path.addText( left, !mInverted ? ( rect().top() + fm.ascent() ) : ( rect().bottom() - fm.descent() ), f, locale.toString( mLimits.upper() ) );
 
  394  p.setRenderHint( QPainter::Antialiasing, 
true );
 
  395  const QColor bufferColor = palette().color( QPalette::Window );
 
  396  const QColor textColor = palette().color( QPalette::WindowText );
 
  397  QPen pen( bufferColor );
 
  398  pen.setJoinStyle( Qt::RoundJoin );
 
  399  pen.setCapStyle( Qt::RoundCap );
 
  402  p.setBrush( Qt::NoBrush );
 
  404  p.setPen( Qt::NoPen );
 
  405  p.setBrush( QBrush( textColor ) );
 
  410void QgsElevationControllerLabels::setLimits( 
const QgsDoubleRange &limits )
 
  412  if ( limits == mLimits )
 
  415  const QFontMetrics fm( font() );
 
  416  const int maxChars = std::max( QLocale().toString( std::floor( limits.
lower() ) ).length(),
 
  417                                 QLocale().toString( std::floor( limits.
upper() ) ).length() ) + 3;
 
  418  setMinimumWidth( fm.horizontalAdvance( 
'0' ) * maxChars );
 
  424void QgsElevationControllerLabels::setRange( 
const QgsDoubleRange &range )
 
  426  if ( range == mRange )
 
  433void QgsElevationControllerLabels::setInverted( 
bool inverted )
 
  435  if ( inverted == mInverted )
 
  438  mInverted = inverted;
 
  442void QgsElevationControllerLabels::setSignificantElevations( 
const QList<double> &elevations )
 
  444  if ( elevations == mSignificantElevations )
 
  447  mSignificantElevations = elevations;
 
  455QgsElevationControllerSettingsAction::QgsElevationControllerSettingsAction( QWidget *parent )
 
  456  : QWidgetAction( parent )
 
  458  QGridLayout *gLayout = 
new QGridLayout();
 
  459  gLayout->setContentsMargins( 3, 2, 3, 2 );
 
  461  QLabel *label = 
new QLabel( tr( 
"Fixed Range Size" ) );
 
  462  gLayout->addWidget( label, 0, 0 );
 
  465  mSizeSpin->setDecimals( 4 );
 
  466  mSizeSpin->setMinimum( -1.0 );
 
  467  mSizeSpin->setMaximum( 999999999.0 );
 
  468  mSizeSpin->setClearValue( -1, tr( 
"Not set" ) );
 
  469  mSizeSpin->setKeyboardTracking( 
false );
 
  470  mSizeSpin->setToolTip( tr( 
"Limit elevation range to a fixed size" ) );
 
  472  gLayout->addWidget( mSizeSpin, 0, 1 );
 
  474  QWidget *w = 
new QWidget();
 
  475  w->setLayout( gLayout );
 
  476  setDefaultWidget( w );
 
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
 
QgsRange which stores a range of double values.
 
bool isInfinite() const
Returns true if the range consists of all possible values.
 
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
 
QgsDoubleRange elevationRange() const
Returns the project's elevation range, which indicates the upper and lower elevation limits associate...
 
void elevationRangeChanged(const QgsDoubleRange &range)
Emitted when the project's elevation is changed.
 
static QgsProject * instance()
Returns the QgsProject singleton instance.
 
const QgsProjectElevationProperties * elevationProperties() const
Returns the project's elevation properties, which contains the project's elevation related settings.
 
A slider control with two interactive endpoints, for interactive selection of a range of values.
 
void setRangeLimits(int minimum, int maximum)
Sets the minimum and maximum range limits for values allowed in the widget.
 
int upperValue() const
Returns the upper value for the range selected in the widget.
 
void rangeChanged(int minimum, int maximum)
Emitted when the range selected in the widget is changed.
 
void setFlippedDirection(bool flipped)
Sets whether the slider has its values flipped.
 
void setRange(int lower, int upper)
Sets the current range selected in the widget.
 
int lowerValue() const
Returns the lower value for the range selected in the widget.
 
void setFixedRangeSize(int size)
Sets the slider's fixed range size.
 
T lower() const
Returns the lower bound of the range.
 
T upper() const
Returns the upper bound of the range.
 
int ANALYSIS_EXPORT lower(int n, int i)
Lower function.
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)