blob: 7c75952fd89b56c82f00cead22d0426eda6622fe [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h"
#include "third_party/blink/renderer/core/css/css_property_names.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
#include "third_party/blink/renderer/platform/fonts/font.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
#include "third_party/blink/renderer/platform/text/text_run.h"
namespace blink {
int DateTimeNumericFieldElement::Range::ClampValue(int value) const {
return std::min(std::max(value, minimum), maximum);
}
bool DateTimeNumericFieldElement::Range::IsInRange(int value) const {
return value >= minimum && value <= maximum;
}
// ----------------------------
DateTimeNumericFieldElement::DateTimeNumericFieldElement(
Document& document,
FieldOwner& field_owner,
const Range& range,
const Range& hard_limits,
const String& placeholder,
const DateTimeNumericFieldElement::Step& step)
: DateTimeFieldElement(document, field_owner),
placeholder_(placeholder),
range_(range),
hard_limits_(hard_limits),
step_(step),
value_(0),
has_value_(false) {
DCHECK_NE(step_.step, 0);
DCHECK_LE(range_.minimum, range_.maximum);
DCHECK_LE(hard_limits_.minimum, hard_limits_.maximum);
// We show a direction-neutral string such as "--" as a placeholder. It
// should follow the direction of numeric values.
if (LocaleForOwner().IsRTL()) {
WTF::unicode::CharDirection dir =
WTF::unicode::Direction(FormatValue(Maximum())[0]);
if (dir == WTF::unicode::kLeftToRight ||
dir == WTF::unicode::kEuropeanNumber ||
dir == WTF::unicode::kArabicNumber) {
SetInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValueBidiOverride);
SetInlineStyleProperty(CSSPropertyDirection, CSSValueLtr);
}
}
}
float DateTimeNumericFieldElement::MaximumWidth(const ComputedStyle& style) {
float maximum_width = ComputeTextWidth(style, placeholder_);
maximum_width =
std::max(maximum_width, ComputeTextWidth(style, FormatValue(Maximum())));
maximum_width = std::max(maximum_width, ComputeTextWidth(style, Value()));
return maximum_width + DateTimeFieldElement::MaximumWidth(style);
}
int DateTimeNumericFieldElement::DefaultValueForStepDown() const {
return range_.maximum;
}
int DateTimeNumericFieldElement::DefaultValueForStepUp() const {
return range_.minimum;
}
void DateTimeNumericFieldElement::SetFocused(bool value,
WebFocusType focus_type) {
if (!value) {
int value = TypeAheadValue();
type_ahead_buffer_.Clear();
if (value >= 0)
SetValueAsInteger(value, kDispatchEvent);
}
DateTimeFieldElement::SetFocused(value, focus_type);
}
String DateTimeNumericFieldElement::FormatValue(int value) const {
Locale& locale = LocaleForOwner();
if (hard_limits_.maximum > 999)
return locale.ConvertToLocalizedNumber(String::Format("%04d", value));
if (hard_limits_.maximum > 99)
return locale.ConvertToLocalizedNumber(String::Format("%03d", value));
return locale.ConvertToLocalizedNumber(String::Format("%02d", value));
}
void DateTimeNumericFieldElement::HandleKeyboardEvent(
KeyboardEvent& keyboard_event) {
DCHECK(!IsDisabled());
if (keyboard_event.type() != event_type_names::kKeypress)
return;
UChar char_code = static_cast<UChar>(keyboard_event.charCode());
String number =
LocaleForOwner().ConvertFromLocalizedNumber(String(&char_code, 1));
const int digit = number[0] - '0';
if (digit < 0 || digit > 9)
return;
unsigned maximum_length =
DateTimeNumericFieldElement::FormatValue(range_.maximum).length();
if (type_ahead_buffer_.length() >= maximum_length) {
String current = type_ahead_buffer_.ToString();
type_ahead_buffer_.Clear();
unsigned desired_length = maximum_length - 1;
type_ahead_buffer_.Append(current, current.length() - desired_length,
desired_length);
}
type_ahead_buffer_.Append(number);
int new_value = TypeAheadValue();
if (new_value >= hard_limits_.minimum) {
SetValueAsInteger(new_value, kDispatchEvent);
} else {
has_value_ = false;
UpdateVisibleValue(kDispatchEvent);
}
if (type_ahead_buffer_.length() >= maximum_length ||
new_value * 10 > range_.maximum)
FocusOnNextField();
keyboard_event.SetDefaultHandled();
}
bool DateTimeNumericFieldElement::HasValue() const {
return has_value_;
}
void DateTimeNumericFieldElement::Initialize(const AtomicString& pseudo,
const String& ax_help_text) {
DateTimeFieldElement::Initialize(pseudo, ax_help_text, range_.minimum,
range_.maximum);
}
int DateTimeNumericFieldElement::Maximum() const {
return range_.maximum;
}
String DateTimeNumericFieldElement::Placeholder() const {
return placeholder_;
}
void DateTimeNumericFieldElement::SetEmptyValue(EventBehavior event_behavior) {
if (IsDisabled())
return;
has_value_ = false;
value_ = 0;
type_ahead_buffer_.Clear();
UpdateVisibleValue(event_behavior);
}
void DateTimeNumericFieldElement::SetValueAsInteger(
int value,
EventBehavior event_behavior) {
value_ = hard_limits_.ClampValue(value);
has_value_ = true;
UpdateVisibleValue(event_behavior);
}
void DateTimeNumericFieldElement::StepDown() {
int new_value =
RoundDown(has_value_ ? value_ - 1 : DefaultValueForStepDown());
if (!range_.IsInRange(new_value))
new_value = RoundDown(range_.maximum);
type_ahead_buffer_.Clear();
SetValueAsInteger(new_value, kDispatchEvent);
}
void DateTimeNumericFieldElement::StepUp() {
int new_value = RoundUp(has_value_ ? value_ + 1 : DefaultValueForStepUp());
if (!range_.IsInRange(new_value))
new_value = RoundUp(range_.minimum);
type_ahead_buffer_.Clear();
SetValueAsInteger(new_value, kDispatchEvent);
}
String DateTimeNumericFieldElement::Value() const {
return has_value_ ? FormatValue(value_) : g_empty_string;
}
int DateTimeNumericFieldElement::ValueAsInteger() const {
return has_value_ ? value_ : -1;
}
int DateTimeNumericFieldElement::TypeAheadValue() const {
if (type_ahead_buffer_.length())
return type_ahead_buffer_.ToString().ToInt();
return -1;
}
String DateTimeNumericFieldElement::VisibleValue() const {
if (type_ahead_buffer_.length())
return FormatValue(TypeAheadValue());
return has_value_ ? Value() : placeholder_;
}
int DateTimeNumericFieldElement::RoundDown(int n) const {
n -= step_.step_base;
if (n >= 0)
n = n / step_.step * step_.step;
else
n = -((-n + step_.step - 1) / step_.step * step_.step);
return n + step_.step_base;
}
int DateTimeNumericFieldElement::RoundUp(int n) const {
n -= step_.step_base;
if (n >= 0)
n = (n + step_.step - 1) / step_.step * step_.step;
else
n = -(-n / step_.step * step_.step);
return n + step_.step_base;
}
} // namespace blink