Bug 843725 - Add support for changing the value of <input type=range> using the up/down/left/right/pgup/pgdn/home/end keys. r=mounir, r=smaug.

This commit is contained in:
Jonathan Watt 2013-03-01 15:34:06 +00:00
Родитель c1935f174a
Коммит ad65ad92ae
4 изменённых файлов: 292 добавлений и 3 удалений

Просмотреть файл

@ -2569,6 +2569,17 @@ SelectTextFieldOnFocus()
return gSelectTextFieldOnFocus == 1;
}
static bool
IsLTR(Element* aElement)
{
nsIFrame *frame = aElement->GetPrimaryFrame();
if (frame) {
return frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
}
// at least for HTML, directionality is exclusively LTR or RTL
return aElement->GetDirectionality() == eDir_LTR;
}
nsresult
nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
{
@ -2835,6 +2846,74 @@ nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
NS_ENSURE_SUCCESS(rv, rv);
}
if (aVisitor.mEvent->message == NS_KEY_PRESS &&
mType == NS_FORM_INPUT_RANGE && !keyEvent->IsAlt() &&
!keyEvent->IsControl() && !keyEvent->IsMeta() &&
(keyEvent->keyCode == NS_VK_LEFT ||
keyEvent->keyCode == NS_VK_RIGHT ||
keyEvent->keyCode == NS_VK_UP ||
keyEvent->keyCode == NS_VK_DOWN ||
keyEvent->keyCode == NS_VK_PAGE_UP ||
keyEvent->keyCode == NS_VK_PAGE_DOWN ||
keyEvent->keyCode == NS_VK_HOME ||
keyEvent->keyCode == NS_VK_END)) {
double minimum = GetMinimum();
double maximum = GetMaximum();
MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(minimum) &&
MOZ_DOUBLE_IS_FINITE(maximum));
if (minimum < maximum) { // else the value is locked to the minimum
double value = GetValueAsDouble();
double step = GetStep();
if (step == kStepAny) {
step = GetDefaultStep();
}
MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(value) &&
MOZ_DOUBLE_IS_FINITE(step));
double newValue;
switch (keyEvent->keyCode) {
case NS_VK_LEFT:
newValue = value + (IsLTR(this) ? -step : step);
break;
case NS_VK_RIGHT:
newValue = value + (IsLTR(this) ? step : -step);
break;
case NS_VK_UP:
// Even for horizontal range, "up" means "increase"
newValue = value + step;
break;
case NS_VK_DOWN:
// Even for horizontal range, "down" means "decrease"
newValue = value - step;
break;
case NS_VK_HOME:
newValue = minimum;
break;
case NS_VK_END:
newValue = maximum;
break;
case NS_VK_PAGE_UP:
// For PgUp/PgDn we jump 10% of the total range, unless step
// requires us to jump more.
newValue = value + std::max(step, 0.1 * (maximum - minimum));
break;
case NS_VK_PAGE_DOWN:
newValue = value - std::max(step, 0.1 * (maximum - minimum));
break;
}
MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(newValue));
nsAutoString val;
ConvertNumberToString(newValue, val);
SetValueInternal(val, true, true);
nsIFrame* frame = GetPrimaryFrame();
if (frame) {
// Trigger reflow to update the position of the thumb:
frame->PresContext()->GetPresShell()->
FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
}
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
}
}
} break; // NS_KEY_PRESS || NS_KEY_UP
case NS_MOUSE_BUTTON_DOWN:
@ -4353,7 +4432,8 @@ nsHTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t
return true;
}
if (IsSingleLineTextControl(false)) {
if (IsSingleLineTextControl(false) ||
mType == NS_FORM_INPUT_RANGE) {
*aIsFocusable = true;
return false;
}

Просмотреть файл

@ -18,6 +18,7 @@ MOCHITEST_FILES = \
test_input_attributes_reflection.html \
test_input_list_attribute.html \
test_input_email.html \
test_input_range_key_events.html \
test_input_url.html \
test_pattern_attribute.html \
test_required_attribute.html \

Просмотреть файл

@ -0,0 +1,206 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=843725
-->
<head>
<title>Test key events for range</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<meta charset="UTF-8">
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=843725">Mozilla Bug 843725</a>
<p id="display"></p>
<div id="content">
</div>
<pre id="test">
<script type="application/javascript">
/**
* Test for Bug 843725
* This test checks how the value of <input type=range> changes in response to
* various key events while it is in various states.
**/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
test();
SimpleTest.finish();
});
const defaultMinimum = 0;
const defaultMaximum = 100;
const defaultStep = 1;
// Helpers:
// For the sake of simplicity, we do not currently support fractional value,
// step, etc.
function minimum(element) {
return Number(element.min || defaultMinimum);
}
function maximum(element) {
return Number(element.max || defaultMaximum);
}
function range(element) {
var max = maximum(element);
var min = minimum(element);
if (max < min) {
return 0;
}
return max - min;
}
function defaultValue(element) {
return minimum(element) + range(element)/2;
}
function value(element) {
return Number(element.value || defaultValue(element));
}
function step(element) {
var step = Number(element.step || defaultStep);
return step <= 0 ? defaultStep : step;
}
function clampToRange(value, element) {
var min = minimum(element);
var max = maximum(element);
if (max < min) {
return min;
}
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
}
// Functions used to specify expected test results:
function valuePlusStep(element) {
return clampToRange(value(element) + step(element), element);
}
function valueMinusStep(element) {
return clampToRange(value(element) - step(element), element);
}
/**
* Returns the current value of the range plus whichever is greater of either
* 10% of the range or its current step value, clamped to the range's minimum/
* maximum. The reason for using the step if it is greater than 10% of the
* range is because otherwise the PgUp/PgDn keys would do nothing in that case.
*/
function valuePlusTenPctOrStep(element) {
var tenPct = range(element)/10;
var stp = step(element);
return clampToRange(value(element) + Math.max(tenPct, stp), element);
}
function valueMinusTenPctOrStep(element) {
var tenPct = range(element)/10;
var stp = step(element);
return clampToRange(value(element) - Math.max(tenPct, stp), element);
}
// Test table:
const LTR = "ltr";
const RTL = "rtl";
var testTable = [
["VK_LEFT", LTR, valueMinusStep],
["VK_LEFT", RTL, valuePlusStep],
["VK_RIGHT", LTR, valuePlusStep],
["VK_RIGHT", RTL, valueMinusStep],
["VK_UP", LTR, valuePlusStep],
["VK_UP", RTL, valuePlusStep],
["VK_DOWN", LTR, valueMinusStep],
["VK_DOWN", RTL, valueMinusStep],
["VK_PAGE_UP", LTR, valuePlusTenPctOrStep],
["VK_PAGE_UP", RTL, valuePlusTenPctOrStep],
["VK_PAGE_DOWN", LTR, valueMinusTenPctOrStep],
["VK_PAGE_DOWN", RTL, valueMinusTenPctOrStep],
["VK_HOME", LTR, minimum],
["VK_HOME", RTL, minimum],
["VK_END", LTR, maximum],
["VK_END", RTL, maximum],
]
function test() {
var elem = document.createElement("input");
elem.type = "range";
var content = document.getElementById("content");
content.appendChild(elem);
elem.focus();
for (test of testTable) {
var [key, dir, expectedFunc] = test;
var oldVal, expectedVal;
elem.step = "2";
elem.style.direction = dir;
var flush = document.body.clientWidth;
// Start at middle:
elem.value = oldVal = defaultValue(elem);
expectedVal = expectedFunc(elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test " + key + " for " + dir + " range with value set to the midpoint (" + oldVal + ")");
// Same again:
expectedVal = expectedFunc(elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test repeat of " + key + " for " + dir + " range");
// Start at maximum:
elem.value = oldVal = maximum(elem);
expectedVal = expectedFunc(elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test " + key + " for " + dir + " range with value set to the maximum (" + oldVal + ")");
// Same again:
expectedVal = expectedFunc(elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test repeat of " + key + " for " + dir + " range");
// Start at minimum:
elem.value = oldVal = minimum(elem);
expectedVal = expectedFunc(elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test " + key + " for " + dir + " range with value set to the minimum (" + oldVal + ")");
// Same again:
expectedVal = expectedFunc(elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test repeat of " + key + " for " + dir + " range");
// Test for a step value that is greater than 10% of the range:
elem.step = 20;
elem.value = 60;
expectedVal = expectedFunc(elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test " + key + " for " + dir + " range with a step that is greater than 10% of the range (step=" + elem.step + ")");
// Same again:
expectedVal = expectedFunc(elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test repeat of " + key + " for " + dir + " range");
// reset step:
elem.step = 2;
}
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -720,12 +720,14 @@ meter {
input[type="range"] {
-moz-appearance: none;
display: inline-block !important;
cursor: default;
width: 12em;
height: 1.3em;
margin: 0 0.7em;
/* Override some rules that apply on all input types: */
cursor: default;
background: none;
border: none;
margin: 0 0.7em;
-moz-binding: none; /* we don't want any of platformHTMLBindings.xml#inputFields */
}
/**