Changeset 58564 in webkit


Ignore:
Timestamp:
Apr 30, 2010 1:45:49 AM (14 years ago)
Author:
tkent@chromium.org
Message:

Implement interactive behavior of spin buttons.
https://bugs.webkit.org/show_bug.cgi?id=27968

Reviewed by Adele Peterson.

WebCore:

Introduce SpinButtonElement. It is a shadow element class for
spin buttons. If the upper side of the element is clicked, calls
HTMLInputElement::stepUpFromRenderer(1). If the lower button is
clicked, calls HTMLInputElement::stepUpFromRenderer(-1).

SpinButtonElement tracks the mouse pointer position, and
RenderTheme sets ControlStates::SpinUpState if the pointer is on
the upper side.

Test: platform/mac/fast/forms/input-number-click.html

  • dom/Element.h:

(WebCore::Element::isSpinButtonElement):

  • editing/VisibleSelection.cpp:

(WebCore::VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries):

Remove an assertion. lastEditablePositionBeforePositionInRoot() can
return null in a case that m_end is at a shadow element (a spin button)
and baseRoot is another shadow element (inner text block) in the same
node (an INPUT element).

  • html/HTMLInputElement.cpp:

(WebCore::HTMLInputElement::stepUpFromRenderer):

  • html/HTMLInputElement.h:

(WebCore::HTMLInputElement::hasSpinButton):

Add types supporting step attribute except RANGE.

  • rendering/RenderTextControlSingleLine.cpp:

(WebCore::RenderTextControlSingleLine::nodeAtPoint):
(WebCore::RenderTextControlSingleLine::forwardEvent):
(WebCore::RenderTextControlSingleLine::preferredContentWidth):
(WebCore::RenderTextControlSingleLine::createSubtreeIfNeeded):

  • rendering/RenderTextControlSingleLine.h:
  • rendering/RenderTheme.cpp:

(WebCore::RenderTheme::controlStatesForRenderer):
(WebCore::RenderTheme::isSpinUpButtonPartPressed):
(WebCore::RenderTheme::isSpinUpButtonPartHovered):

  • rendering/RenderTheme.h:
  • rendering/TextControlInnerElements.cpp:

(WebCore::SpinButtonElement::SpinButtonElement):
(WebCore::SpinButtonElement::defaultEventHandler):

  • rendering/TextControlInnerElements.h:

(WebCore::SpinButtonElement::isSpinButtonElement):
(WebCore::SpinButtonElement::isEnabledFormControl):
(WebCore::SpinButtonElement::onUpButton):

LayoutTests:

  • platform/mac/fast/forms/input-number-click-expected.txt: Added.
  • platform/mac/fast/forms/input-number-click.html: Added.
