PlatformScrollBarQt.cpp [plain text]
#include "config.h"
#include "PlatformScrollBar.h"
#include "EventHandler.h"
#include "FrameView.h"
#include "Frame.h"
#include "GraphicsContext.h"
#include "IntRect.h"
#include "PlatformMouseEvent.h"
#include <QApplication>
#include <QDebug>
#include <QPainter>
#include <QStyle>
using namespace std;
namespace WebCore {
const double cInitialTimerDelay = 0.25;
const double cNormalTimerDelay = 0.05;
PlatformScrollbar::PlatformScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize size)
: Scrollbar(client, orientation, size)
, m_pressedPos(0)
, m_pressedPart(QStyle::SC_None)
, m_hoveredPart(QStyle::SC_None)
, m_scrollTimer(this, &PlatformScrollbar::autoscrollTimerFired)
{
QStyle *s = QApplication::style();
m_opt.state = QStyle::State_Active | QStyle::State_Enabled;
m_opt.sliderValue = m_opt.sliderPosition = 0;
m_opt.upsideDown = false;
setEnabled(true);
if (size != RegularScrollbar)
m_opt.state |= QStyle::State_Mini;
if (orientation == HorizontalScrollbar) {
m_opt.rect.setHeight(horizontalScrollbarHeight(size));
m_opt.orientation = Qt::Horizontal;
m_opt.state |= QStyle::State_Horizontal;
} else {
m_opt.rect.setWidth(verticalScrollbarWidth(size));
m_opt.orientation = Qt::Vertical;
m_opt.state &= ~QStyle::State_Horizontal;
}
}
PlatformScrollbar::~PlatformScrollbar()
{
stopTimerIfNeeded();
}
void PlatformScrollbar::updateThumbPosition()
{
invalidate();
}
void PlatformScrollbar::updateThumbProportion()
{
invalidate();
}
int PlatformScrollbar::width() const
{
return m_opt.rect.width();
}
int PlatformScrollbar::height() const
{
return m_opt.rect.height();
}
void PlatformScrollbar::setRect(const IntRect& rect)
{
setFrameGeometry(rect);
}
IntRect PlatformScrollbar::frameGeometry() const
{
return m_opt.rect;
}
void PlatformScrollbar::setFrameGeometry(const IntRect& rect)
{
m_opt.rect = rect;
}
bool PlatformScrollbar::isEnabled() const
{
return m_opt.state & QStyle::State_Enabled;
}
void PlatformScrollbar::setEnabled(bool enabled)
{
if (enabled != isEnabled()) {
if (enabled) {
m_opt.state |= QStyle::State_Enabled;
} else {
m_opt.state &= ~QStyle::State_Enabled;
}
invalidate();
}
}
void PlatformScrollbar::paint(GraphicsContext* graphicsContext, const IntRect& damageRect)
{
if (controlSize() != RegularScrollbar) {
m_opt.state |= QStyle::State_Mini;
} else {
m_opt.state &= ~QStyle::State_Mini;
}
m_opt.orientation = (orientation() == VerticalScrollbar) ? Qt::Vertical : Qt::Horizontal;
QStyle *s = QApplication::style();
if (orientation() == HorizontalScrollbar) {
m_opt.rect.setHeight(horizontalScrollbarHeight(controlSize()));
m_opt.state |= QStyle::State_Horizontal;
} else {
m_opt.rect.setWidth(verticalScrollbarWidth(controlSize()));
m_opt.state &= ~QStyle::State_Horizontal;
}
if (graphicsContext->paintingDisabled() || !m_opt.rect.isValid())
return;
QRect clip = m_opt.rect.intersected(damageRect);
if (clip.isEmpty())
return;
QPainter *p = graphicsContext->platformContext();
p->save();
p->setClipRect(clip);
m_opt.sliderValue = value();
m_opt.sliderPosition = value();
m_opt.pageStep = m_visibleSize;
m_opt.singleStep = m_lineStep;
m_opt.minimum = 0;
m_opt.maximum = qMax(0, m_totalSize - m_visibleSize);
if (m_pressedPart != QStyle::SC_None) {
m_opt.activeSubControls = m_pressedPart;
} else {
m_opt.activeSubControls = m_hoveredPart;
}
const QPoint topLeft = m_opt.rect.topLeft();
p->translate(topLeft);
m_opt.rect.moveTo(QPoint(0, 0));
QApplication::style()->drawComplexControl(QStyle::CC_ScrollBar, &m_opt, p, 0);
m_opt.rect.moveTo(topLeft);
p->restore();
}
int PlatformScrollbar::thumbPosition() const
{
if (isEnabled())
return (int)((float)m_currentPos * (trackLength() - thumbLength()) / (m_totalSize - m_visibleSize));
return 0;
}
int PlatformScrollbar::thumbLength() const
{
IntRect thumb = QApplication::style()->subControlRect(QStyle::CC_ScrollBar, &m_opt, QStyle::SC_ScrollBarSlider, 0);
return m_orientation == HorizontalScrollbar ? thumb.width() : thumb.height();
}
int PlatformScrollbar::trackLength() const
{
IntRect track = QApplication::style()->subControlRect(QStyle::CC_ScrollBar, &m_opt, QStyle::SC_ScrollBarGroove, 0);
return m_orientation == HorizontalScrollbar ? track.width() : track.height();
}
bool PlatformScrollbar::handleMouseMoveEvent(const PlatformMouseEvent& evt)
{
const QPoint pos = convertFromContainingWindow(evt.pos());
m_opt.state |= QStyle::State_MouseOver;
const QPoint topLeft = m_opt.rect.topLeft();
m_opt.rect.moveTo(QPoint(0, 0));
QStyle::SubControl sc = QApplication::style()->hitTestComplexControl(QStyle::CC_ScrollBar, &m_opt, pos, 0);
m_opt.rect.moveTo(topLeft);
if (sc == m_pressedPart) {
m_opt.state |= QStyle::State_Sunken;
} else {
m_opt.state &= ~QStyle::State_Sunken;
}
if (m_pressedPart == QStyle::SC_ScrollBarSlider) {
int thumbPos = thumbPosition();
int thumbLen = thumbLength();
int trackLen = trackLength();
int maxPos = trackLen - thumbLen;
int delta = 0;
if (m_orientation == HorizontalScrollbar)
delta = pos.x() - m_pressedPos;
else
delta = pos.y() - m_pressedPos;
if (delta > 0)
delta = min(maxPos - thumbPos, delta);
else if (delta < 0)
delta = max(-thumbPos, delta);
if (delta != 0) {
setValue((int)((float)(thumbPos + delta) * (m_totalSize - m_visibleSize) / (trackLen - thumbLen)));
m_pressedPos += thumbPosition() - thumbPos;
}
return true;
}
if (m_pressedPart != QStyle::SC_None)
m_pressedPos = m_orientation == HorizontalScrollbar ? pos.x() : pos.y();
if (sc != m_hoveredPart) {
if (m_pressedPart != QStyle::SC_None) {
if (sc == m_pressedPart) {
startTimerIfNeeded(cNormalTimerDelay);
invalidate();
} else if (m_hoveredPart == m_pressedPart) {
stopTimerIfNeeded();
invalidate();
}
} else {
invalidate();
}
m_hoveredPart = sc;
}
return true;
}
bool PlatformScrollbar::handleMouseOutEvent(const PlatformMouseEvent& evt)
{
m_opt.state &= ~QStyle::State_MouseOver;
m_opt.state &= ~QStyle::State_Sunken;
invalidate();
return true;
}
bool PlatformScrollbar::handleMousePressEvent(const PlatformMouseEvent& evt)
{
const QPoint pos = convertFromContainingWindow(evt.pos());
const QPoint topLeft = m_opt.rect.topLeft();
m_opt.rect.moveTo(QPoint(0, 0));
QStyle::SubControl sc = QApplication::style()->hitTestComplexControl(QStyle::CC_ScrollBar, &m_opt, pos, 0);
m_opt.rect.moveTo(topLeft);
switch (sc) {
case QStyle::SC_ScrollBarAddLine:
case QStyle::SC_ScrollBarSubLine:
case QStyle::SC_ScrollBarSlider:
m_opt.state |= QStyle::State_Sunken;
case QStyle::SC_ScrollBarAddPage:
case QStyle::SC_ScrollBarSubPage:
case QStyle::SC_ScrollBarGroove:
m_pressedPart = sc;
break;
default:
m_pressedPart = QStyle::SC_None;
return false;
}
m_pressedPos = m_orientation == HorizontalScrollbar ? pos.x() : pos.y();
autoscrollPressedPart(cInitialTimerDelay);
invalidate();
return true;
}
bool PlatformScrollbar::handleMouseReleaseEvent(const PlatformMouseEvent&)
{
m_opt.state &= ~QStyle::State_Sunken;
m_pressedPart = QStyle::SC_None;
m_pressedPos = 0;
stopTimerIfNeeded();
invalidate();
return true;
}
void PlatformScrollbar::startTimerIfNeeded(double delay)
{
if (m_pressedPart == QStyle::SC_ScrollBarSlider)
return;
if (m_pressedPart == QStyle::SC_ScrollBarGroove && thumbUnderMouse()) {
invalidate();
m_hoveredPart = QStyle::SC_ScrollBarSlider;
return;
}
ScrollDirection dir = pressedPartScrollDirection();
if (dir == ScrollUp || dir == ScrollLeft) {
if (m_currentPos == 0)
return;
} else {
if (m_currentPos == m_totalSize - m_visibleSize)
return;
}
m_scrollTimer.startOneShot(delay);
}
void PlatformScrollbar::stopTimerIfNeeded()
{
if (m_scrollTimer.isActive())
m_scrollTimer.stop();
}
void PlatformScrollbar::autoscrollPressedPart(double delay)
{
if (m_pressedPart == QStyle::SC_ScrollBarSlider || m_pressedPart == QStyle::SC_None)
return;
if (m_pressedPart == QStyle::SC_ScrollBarGroove && thumbUnderMouse()) {
invalidate();
m_hoveredPart = QStyle::SC_ScrollBarSlider;
return;
}
if (scroll(pressedPartScrollDirection(), pressedPartScrollGranularity()))
startTimerIfNeeded(delay);
}
void PlatformScrollbar::autoscrollTimerFired(Timer<PlatformScrollbar>*)
{
autoscrollPressedPart(cNormalTimerDelay);
}
ScrollDirection PlatformScrollbar::pressedPartScrollDirection()
{
if (m_orientation == HorizontalScrollbar) {
if (m_pressedPart == QStyle::SC_ScrollBarSubLine || m_pressedPart == QStyle::SC_ScrollBarSubPage)
return ScrollLeft;
return ScrollRight;
} else {
if (m_pressedPart == QStyle::SC_ScrollBarSubLine || m_pressedPart == QStyle::SC_ScrollBarSubPage)
return ScrollUp;
return ScrollDown;
}
}
ScrollGranularity PlatformScrollbar::pressedPartScrollGranularity()
{
if (m_pressedPart == QStyle::SC_ScrollBarSubLine || m_pressedPart == QStyle::SC_ScrollBarAddLine)
return ScrollByLine;
return ScrollByPage;
}
bool PlatformScrollbar::thumbUnderMouse()
{
IntRect thumb = QApplication::style()->subControlRect(QStyle::CC_ScrollBar, &m_opt, QStyle::SC_ScrollBarSlider, 0);
thumb.move(-m_opt.rect.x(), -m_opt.rect.y());
int begin = (m_orientation == HorizontalScrollbar) ? thumb.x() : thumb.y();
int end = (m_orientation == HorizontalScrollbar) ? thumb.right() : thumb.bottom();
return (begin <= m_pressedPos && m_pressedPos < end);
}
int PlatformScrollbar::horizontalScrollbarHeight(ScrollbarControlSize controlSize)
{
QStyle *s = QApplication::style();
QStyleOptionSlider o;
o.orientation = Qt::Horizontal;
o.state |= QStyle::State_Horizontal;
if (controlSize != RegularScrollbar)
o.state |= QStyle::State_Mini;
return s->pixelMetric(QStyle::PM_ScrollBarExtent, &o, 0);
}
int PlatformScrollbar::verticalScrollbarWidth(ScrollbarControlSize controlSize)
{
QStyle *s = QApplication::style();
QStyleOptionSlider o;
o.orientation = Qt::Vertical;
o.state &= ~QStyle::State_Horizontal;
if (controlSize != RegularScrollbar)
o.state |= QStyle::State_Mini;
return s->pixelMetric(QStyle::PM_ScrollBarExtent, &o, 0);
}
void PlatformScrollbar::invalidate()
{
ScrollView* outermostView = topLevel();
if (!outermostView)
return;
IntRect windowRect = convertToContainingWindow(IntRect(0, 0, width(), height()));
outermostView->addToDirtyRegion(windowRect);
}
}