File indexing completed on 2024-05-12 04:19:41
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2011 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "hud/hudslider.h" 0023 0024 // Local 0025 #include "gwenview_lib_debug.h" 0026 #include <hud/hudtheme.h> 0027 0028 // KF 0029 0030 // Qt 0031 #include <QApplication> 0032 #include <QGraphicsSceneEvent> 0033 #include <QPainter> 0034 #include <QStyle> 0035 #include <QStyleOptionGraphicsItem> 0036 #include <QTimer> 0037 0038 namespace Gwenview 0039 { 0040 static const int FIRST_REPEAT_DELAY = 500; 0041 0042 struct HudSliderPrivate { 0043 HudSlider *q = nullptr; 0044 int mMin, mMax, mPageStep, mSingleStep; 0045 int mSliderPosition; 0046 int mRepeatX; 0047 QAbstractSlider::SliderAction mRepeatAction; 0048 int mValue; 0049 bool mIsDown; 0050 0051 QRectF mHandleRect; 0052 0053 bool hasValidRange() const 0054 { 0055 return mMax > mMin; 0056 } 0057 0058 void updateHandleRect() 0059 { 0060 static const HudTheme::RenderInfo renderInfo = HudTheme::renderInfo(HudTheme::SliderWidgetHandle); 0061 static const int radius = renderInfo.borderRadius; 0062 0063 const QRectF sliderRect = q->boundingRect(); 0064 const qreal posX = xForPosition(mSliderPosition) - radius; 0065 const qreal posY = sliderRect.height() / 2 - radius; 0066 mHandleRect = QRectF(posX, posY, radius * 2, radius * 2); 0067 } 0068 0069 int positionForX(qreal x) const 0070 { 0071 static const HudTheme::RenderInfo renderInfo = HudTheme::renderInfo(HudTheme::SliderWidgetHandle); 0072 static const int radius = renderInfo.borderRadius; 0073 0074 const qreal sliderWidth = q->boundingRect().width(); 0075 0076 x -= radius; 0077 if (QApplication::isRightToLeft()) { 0078 x = sliderWidth - 2 * radius - x; 0079 } 0080 return mMin + int(x / (sliderWidth - 2 * radius) * (mMax - mMin)); 0081 } 0082 0083 qreal xForPosition(int pos) const 0084 { 0085 static const HudTheme::RenderInfo renderInfo = HudTheme::renderInfo(HudTheme::SliderWidgetHandle); 0086 static const int radius = renderInfo.borderRadius; 0087 0088 const qreal sliderWidth = q->boundingRect().width(); 0089 qreal x = (qreal(pos - mMin) / (mMax - mMin)) * (sliderWidth - 2 * radius); 0090 if (QApplication::isRightToLeft()) { 0091 x = sliderWidth - 2 * radius - x; 0092 } 0093 return x + radius; 0094 } 0095 }; 0096 0097 HudSlider::HudSlider(QGraphicsItem *parent) 0098 : QGraphicsWidget(parent) 0099 , d(new HudSliderPrivate) 0100 { 0101 d->q = this; 0102 d->mMin = 0; 0103 d->mMax = 100; 0104 d->mPageStep = 10; 0105 d->mSingleStep = 1; 0106 d->mSliderPosition = d->mValue = 0; 0107 d->mIsDown = false; 0108 d->mRepeatAction = QAbstractSlider::SliderNoAction; 0109 setCursor(Qt::ArrowCursor); 0110 setAcceptHoverEvents(true); 0111 setFocusPolicy(Qt::WheelFocus); 0112 0113 QTimer::singleShot(0, this, [this]() { 0114 d->updateHandleRect(); 0115 }); 0116 } 0117 0118 HudSlider::~HudSlider() 0119 { 0120 delete d; 0121 } 0122 0123 void HudSlider::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) 0124 { 0125 bool drawHandle = d->hasValidRange(); 0126 HudTheme::State state; 0127 if (drawHandle && option->state.testFlag(QStyle::State_MouseOver)) { 0128 state = d->mIsDown ? HudTheme::DownState : HudTheme::MouseOverState; 0129 } else { 0130 state = HudTheme::NormalState; 0131 } 0132 painter->setRenderHint(QPainter::Antialiasing); 0133 0134 const QRectF sliderRect = boundingRect(); 0135 0136 // Groove 0137 HudTheme::RenderInfo renderInfo = HudTheme::renderInfo(HudTheme::SliderWidgetGroove, state); 0138 painter->setPen(renderInfo.borderPen); 0139 painter->setBrush(renderInfo.bgBrush); 0140 qreal centerY = d->mHandleRect.center().y(); 0141 QRectF grooveRect = QRectF(0, centerY - renderInfo.borderRadius, sliderRect.width(), 2 * renderInfo.borderRadius); 0142 0143 if (drawHandle) { 0144 // Clip out handle 0145 QPainterPath clipPath; 0146 clipPath.addRect(QRectF(QPointF(0, 0), d->mHandleRect.bottomLeft()).adjusted(0, 0, 1, 0)); 0147 clipPath.addRect(QRectF(d->mHandleRect.topRight(), sliderRect.bottomRight()).adjusted(-1, 0, 0, 0)); 0148 painter->setClipPath(clipPath); 0149 } 0150 painter->drawRoundedRect(grooveRect.adjusted(.5, .5, -.5, -.5), renderInfo.borderRadius, renderInfo.borderRadius); 0151 if (!drawHandle) { 0152 return; 0153 } 0154 painter->setClipping(false); 0155 0156 // Handle 0157 renderInfo = HudTheme::renderInfo(HudTheme::SliderWidgetHandle, state); 0158 painter->setPen(renderInfo.borderPen); 0159 painter->setBrush(renderInfo.bgBrush); 0160 painter->drawRoundedRect(d->mHandleRect.adjusted(.5, .5, -.5, -.5), renderInfo.borderRadius, renderInfo.borderRadius); 0161 } 0162 0163 void HudSlider::mousePressEvent(QGraphicsSceneMouseEvent *event) 0164 { 0165 if (!d->hasValidRange()) { 0166 return; 0167 } 0168 const int pos = d->positionForX(event->pos().x()); 0169 if (d->mHandleRect.contains(event->pos())) { 0170 switch (event->button()) { 0171 case Qt::LeftButton: 0172 d->mIsDown = true; 0173 break; 0174 case Qt::MiddleButton: 0175 setSliderPosition(pos); 0176 triggerAction(QAbstractSlider::SliderMove); 0177 break; 0178 default: 0179 break; 0180 } 0181 } else { 0182 d->mRepeatX = event->pos().x(); 0183 d->mRepeatAction = pos < d->mSliderPosition ? QAbstractSlider::SliderPageStepSub : QAbstractSlider::SliderPageStepAdd; 0184 doRepeatAction(FIRST_REPEAT_DELAY); 0185 } 0186 update(); 0187 } 0188 0189 void HudSlider::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 0190 { 0191 if (!d->hasValidRange()) { 0192 return; 0193 } 0194 if (d->mIsDown) { 0195 setSliderPosition(d->positionForX(event->pos().x())); 0196 triggerAction(QAbstractSlider::SliderMove); 0197 update(); 0198 } 0199 } 0200 0201 void HudSlider::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/) 0202 { 0203 if (!d->hasValidRange()) { 0204 return; 0205 } 0206 d->mIsDown = false; 0207 d->mRepeatAction = QAbstractSlider::SliderNoAction; 0208 update(); 0209 } 0210 0211 void HudSlider::wheelEvent(QGraphicsSceneWheelEvent *event) 0212 { 0213 if (!d->hasValidRange()) { 0214 return; 0215 } 0216 int step = qMin(QApplication::wheelScrollLines() * d->mSingleStep, d->mPageStep); 0217 if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) { 0218 step = d->mPageStep; 0219 } 0220 setSliderPosition(d->mSliderPosition + event->delta() * step / 120); 0221 triggerAction(QAbstractSlider::SliderMove); 0222 } 0223 0224 void HudSlider::keyPressEvent(QKeyEvent *event) 0225 { 0226 if (!d->hasValidRange()) { 0227 return; 0228 } 0229 bool rtl = QApplication::isRightToLeft(); 0230 switch (event->key()) { 0231 case Qt::Key_Left: 0232 triggerAction(rtl ? QAbstractSlider::SliderSingleStepAdd : QAbstractSlider::SliderSingleStepSub); 0233 break; 0234 case Qt::Key_Right: 0235 triggerAction(rtl ? QAbstractSlider::SliderSingleStepSub : QAbstractSlider::SliderSingleStepAdd); 0236 break; 0237 case Qt::Key_PageUp: 0238 triggerAction(QAbstractSlider::SliderPageStepSub); 0239 break; 0240 case Qt::Key_PageDown: 0241 triggerAction(QAbstractSlider::SliderPageStepAdd); 0242 break; 0243 case Qt::Key_Home: 0244 triggerAction(QAbstractSlider::SliderToMinimum); 0245 break; 0246 case Qt::Key_End: 0247 triggerAction(QAbstractSlider::SliderToMaximum); 0248 break; 0249 default: 0250 event->ignore(); 0251 break; 0252 } 0253 } 0254 0255 void HudSlider::keyReleaseEvent(QKeyEvent * /*event*/) 0256 { 0257 if (!d->hasValidRange()) { 0258 return; 0259 } 0260 d->mRepeatAction = QAbstractSlider::SliderNoAction; 0261 } 0262 0263 void HudSlider::setRange(int min, int max) 0264 { 0265 if (min == d->mMin && max == d->mMax) { 0266 return; 0267 } 0268 d->mMin = min; 0269 d->mMax = max; 0270 setValue(d->mValue); // ensure value is within min and max 0271 d->updateHandleRect(); 0272 update(); 0273 } 0274 0275 void HudSlider::setPageStep(int step) 0276 { 0277 d->mPageStep = step; 0278 } 0279 0280 void HudSlider::setSingleStep(int step) 0281 { 0282 d->mSingleStep = step; 0283 } 0284 0285 void HudSlider::setValue(int value) 0286 { 0287 value = qBound(d->mMin, value, d->mMax); 0288 if (value != d->mValue) { 0289 d->mValue = value; 0290 setSliderPosition(value); 0291 update(); 0292 Q_EMIT valueChanged(d->mValue); 0293 } 0294 } 0295 0296 int HudSlider::sliderPosition() const 0297 { 0298 return d->mSliderPosition; 0299 } 0300 0301 void HudSlider::setSliderPosition(int pos) 0302 { 0303 pos = qBound(d->mMin, pos, d->mMax); 0304 if (pos != d->mSliderPosition) { 0305 d->mSliderPosition = pos; 0306 d->updateHandleRect(); 0307 update(); 0308 } 0309 } 0310 0311 bool HudSlider::isSliderDown() const 0312 { 0313 return d->mIsDown; 0314 } 0315 0316 void HudSlider::triggerAction(QAbstractSlider::SliderAction action) 0317 { 0318 switch (action) { 0319 case QAbstractSlider::SliderSingleStepAdd: 0320 setSliderPosition(d->mValue + d->mSingleStep); 0321 break; 0322 case QAbstractSlider::SliderSingleStepSub: 0323 setSliderPosition(d->mValue - d->mSingleStep); 0324 break; 0325 case QAbstractSlider::SliderPageStepAdd: 0326 setSliderPosition(d->mValue + d->mPageStep); 0327 break; 0328 case QAbstractSlider::SliderPageStepSub: 0329 setSliderPosition(d->mValue - d->mPageStep); 0330 break; 0331 case QAbstractSlider::SliderToMinimum: 0332 setSliderPosition(d->mMin); 0333 break; 0334 case QAbstractSlider::SliderToMaximum: 0335 setSliderPosition(d->mMax); 0336 break; 0337 case QAbstractSlider::SliderMove: 0338 case QAbstractSlider::SliderNoAction: 0339 break; 0340 }; 0341 Q_EMIT actionTriggered(action); 0342 setValue(d->mSliderPosition); 0343 } 0344 0345 void HudSlider::doRepeatAction(int time) 0346 { 0347 int step = 0; 0348 switch (d->mRepeatAction) { 0349 case QAbstractSlider::SliderSingleStepAdd: 0350 case QAbstractSlider::SliderSingleStepSub: 0351 step = d->mSingleStep; 0352 break; 0353 case QAbstractSlider::SliderPageStepAdd: 0354 case QAbstractSlider::SliderPageStepSub: 0355 step = d->mPageStep; 0356 break; 0357 case QAbstractSlider::SliderToMinimum: 0358 case QAbstractSlider::SliderToMaximum: 0359 case QAbstractSlider::SliderMove: 0360 qCWarning(GWENVIEW_LIB_LOG) << "Not much point in repeating action of type" << d->mRepeatAction; 0361 return; 0362 case QAbstractSlider::SliderNoAction: 0363 return; 0364 } 0365 0366 int pos = d->positionForX(d->mRepeatX); 0367 if (qAbs(pos - d->mSliderPosition) >= step) { 0368 // We are far enough from the position where the mouse button was held 0369 // down to be able to repeat the action one more time 0370 triggerAction(d->mRepeatAction); 0371 QTimer::singleShot(time, this, SLOT(doRepeatAction())); 0372 } else { 0373 // We are too close to the held down position, reach the position and 0374 // don't repeat 0375 d->mRepeatAction = QAbstractSlider::SliderNoAction; 0376 setSliderPosition(pos); 0377 triggerAction(QAbstractSlider::SliderMove); 0378 return; 0379 } 0380 } 0381 0382 } // namespace 0383 0384 #include "moc_hudslider.cpp"