kde-extraapps/gwenview/lib/hud/hudslider.cpp
Ivailo Monev cbf29a08cf generic: make changes required for building against Katie
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2015-11-25 10:01:00 +00:00

389 lines
11 KiB
C++

// vim: set tabstop=4 shiftwidth=4 expandtab:
/*
Gwenview: an image viewer
Copyright 2011 Aurélien Gâteau <agateau@kde.org>
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
*/
// Self
#include "hud/moc_hudslider.cpp"
// Local
#include <hud/hudtheme.h>
// KDE
#include <KDebug>
#include <KGlobalSettings>
#include <KIconLoader>
// Qt
#include <QApplication>
#include <QGraphicsSceneEvent>
#include <QPainter>
#include <QStyle>
#include <QtGui/qstyleoption.h>
#include <QTimer>
namespace Gwenview
{
static const int FIRST_REPEAT_DELAY = 500;
struct HudSliderPrivate
{
HudSlider* q;
int mMin, mMax, mPageStep, mSingleStep;
int mSliderPosition;
int mRepeatX;
QAbstractSlider::SliderAction mRepeatAction;
int mValue;
bool mIsDown;
QRectF mHandleRect;
bool hasValidRange() const
{
return mMax > mMin;
}
void updateHandleRect()
{
static const HudTheme::RenderInfo renderInfo = HudTheme::renderInfo(HudTheme::SliderWidgetHandle);
static const int radius = renderInfo.borderRadius;
const QRectF sliderRect = q->boundingRect();
const qreal posX = xForPosition(mSliderPosition) - radius;
const qreal posY = sliderRect.height() / 2 - radius;
mHandleRect = QRectF(posX, posY, radius * 2, radius * 2);
}
int positionForX(qreal x) const
{
static const HudTheme::RenderInfo renderInfo = HudTheme::renderInfo(HudTheme::SliderWidgetHandle);
static const int radius = renderInfo.borderRadius;
const qreal sliderWidth = q->boundingRect().width();
x -= radius;
if (QApplication::isRightToLeft()) {
x = sliderWidth - 2 * radius - x;
}
return mMin + int(x / (sliderWidth - 2 * radius) * (mMax - mMin));
}
qreal xForPosition(int pos) const
{
static const HudTheme::RenderInfo renderInfo = HudTheme::renderInfo(HudTheme::SliderWidgetHandle);
static const int radius = renderInfo.borderRadius;
const qreal sliderWidth = q->boundingRect().width();
qreal x = (qreal(pos - mMin) / (mMax - mMin)) * (sliderWidth - 2 * radius);
if (QApplication::isRightToLeft()) {
x = sliderWidth - 2 * radius - x;
}
return x + radius;
}
};
HudSlider::HudSlider(QGraphicsItem* parent)
: QGraphicsWidget(parent)
, d(new HudSliderPrivate)
{
d->q = this;
d->mMin = 0;
d->mMax = 100;
d->mPageStep = 10;
d->mSingleStep = 1;
d->mSliderPosition = d->mValue = 0;
d->mIsDown = false;
d->mRepeatAction = QAbstractSlider::SliderNoAction;
d->updateHandleRect();
setCursor(Qt::ArrowCursor);
setAcceptHoverEvents(true);
setFocusPolicy(Qt::WheelFocus);
}
HudSlider::~HudSlider()
{
delete d;
}
void HudSlider::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget*)
{
bool drawHandle = d->hasValidRange();
HudTheme::State state;
if (drawHandle && option->state.testFlag(QStyle::State_MouseOver)) {
state = d->mIsDown ? HudTheme::DownState : HudTheme::MouseOverState;
} else {
state = HudTheme::NormalState;
}
painter->setRenderHint(QPainter::Antialiasing);
const QRectF sliderRect = boundingRect();
// Groove
HudTheme::RenderInfo renderInfo = HudTheme::renderInfo(HudTheme::SliderWidgetGroove, state);
painter->setPen(renderInfo.borderPen);
painter->setBrush(renderInfo.bgBrush);
qreal centerY = d->mHandleRect.center().y();
QRectF grooveRect = QRectF(
0, centerY - renderInfo.borderRadius,
sliderRect.width(),
2 * renderInfo.borderRadius
);
if (drawHandle) {
// Clip out handle
QPainterPath clipPath;
clipPath.addRect(QRectF(QPointF(0, 0), d->mHandleRect.bottomLeft()).adjusted(0, 0, 1, 0));
clipPath.addRect(QRectF(d->mHandleRect.topRight(), sliderRect.bottomRight()).adjusted(-1, 0, 0, 0));
painter->setClipPath(clipPath);
}
painter->drawRoundedRect(grooveRect.adjusted(.5, .5, -.5, -.5), renderInfo.borderRadius, renderInfo.borderRadius);
if (!drawHandle) {
return;
}
painter->setClipping(false);
// Handle
renderInfo = HudTheme::renderInfo(HudTheme::SliderWidgetHandle, state);
painter->setPen(renderInfo.borderPen);
painter->setBrush(renderInfo.bgBrush);
painter->drawRoundedRect(d->mHandleRect.adjusted(.5, .5, -.5, -.5), renderInfo.borderRadius, renderInfo.borderRadius);
}
void HudSlider::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
if (!d->hasValidRange()) {
return;
}
const int pos = d->positionForX(event->pos().x());
if (d->mHandleRect.contains(event->pos())) {
switch (event->button()) {
case Qt::LeftButton:
d->mIsDown = true;
break;
case Qt::MiddleButton:
setSliderPosition(pos);
triggerAction(QAbstractSlider::SliderMove);
break;
default:
break;
}
} else {
d->mRepeatX = event->pos().x();
d->mRepeatAction = pos < d->mSliderPosition
? QAbstractSlider::SliderPageStepSub
: QAbstractSlider::SliderPageStepAdd;
doRepeatAction(FIRST_REPEAT_DELAY);
}
update();
}
void HudSlider::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
if (!d->hasValidRange()) {
return;
}
if (d->mIsDown) {
setSliderPosition(d->positionForX(event->pos().x()));
triggerAction(QAbstractSlider::SliderMove);
update();
}
}
void HudSlider::mouseReleaseEvent(QGraphicsSceneMouseEvent* /*event*/)
{
if (!d->hasValidRange()) {
return;
}
d->mIsDown = false;
d->mRepeatAction = QAbstractSlider::SliderNoAction;
update();
}
void HudSlider::wheelEvent(QGraphicsSceneWheelEvent* event)
{
if (!d->hasValidRange()) {
return;
}
int step = qMin(QApplication::wheelScrollLines() * d->mSingleStep, d->mPageStep);
if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) {
step = d->mPageStep;
}
setSliderPosition(d->mSliderPosition + event->delta() * step / 120);
triggerAction(QAbstractSlider::SliderMove);
}
void HudSlider::keyPressEvent(QKeyEvent* event)
{
if (!d->hasValidRange()) {
return;
}
bool rtl = QApplication::isRightToLeft();
switch (event->key()) {
case Qt::Key_Left:
triggerAction(rtl ? QAbstractSlider::SliderSingleStepAdd : QAbstractSlider::SliderSingleStepSub);
break;
case Qt::Key_Right:
triggerAction(rtl ? QAbstractSlider::SliderSingleStepSub : QAbstractSlider::SliderSingleStepAdd);
break;
case Qt::Key_PageUp:
triggerAction(QAbstractSlider::SliderPageStepSub);
break;
case Qt::Key_PageDown:
triggerAction(QAbstractSlider::SliderPageStepAdd);
break;
case Qt::Key_Home:
triggerAction(QAbstractSlider::SliderToMinimum);
break;
case Qt::Key_End:
triggerAction(QAbstractSlider::SliderToMaximum);
break;
default:
event->ignore();
break;
}
}
void HudSlider::keyReleaseEvent(QKeyEvent* /*event*/)
{
if (!d->hasValidRange()) {
return;
}
d->mRepeatAction = QAbstractSlider::SliderNoAction;
}
void HudSlider::setRange(int min, int max)
{
if (min == d->mMin && max == d->mMax) {
return;
}
d->mMin = min;
d->mMax = max;
setValue(d->mValue); // ensure value is within min and max
d->updateHandleRect();
update();
}
void HudSlider::setPageStep(int step)
{
d->mPageStep = step;
}
void HudSlider::setSingleStep(int step)
{
d->mSingleStep = step;
}
void HudSlider::setValue(int value)
{
value = qBound(d->mMin, value, d->mMax);
if (value != d->mValue) {
d->mValue = value;
setSliderPosition(value);
update();
valueChanged(d->mValue);
}
}
int HudSlider::sliderPosition() const
{
return d->mSliderPosition;
}
void HudSlider::setSliderPosition(int pos)
{
pos = qBound(d->mMin, pos, d->mMax);
if (pos != d->mSliderPosition) {
d->mSliderPosition = pos;
d->updateHandleRect();
update();
}
}
bool HudSlider::isSliderDown() const
{
return d->mIsDown;
}
void HudSlider::triggerAction(QAbstractSlider::SliderAction action)
{
switch (action) {
case QAbstractSlider::SliderSingleStepAdd:
setSliderPosition(d->mValue + d->mSingleStep);
break;
case QAbstractSlider::SliderSingleStepSub:
setSliderPosition(d->mValue - d->mSingleStep);
break;
case QAbstractSlider::SliderPageStepAdd:
setSliderPosition(d->mValue + d->mPageStep);
break;
case QAbstractSlider::SliderPageStepSub:
setSliderPosition(d->mValue - d->mPageStep);
break;
case QAbstractSlider::SliderToMinimum:
setSliderPosition(d->mMin);
break;
case QAbstractSlider::SliderToMaximum:
setSliderPosition(d->mMax);
break;
case QAbstractSlider::SliderMove:
case QAbstractSlider::SliderNoAction:
break;
};
actionTriggered(action);
setValue(d->mSliderPosition);
}
void HudSlider::doRepeatAction(int time)
{
int step = 0;
switch (d->mRepeatAction) {
case QAbstractSlider::SliderSingleStepAdd:
case QAbstractSlider::SliderSingleStepSub:
step = d->mSingleStep;
break;
case QAbstractSlider::SliderPageStepAdd:
case QAbstractSlider::SliderPageStepSub:
step = d->mPageStep;
break;
case QAbstractSlider::SliderToMinimum:
case QAbstractSlider::SliderToMaximum:
case QAbstractSlider::SliderMove:
kWarning() << "Not much point in repeating action of type" << d->mRepeatAction;
return;
case QAbstractSlider::SliderNoAction:
return;
}
int pos = d->positionForX(d->mRepeatX);
if (qAbs(pos - d->mSliderPosition) >= step) {
// We are far enough from the position where the mouse button was held
// down to be able to repeat the action one more time
triggerAction(d->mRepeatAction);
QTimer::singleShot(time, this, SLOT(doRepeatAction()));
} else {
// We are too close to the held down position, reach the position and
// don't repeat
d->mRepeatAction = QAbstractSlider::SliderNoAction;
setSliderPosition(pos);
triggerAction(QAbstractSlider::SliderMove);
return;
}
}
} // namespace