Index: chrome/browser/resources/keyboard/common.js |
diff --git a/chrome/browser/resources/keyboard/common.js b/chrome/browser/resources/keyboard/common.js |
index a3dff78eca727ce3e482ba347865a5b989f48d01..ca7ca4cdeb2c825fe5aedf9350da9cad74b09d2c 100644 |
--- a/chrome/browser/resources/keyboard/common.js |
+++ b/chrome/browser/resources/keyboard/common.js |
@@ -47,6 +47,18 @@ var REPEAT_DELAY_MSEC = 500; |
var REPEAT_INTERVAL_MSEC = 50; |
/** |
+ * The keyboard layout name currently in use. |
+ * @type {string} |
+ */ |
+var currentKeyboardLayout = 'us'; |
+ |
+/** |
+ * The popup keyboard layout name currently in use. |
+ * @type {string} |
+ */ |
+var currentPopupName = ''; |
+ |
+/** |
* A structure to track the currently repeating key on the keyboard. |
*/ |
var repeatKey = { |
@@ -81,6 +93,31 @@ var repeatKey = { |
}; |
/** |
+ * An array to track the currently touched keys on the popup keyboard. |
+ */ |
+var touchedKeys = []; |
+ |
+/** |
+ * Set the keyboard mode. |
+ * @param {string} mode The new mode. |
+ * @return {void} |
+ */ |
+function setMode(mode) { |
+ var rows = KEYBOARDS[currentKeyboardLayout]['rows']; |
+ for (var i = 0; i < rows.length; ++i) { |
+ rows[i].showMode(mode); |
+ } |
+ |
+ if (!currentPopupName) { |
+ return; |
+ } |
+ var popupRows = KEYBOARDS[currentPopupName]['rows']; |
+ for (var i = 0; i < popupRows.length; ++i) { |
+ popupRows[i].showMode(mode); |
+ } |
+} |
+ |
+/** |
* Transition the mode according to the given transition. |
* @param {string} transition The transition to take. |
* @return {void} |
@@ -214,8 +251,14 @@ function setupKeyEventHandlers(key, element, handlers) { |
} |
if (keyLongHandler) { |
+ // Copy the current state of event because |evt| can be modified before |
+ // |keyLongHandler| is called. |
+ var evt2 = {}; |
+ for (var name in evt) { |
+ evt2[name] = evt[name]; |
+ } |
key.longPressTimer = setTimeout(function() { |
- keyLongHandler(evt), |
+ keyLongHandler(evt2), |
clearTimeout(key.longPressTimer); |
delete key.longPressTimer; |
key.pressed = false; |
@@ -252,6 +295,10 @@ function setupKeyEventHandlers(key, element, handlers) { |
}; |
var outHandler = function(evt) { |
+ if (evt.target != evt.currentTarget || |
+ evt.toElement.parentNode == evt.fromElement) { |
+ return; |
+ } |
// Reset key press state if the point goes out of the element. |
key.pressed = false; |
// Reset long-press timer. |
@@ -285,12 +332,123 @@ function sendKeyFunction(key) { |
} |
/** |
+ * Dispatch custom events to the elements at the touch points. |
+ * touchpointmove events are dispatched responding to a touchmove and |
+ * touchpointend events responding to a touchend event respectively. |
+ * @param {UIEvent} evt The touch event that contains touch points information. |
+ * @return {void} |
+ */ |
+function dispatchTouchPointEvent(evt) { |
+ var type = null; |
+ var touches = null; |
+ if (evt.type == 'touchmove') { |
+ type = 'touchpointmove'; |
+ touches = evt.touches; |
+ } else if (evt.type == 'touchend') { |
+ type = 'touchpointend'; |
+ touches = evt.changedTouches; |
+ } else { |
+ return; |
+ } |
+ |
+ for (var i = 0; i < touches.length; ++i) { |
+ var dispatchedEvent = document.createEvent('Event'); |
+ dispatchedEvent.initEvent(type, true, false); |
+ var touch = touches[i]; |
+ var key = document.elementFromPoint(touch.screenX, touch.screenY); |
+ if (key) { |
+ key.dispatchEvent(dispatchedEvent); |
+ } |
+ } |
+} |
+ |
+/** |
+ * Handle a touch move event on the key to make changes to the popup keyboard. |
+ * @param {UIEvent} evt The UI event which triggered the touch move. |
+ * @return {void} |
+*/ |
+function trackTouchMoveForPopup(evt) { |
+ var previous = touchedKeys; |
+ touchedKeys = []; |
+ dispatchTouchPointEvent(evt); |
+ for (var i = 0; i < previous.length; ++i) { |
+ if (touchedKeys.indexOf(previous[i]) == -1) { |
+ previous[i].classList.remove('highlighted'); |
+ } |
+ } |
+ for (var i = 0; i < touchedKeys.length; ++i) { |
+ touchedKeys[i].classList.add('highlighted'); |
+ } |
+} |
+ |
+/** |
+ * Handle a touch end event on the key to make changes to the popup keyboard. |
+ * @param {UIEvent} evt The UI event which triggered the touch end. |
+ * @return {void} |
+*/ |
+function trackTouchEndForPopup(evt) { |
+ for (var i = 0; i < touchedKeys.length; ++i) { |
+ touchedKeys[i].classList.remove('highlighted'); |
+ } |
+ dispatchTouchPointEvent(evt); |
+ hidePopupKeyboard(); |
+ |
+ touchedKeys = []; |
+ evt.target.removeEventListener('touchmove', trackTouchMoveForPopup); |
+ evt.target.removeEventListener('touchend', trackTouchEndForPopup); |
+} |
+ |
+/** |
* Show the popup keyboard. |
* @param {string} name The name of the popup keyboard. |
+ * @return {void} |
*/ |
-function showPopupKeyboard(name) { |
- // TODO(mazda): Implement this function. |
- console.warn('Popup keyboard is not implemented yet.'); |
+function showPopupKeyboard(name, evt) { |
+ var popupDiv = document.getElementById('popup'); |
+ if (popupDiv.style.visibility == 'visible') { |
+ return; |
+ } |
+ |
+ // Reinitialize the rows of the popup keyboard |
+ if (currentPopupName != name) { |
+ while (popupDiv.firstChild) { |
+ popupDiv.removeChild(popupDiv.firstChild); |
+ } |
+ if (currentPopupName in KEYBOARDS) { |
+ delete KEYBOARDS[currentPopupName].rows; |
+ } |
+ initRows(name, popupDiv, true); |
+ currentPopupName = name; |
+ } |
+ |
+ // Set the mode of the popup keyboard |
+ var popupRows = KEYBOARDS[currentPopupName]['rows']; |
+ for (var i = 0; i < popupRows.length; ++i) { |
+ popupRows[i].showMode(currentMode); |
+ } |
+ |
+ // Calculate the size of popup keyboard based on the size of the key. |
+ var keyElement = evt.currentTarget; |
+ var keyboard = KEYBOARDS[name]; |
+ var rows = keyboard['definition']; |
+ var height = keyElement.offsetHeight * rows.length; |
+ var aspect = keyboard['aspect']; |
+ var width = aspect * height; |
+ popupDiv.style.width = width + 'px'; |
+ popupDiv.style.height = height + 'px'; |
+ |
+ // Place the popup keyboard above the key |
+ var rect = keyElement.getBoundingClientRect(); |
+ var left = (rect.left + rect.right) / 2 - width / 2; |
+ left = Math.min(Math.max(left, 0), window.innerWidth - width); |
+ var top = rect.top - height; |
+ top = Math.min(Math.max(top, 0), window.innerHeight - height); |
+ popupDiv.style.left = left + 'px'; |
+ popupDiv.style.top = top + 'px'; |
+ popupDiv.style.visibility = 'visible'; |
+ |
+ keyElement.addEventListener('touchmove', trackTouchMoveForPopup); |
+ keyElement.addEventListener('touchend', trackTouchEndForPopup); |
} |
/** |
@@ -299,12 +457,21 @@ function showPopupKeyboard(name) { |
* @return {function()} A function which calls showPopupKeyboard(name). |
*/ |
function showPopupKeyboardFunction(name) { |
- return function () { |
- showPopupKeyboard(name); |
+ return function (evt) { |
+ showPopupKeyboard(name, evt); |
}; |
} |
/** |
+ * Hide the popup keyboard. |
+ * @return {void} |
+ */ |
+function hidePopupKeyboard() { |
+ var popupDiv = document.getElementById('popup'); |
+ popupDiv.style.visibility = 'hidden'; |
+} |
+ |
+/** |
* Plain-old-data class to represent a character. |
* @param {string} display The HTML to be displayed. |
* @param {string} id The key identifier for this Character. |
@@ -317,10 +484,30 @@ function Character(display, id) { |
/** |
* Convenience function to make the keyboard data more readable. |
- * @param {string} display Both the display and id for the created Character. |
+ * @param {string} display The display for the created Character. |
+ * @param {string} opt_id The id for the created Character. |
+ * @return {Character} A character that contains display and opt_id. If |
+ * opt_id is omitted, display is used as the id. |
*/ |
-function C(display) { |
- return new Character(display, display); |
+function C(display, opt_id) { |
+ var id = opt_id || display; |
+ return new Character(display, id); |
+} |
+ |
+/** |
+ * Convenience function to make the keyboard data more readable. |
+ * @param {string} display The display for the created Character. |
+ * @param {string} opt_id The id for the created Character. |
+ * @param {string} opt_popupName The popup keyboard name for this character. |
+ * @return {Object} An object that contains a Character and the popup keyboard |
+ * name. |
+ */ |
+function CP(display, opt_id, opt_popupName) { |
+ var result = { character: C(display, opt_id) }; |
+ if (opt_popupName) { |
+ result['popupName'] = opt_popupName; |
+ } |
+ return result; |
} |
/** |
@@ -402,6 +589,56 @@ BaseKey.prototype = { |
/** |
* A simple key which displays Characters. |
+ * @param {Object} key The Character and the popup name for KEY_MODE. |
+ * @param {Object} shift The Character and the popup name for SHIFT_MODE. |
+ * @param {Object} num The Character and the popup name for NUMBER_MODE. |
+ * @param {Object} symbol The Character and the popup name for SYMBOL_MODE. |
+ * @constructor |
+ * @extends {BaseKey} |
+ */ |
+function Key(key, shift, num, symbol) { |
+ this.modeElements_ = {}; |
+ this.cellType_ = ''; |
+ |
+ this.modes_ = {}; |
+ this.modes_[KEY_MODE] = key ? key.character : null; |
+ this.modes_[SHIFT_MODE] = shift ? shift.character : null; |
+ this.modes_[NUMBER_MODE] = num ? num.character : null; |
+ this.modes_[SYMBOL_MODE] = symbol ? symbol.character : null; |
+ |
+ this.popupNames_ = {}; |
+ this.popupNames_[KEY_MODE] = key ? key.popupName : null; |
+ this.popupNames_[SHIFT_MODE] = shift ? shift.popupName : null; |
+ this.popupNames_[NUMBER_MODE] = num ? num.popupName : null; |
+ this.popupNames_[SYMBOL_MODE] = symbol ? symbol.popupName : null; |
+} |
+ |
+Key.prototype = { |
+ __proto__: BaseKey.prototype, |
+ |
+ /** @inheritDoc */ |
+ makeDOM: function(mode) { |
+ if (!this.modes_[mode]) { |
+ return null; |
+ } |
+ |
+ this.modeElements_[mode] = document.createElement('div'); |
+ var element = this.modeElements_[mode]; |
+ element.className = 'key'; |
+ |
+ addContent(element, this.modes_[mode].display); |
+ |
+ var longHandler = this.popupNames_[mode] ? |
+ showPopupKeyboardFunction(this.popupNames_[mode]) : null; |
+ setupKeyEventHandlers(this, element, |
+ { 'up': sendKeyFunction(this.modes_[mode].keyIdentifier), |
+ 'long': longHandler }); |
+ return element; |
+ } |
+}; |
+ |
+/** |
+ * A simple key which displays Characters on the popup keyboard. |
* @param {Character} key The Character for KEY_MODE. |
* @param {Character} shift The Character for SHIFT_MODE. |
* @param {Character} num The Character for NUMBER_MODE. |
@@ -409,7 +646,7 @@ BaseKey.prototype = { |
* @constructor |
* @extends {BaseKey} |
*/ |
-function Key(key, shift, num, symbol) { |
+function PopupKey(key, shift, num, symbol) { |
this.modeElements_ = {}; |
this.cellType_ = ''; |
@@ -420,7 +657,7 @@ function Key(key, shift, num, symbol) { |
this.modes_[SYMBOL_MODE] = symbol; |
} |
-Key.prototype = { |
+PopupKey.prototype = { |
__proto__: BaseKey.prototype, |
/** @inheritDoc */ |
@@ -430,16 +667,24 @@ Key.prototype = { |
} |
this.modeElements_[mode] = document.createElement('div'); |
- this.modeElements_[mode].className = 'key'; |
- addContent(this.modeElements_[mode], this.modes_[mode].display); |
- |
- // TODO(mazda): Set the long-press handler only if the key has characters |
- // to show on the popup keyboard. |
- setupKeyEventHandlers(this, this.modeElements_[mode], |
- { 'up': sendKeyFunction(this.modes_[mode].keyIdentifier), |
- 'long': showPopupKeyboardFunction('') }); |
- |
- return this.modeElements_[mode]; |
+ var element = this.modeElements_[mode]; |
+ element.className = 'key popupkey'; |
+ |
+ addContent(element, this.modes_[mode].display); |
+ |
+ var upHandler = sendKeyFunction(this.modes_[mode].keyIdentifier); |
+ element.addEventListener('touchpointmove', function(evt) { |
+ touchedKeys.push(element); |
+ }); |
+ element.addEventListener('touchpointend', upHandler); |
+ element.addEventListener('mouseup', upHandler); |
+ element.addEventListener('mouseover', function(evt) { |
+ element.classList.add('highlighted'); |
+ }); |
+ element.addEventListener('mouseout', function(evt) { |
+ element.classList.remove('highlighted'); |
+ }); |
+ return element; |
} |
}; |
@@ -715,4 +960,12 @@ Row.prototype = { |
} |
this.modeElements_[mode].style.display = '-webkit-box'; |
}, |
+ |
+ /** |
+ * Returns the size of keys this row contains. |
+ * @return {number} The size of keys. |
+ */ |
+ get length() { |
+ return this.keys_.length; |
+ } |
}; |