Location:
trunk
Files:
2 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r58563 r58564  
     12010-04-30  Kent Tamura  <tkent@chromium.org>
     2
     3        Reviewed by Adele Peterson.
     4
     5        Implement interactive behavior of spin buttons.
     6        https://bugs.webkit.org/show_bug.cgi?id=27968
     7
     8        * platform/mac/fast/forms/input-number-click-expected.txt: Added.
     9        * platform/mac/fast/forms/input-number-click.html: Added.
     10
    1112010-04-30  Yael Aharon  <yael.aharon@nokia.com>
    212
  • trunk/WebCore/ChangeLog

    r58563 r58564  
     12010-04-30  Kent Tamura  <tkent@chromium.org>
     2
     3        Reviewed by Adele Peterson.
     4
     5        Implement interactive behavior of spin buttons.
     6        https://bugs.webkit.org/show_bug.cgi?id=27968
     7
     8        Introduce SpinButtonElement. It is a shadow element class for
     9        spin buttons. If the upper side of the element is clicked, calls
     10        HTMLInputElement::stepUpFromRenderer(1). If the lower button is
     11        clicked, calls HTMLInputElement::stepUpFromRenderer(-1).
     12
     13        SpinButtonElement tracks the mouse pointer position, and
     14        RenderTheme sets ControlStates::SpinUpState if the pointer is on
     15        the upper side.
     16
     17        Test: platform/mac/fast/forms/input-number-click.html
     18
     19        * dom/Element.h:
     20        (WebCore::Element::isSpinButtonElement):
     21        * editing/VisibleSelection.cpp:
     22        (WebCore::VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries):
     23         Remove an assertion. lastEditablePositionBeforePositionInRoot() can
     24         return null in a case that m_end is at a shadow element (a spin button)
     25         and baseRoot is another shadow element (inner text block) in the same
     26         node (an INPUT element).
     27        * html/HTMLInputElement.cpp:
     28        (WebCore::HTMLInputElement::stepUpFromRenderer):
     29        * html/HTMLInputElement.h:
     30        (WebCore::HTMLInputElement::hasSpinButton):
     31         Add types supporting step attribute except RANGE.
     32        * rendering/RenderTextControlSingleLine.cpp:
     33        (WebCore::RenderTextControlSingleLine::nodeAtPoint):
     34        (WebCore::RenderTextControlSingleLine::forwardEvent):
     35        (WebCore::RenderTextControlSingleLine::preferredContentWidth):
     36        (WebCore::RenderTextControlSingleLine::createSubtreeIfNeeded):
     37        * rendering/RenderTextControlSingleLine.h:
     38        * rendering/RenderTheme.cpp:
     39        (WebCore::RenderTheme::controlStatesForRenderer):
     40        (WebCore::RenderTheme::isSpinUpButtonPartPressed):
     41        (WebCore::RenderTheme::isSpinUpButtonPartHovered):
     42        * rendering/RenderTheme.h:
     43        * rendering/TextControlInnerElements.cpp:
     44        (WebCore::SpinButtonElement::SpinButtonElement):
     45        (WebCore::SpinButtonElement::defaultEventHandler):
     46        * rendering/TextControlInnerElements.h:
     47        (WebCore::SpinButtonElement::isSpinButtonElement):
     48        (WebCore::SpinButtonElement::isEnabledFormControl):
     49        (WebCore::SpinButtonElement::onUpButton):
     50
    1512010-04-30  Yael Aharon  <yael.aharon@nokia.com>
    252
  • trunk/WebCore/css/html.css

    r56629 r58564  
    363363    -webkit-appearance: inner-spin-button;
    364364    display: inline-block;
     365    -webkit-user-select: none;
    365366}
    366367
     
    369370    display: inline-block;
    370371    margin-left: 2px;
     372    -webkit-user-select: none;
    371373}
    372374
  • trunk/WebCore/dom/Element.h

    r57809 r58564  
    253253    virtual bool isEnabledFormControl() const { return true; }
    254254    virtual bool isReadOnlyFormControl() const { return false; }
     255    virtual bool isSpinButtonElement() const { return false; }
    255256    virtual bool isTextFormControl() const { return false; }
    256257    virtual bool isOptionalFormControl() const { return false; }
  • trunk/WebCore/editing/VisibleSelection.cpp

    r58040 r58564  
    482482            VisiblePosition last = lastEditablePositionBeforePositionInRoot(m_end, baseRoot);
    483483            m_end = last.deepEquivalent();
    484             if (m_end.isNull()) {
    485                 ASSERT_NOT_REACHED();
     484            if (m_end.isNull())
    486485                m_end = m_start;
    487             }
    488486        }
    489487    // The selection is based in non-editable content.
  • trunk/WebCore/html/HTMLInputElement.cpp

    r58520 r58564  
    27622762#endif // ENABLE(DATALIST)
    27632763
     2764void HTMLInputElement::stepUpFromRenderer(int n)
     2765{
     2766    // The difference from stepUp()/stepDown() is:
     2767    // If the current value is invalid, the value will be
     2768    //  - the minimum value if n > 0
     2769    //  - the maximum value if n < 0
     2770
     2771    ASSERT(hasSpinButton());
     2772    if (!hasSpinButton())
     2773        return;
     2774    ASSERT(n);
     2775    if (!n)
     2776        return;
     2777
     2778    const double nan = numeric_limits<double>::quiet_NaN();
     2779    double current = parseToDouble(value(), nan);
     2780    if (!isfinite(current)) {
     2781        setValue(serialize(n > 0 ? minimum() : maximum()));
     2782        return;
     2783    }
     2784    ExceptionCode ec;
     2785    stepUp(n, ec);
     2786}
     2787
    27642788} // namespace
  • trunk/WebCore/html/HTMLInputElement.h

    r58561 r58564  
    122122    void stepUp(ExceptionCode& ec) { stepUp(1, ec); }
    123123    void stepDown(ExceptionCode& ec) { stepDown(1, ec); }
     124    // stepUp()/stepDown() for user-interaction.
     125    void stepUpFromRenderer(int);
    124126
    125127    bool isTextButton() const { return m_type == SUBMIT || m_type == RESET || m_type == BUTTON; }
     
    129131    virtual bool isInputTypeHidden() const { return m_type == HIDDEN; }
    130132    virtual bool isPasswordField() const { return m_type == PASSWORD; }
    131     virtual bool hasSpinButton() const { return m_type == NUMBER; }
     133    virtual bool hasSpinButton() const { return m_type == NUMBER || m_type == DATE || m_type == DATETIME || m_type == DATETIMELOCAL || m_type == MONTH || m_type == TIME || m_type == WEEK; }
    132134
    133135    bool checked() const { return m_checked; }
  • trunk/WebCore/rendering/RenderTextControlSingleLine.cpp

    r58561 r58564  
    282282        hitInnerTextElement(result, xPos, yPos, tx, ty);
    283283
     284    // If we found a spin button, we're done.
     285    if (m_outerSpinButton && result.innerNode() == m_outerSpinButton)
     286        return true;
    284287    // If we're not a search field, or we already found the results or cancel buttons, we're done.
    285288    if (!m_innerBlock || result.innerNode() == m_resultsButton || result.innerNode() == m_cancelButton)
     
    332335
    333336    FloatPoint localPoint = innerTextRenderer->absoluteToLocal(static_cast<MouseEvent*>(event)->absoluteLocation(), false, true);
     337    int textRight = innerTextRenderer->borderBoxRect().right();
    334338    if (m_resultsButton && localPoint.x() < innerTextRenderer->borderBoxRect().x())
    335339        m_resultsButton->defaultEventHandler(event);
    336     else if (m_cancelButton && localPoint.x() > innerTextRenderer->borderBoxRect().right())
     340    else if (m_cancelButton && localPoint.x() > textRight && localPoint.x() < textRight + m_cancelButton->renderBox()->width())
    337341        m_cancelButton->defaultEventHandler(event);
     342    else if (m_outerSpinButton && localPoint.x() > textRight)
     343        m_outerSpinButton->defaultEventHandler(event);
    338344    else
    339345        RenderTextControl::forwardEvent(event);
     
    503509        RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get());
    504510        if (inputElement()->hasSpinButton() && !m_outerSpinButton) {
    505             // FIXME: Introduce a dedicated element for spin buttons.
    506             m_outerSpinButton = new TextControlInnerElement(document(), node());
     511            m_outerSpinButton = new SpinButtonElement(document(), node());
    507512            m_outerSpinButton->attachInnerElement(node(), createOuterSpinButtonStyle(), renderArena());
    508513        }
  • trunk/WebCore/rendering/RenderTextControlSingleLine.h

    r58561 r58564  
    3333class SearchFieldResultsButtonElement;
    3434class SearchPopupMenu;
     35class SpinButtonElement;
    3536class TextControlInnerElement;
    3637
  • trunk/WebCore/rendering/RenderTheme.cpp

    r58228 r58564  
    3737#include "SelectionController.h"
    3838#include "Settings.h"
     39#include "TextControlInnerElements.h"
    3940
    4041// The methods in this file are shared by all themes on every platform.
     
    658659{
    659660    ControlStates result = 0;
    660     if (isHovered(o))
     661    if (isHovered(o)) {
    661662        result |= HoverState;
    662     if (isPressed(o))
     663        if (isSpinUpButtonPartHovered(o))
     664            result |= SpinUpState;
     665    }
     666    if (isPressed(o)) {
    663667        result |= PressedState;
     668        if (isSpinUpButtonPartPressed(o))
     669            result |= SpinUpState;
     670    }
    664671    if (isFocused(o) && o->style()->outlineStyleIsAuto())
    665672        result |= FocusState;
     
    745752}
    746753
     754bool RenderTheme::isSpinUpButtonPartPressed(const RenderObject* o) const
     755{
     756    Node* node = o->node();
     757    if (!node || !node->active() || !node->isElementNode()
     758        || !static_cast<Element*>(node)->isSpinButtonElement())
     759        return false;
     760    SpinButtonElement* element = static_cast<SpinButtonElement*>(node);
     761    return element->onUpButton();
     762}
     763
    747764bool RenderTheme::isReadOnlyControl(const RenderObject* o) const
    748765{
     
    758775        return false;
    759776    return o->node()->hovered();
     777}
     778
     779bool RenderTheme::isSpinUpButtonPartHovered(const RenderObject* o) const
     780{
     781    Node* node = o->node();
     782    if (!node || !node->active() || !node->isElementNode()
     783        || !static_cast<Element*>(node)->isSpinButtonElement())
     784        return false;
     785    SpinButtonElement* element = static_cast<SpinButtonElement*>(node);
     786    return element->onUpButton();
    760787}
    761788
  • trunk/WebCore/rendering/RenderTheme.h

    r56850 r58564  
    290290    bool isFocused(const RenderObject*) const;
    291291    bool isPressed(const RenderObject*) const;
     292    bool isSpinUpButtonPartPressed(const RenderObject*) const;
    292293    bool isHovered(const RenderObject*) const;
     294    bool isSpinUpButtonPartHovered(const RenderObject*) const;
    293295    bool isReadOnlyControl(const RenderObject*) const;
    294296    bool isDefault(const RenderObject*) const;
  • trunk/WebCore/rendering/TextControlInnerElements.cpp

    r55090 r58564  
    212212}
    213213
    214 }
     214SpinButtonElement::SpinButtonElement(Document* doc, Node* shadowParent)
     215    : TextControlInnerElement(doc, shadowParent)
     216    , m_capturing(false)
     217    , m_onUpButton(false)
     218{
     219}
     220
     221void SpinButtonElement::defaultEventHandler(Event* evt)
     222{
     223    if (!evt->isMouseEvent()) {
     224        if (!evt->defaultHandled())
     225            HTMLDivElement::defaultEventHandler(evt);
     226        return;
     227    }
     228    const MouseEvent* mevt = static_cast<MouseEvent*>(evt);
     229    if (mevt->button() != LeftButton) {
     230        if (!evt->defaultHandled())
     231            HTMLDivElement::defaultEventHandler(evt);
     232        return;
     233    }
     234
     235    HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
     236    IntPoint local = roundedIntPoint(renderBox()->absoluteToLocal(mevt->absoluteLocation(), false, true));
     237    if (evt->type() == eventNames().clickEvent) {
     238        if (renderBox()->borderBoxRect().contains(local)) {
     239            input->focus();
     240            input->select();
     241            if (local.y() < renderBox()->y() + renderBox()->height() / 2)
     242                input->stepUpFromRenderer(1);
     243            else
     244                input->stepUpFromRenderer(-1);
     245            evt->setDefaultHandled();
     246        }
     247    } else if (evt->type() == eventNames().mousemoveEvent) {
     248        if (renderBox()->borderBoxRect().contains(local)) {
     249            if (!m_capturing) {
     250                if (Frame* frame = document()->frame()) {
     251                    frame->eventHandler()->setCapturingMouseEventsNode(input);
     252                    m_capturing = true;
     253                }
     254            }
     255            bool oldOnUpButton = m_onUpButton;
     256            m_onUpButton = local.y() < renderBox()->y() + renderBox()->height() / 2;
     257            if (m_onUpButton != oldOnUpButton)
     258                renderer()->repaint();
     259        } else {
     260            if (m_capturing) {
     261                if (Frame* frame = document()->frame()) {
     262                    frame->eventHandler()->setCapturingMouseEventsNode(0);
     263                    m_capturing = false;
     264                }
     265            }
     266        }
     267    }
     268    if (!evt->defaultHandled())
     269        HTMLDivElement::defaultEventHandler(evt);
     270}
     271
     272}
  • trunk/WebCore/rendering/TextControlInnerElements.h

    r45662 r58564  
    6969};
    7070
     71class SpinButtonElement : public TextControlInnerElement {
     72public:
     73    SpinButtonElement(Document*, Node*);
     74    virtual bool isSpinButtonElement() const { return true; }
     75    virtual bool isEnabledFormControl() { return static_cast<Element*>(shadowAncestorNode())->isEnabledFormControl(); }
     76    virtual void defaultEventHandler(Event*);
     77
     78    bool onUpButton() const { return m_onUpButton; }
     79    static const AtomicString& spinButtonNodeName();
     80
     81private:
     82    bool m_capturing;
     83    bool m_onUpButton;
     84};
     85
    7186} //namespace
    7287
Note: See TracChangeset for help on using the changeset viewer.