SliderThumbElement.cpp revision 6fbaea61d241814b015fd7e022796e68d8ef3e8e
1/* 2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 33#include "config.h" 34#include "SliderThumbElement.h" 35 36#include "Event.h" 37#include "Frame.h" 38#include "HTMLInputElement.h" 39#include "HTMLParserIdioms.h" 40#include "MouseEvent.h" 41#include "RenderSlider.h" 42#include "RenderTheme.h" 43#include "StepRange.h" 44#include <wtf/MathExtras.h> 45 46#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 47#include "Page.h" 48#include "TouchEvent.h" 49#endif 50 51using namespace std; 52 53namespace WebCore { 54 55// FIXME: Find a way to cascade appearance (see the layout method) and get rid of this class. 56class RenderSliderThumb : public RenderBlock { 57public: 58 RenderSliderThumb(Node*); 59 virtual void layout(); 60}; 61 62RenderSliderThumb::RenderSliderThumb(Node* node) 63 : RenderBlock(node) 64{ 65} 66 67void RenderSliderThumb::layout() 68{ 69 // FIXME: Hard-coding this cascade of appearance is bad, because it's something 70 // that CSS usually does. We need to find a way to express this in CSS. 71 RenderStyle* parentStyle = parent()->style(); 72 if (parentStyle->appearance() == SliderVerticalPart) 73 style()->setAppearance(SliderThumbVerticalPart); 74 else if (parentStyle->appearance() == SliderHorizontalPart) 75 style()->setAppearance(SliderThumbHorizontalPart); 76 else if (parentStyle->appearance() == MediaSliderPart) 77 style()->setAppearance(MediaSliderThumbPart); 78 else if (parentStyle->appearance() == MediaVolumeSliderPart) 79 style()->setAppearance(MediaVolumeSliderThumbPart); 80 81 if (style()->hasAppearance()) { 82 // FIXME: This should pass the style, not the renderer, to the theme. 83 theme()->adjustSliderThumbSize(this); 84 } 85 RenderBlock::layout(); 86} 87 88RenderObject* SliderThumbElement::createRenderer(RenderArena* arena, RenderStyle*) 89{ 90 return new (arena) RenderSliderThumb(this); 91} 92 93void SliderThumbElement::dragFrom(const IntPoint& point) 94{ 95 setPosition(point); 96 startDragging(); 97} 98 99void SliderThumbElement::setPosition(const IntPoint& point) 100{ 101 HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost()); 102 ASSERT(input); 103 104 if (!input->renderer() || !renderer()) 105 return; 106 107 IntPoint offset = roundedIntPoint(input->renderer()->absoluteToLocal(point, false, true)); 108 RenderStyle* sliderStyle = input->renderer()->style(); 109 bool isVertical = sliderStyle->appearance() == SliderVerticalPart || sliderStyle->appearance() == MediaVolumeSliderPart; 110 111 int trackSize; 112 int position; 113 int currentPosition; 114 if (isVertical) { 115 trackSize = input->renderBox()->contentHeight() - renderBox()->height(); 116 position = offset.y() - renderBox()->height() / 2; 117 currentPosition = renderBox()->y() - input->renderBox()->contentBoxRect().y(); 118 } else { 119 trackSize = input->renderBox()->contentWidth() - renderBox()->width(); 120 position = offset.x() - renderBox()->width() / 2; 121 currentPosition = renderBox()->x() - input->renderBox()->contentBoxRect().x(); 122 } 123 position = max(0, min(position, trackSize)); 124 if (position == currentPosition) 125 return; 126 127 StepRange range(input); 128 double fraction = static_cast<double>(position) / trackSize; 129 if (isVertical) 130 fraction = 1 - fraction; 131 double value = range.clampValue(range.valueFromProportion(fraction)); 132 133 // FIXME: This is no longer being set from renderer. Consider updating the method name. 134 input->setValueFromRenderer(serializeForNumberType(value)); 135 renderer()->setNeedsLayout(true); 136 input->dispatchFormControlChangeEvent(); 137} 138 139void SliderThumbElement::startDragging() 140{ 141 if (Frame* frame = document()->frame()) { 142 frame->eventHandler()->setCapturingMouseEventsNode(this); 143#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 144 // Touch events come from Java to the main frame event handler, so we need 145 // to flag we are capturing those events also on the main frame event 146 // handler. 147 frame->page()->mainFrame()->eventHandler()->setCapturingTouchEventsNode(this); 148#endif 149 m_inDragMode = true; 150 } 151} 152 153void SliderThumbElement::stopDragging() 154{ 155 if (!m_inDragMode) 156 return; 157 158 if (Frame* frame = document()->frame()) 159 frame->eventHandler()->setCapturingMouseEventsNode(0); 160 161#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 162 if (Frame* frame = document()->frame()) 163 frame->page()->mainFrame()->eventHandler()->setCapturingTouchEventsNode(0); 164#endif 165 m_inDragMode = false; 166} 167 168void SliderThumbElement::defaultEventHandler(Event* event) 169{ 170 if (!event->isMouseEvent() 171#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 172 && !event->isTouchEvent() 173#endif 174 ) { 175 HTMLDivElement::defaultEventHandler(event); 176 return; 177 } 178 179#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 180 bool isLeftButton = false; 181 182 if (event->isMouseEvent()) { 183 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); 184 isLeftButton = mouseEvent->button() == LeftButton; 185 } 186#else 187 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); 188 bool isLeftButton = mouseEvent->button() == LeftButton; 189#endif 190 const AtomicString& eventType = event->type(); 191 192 if (eventType == eventNames().mousedownEvent && isLeftButton 193#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 194 || eventType == eventNames().touchstartEvent 195#endif 196 ) { 197 startDragging(); 198 return; 199 } else if (eventType == eventNames().mouseupEvent && isLeftButton 200#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 201 || eventType == eventNames().touchendEvent 202 || eventType == eventNames().touchcancelEvent 203#endif 204 ) { 205 stopDragging(); 206 return; 207 } else if (eventType == eventNames().mousemoveEvent 208#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 209 || eventType == eventNames().touchmoveEvent 210#endif 211 ) { 212 if (m_inDragMode) 213#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 214 { 215 if (event->isMouseEvent()) { 216 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); 217#endif 218 setPosition(mouseEvent->absoluteLocation()); 219#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 220 } else if (event->isTouchEvent()) { 221 TouchEvent* touchEvent = static_cast<TouchEvent*>(event); 222 if (touchEvent->touches() && touchEvent->touches()->item(0)) { 223 IntPoint curPoint; 224 curPoint.setX(touchEvent->touches()->item(0)->pageX()); 225 curPoint.setY(touchEvent->touches()->item(0)->pageY()); 226 setPosition(curPoint); 227 // Tell the webview that webkit will handle the following move events 228 event->setDefaultPrevented(true); 229 } 230 } 231 232 } 233#endif 234 return; 235 } 236 237 HTMLDivElement::defaultEventHandler(event); 238} 239 240void SliderThumbElement::detach() 241{ 242 if (m_inDragMode) { 243 if (Frame* frame = document()->frame()) 244 frame->eventHandler()->setCapturingMouseEventsNode(0); 245 } 246 HTMLDivElement::detach(); 247} 248 249} 250 251