465 lines
14 KiB
C++
465 lines
14 KiB
C++
#include "osthumb.h"
|
|
|
|
#include <QTimer>
|
|
#include <QPainter>
|
|
#include <QApplication>
|
|
#include <QFontMetrics>
|
|
#include <QMouseEvent>
|
|
|
|
static int thumbWidth=20;
|
|
static int thumbHeight=72;
|
|
//#define THUMB_WIDTH 20
|
|
//#define THUMB_HEIGHT 72
|
|
#define THUMB_WIDTH thumbWidth
|
|
#define THUMB_HEIGHT thumbHeight
|
|
#define OUTLINE_THICKNESS 2
|
|
#define ANIMATION_TIMEOUT 10
|
|
#define HIDE_TIMEOUT 100
|
|
#define ANIMATION_ALPHA_INC 15
|
|
|
|
///*
|
|
// * Variables used only for implementing methods.
|
|
// */
|
|
//QTimer *animationTimer; // The timer used to set the timeout for updating the alpha of the thumb
|
|
// // (used for fade in/out animation).
|
|
//QTimer *hideTimer;
|
|
//int alpha; // Current alpha of the thumb.
|
|
//int yPos; // The position the thumb is to be moved (when aligned vertically).
|
|
//int xPos; // Same as above (when aligned horizontally).
|
|
//bool mouseButtonPressed; // Holds the state of the left mouse button.
|
|
//bool wasDragged; // The widget has been dragged (neded to separate click on page up/down and drag action).
|
|
|
|
class OsThumbPrivate
|
|
{
|
|
Q_DECLARE_PUBLIC(OsThumb)
|
|
|
|
public:
|
|
OsThumbPrivate(OsThumb *q);
|
|
bool hidden;
|
|
Qt::Orientation orientation;
|
|
int minimum;
|
|
int maximum;
|
|
OsThumb *q_ptr;
|
|
void checkForLeave(QMouseEvent *event);
|
|
|
|
/*
|
|
* Variables used only for implementing methods.
|
|
*/
|
|
QTimer *animationTimer; // The timer used to set the timeout for updating the alpha of the thumb
|
|
// (used for fade in/out animation).
|
|
QTimer *hideTimer;
|
|
int alpha; // Current alpha of the thumb.
|
|
int yPos; // The position the thumb is to be moved (when aligned vertically).
|
|
int xPos; // Same as above (when aligned horizontally).
|
|
bool mouseButtonPressed; // Holds the state of the left mouse button.
|
|
bool wasDragged; // The widget has been dragged (neded to separate click on page up/down and drag action).
|
|
};
|
|
|
|
OsThumbPrivate::OsThumbPrivate(OsThumb *q) : q_ptr(q)
|
|
{
|
|
/*
|
|
* Initialize variables used only in implementation.
|
|
*/
|
|
alpha = 0;
|
|
animationTimer = new QTimer(q);
|
|
hideTimer = new QTimer(q);
|
|
hideTimer->setSingleShot(true);
|
|
wasDragged = false;
|
|
}
|
|
|
|
void OsThumbPrivate::checkForLeave(QMouseEvent *event)
|
|
{
|
|
Q_Q(OsThumb);
|
|
|
|
if (mouseButtonPressed) {
|
|
return;
|
|
}
|
|
if (orientation == Qt::Vertical) {
|
|
if ((event->globalX() >= q->geometry().x() + q->width()) ||
|
|
(event->globalY() < q->geometry().y()) ||
|
|
(event->globalY() >= q->geometry().y() + q->height())
|
|
)
|
|
hideTimer->start(HIDE_TIMEOUT);
|
|
} else {
|
|
if ((event->globalY() >= q->geometry().y() + q->height()) ||
|
|
(event->globalX() < q->geometry().x()) ||
|
|
(event->globalX() >= q->geometry().x() + q->width())
|
|
)
|
|
hideTimer->start(HIDE_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
OsThumb::OsThumb(Qt::Orientation o, QWidget *parent) :
|
|
QWidget(parent), d_ptr(new OsThumbPrivate(this))
|
|
{
|
|
int fh=QApplication::fontMetrics().height();
|
|
thumbWidth=qMax(20, fh);
|
|
thumbWidth=(((int)(thumbWidth/2))*2)+(thumbWidth%2 ? 2 : 0);
|
|
thumbHeight=thumbWidth*3.5;
|
|
|
|
/*
|
|
* Set up orientation.
|
|
*/
|
|
setOrientation(o);
|
|
if (o == Qt::Vertical)
|
|
setFixedSize(THUMB_WIDTH, THUMB_HEIGHT);
|
|
else
|
|
setFixedSize(THUMB_HEIGHT, THUMB_WIDTH);
|
|
|
|
/*
|
|
* Set some window properties. The thumb needs to be a separate window with no border, no background
|
|
* and it needs to bypass the window manager in order to have a custom behaviour.
|
|
* (Thank you Andrea for the idea)
|
|
*/
|
|
setWindowFlags(Qt::X11BypassWindowManagerHint);
|
|
setAttribute(Qt::WA_TranslucentBackground);
|
|
|
|
/*
|
|
* Initialize class properties.
|
|
*/
|
|
setHidden(false);
|
|
setMouseTracking(true);
|
|
|
|
// /*
|
|
// * Initialize variables used only in implementation.
|
|
// */
|
|
// alpha = 0;
|
|
// animationTimer = new QTimer(this);
|
|
// hideTimer = new QTimer(this);
|
|
// hideTimer->setSingleShot(true);
|
|
// wasDragged = false;
|
|
|
|
/*
|
|
* Connect the animationTimer to the update() method so that the paintEvent() is triggered and the thumb is
|
|
* redrawn with a new alpha. The timer stops when alpha reaches 255 (fade in) or 0 (fade out). Also connect
|
|
* the hideTimer so that when the mouse pointer leaves the thumb it hides after 200 ms if there is no other
|
|
* action performed on the thumb.
|
|
*/
|
|
QObject::connect(d_ptr->animationTimer, SIGNAL(timeout()), this, SLOT(update()));
|
|
QObject::connect(d_ptr->hideTimer, SIGNAL(timeout()), this, SLOT(hide()));
|
|
}
|
|
|
|
void OsThumb::paintEvent(QPaintEvent *event)
|
|
{
|
|
Q_UNUSED(event)
|
|
Q_D(OsThumb);
|
|
/*
|
|
* Initialize the painter and set up the colors that are going to be used for drawing the thumb's
|
|
* elements.
|
|
*/
|
|
QPainter painter(this);
|
|
QPalette palette;
|
|
QColor thumbStartColor = QColor::fromRgb(0xf2f1f0);
|
|
QColor thumbStopColor = QColor::fromRgb(0xd2dedc);
|
|
QColor outlineColor = palette.color(QPalette::Highlight);
|
|
QColor arrowColor(Qt::black);
|
|
QColor etchColor(Qt::white);
|
|
QLinearGradient fill(1, 1, THUMB_WIDTH - 2, THUMB_HEIGHT - 2);
|
|
|
|
int start=(THUMB_WIDTH/4)*1.5;
|
|
int size=THUMB_WIDTH/5;
|
|
QPoint pgUpPoints[] = {
|
|
QPoint(THUMB_WIDTH/2, start),
|
|
QPoint(THUMB_WIDTH/2 + size, start+size+1),
|
|
QPoint(THUMB_WIDTH/2 - size, start+size+1)
|
|
};
|
|
QPoint pgDownPoints[] = {
|
|
QPoint(THUMB_WIDTH/2, THUMB_HEIGHT - start/*75*/),
|
|
QPoint(THUMB_WIDTH/2 + size, THUMB_HEIGHT - (start+size+1)/*69*/),
|
|
QPoint(THUMB_WIDTH/2 - size, THUMB_HEIGHT - (start+size+1)/*69*/)
|
|
};
|
|
|
|
/*
|
|
* The folowing condition is responsible for the alpha setting during the animation. If the thumb
|
|
* is hidden that means that is about to be drawn therefor we increment the alpha value by
|
|
* ANIMATION_ALPHA_INC, once the alpha is at it's maximum value (255) we stop the timer. If the
|
|
* thumb is shown (!hidden) we decrement the alpha value by ANIMATION_ALPHA_INC until it reaches
|
|
* it's minimum (0), at which time we stop the timer and hide the widget.
|
|
*/
|
|
if (hidden())
|
|
{
|
|
if (d->alpha >= 255)
|
|
{
|
|
d->animationTimer->stop();
|
|
}
|
|
else
|
|
d->alpha += ANIMATION_ALPHA_INC;
|
|
if (d->alpha>100) {
|
|
emit showing();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (d->alpha <= 0)
|
|
{
|
|
d->animationTimer->stop();
|
|
setVisible(false);
|
|
}
|
|
else
|
|
d->alpha -= ANIMATION_ALPHA_INC;
|
|
if (d->alpha<100) {
|
|
emit hiding();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the current alpha for each individual color of the widget.
|
|
*/
|
|
thumbStartColor.setAlpha(d->alpha);
|
|
thumbStopColor.setAlpha(d->alpha);
|
|
outlineColor.setAlpha(d->alpha);
|
|
arrowColor.setAlpha(d->alpha*0.5);
|
|
etchColor.setAlpha(d->alpha*0.5);
|
|
|
|
/*
|
|
* Set up the gradient used for filling the widget
|
|
*/
|
|
fill.setColorAt(0, thumbStartColor);
|
|
fill.setColorAt(0.5, thumbStopColor);
|
|
fill.setColorAt(1, thumbStartColor);
|
|
|
|
/*
|
|
* Save the current painter state onto the stack
|
|
*/
|
|
painter.save();
|
|
|
|
/*
|
|
* Set up antialiasing
|
|
*/
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
/*
|
|
* Check if the widget is horizontal, and if it is apply required transformations to the painter and thumb
|
|
*/
|
|
if (orientation() == Qt::Horizontal)
|
|
{
|
|
painter.translate(THUMB_HEIGHT, 0);
|
|
painter.rotate(90);
|
|
}
|
|
|
|
/*
|
|
* Draw the widget's contents
|
|
*/
|
|
painter.setPen(Qt::NoPen);
|
|
painter.setBrush(QBrush(fill));
|
|
painter.drawRect(1, 1, THUMB_WIDTH - 2, THUMB_HEIGHT - 2);
|
|
|
|
painter.setPen(QPen(outlineColor, OUTLINE_THICKNESS));
|
|
painter.setBrush(Qt::NoBrush);
|
|
painter.drawRect(1, 1, THUMB_WIDTH - 2, THUMB_HEIGHT - 2);
|
|
|
|
painter.setPen(Qt::NoPen);
|
|
painter.setBrush(QBrush(arrowColor, Qt::SolidPattern));
|
|
painter.drawConvexPolygon(pgUpPoints, 3);
|
|
painter.drawConvexPolygon(pgDownPoints, 3);
|
|
|
|
arrowColor.setAlphaF(arrowColor.alphaF()*0.4);
|
|
painter.setPen(arrowColor);
|
|
painter.drawLine(QPointF(3.5, (THUMB_HEIGHT/2)+0.5), QPointF(THUMB_WIDTH-3.5, (THUMB_HEIGHT/2)+0.5));
|
|
painter.setPen(etchColor);
|
|
painter.drawLine(QPointF(3.5, (THUMB_HEIGHT/2)+1.5), QPointF(THUMB_WIDTH-3.5, (THUMB_HEIGHT/2)+1.5));
|
|
|
|
// TODO: Draw dots...
|
|
|
|
/*
|
|
* Pop the new painter state from the stack
|
|
*/
|
|
painter.restore();
|
|
}
|
|
|
|
void OsThumb::show()
|
|
{
|
|
Q_D(OsThumb);
|
|
/*
|
|
* Show the thumb (in case it was not already shown), in case it was fading out stop the animation,
|
|
* set the thumb's current state (visibility wise) (hidden property) to true in order for
|
|
* the paintEvent to start incrementing the alpha value of the thumb, and start the animation again.
|
|
*/
|
|
setVisible(true);
|
|
d->animationTimer->stop();
|
|
setHidden(true);
|
|
d->animationTimer->start(ANIMATION_TIMEOUT);
|
|
}
|
|
|
|
void OsThumb::hide()
|
|
{
|
|
Q_D(OsThumb);
|
|
/*
|
|
* In case it was fading out stop the animation, set the thumb's current state (visibility wise)
|
|
* (hidden property) to false in order for the paintEvent to start decrementing the alpha value
|
|
* of the thumb, and start the animation again.
|
|
*/
|
|
d->animationTimer->stop();
|
|
setHidden(false);
|
|
d->animationTimer->start(ANIMATION_TIMEOUT);
|
|
}
|
|
|
|
void OsThumb::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
Q_D(OsThumb);
|
|
d->hideTimer->stop();
|
|
/*
|
|
* Verify that the left mouse button was pressed and save the position at which the event
|
|
* occurred. Set the mouseButtonPressed property to true in order to initiate dragging in case
|
|
* the user moves the mouse (mouseMoveEvent occurres) while the left mouse button is pressed.
|
|
*/
|
|
if (event->button() == Qt::LeftButton)
|
|
{
|
|
d->yPos = event->pos().y();
|
|
d->xPos = event->pos().x();
|
|
d->mouseButtonPressed = true;
|
|
}
|
|
//else QApplication::quit();
|
|
}
|
|
|
|
void OsThumb::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
Q_D(OsThumb);
|
|
|
|
d->hideTimer->stop();
|
|
d->checkForLeave(event);
|
|
/*
|
|
* If the mouse button is pressed and the user starts moving the mouse; move the thumb
|
|
* to the mouse's Y position (vertical) or X position (horizontal). Also don't move the thumb
|
|
* outside the proximity region's boundries. Set wasDragged property to true so that when
|
|
* the mouseReleaseEvent occurs don't initiate a pageUp or pageDown movement within the
|
|
* scrollable content.
|
|
*/
|
|
if (d->mouseButtonPressed) {
|
|
int toXPos = x();
|
|
int toYPos = y();
|
|
|
|
if (orientation() == Qt::Vertical) {
|
|
// vertical movement
|
|
toYPos = event->globalY() - d->yPos;
|
|
int minYPos = minimum();
|
|
int maxYPos = maximum();
|
|
|
|
if (toYPos < minYPos)
|
|
toYPos = minYPos;
|
|
if (toYPos > maxYPos)
|
|
toYPos = maxYPos;
|
|
// end vertical movement
|
|
} else {
|
|
// horizontal movement
|
|
toXPos = event->globalX() - d->xPos;
|
|
int minXPos = minimum();
|
|
int maxXPos = maximum();
|
|
|
|
if (toXPos < minXPos)
|
|
toXPos = minXPos;
|
|
if (toXPos > maxXPos)
|
|
toXPos = maxXPos;
|
|
// end horizontal movement
|
|
}
|
|
|
|
move(toXPos, toYPos);
|
|
|
|
d->wasDragged = true;
|
|
|
|
emit thumbDragged(QPoint(toXPos,toYPos));
|
|
}
|
|
}
|
|
|
|
void OsThumb::mouseReleaseEvent(QMouseEvent *event)
|
|
{
|
|
Q_D(OsThumb);
|
|
|
|
d->hideTimer->stop();
|
|
|
|
/*
|
|
* When the mouse button is released set mouseButton pressed to false, verify if the thumb has beed dragged
|
|
* if it has then check if the mouse pointer has left the thumb's surface and hide it or not accordingly.
|
|
*/
|
|
d->mouseButtonPressed = false;
|
|
|
|
if (d->wasDragged) {
|
|
d->wasDragged = false;
|
|
|
|
d->checkForLeave(event);
|
|
|
|
/*
|
|
* If the thumb wasn't dragged, based on orientation, the user clicked the top/bottom, lef/right arrow and
|
|
* the content should scroll one page in the respective direction.
|
|
*/
|
|
} else {
|
|
if (orientation() == Qt::Vertical)
|
|
{
|
|
if (event->y() < THUMB_HEIGHT / 2)
|
|
emit pageUp();
|
|
if (event->y() > THUMB_HEIGHT / 2)
|
|
emit pageDown();
|
|
}
|
|
else
|
|
{
|
|
if (event->x() < THUMB_HEIGHT / 2)
|
|
emit pageUp();
|
|
if (event->x() > THUMB_HEIGHT / 2)
|
|
emit pageDown();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool OsThumb::isVisible() const
|
|
{
|
|
return hidden();
|
|
}
|
|
|
|
bool OsThumb::mouseButtonPressed() const
|
|
{
|
|
Q_D(const OsThumb);
|
|
return d->mouseButtonPressed;
|
|
}
|
|
|
|
Qt::Orientation OsThumb::orientation() const
|
|
{
|
|
Q_D(const OsThumb);
|
|
return d->orientation;
|
|
}
|
|
|
|
void OsThumb::setOrientation(Qt::Orientation o)
|
|
{
|
|
Q_D(OsThumb);
|
|
d->orientation = o;
|
|
if (o == Qt::Vertical)
|
|
setFixedSize(THUMB_WIDTH, THUMB_HEIGHT);
|
|
else
|
|
setFixedSize(THUMB_HEIGHT, THUMB_WIDTH);
|
|
}
|
|
|
|
int OsThumb::minimum() const
|
|
{
|
|
Q_D(const OsThumb);
|
|
return d->minimum;
|
|
}
|
|
|
|
int OsThumb::maximum() const
|
|
{
|
|
Q_D(const OsThumb);
|
|
return d->maximum;
|
|
}
|
|
|
|
void OsThumb::setMinimum(int minimum)
|
|
{
|
|
Q_D(OsThumb);
|
|
d->minimum = minimum;
|
|
}
|
|
|
|
void OsThumb::setMaximum(int maximum)
|
|
{
|
|
Q_D(OsThumb);
|
|
d->maximum = maximum;
|
|
}
|
|
|
|
bool OsThumb::hidden() const
|
|
{
|
|
Q_D(const OsThumb);
|
|
return d->hidden;
|
|
}
|
|
|
|
void OsThumb::setHidden(bool hidden)
|
|
{
|
|
Q_D(OsThumb);
|
|
d->hidden = hidden;
|
|
}
|