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)