Modified: trunk/Source/WebCore/ChangeLog (124472 => 124473)
--- trunk/Source/WebCore/ChangeLog 2012-08-02 16:53:21 UTC (rev 124472)
+++ trunk/Source/WebCore/ChangeLog 2012-08-02 17:01:56 UTC (rev 124473)
@@ -1,3 +1,35 @@
+2012-08-02 Kevin Ellis <[email protected]>
+
+ Month-year selector on calendar picker should be touch friendly.
+ https://bugs.webkit.org/show_bug.cgi?id=92678
+
+ Reviewed by Kent Tamura.
+
+ Previously the month-year popup menu was implemented using a listbox,
+ which does not support CSS customization. Entries in the listbox
+ were too shallow to reliably target with touch gestures. The
+ replacement popup is CSS configurable with larger entries on devices
+ that support touch input.
+
+ Manually tested with and without touch support in English, Japanese
+ and Arabic.
+
+ * Resources/calendarPicker.css:
+ (.month-selector-popup): Update to use scrollable div instead of listbox.
+ (.month-selector-popup-contents): Render popup as a table within a scrollable div.
+ (.month-selector-popup-entry): Formatting entries in the month-year popup.
+ (.selected-month-year): Highlight the selected month-year.
+ (@media (pointer:coarse)): Enlarge entries in the popup meu on devices that support touch.
+ * Resources/calendarPicker.js:
+ (YearMonthController.prototype.attachTo): Change selector popup from a list-box to a div.
+ (YearMonthController.prototype._redraw): Populate table based popup rather than listbox.
+ (YearMonthController.prototype._showPopup): Set scroll position and resize for scrollbar.
+ (YearMonthController.prototype._closePopup): Restore focus to the calendar.
+ (YearMonthController.prototype._getSelection): Added to retrieve the selected month-year.
+ (YearMonthController.prototype._handleMouseMove): Added to update selected month-year on hover.
+ (YearMonthController.prototype._handleMonthPopupKey): Add keyboard navigation.
+ (YearMonthController.prototype._handleYearMonthChange): Retrieve value from selected month-year.
+
2012-08-02 Vsevolod Vlasov <[email protected]>
Web Inspector: [Regression] context menu does not open on Sources panel tabs on mac
Modified: trunk/Source/WebCore/Resources/calendarPicker.css (124472 => 124473)
--- trunk/Source/WebCore/Resources/calendarPicker.css 2012-08-02 16:53:21 UTC (rev 124472)
+++ trunk/Source/WebCore/Resources/calendarPicker.css 2012-08-02 17:01:56 UTC (rev 124473)
@@ -82,11 +82,49 @@
}
.month-selector-popup {
+ -webkit-box-sizing: border-box;
+ -webkit-box-shadow:
+ 0 0 0 1px rgba(0,0,0,0.1),
+ 0 5px 1px 1px rgba(0,0,0,0.1),
+ 0 5px 2px 1px rgba(0,0,0,0.1),
+ 0 5px 12px 1px rgba(0,0,0,0.5);
+ background: #fff;
display: none;
- outline: none;
- z-index: 2;
+ max-height: 10.8em;
+ overflow-y: auto;
+ position: absolute;
+ white-space: nowrap;
}
+.month-selector-popup-contents {
+ display: table;
+}
+
+.month-selector-popup-entry {
+ -webkit-padding-start: 5px;
+ -webkit-padding-end: 5px;
+ cursor: pointer;
+ display; table-row;
+ line-height: 1.2em;
+}
+
+.selected-month-year {
+ background-color: highlight;
+ color: highlighttext;
+ text-shadow: none;
+}
+
+@media (pointer:coarse) {
+
+ .month-selector-popup {
+ max-height: 150px;
+ }
+
+ .month-selector-popup-entry {
+ line-height: 30px;
+ }
+}
+
.month-selector-wall {
display: none;
height: 100%;
Modified: trunk/Source/WebCore/Resources/calendarPicker.js (124472 => 124473)
--- trunk/Source/WebCore/Resources/calendarPicker.js 2012-08-02 16:53:21 UTC (rev 124472)
+++ trunk/Source/WebCore/Resources/calendarPicker.js 2012-08-02 17:01:56 UTC (rev 124473)
@@ -49,10 +49,13 @@
MonthSelector: "month-selector",
MonthSelectorBox: "month-selector-box",
MonthSelectorPopup: "month-selector-popup",
+ MonthSelectorPopupContents: "month-selector-popup-contents",
+ MonthSelectorPopupEntry: "month-selector-popup-entry",
MonthSelectorWall: "month-selector-wall",
NoFocusRing: "no-focus-ring",
NotThisMonth: "not-this-month",
Selected: "day-selected",
+ SelectedMonthYear: "selected-month-year",
TodayButton: "today-button",
TodayClearArea: "today-clear-area",
Unavailable: "unavailable",
@@ -432,10 +435,14 @@
var box = createElement("div", ClassNames.MonthSelectorBox);
innerContainer.appendChild(box);
// We can't use <select> popup in PagePopup.
- // FIXME: The popup-menu emulation by a listbox is not great.
- this._monthPopup = createElement("select", ClassNames.MonthSelectorPopup);
+ this._monthPopup = createElement("div", ClassNames.MonthSelectorPopup);
this._monthPopup.addEventListener("click", bind(this._handleYearMonthChange, this), false);
this._monthPopup.addEventListener("keydown", bind(this._handleMonthPopupKey, this), false);
+ this._monthPopup.addEventListener("mousemove", bind(this._handleMouseMove, this), false);
+ this._updateSelectionOnMouseMove = true;
+ this._monthPopup.tabIndex = 0;
+ this._monthPopupContents = createElement("div", ClassNames.MonthSelectorPopupContents);
+ this._monthPopup.appendChild(this._monthPopupContents);
box.appendChild(this._monthPopup);
this._month = createElement("div", ClassNames.MonthSelector);
this._month.addEventListener("click", bind(this._showPopup, this), false);
@@ -462,7 +469,7 @@
}
this._month.style.minWidth = maxWidth + 'px';
- global.firstFocusableControl = this._left2; // FIXME: Shoud it be this.month?
+ global.firstFocusableControl = this._left2; // FIXME: Should it be this.month?
};
YearMonthController.addTenYearsButtons = false;
@@ -559,44 +566,119 @@
if (this._right3)
this._right3.disabled = current + 13 > max;
this._month.innerText = formatYearMonth(this._currentYear, this._currentMonth);
- while (this._monthPopup.hasChildNodes())
- this._monthPopup.removeChild(this._monthPopup.firstChild);
+ while (this._monthPopupContents.hasChildNodes())
+ this._monthPopupContents.removeChild(this._monthPopupContents.firstChild);
+
for (var m = current - 6; m <= current + 6; m++) {
if (m < min || m > max)
continue;
- var option = createElement("option", undefined, formatYearMonth(Math.floor(m / 12), m % 12));
- option.value = String(Math.floor(m / 12)) + "-" + String(m % 12);
- this._monthPopup.appendChild(option);
+ var option = createElement("div", ClassNames.MonthSelectorPopupEntry, formatYearMonth(Math.floor(m / 12), m % 12));
+ option.dataset.value = String(Math.floor(m / 12)) + "-" + String(m % 12);
+ this._monthPopupContents.appendChild(option);
if (m == current)
- option.selected = true;
+ option.classList.add(ClassNames.SelectedMonthYear);
}
};
YearMonthController.prototype._showPopup = function() {
- this._monthPopup.size = Math.max(4, Math.min(10, this._monthPopup.length));
this._monthPopup.style.display = "block";
- this._monthPopup.style.position = "absolute";
this._monthPopup.style.zIndex = "1000"; // Larger than the days area.
this._monthPopup.style.left = this._month.offsetLeft + (this._month.offsetWidth - this._monthPopup.offsetWidth) / 2 + "px";
this._monthPopup.style.top = this._month.offsetTop + this._month.offsetHeight + "px";
- this._monthPopup.focus();
this._wall.style.display = "block";
this._wall.style.zIndex = "999"; // This should be smaller than the z-index of monthPopup.
+
+ var popupHeight = this._monthPopup.clientHeight;
+ var fullHeight = this._monthPopupContents.clientHeight;
+ if (fullHeight > popupHeight) {
+ var selected = this._getSelection();
+ if (selected) {
+ var bottom = selected.offsetTop + selected.clientHeight;
+ if (bottom > popupHeight)
+ this._monthPopup.scrollTop = bottom - popupHeight;
+ }
+ this._monthPopup.style.webkitPaddingEnd = '15px';
+ }
+ this._monthPopup.focus();
};
YearMonthController.prototype._closePopup = function() {
this._monthPopup.style.display = "none";
this._wall.style.display = "none";
+ var container = document.querySelector("." + ClassNames.DaysAreaContainer);
+ container.focus();
};
/**
+ * @return {Element} Selected element in the month-year popup.
+ */
+YearMonthController.prototype._getSelection = function()
+{
+ return document.querySelector("." + ClassNames.SelectedMonthYear);
+}
+
+/**
* @param {Event} event
*/
+YearMonthController.prototype._handleMouseMove = function(event)
+{
+ if (!this._updateSelectionOnMouseMove) {
+ // Selection update turned off while navigating with keyboard to prevent a mouse
+ // move trigged during a scroll from resetting the selection. Automatically
+ // rearm control to enable mouse-based selection.
+ this._updateSelectionOnMouseMove = true;
+ } else {
+ var target = event.target;
+ var selection = this._getSelection();
+ if (target && target != selection && target.classList.contains(ClassNames.MonthSelectorPopupEntry)) {
+ if (selection)
+ selection.classList.remove(ClassNames.SelectedMonthYear);
+ target.classList.add(ClassNames.SelectedMonthYear);
+ }
+ }
+ event.stopPropagation();
+ event.preventDefault();
+}
+
+/**
+ * @param {Event} event
+ */
YearMonthController.prototype._handleMonthPopupKey = function(event)
{
var key = event.keyIdentifier;
- if (key == "U+001B") {
+ if (key == "Down") {
+ var selected = this._getSelection();
+ if (selected) {
+ var next = selected.nextSibling;
+ if (next) {
+ selected.classList.remove(ClassNames.SelectedMonthYear);
+ next.classList.add(ClassNames.SelectedMonthYear);
+ var bottom = next.offsetTop + next.clientHeight;
+ if (bottom > this._monthPopup.scrollTop + this._monthPopup.clientHeight) {
+ this._updateSelectionOnMouseMove = false;
+ this._monthPopup.scrollTop = bottom - this._monthPopup.clientHeight;
+ }
+ }
+ }
+ event.stopPropagation();
+ event.preventDefault();
+ } else if (key == "Up") {
+ var selected = this._getSelection();
+ if (selected) {
+ var previous = selected.previousSibling;
+ if (previous) {
+ selected.classList.remove(ClassNames.SelectedMonthYear);
+ previous.classList.add(ClassNames.SelectedMonthYear);
+ if (previous.offsetTop < this._monthPopup.scrollTop) {
+ this._updateSelectionOnMouseMove = false;
+ this._monthPopup.scrollTop = previous.offsetTop;
+ }
+ }
+ }
+ event.stopPropagation();
+ event.preventDefault();
+ } else if (key == "U+001B") {
this._closePopup();
event.stopPropagation();
event.preventDefault();
@@ -609,8 +691,11 @@
YearMonthController.prototype._handleYearMonthChange = function() {
this._closePopup();
-
- var result = this._monthPopup.value.match(/(\d+)-(\d+)/);
+ var selection = this._getSelection();
+ if (!selection)
+ return;
+ var value = selection.dataset.value;
+ var result = value.match(/(\d+)-(\d+)/);
if (!result)
return;
var newYear = Number(result[1]);