SliderThumbElement.cpp [plain text]
#include "config.h"
#include "SliderThumbElement.h"
#include "Event.h"
#include "Frame.h"
#include "HTMLInputElement.h"
#include "HTMLParserIdioms.h"
#include "MouseEvent.h"
#include "RenderSlider.h"
#include "RenderTheme.h"
#include "StepRange.h"
#include <wtf/MathExtras.h>
#include "Document.h"
#include "Page.h"
#include "TouchEvent.h"
using namespace std;
namespace WebCore {
class RenderSliderThumb : public RenderBlock {
public:
RenderSliderThumb(Node*);
private:
virtual void layout();
};
RenderSliderThumb::RenderSliderThumb(Node* node)
: RenderBlock(node)
{
}
void RenderSliderThumb::layout()
{
RenderStyle* parentStyle = parent()->style();
if (parentStyle->appearance() == SliderVerticalPart)
style()->setAppearance(SliderThumbVerticalPart);
else if (parentStyle->appearance() == SliderHorizontalPart)
style()->setAppearance(SliderThumbHorizontalPart);
else if (parentStyle->appearance() == MediaSliderPart)
style()->setAppearance(MediaSliderThumbPart);
else if (parentStyle->appearance() == MediaVolumeSliderPart)
style()->setAppearance(MediaVolumeSliderThumbPart);
if (style()->hasAppearance()) {
theme()->adjustSliderThumbSize(this);
}
RenderBlock::layout();
}
void SliderThumbElement::setPositionFromValue()
{
if (renderer())
renderer()->setNeedsLayout(true);
}
RenderObject* SliderThumbElement::createRenderer(RenderArena* arena, RenderStyle*)
{
return new (arena) RenderSliderThumb(this);
}
void SliderThumbElement::dragFrom(const IntPoint& point)
{
setPositionFromPoint(point);
}
void SliderThumbElement::setPositionFromPoint(const IntPoint& point)
{
HTMLInputElement* input = hostInput();
ASSERT(input);
if (!input->renderer() || !renderer())
return;
IntPoint offset = roundedIntPoint(input->renderer()->absoluteToLocal(point, false, true));
RenderStyle* sliderStyle = input->renderer()->style();
bool isVertical = sliderStyle->appearance() == SliderVerticalPart || sliderStyle->appearance() == MediaVolumeSliderPart;
int trackSize;
int position;
int currentPosition;
if (isVertical) {
trackSize = input->renderBox()->contentHeight() - renderBox()->height();
position = offset.y() - renderBox()->height() / 2;
currentPosition = renderBox()->y() - input->renderBox()->contentBoxRect().y();
} else {
trackSize = input->renderBox()->contentWidth() - renderBox()->width();
position = offset.x() - renderBox()->width() / 2;
currentPosition = renderBox()->x() - input->renderBox()->contentBoxRect().x();
}
position = max(0, min(position, trackSize));
if (position == currentPosition)
return;
StepRange range(input);
double fraction = static_cast<double>(position) / trackSize;
if (isVertical)
fraction = 1 - fraction;
double value = range.clampValue(range.valueFromProportion(fraction));
input->setValueFromRenderer(serializeForNumberType(value));
renderer()->setNeedsLayout(true);
input->dispatchFormControlChangeEvent();
}
void SliderThumbElement::startDragging()
{
if (Frame* frame = document()->frame()) {
frame->eventHandler()->setCapturingMouseEventsNode(this);
m_inDragMode = true;
}
}
void SliderThumbElement::stopDragging()
{
if (!m_inDragMode)
return;
if (Frame* frame = document()->frame())
frame->eventHandler()->setCapturingMouseEventsNode(0);
m_inDragMode = false;
if (renderer())
renderer()->setNeedsLayout(true);
}
void SliderThumbElement::detach()
{
if (m_inDragMode) {
if (Frame* frame = document()->frame())
frame->eventHandler()->setCapturingMouseEventsNode(0);
}
document()->removeTouchEventListener(this);
HTMLDivElement::detach();
}
unsigned SliderThumbElement::exclusiveTouchIdentifier() const
{
return m_exclusiveTouchIdentifier;
}
void SliderThumbElement::setExclusiveTouchIdentifier(unsigned identifier)
{
ASSERT(m_exclusiveTouchIdentifier == NoIdentifier);
m_exclusiveTouchIdentifier = identifier;
}
void SliderThumbElement::clearExclusiveTouchIdentifier()
{
m_exclusiveTouchIdentifier = NoIdentifier;
}
static Touch* findTouchWithIdentifier(TouchList* list, unsigned identifier)
{
for (unsigned i = 0; i < list->length(); ++i) {
Touch* touch = list->item(i);
if (touch->identifier() == identifier)
return touch;
}
return 0;
}
void SliderThumbElement::handleTouchStart(TouchEvent* touchEvent)
{
TouchList* targetTouches = touchEvent->targetTouches();
if (targetTouches->length() != 1)
return;
Touch* touch = targetTouches->item(0);
IntRect boundingBox = renderer()->absoluteBoundingBoxRect();
if (!boundingBox.contains(touch->pageX(), touch->pageY()))
return;
setExclusiveTouchIdentifier(touch->identifier());
startDragging();
touchEvent->setDefaultHandled();
}
void SliderThumbElement::handleTouchMove(TouchEvent* touchEvent)
{
unsigned identifier = exclusiveTouchIdentifier();
if (identifier == NoIdentifier)
return;
Touch* touch = findTouchWithIdentifier(touchEvent->targetTouches(), identifier);
if (!touch)
return;
if (m_inDragMode)
setPositionFromPoint(IntPoint(touch->pageX(), touch->pageY()));
touchEvent->setDefaultHandled();
}
void SliderThumbElement::handleTouchEndAndCancel(TouchEvent* touchEvent)
{
unsigned identifier = exclusiveTouchIdentifier();
if (identifier == NoIdentifier)
return;
Touch* exclusiveTouch = findTouchWithIdentifier(touchEvent->targetTouches(), identifier);
if (exclusiveTouch)
return;
clearExclusiveTouchIdentifier();
stopDragging();
}
void SliderThumbElement::attach()
{
HTMLDivElement::attach();
document()->addTouchEventListener(this);
}
void SliderThumbElement::handleTouchEvent(TouchEvent* touchEvent)
{
const AtomicString& eventType = touchEvent->type();
if (eventType == eventNames().touchstartEvent) {
handleTouchStart(touchEvent);
return;
} else if (eventType == eventNames().touchendEvent || eventType == eventNames().touchcancelEvent) {
handleTouchEndAndCancel(touchEvent);
return;
} else if (eventType == eventNames().touchmoveEvent) {
handleTouchMove(touchEvent);
return;
}
HTMLDivElement::defaultEventHandler(touchEvent);
}
HTMLInputElement* SliderThumbElement::hostInput()
{
ASSERT(parentNode());
return static_cast<HTMLInputElement*>(parentNode()->shadowHost());
}
const AtomicString& SliderThumbElement::shadowPseudoId() const
{
DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-thumb"));
return sliderThumb;
}
}