19#include "moc_qgselevationcontrollerwidget.cpp"
33#include <QPainterPath>
39 QVBoxLayout *vl =
new QVBoxLayout();
40 vl->setContentsMargins( 0, 0, 0, 0 );
42 mConfigureButton =
new QToolButton();
43 mConfigureButton->setPopupMode( QToolButton::InstantPopup );
45 QHBoxLayout *hl =
new QHBoxLayout();
46 hl->setContentsMargins( 0, 0, 0, 0 );
47 hl->addWidget( mConfigureButton );
50 mMenu =
new QMenu(
this );
51 mConfigureButton->setMenu( mMenu );
53 mSettingsAction =
new QgsElevationControllerSettingsAction( mMenu );
54 mMenu->addAction( mSettingsAction );
55 mInvertDirectionAction =
new QAction( tr(
"Invert Direction" ),
this );
56 mInvertDirectionAction->setCheckable(
true );
57 mMenu->addAction( mInvertDirectionAction );
59 mSettingsAction->sizeSpin()->clear();
60 connect( mSettingsAction->sizeSpin(), qOverload< double >( &QgsDoubleSpinBox::valueChanged ),
this, [
this](
double size )
62 setFixedRangeSize( size < 0 ? -1 : size );
65 mMenu->addSeparator();
70 mSliderLabels =
new QgsElevationControllerLabels();
72 QHBoxLayout *hlSlider =
new QHBoxLayout();
73 hlSlider->setContentsMargins( 0, 0, 0, 0 );
74 hlSlider->setSpacing( 2 );
75 hlSlider->addWidget( mSlider );
76 hlSlider->addWidget( mSliderLabels, 1 );
77 hlSlider->addStretch();
78 vl->addLayout( hlSlider );
80 setCursor( Qt::ArrowCursor );
96 if ( mBlockSliderChanges )
100 mSliderLabels->setRange(
range() );
103 connect( mInvertDirectionAction, &QAction::toggled,
this, [
this]()
106 mSliderLabels->setInverted( mInvertDirectionAction->isChecked() );
118 QWidget::resizeEvent( event );
125 const int snappedLower =
static_cast< int >( std::floor( mCurrentRange.
lower() * mSliderPrecision ) );
126 const int snappedUpper =
static_cast< int >( std::ceil( mCurrentRange.
upper() * mSliderPrecision ) );
128 return mCurrentRange;
131 if ( mFixedRangeSize >= 0 )
135 if ( sliderRange.
upper() + mFixedRangeSize <= mRangeLimits.
upper() )
163 if (
range == mCurrentRange )
166 mCurrentRange =
range;
167 mBlockSliderChanges =
true;
168 mSlider->
setRange(
static_cast< int >( std::floor(
range.
lower() * mSliderPrecision ) ),
169 static_cast< int >( std::ceil(
range.
upper() * mSliderPrecision ) ) );
170 mBlockSliderChanges =
false;
173 mSliderLabels->setRange( mCurrentRange );
181 mRangeLimits = limits;
183 const double limitRange = limits.
upper() - limits.
lower();
186 mSliderPrecision = std::max( 1000, mSlider->height() ) / limitRange;
188 mBlockSliderChanges =
true;
189 mSlider->
setRangeLimits(
static_cast< int >( std::floor( limits.
lower() * mSliderPrecision ) ),
190 static_cast< int >( std::ceil( limits.
upper() * mSliderPrecision ) ) );
193 const double newCurrentLower = std::max( mCurrentRange.
lower(), limits.
lower() );
194 const double newCurrentUpper = std::min( mCurrentRange.
upper(), limits.
upper() );
195 const bool rangeHasChanged = newCurrentLower != mCurrentRange.
lower() || newCurrentUpper != mCurrentRange.
upper();
197 mSlider->
setRange(
static_cast< int >( std::floor( newCurrentLower * mSliderPrecision ) ),
198 static_cast< int >( std::ceil( newCurrentUpper * mSliderPrecision ) ) );
199 mCurrentRange =
QgsDoubleRange( newCurrentLower, newCurrentUpper );
200 mBlockSliderChanges =
false;
201 if ( rangeHasChanged )
204 mSliderLabels->setLimits( mRangeLimits );
207void QgsElevationControllerWidget::updateWidgetMask()
215 QRegion reg( frameGeometry() );
216 reg -= QRegion( geometry() );
217 reg += childrenRegion();
223 return mFixedRangeSize;
228 if ( size == mFixedRangeSize )
231 mFixedRangeSize = size;
232 if ( mFixedRangeSize < 0 )
238 mSlider->
setFixedRangeSize(
static_cast< int >( std::round( mFixedRangeSize * mSliderPrecision ) ) );
240 if ( mFixedRangeSize != mSettingsAction->sizeSpin()->value() )
241 mSettingsAction->sizeSpin()->setValue( mFixedRangeSize );
247 mInvertDirectionAction->setChecked( inverted );
252 mSliderLabels->setSignificantElevations( elevations );
259QgsElevationControllerLabels::QgsElevationControllerLabels( QWidget *parent )
263 QFont smallerFont = font();
264 int fontSize = smallerFont.pointSize();
266 fontSize = std::max( fontSize - 1, 8 );
268 fontSize = std::max( fontSize - 2, 7 );
270 smallerFont.setPointSize( fontSize );
271 setFont( smallerFont );
273 const QFontMetrics fm( smallerFont );
274 setMinimumWidth( fm.horizontalAdvance(
'0' ) * 5 );
275 setAttribute( Qt::WA_TransparentForMouseEvents );
278void QgsElevationControllerLabels::paintEvent( QPaintEvent * )
280 QStyleOptionSlider styleOption;
281 styleOption.initFrom(
this );
283 const QRect sliderRect = style()->subControlRect( QStyle::CC_Slider, &styleOption, QStyle::SC_SliderHandle,
this );
284 const int sliderHeight = sliderRect.height();
287 const QFontMetrics fm( f );
289 const int left = rect().left() + 2;
291 const double limitRange = mLimits.upper() - mLimits.lower();
292 const double lowerFraction = ( mRange.lower() - mLimits.lower() ) / limitRange;
293 const double upperFraction = ( mRange.upper() - mLimits.lower() ) / limitRange;
294 const int lowerY = !mInverted
295 ? ( std::min(
static_cast< int >( std::round( rect().bottom() - sliderHeight * 0.5 - ( rect().height() - sliderHeight ) * lowerFraction + fm.ascent() ) ),
296 rect().bottom() - fm.descent() ) )
297 : ( std::max( static_cast< int >( std::round( rect().top() + sliderHeight * 0.5 + ( rect().height() - sliderHeight ) * lowerFraction - fm.descent() ) ),
298 rect().top() + fm.ascent() ) );
299 const int upperY = !mInverted ?
300 ( std::max(
static_cast< int >( std::round( rect().bottom() - sliderHeight * 0.5 - ( rect().height() - sliderHeight ) * upperFraction - fm.descent() ) ),
301 rect().top() + fm.ascent() ) )
302 : ( std::min( static_cast< int >( std::round( rect().top() + sliderHeight * 0.5 + ( rect().height() - sliderHeight ) * upperFraction + fm.ascent() ) ),
303 rect().bottom() - fm.descent() ) );
305 const bool lowerIsCloseToLimit = !mInverted
306 ? ( lowerY + fm.height() > rect().bottom() - fm.descent() )
307 : ( lowerY - fm.height() < rect().top() + fm.ascent() ) ;
308 const bool upperIsCloseToLimit = !mInverted
309 ? ( upperY - fm.height() < rect().top() + fm.ascent() )
310 : ( upperY + fm.height() > rect().bottom() - fm.descent() ) ;
311 const bool lowerIsCloseToUpperLimit = !mInverted
312 ? ( lowerY - fm.height() < rect().top() + fm.ascent() )
313 : ( lowerY + fm.height() > rect().bottom() - fm.descent() );
319 for (
double value : std::as_const( mSignificantElevations ) )
321 const double valueFraction = ( value - mLimits.lower() ) / limitRange;
322 const double verticalCenter = !mInverted
323 ? ( std::min(
static_cast< int >( std::round( rect().bottom() - sliderHeight * 0.5 - ( rect().height() - sliderHeight ) * valueFraction + fm.capHeight() * 0.5 ) ),
324 rect().bottom() - fm.descent() ) )
325 : ( std::max( static_cast< int >( std::round( rect().top() + sliderHeight * 0.5 + ( rect().height() - sliderHeight ) * valueFraction + fm.capHeight() * 0.5 ) ),
326 rect().top() + fm.ascent() ) );
328 const bool valueIsCloseToLower = verticalCenter + fm.height() > lowerY && verticalCenter - fm.height() < lowerY;
329 if ( valueIsCloseToLower )
332 const bool valueIsCloseToUpper = verticalCenter + fm.height() > upperY && verticalCenter - fm.height() < upperY;
333 if ( valueIsCloseToUpper )
336 const bool valueIsCloseToLowerLimit = !mInverted
337 ? ( verticalCenter + fm.height() > rect().bottom() - fm.descent() )
338 : ( verticalCenter - fm.height() < rect().top() + fm.ascent() ) ;
339 if ( valueIsCloseToLowerLimit )
342 const bool valueIsCloseToUpperLimit = !mInverted
343 ? ( verticalCenter - fm.height() < rect().top() + fm.ascent() )
344 : ( verticalCenter + fm.height() > rect().bottom() - fm.descent() ) ;
345 if ( valueIsCloseToUpperLimit )
348 path.addText( left, verticalCenter, f, locale.toString( value ) );
351 if ( mLimits.lower() > std::numeric_limits< double >::lowest() )
353 if ( lowerIsCloseToLimit )
356 path.addText( left, lowerY, f, locale.toString( mRange.lower() ) );
361 path.addText( left, lowerY, f, locale.toString( mRange.lower() ) );
363 path.addText( left, !mInverted ? ( rect().bottom() - fm.descent() ) : ( rect().top() + fm.ascent() ), f, locale.toString( mLimits.
lower() ) );
367 if ( mLimits.upper() < std::numeric_limits< double >::max() )
371 if ( !lowerIsCloseToUpperLimit )
374 path.addText( left, !mInverted ? ( rect().top() + fm.ascent() ) : ( rect().bottom() - fm.descent() ), f, locale.toString( mLimits.upper() ) );
379 if ( upperIsCloseToLimit )
382 path.addText( left, upperY, f, locale.toString( mRange.upper() ) );
387 path.addText( left, upperY, f, locale.toString( mRange.upper() ) );
389 path.addText( left, !mInverted ? ( rect().top() + fm.ascent() ) : ( rect().bottom() - fm.descent() ), f, locale.toString( mLimits.upper() ) );
395 p.setRenderHint( QPainter::Antialiasing,
true );
396 const QColor bufferColor = palette().color( QPalette::Window );
397 const QColor textColor = palette().color( QPalette::WindowText );
398 QPen pen( bufferColor );
399 pen.setJoinStyle( Qt::RoundJoin );
400 pen.setCapStyle( Qt::RoundCap );
403 p.setBrush( Qt::NoBrush );
405 p.setPen( Qt::NoPen );
406 p.setBrush( QBrush( textColor ) );
411void QgsElevationControllerLabels::setLimits(
const QgsDoubleRange &limits )
413 if ( limits == mLimits )
416 const QFontMetrics fm( font() );
417 const int maxChars = std::max( QLocale().toString( std::floor( limits.
lower() ) ).length(),
418 QLocale().toString( std::floor( limits.
upper() ) ).length() ) + 3;
419 setMinimumWidth( fm.horizontalAdvance(
'0' ) * maxChars );
425void QgsElevationControllerLabels::setRange(
const QgsDoubleRange &range )
427 if ( range == mRange )
434void QgsElevationControllerLabels::setInverted(
bool inverted )
436 if ( inverted == mInverted )
439 mInverted = inverted;
443void QgsElevationControllerLabels::setSignificantElevations(
const QList<double> &elevations )
445 if ( elevations == mSignificantElevations )
448 mSignificantElevations = elevations;
456QgsElevationControllerSettingsAction::QgsElevationControllerSettingsAction( QWidget *parent )
457 : QWidgetAction( parent )
459 QGridLayout *gLayout =
new QGridLayout();
460 gLayout->setContentsMargins( 3, 2, 3, 2 );
462 QLabel *label =
new QLabel( tr(
"Fixed Range Size" ) );
463 gLayout->addWidget( label, 0, 0 );
466 mSizeSpin->setDecimals( 4 );
467 mSizeSpin->setMinimum( -1.0 );
468 mSizeSpin->setMaximum( 999999999.0 );
469 mSizeSpin->setClearValue( -1, tr(
"Not set" ) );
470 mSizeSpin->setKeyboardTracking(
false );
471 mSizeSpin->setToolTip( tr(
"Limit elevation range to a fixed size" ) );
473 gLayout->addWidget( mSizeSpin, 0, 1 );
475 QWidget *w =
new QWidget();
476 w->setLayout( gLayout );
477 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)