Commit 30fe7c4e authored by My Nguyen's avatar My Nguyen Committed by Commit Bot

[OsSettingsLanguages] Add suggested input methods UI

Suggested input method list is similar to the current list shown to user
in current Manage Input Methods page. However, it is less complicated
because we don’t need to group variants of the same base language
together. We only need to display the input methods based on the user's
enabled languages and arc ime that are not yet enabled.

Mock: http://go/cros-lang-settings-ux-slide#slide=25
Current view: http://screen/54wEoVjUWS3GFip

Bug: 1113439
Change-Id: I525fb0ea300c2ac751a8cef56a9476772173e2c7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2398037
Commit-Queue: My Nguyen <myy@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarRegan Hsu <hsuregan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806931}
parent 35c61626
...@@ -298,6 +298,12 @@ ...@@ -298,6 +298,12 @@
<message name="IDS_OS_SETTINGS_LANGUAGES_REMOVE_INPUT_METHOD_LABEL" translateable="false" desc="Title of the dialog that lets the user remove a given keyboard layout or input method editor."> <message name="IDS_OS_SETTINGS_LANGUAGES_REMOVE_INPUT_METHOD_LABEL" translateable="false" desc="Title of the dialog that lets the user remove a given keyboard layout or input method editor.">
Remove <ph name="INPUT_METHOD_NAME">$1<ex>US keyboard</ex></ph>? Remove <ph name="INPUT_METHOD_NAME">$1<ex>US keyboard</ex></ph>?
</message> </message>
<message name="IDS_OS_SETTINGS_LANGUAGES_SUGGESTED_INPUT_METHODS_LABEL" translateable="false" desc="Title of the list of keyboard layouts or input method editors suggested to users.">
Suggested
</message>
<message name="IDS_OS_SETTINGS_LANGUAGES_ALL_INPUT_METHODS_LABEL" translateable="false" desc="Title of the list of all keyboard layouts or input method editors.">
All input methods
</message>
<message name="IDS_OS_SETTINGS_LANGUAGES_SPELL_CHECK_TITLE" translateable="false" desc="Title for the section containing all the options for spell check settings. The options include picking between using the system's spell check or using Google's web service and a list of the enabled languages which support spell checking."> <message name="IDS_OS_SETTINGS_LANGUAGES_SPELL_CHECK_TITLE" translateable="false" desc="Title for the section containing all the options for spell check settings. The options include picking between using the system's spell check or using Google's web service and a list of the enabled languages which support spell checking.">
Spell check Spell check
</message> </message>
......
...@@ -29,6 +29,7 @@ js_type_check("closure_compile") { ...@@ -29,6 +29,7 @@ js_type_check("closure_compile") {
} }
js_library("add_input_methods_dialog") { js_library("add_input_methods_dialog") {
deps = [ "//ui/webui/resources/cr_elements:cr_scrollable_behavior" ]
} }
js_library("change_device_language_dialog") { js_library("change_device_language_dialog") {
...@@ -194,6 +195,7 @@ js_type_check("closure_compile_module") { ...@@ -194,6 +195,7 @@ js_type_check("closure_compile_module") {
js_library("add_input_methods_dialog.m") { js_library("add_input_methods_dialog.m") {
sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_languages_page/add_input_methods_dialog.m.js" ] sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_languages_page/add_input_methods_dialog.m.js" ]
deps = [ "//ui/webui/resources/cr_elements:cr_scrollable_behavior.m" ]
extra_deps = [ ":add_input_methods_dialog_module" ] extra_deps = [ ":add_input_methods_dialog_module" ]
} }
......
<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
<link rel="import" href="../metrics_recorder.html">
<link rel="import" href="../../languages_page/languages.html">
<link rel="import" href="../../settings_shared_css.html">
<dom-module id="os-settings-add-input-methods-dialog"> <dom-module id="os-settings-add-input-methods-dialog">
<template> <template>
<style include="settings-shared">
#dialogBody {
display: flex;
flex-direction: column;
height: 336px;
overflow: auto;
padding-inline-end: 0;
}
.list-item {
color: var(--cros-text-color-primary);
min-height: 36px;
}
cr-checkbox::part(label-container) {
white-space: nowrap;
}
</style>
<cr-dialog id="dialog" close-text="$i18n{close}" show-on-attach> <cr-dialog id="dialog" close-text="$i18n{close}" show-on-attach>
<div slot="title">$i18n{addInputMethodLabel}</div> <div slot="title">$i18n{addInputMethodLabel}</div>
<div id="dialogBody" slot="body" scrollable>
<template is="dom-if" if="[[showSuggestedList_]]">
<div id="suggestedInputMethods">
$i18n{suggestedInputMethodsLabel}
<iron-list scroll-target="[[$$('suggestedInputMethods')]]"
items="[[suggestedInputMethods_]]">
<template>
<cr-checkbox class="list-item no-outline"
tab-index="[[tabIndex]]" on-change="onCheckboxChange_">
[[item.displayName]]
</cr-checkbox>
</template>
</iron-list>
</div>
</template>
</div>
<div slot="button-container"> <div slot="button-container">
<cr-button class="cancel-button" on-click="onCancelButtonTap_"> <cr-button class="cancel-button" on-click="onCancelButtonTap_">
$i18n{cancel} $i18n{cancel}
......
...@@ -9,21 +9,111 @@ ...@@ -9,21 +9,111 @@
Polymer({ Polymer({
is: 'os-settings-add-input-methods-dialog', is: 'os-settings-add-input-methods-dialog',
behaviors: [
CrScrollableBehavior,
],
properties: { properties: {
/** @type {!LanguagesModel|undefined} */
languages: Object,
/** @type {!LanguageHelper} */
languageHelper: Object,
/** @private {!Set<string>} */
inputMethodsToAdd_: {
type: Object,
value() {
return new Set();
},
},
/** @private {!Array<!chrome.languageSettingsPrivate.InputMethod>} */
suggestedInputMethods_: {
type: Array,
value: [],
computed:
'getSuggestedInputMethods_(languages, languages.enabled.*, languages.inputMethods.*)',
},
/** @private */
showSuggestedList_: {
type: Boolean,
value: false,
computed: 'shouldShowSuggestedList_(suggestedInputMethods_)'
},
/** @private */ /** @private */
disableActionButton_: { disableActionButton_: {
type: Boolean, type: Boolean,
value: true, value: true,
computed: 'shouldDisableActionButton_(inputMethodsToAdd_.size)',
}, },
}, },
/**
* Get suggested input methods based on user's enabled languages and ARC IMEs
* @return {!Array<!chrome.languageSettingsPrivate.InputMethod>}
* @private
*/
getSuggestedInputMethods_() {
const languageCodes = [
...this.languageHelper.getEnabledLanguageCodes(),
this.languageHelper.getArcImeLanguageCode()
];
return this.languageHelper.getInputMethodsForLanguages(languageCodes)
.filter(inputMethod => {
return !this.languageHelper.isInputMethodEnabled(inputMethod.id);
});
},
/**
* @return {boolean}
* @private
*/
shouldShowSuggestedList_() {
return this.suggestedInputMethods_.length > 0;
},
/**
* Handler for an input method checkbox.
* @param {!{model: !{item: chrome.languageSettingsPrivate.InputMethod},
* target: !Element}} e
* @private
*/
onCheckboxChange_(e) {
const inputMethodId = e.model.item.id;
if (e.target.checked) {
this.inputMethodsToAdd_.add(inputMethodId);
} else {
this.inputMethodsToAdd_.delete(inputMethodId);
}
// Polymer doesn't notify changes to set size.
this.notifyPath('inputMethodsToAdd_.size');
},
/**
* @return {boolean}
* @private
*/
shouldDisableActionButton_() {
return !this.inputMethodsToAdd_.size;
},
/** @private */ /** @private */
onCancelButtonTap_() { onCancelButtonTap_() {
this.$.dialog.close(); this.$.dialog.close();
}, },
/** @private */ /**
* Add input methods.
* @private
*/
onActionButtonTap_() { onActionButtonTap_() {
this.inputMethodsToAdd_.forEach(id => {
this.languageHelper.addInputMethod(id);
});
settings.recordSettingChange();
this.$.dialog.close(); this.$.dialog.close();
}, },
}); });
...@@ -242,7 +242,8 @@ ...@@ -242,7 +242,8 @@
</div> </div>
<template is="dom-if" if="[[showAddInputMethodsDialog_]]" restamp> <template is="dom-if" if="[[showAddInputMethodsDialog_]]" restamp>
<os-settings-add-input-methods-dialog <os-settings-add-input-methods-dialog languages="[[languages]]"
language-helper="[[languageHelper]]"
on-close="onAddInputMethodsDialogClose_"> on-close="onAddInputMethodsDialogClose_">
</os-settings-add-input-methods-dialog> </os-settings-add-input-methods-dialog>
</template> </template>
......
...@@ -130,6 +130,17 @@ Polymer({ ...@@ -130,6 +130,17 @@ Polymer({
return new Map(); return new Map();
}, },
}, },
/**
* Hash set of input method ids that are enabled.
* @private {!Set<string>}
*/
enabledInputMethodSet_: {
type: Object,
value() {
return new Set();
}
},
// </if> // </if>
/** @private Prospective UI language when the page was loaded. */ /** @private Prospective UI language when the page was loaded. */
...@@ -999,6 +1010,7 @@ Polymer({ ...@@ -999,6 +1010,7 @@ Polymer({
enabledInputMethodIds = enabledInputMethodIds.concat( enabledInputMethodIds = enabledInputMethodIds.concat(
this.getPref('settings.language.enabled_extension_imes') this.getPref('settings.language.enabled_extension_imes')
.value.split(',')); .value.split(','));
this.enabledInputMethodSet_ = new Set(enabledInputMethodIds);
// Return only supported input methods. // Return only supported input methods.
return enabledInputMethodIds return enabledInputMethodIds
...@@ -1066,6 +1078,45 @@ Polymer({ ...@@ -1066,6 +1078,45 @@ Polymer({
return this.languageInputMethods_.get(languageCode) || []; return this.languageInputMethods_.get(languageCode) || [];
}, },
/**
* Returns the input methods that support any of the given languages.
* @param {!Array<string>} languageCodes
* @return {!Array<!chrome.languageSettingsPrivate.InputMethod>}
*/
getInputMethodsForLanguages(languageCodes) {
// Input methods that have already been listed for this language.
const /** !Set<string> */ usedInputMethods = new Set();
/** @type {!Array<chrome.languageSettingsPrivate.InputMethod>} */
const combinedInputMethods = [];
for (const languageCode of languageCodes) {
const inputMethods = this.getInputMethodsForLanguage(languageCode);
// Get the language's unused input methods.
const newInputMethods = inputMethods.filter(
inputMethod => !usedInputMethods.has(inputMethod.id));
// Some input methods might be used by different languages, save the new
// ones in usedInputMethods to prevent adding them twice.
newInputMethods.forEach(
inputMethod => usedInputMethods.add(inputMethod.id));
combinedInputMethods.push(...newInputMethods);
}
return combinedInputMethods;
},
/**
* @return {!Set<string>} list of enabled language code.
*/
getEnabledLanguageCodes() {
return this.enabledLanguageSet_;
},
/**
* @param {string} id the input method id
* @return {boolean} True if the input method is enabled
*/
isInputMethodEnabled(id) {
return this.enabledInputMethodSet_.has(id);
},
/** /**
* @param {!chrome.languageSettingsPrivate.InputMethod} inputMethod * @param {!chrome.languageSettingsPrivate.InputMethod} inputMethod
* @return {boolean} * @return {boolean}
......
...@@ -222,6 +222,24 @@ class LanguageHelper { ...@@ -222,6 +222,24 @@ class LanguageHelper {
*/ */
getInputMethodsForLanguage(languageCode) {} getInputMethodsForLanguage(languageCode) {}
/**
* Returns the input methods that support any of the given languages.
* @param {!Array<string>} languageCodes
* @return {!Array<!chrome.languageSettingsPrivate.InputMethod>}
*/
getInputMethodsForLanguages(languageCodes) {}
/**
* @return {!Set<string>} list of enabled language code.
*/
getEnabledLanguageCodes() {}
/**
* @param {string} id the input method id
* @return {boolean} True if the input method is enabled
*/
isInputMethodEnabled(id) {}
/** /**
* @param {!chrome.languageSettingsPrivate.InputMethod} inputMethod * @param {!chrome.languageSettingsPrivate.InputMethod} inputMethod
* @return {boolean} * @return {boolean}
......
...@@ -303,6 +303,10 @@ void AddInputPageStringsV2(content::WebUIDataSource* html_source) { ...@@ -303,6 +303,10 @@ void AddInputPageStringsV2(content::WebUIDataSource* html_source) {
{"addInputMethodLabel", IDS_OS_SETTINGS_LANGUAGES_ADD_INPUT_METHOD_LABEL}, {"addInputMethodLabel", IDS_OS_SETTINGS_LANGUAGES_ADD_INPUT_METHOD_LABEL},
{"removeInputMethodLabel", {"removeInputMethodLabel",
IDS_OS_SETTINGS_LANGUAGES_REMOVE_INPUT_METHOD_LABEL}, IDS_OS_SETTINGS_LANGUAGES_REMOVE_INPUT_METHOD_LABEL},
{"suggestedInputMethodsLabel",
IDS_OS_SETTINGS_LANGUAGES_SUGGESTED_INPUT_METHODS_LABEL},
{"allInputMethodsLabel",
IDS_OS_SETTINGS_LANGUAGES_ALL_INPUT_METHODS_LABEL},
{"spellCheckTitle", IDS_OS_SETTINGS_LANGUAGES_SPELL_CHECK_TITLE}, {"spellCheckTitle", IDS_OS_SETTINGS_LANGUAGES_SPELL_CHECK_TITLE},
{"spellCheckEnhancedLabel", {"spellCheckEnhancedLabel",
IDS_OS_SETTINGS_LANGUAGES_SPELL_CHECK_ENHANCED_LABEL}, IDS_OS_SETTINGS_LANGUAGES_SPELL_CHECK_ENHANCED_LABEL},
......
...@@ -167,11 +167,83 @@ suite('input page', () => { ...@@ -167,11 +167,83 @@ suite('input page', () => {
}); });
suite('add input methods dialog', () => { suite('add input methods dialog', () => {
test('opens when clicking addInputMethod button', () => { let dialog;
let suggestedInputMethods;
let cancelButton;
let actionButton;
setup(() => {
assertFalse(!!inputPage.$$('os-settings-add-input-methods-dialog')); assertFalse(!!inputPage.$$('os-settings-add-input-methods-dialog'));
inputPage.$$('#addInputMethod').click(); inputPage.$$('#addInputMethod').click();
Polymer.dom.flush(); Polymer.dom.flush();
assertTrue(!!inputPage.$$('os-settings-add-input-methods-dialog'));
dialog = inputPage.$$('os-settings-add-input-methods-dialog');
assertTrue(!!dialog);
actionButton = dialog.$$('.action-button');
assertTrue(!!actionButton);
cancelButton = dialog.$$('.cancel-button');
assertTrue(!!cancelButton);
suggestedInputMethods = dialog.$$('#suggestedInputMethods');
assertTrue(!!suggestedInputMethods);
// No input methods has been selected, so the action button is disabled.
assertTrue(actionButton.disabled);
assertFalse(cancelButton.disabled);
});
test('has action button working correctly', () => {
const listItems = suggestedInputMethods.querySelectorAll('.list-item');
// selecting a language enables action button
listItems[0].click();
assertFalse(actionButton.disabled);
// selecting the same language again disables action button
listItems[0].click();
assertTrue(actionButton.disabled);
});
test('adds input methods', () => {
const listItems = suggestedInputMethods.querySelectorAll('.list-item');
assertEquals(2, listItems.length);
assertEquals('US Swahili keyboard', listItems[0].textContent.trim());
assertEquals('Swahili keyboard', listItems[1].textContent.trim());
// selecting two input methods.
listItems[0].click();
listItems[1].click();
actionButton.click();
assertTrue(languageHelper.isInputMethodEnabled(
'_comp_ime_abcdefghijklmnopqrstuvwxyzabcdefxkb:sw:sw'));
assertTrue(languageHelper.isInputMethodEnabled(
'_comp_ime_abcdefghijklmnopqrstuvwxyzabcdefxkb:us:sw'));
});
test('suggested input methods hidden when no languages is enabled', () => {
languageHelper.setPrefValue('settings.language.preferred_languages', '');
Polymer.dom.flush();
suggestedInputMethods = dialog.$$('#suggestedInputMethods');
// suggested input methods is rendered previously.
assertTrue(!!suggestedInputMethods);
assertEquals('none', getComputedStyle(suggestedInputMethods).display);
});
test('suggested input methods hidden when no input methods left', () => {
const languageCode = 'sw';
languageHelper.setPrefValue(
'settings.language.preferred_languages', languageCode);
languageHelper.getInputMethodsForLanguage(languageCode)
.forEach(inputMethod => {
languageHelper.addInputMethod(inputMethod.id);
});
Polymer.dom.flush();
suggestedInputMethods = dialog.$$('#suggestedInputMethods');
// suggested input methods is rendered previously.
assertTrue(!!suggestedInputMethods);
assertEquals('none', getComputedStyle(suggestedInputMethods).display);
}); });
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment