Title: [124473] trunk/Source/WebCore
Revision
124473
Author
[email protected]
Date
2012-08-02 10:01:56 -0700 (Thu, 02 Aug 2012)

Log Message

Month-year selector on calendar picker should be touch friendly.
https://bugs.webkit.org/show_bug.cgi?id=92678

Patch by Kevin Ellis <[email protected]> on 2012-08-02
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.

Modified Paths

Diff

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]);
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to