diff --git a/apps/keyboard/js/keyboard/active_targets_manager.js b/apps/keyboard/js/keyboard/active_targets_manager.js index f97d18232084..ba6d26a95b80 100644 --- a/apps/keyboard/js/keyboard/active_targets_manager.js +++ b/apps/keyboard/js/keyboard/active_targets_manager.js @@ -93,7 +93,9 @@ ActiveTargetsManager.prototype._handlePressStart = function(press, id) { // All targets before the new touch need to be committed, // according to UX requirement. this.activeTargets.forEach(function(target, id) { - this._handlePressEnd(press, id); + if (!this.app.upperCaseStateManager.isUpperCasePressed) { + this._handlePressEnd(press, id); + } }, this); var target = press.target; diff --git a/apps/keyboard/js/keyboard/target_handlers.js b/apps/keyboard/js/keyboard/target_handlers.js index 283cd4e10fbd..3704f373a6b7 100644 --- a/apps/keyboard/js/keyboard/target_handlers.js +++ b/apps/keyboard/js/keyboard/target_handlers.js @@ -38,6 +38,11 @@ DefaultTargetHandler.prototype.commit = function() { return; } + if (this.app.upperCaseStateManager.isUpperCasePressed) { + this.app.upperCaseStateManager.switchUpperCaseState({ + isUpperCaseCombo: true + }); + } var keyCode = parseInt(this.target.dataset.keycode, 10); var upperCaseKeyCode = parseInt(this.target.dataset.keycodeUpper, 10); var engine = this.app.inputMethodManager.currentIMEngine; @@ -244,10 +249,20 @@ var CapsLockTargetHandler = function(target, app) { DefaultTargetHandler.apply(this, arguments); }; CapsLockTargetHandler.prototype = Object.create(DefaultTargetHandler.prototype); +CapsLockTargetHandler.prototype.activate = function() { + this.app.upperCaseStateManager.switchUpperCaseState({ + isUpperCasePressed: true + }); + this.app.feedbackManager.triggerFeedback(this.target); + this.app.visualHighlightManager.show(this.target); +}; CapsLockTargetHandler.prototype.commit = function() { this.app.upperCaseStateManager.switchUpperCaseState({ - isUpperCase: !this.app.upperCaseStateManager.isUpperCase, - isUpperCaseLocked: false + isUpperCase: !this.app.upperCaseStateManager.isUpperCaseCombo && + !this.app.upperCaseStateManager.isUpperCase, + isUpperCaseLocked: false, + isUpperCasePressed: false, + isUpperCaseCombo: false }); this.app.visualHighlightManager.hide(this.target); }; diff --git a/apps/keyboard/js/keyboard/upper_case_state_manager.js b/apps/keyboard/js/keyboard/upper_case_state_manager.js index b38af33095fe..005f23d777b9 100644 --- a/apps/keyboard/js/keyboard/upper_case_state_manager.js +++ b/apps/keyboard/js/keyboard/upper_case_state_manager.js @@ -5,6 +5,8 @@ var UpperCaseStateManager = function() { this.isUpperCase = undefined; this.isUpperCaseLocked = undefined; + this.isUpperCasePressed = undefined; + this.isUpperCaseCombo = undefined; }; UpperCaseStateManager.prototype.onstatechange = null; @@ -13,11 +15,15 @@ UpperCaseStateManager.prototype.start = UpperCaseStateManager.prototype.reset = function() { this.isUpperCase = false; this.isUpperCaseLocked = false; + this.isUpperCasePressed = false; + this.isUpperCaseCombo = false; }; UpperCaseStateManager.prototype.stop = function() { this.isUpperCase = undefined; this.isUpperCaseLocked = undefined; + this.isUpperCasePressed = undefined; + this.isUpperCaseCombo = undefined; }; UpperCaseStateManager.prototype.switchUpperCaseState = function(state) { @@ -31,17 +37,23 @@ UpperCaseStateManager.prototype.switchUpperCaseState = function(state) { state.isUpperCase : this.isUpperCase; var newIsUpperCaseLocked = (typeof state.isUpperCaseLocked === 'boolean') ? state.isUpperCaseLocked : this.isUpperCaseLocked; + var newIsUpperCasePressed = (typeof state.isUpperCasePressed === 'boolean') ? + state.isUpperCasePressed : this.isUpperCasePressed; + var newIsUpperCaseCombo = (typeof state.isUpperCaseCombo === 'boolean') ? + state.isUpperCaseCombo : this.isUpperCaseCombo; // It doesn't really make any sense to set isUpperCase to false but // change/keep isUpperCaseLocked to true. // This also means isUpperCaseLocked can overwrite isUpperCase changes, // and literally keep the caps "lock". - if (newIsUpperCaseLocked) { + if (newIsUpperCaseLocked || newIsUpperCaseCombo) { newIsUpperCase = true; } var statechanged = (this.isUpperCase !== newIsUpperCase) || - (this.isUpperCaseLocked !== newIsUpperCaseLocked); + (this.isUpperCaseLocked !== newIsUpperCaseLocked) || + (this.isUpperCasePressed !== newIsUpperCasePressed) || + (this.isUpperCaseCombo !== newIsUpperCaseCombo); // Don't do anything if the state is unchanged. if (!statechanged) { @@ -51,6 +63,8 @@ UpperCaseStateManager.prototype.switchUpperCaseState = function(state) { // Set the new states. this.isUpperCase = newIsUpperCase; this.isUpperCaseLocked = newIsUpperCaseLocked; + this.isUpperCasePressed = newIsUpperCasePressed; + this.isUpperCaseCombo = newIsUpperCaseCombo; // Call onstatechange callback. if (typeof this.onstatechange === 'function') { diff --git a/apps/keyboard/js/render.js b/apps/keyboard/js/render.js index 64b33ed432af..8405c2cf2677 100644 --- a/apps/keyboard/js/render.js +++ b/apps/keyboard/js/render.js @@ -107,6 +107,7 @@ var IMERender = (function() { }; // Accepts a state object with two properties. + // Set isUpperCasePressed to true if pressed // Set isUpperCaseLocked to true if locked // Set isUpperCase to true when uppercase is enabled // Use false on both of these properties when uppercase is disabled @@ -123,8 +124,10 @@ var IMERender = (function() { capsLockKey.classList.remove('kbr-key-active'); capsLockKey.classList.add('kbr-key-hold'); } else if (state.isUpperCase) { - capsLockKey.classList.add('kbr-key-active'); - capsLockKey.classList.remove('kbr-key-hold'); + if (!state.isUpperCasePressed) { + capsLockKey.classList.add('kbr-key-active'); + capsLockKey.classList.remove('kbr-key-hold'); + } } else { capsLockKey.classList.remove('kbr-key-active'); capsLockKey.classList.remove('kbr-key-hold'); diff --git a/apps/keyboard/test/unit/keyboard/target_handlers_test.js b/apps/keyboard/test/unit/keyboard/target_handlers_test.js index 3d12aacb4919..81017c07863c 100644 --- a/apps/keyboard/test/unit/keyboard/target_handlers_test.js +++ b/apps/keyboard/test/unit/keyboard/target_handlers_test.js @@ -743,8 +743,14 @@ suite('target handlers', function() { }); test('activate', function() { - assert.equal(handler.activate, DefaultTargetHandler.prototype.activate, - 'function not overwritten'); + handler.activate(); + + assert.isTrue(app.upperCaseStateManager.switchUpperCaseState.calledWith({ + isUpperCasePressed: true + })); + + assert.isTrue(app.visualHighlightManager.hide.calledWith(target)); + assert.isTrue(app.visualHighlightManager.hide.calledOnce); }); test('longPress', function() { @@ -767,7 +773,9 @@ suite('target handlers', function() { assert.isTrue(app.upperCaseStateManager.switchUpperCaseState.calledWith({ isUpperCase: true, - isUpperCaseLocked: false + isUpperCaseLocked: false, + isUpperCasePressed: false, + isUpperCaseCombo: false })); assert.isTrue(app.visualHighlightManager.hide.calledWith(target)); diff --git a/apps/keyboard/test/unit/keyboard/upper_case_state_manager_test.js b/apps/keyboard/test/unit/keyboard/upper_case_state_manager_test.js index 5c7ea20dc5e2..93434bba3d76 100644 --- a/apps/keyboard/test/unit/keyboard/upper_case_state_manager_test.js +++ b/apps/keyboard/test/unit/keyboard/upper_case_state_manager_test.js @@ -97,6 +97,40 @@ suite('UpperCaseStateManager', function() { assert.isFalse(manager.isUpperCaseLocked); assert.isFalse(manager.onstatechange.calledOnce); }); + + test('set isUpperCasePressed = true', function() { + manager.switchUpperCaseState({ + isUpperCasePressed: true + }); + + assert.isTrue(manager.isUpperCase); + assert.isTrue(manager.isUpperCasePressed); + assert.isFalse(manager.onstatechange.calledOnce); + }); + + test('set isUpperCaseCombo = true', function() { + manager.switchUpperCaseState({ + isUpperCaseCombo: true + }); + + assert.isTrue(manager.isUpperCase); + assert.isTrue(manager.isUpperCasePressed); + assert.isTrue(manager.isUpperCaseCombo); + assert.isFalse(manager.onstatechange.calledOnce); + }); + + test('set isUpperCasePressed = false, isUpperCaseCombo = false', + function() { + manager.switchUpperCaseState({ + isUpperCasePressed: false, + isUpperCaseCombo: false + }); + + assert.isFalse(manager.isUpperCase); + assert.isFalse(manager.isUpperCasePressed); + assert.isFalse(manager.isUpperCaseCombo); + assert.isFalse(manager.onstatechange.calledOnce); + }); }); suite('isUpperCase = true, isUpperCaseLocked = false', function() { @@ -187,6 +221,40 @@ suite('UpperCaseStateManager', function() { assert.isFalse(manager.isUpperCaseLocked); assert.isFalse(manager.onstatechange.calledOnce); }); + + test('set isUpperCasePressed = true', function() { + manager.switchUpperCaseState({ + isUpperCasePressed: true + }); + + assert.isTrue(manager.isUpperCase); + assert.isTrue(manager.isUpperCasePressed); + assert.isFalse(manager.onstatechange.calledOnce); + }); + + test('set isUpperCaseCombo = true', function() { + manager.switchUpperCaseState({ + isUpperCaseCombo: true + }); + + assert.isTrue(manager.isUpperCase); + assert.isTrue(manager.isUpperCasePressed); + assert.isTrue(manager.isUpperCaseCombo); + assert.isFalse(manager.onstatechange.calledOnce); + }); + + test('set isUpperCasePressed = false, isUpperCaseCombo = false', + function() { + manager.switchUpperCaseState({ + isUpperCasePressed: false, + isUpperCaseCombo: false + }); + + assert.isFalse(manager.isUpperCase); + assert.isFalse(manager.isUpperCasePressed); + assert.isFalse(manager.isUpperCaseCombo); + assert.isFalse(manager.onstatechange.calledOnce); + }); }); suite('isUpperCase = true, isUpperCaseLocked = true', function() { @@ -278,5 +346,39 @@ suite('UpperCaseStateManager', function() { assert.isFalse(manager.isUpperCaseLocked); assert.isTrue(manager.onstatechange.calledOnce); }); + + test('set isUpperCasePressed = true', function() { + manager.switchUpperCaseState({ + isUpperCasePressed: true + }); + + assert.isTrue(manager.isUpperCase); + assert.isTrue(manager.isUpperCasePressed); + assert.isFalse(manager.onstatechange.calledOnce); + }); + + test('set isUpperCaseCombo = true', function() { + manager.switchUpperCaseState({ + isUpperCaseCombo: true + }); + + assert.isTrue(manager.isUpperCase); + assert.isTrue(manager.isUpperCasePressed); + assert.isTrue(manager.isUpperCaseCombo); + assert.isFalse(manager.onstatechange.calledOnce); + }); + + test('set isUpperCasePressed = false, isUpperCaseCombo = false', + function() { + manager.switchUpperCaseState({ + isUpperCasePressed: false, + isUpperCaseCombo: false + }); + + assert.isFalse(manager.isUpperCase); + assert.isFalse(manager.isUpperCasePressed); + assert.isFalse(manager.isUpperCaseCombo); + assert.isFalse(manager.onstatechange.calledOnce); + }); }); });