From 744e53b76b03269120d44d0b8aa068240881deac Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Tue, 16 Mar 2021 12:30:18 +1300 Subject: [PATCH] move checking if a layout needs a toggle to server side subiquity inherits this behavior from d-i where if a the user selects a layout that does not allow typing latin characters, the user is prompted to choose a key to toggle between the one they selected and a related one that does allow latin characters. This change moves the handling of this to the server side, so the client just sees the keyboard layout the user selects, and calls an API method to know if to ask the user about a toggle key. --- subiquity/client/controllers/keyboard.py | 4 ++ subiquity/client/keyboard.py | 86 ----------------------- subiquity/common/apidef.py | 3 + subiquity/server/controllers/keyboard.py | 88 +++++++++++++++++++++++- subiquity/ui/views/keyboard.py | 20 +++--- 5 files changed, 105 insertions(+), 96 deletions(-) diff --git a/subiquity/client/controllers/keyboard.py b/subiquity/client/controllers/keyboard.py index 231173b2..54561ee8 100644 --- a/subiquity/client/controllers/keyboard.py +++ b/subiquity/client/controllers/keyboard.py @@ -68,6 +68,10 @@ class KeyboardController(SubiquityTuiController): async def get_step(self, index): return await self.endpoint.steps.GET(index) + async def needs_toggle(self, setting): + return await self.endpoint.needs_toggle.GET( + layout_code=setting.layout, variant_code=setting.variant) + def done(self, setting, apply): log.debug("KeyboardController.done %s next_screen", setting) if apply: diff --git a/subiquity/client/keyboard.py b/subiquity/client/keyboard.py index 45128cc3..5b3bf99a 100644 --- a/subiquity/client/keyboard.py +++ b/subiquity/client/keyboard.py @@ -18,95 +18,9 @@ import os from subiquity.common.serialize import Serializer from subiquity.common.types import ( KeyboardLayout, - KeyboardSetting, ) -# Non-latin keyboard layouts that are handled in a uniform way -standard_non_latin_layouts = set( - ('af', 'am', 'ara', 'ben', 'bd', 'bg', 'bt', 'by', 'et', 'ge', - 'gh', 'gr', 'guj', 'guru', 'il', 'in', 'iq', 'ir', 'iku', 'kan', - 'kh', 'kz', 'la', 'lao', 'lk', 'kg', 'ma', 'mk', 'mm', 'mn', 'mv', - 'mal', 'np', 'ori', 'pk', 'ru', 'scc', 'sy', 'syr', 'tel', 'th', - 'tj', 'tam', 'tib', 'ua', 'ug', 'uz') -) - - -def latinizable(setting): - """ - If this setting does not allow the typing of latin characters, - return a setting that can be switched to one that can. - """ - if setting.layout == 'rs': - if setting.variant.startswith('latin'): - return setting - else: - if setting.variant == 'yz': - new_variant = 'latinyz' - elif setting.variant == 'alternatequotes': - new_variant = 'latinalternatequotes' - else: - new_variant = 'latin' - return KeyboardSetting(layout='rs,rs', - variant=(new_variant + - ',' + setting.variant)) - elif setting.layout == 'jp': - if setting.variant in ('106', 'common', 'OADG109A', - 'nicola_f_bs', ''): - return setting - else: - return KeyboardSetting(layout='jp,jp', - variant=',' + setting.variant) - elif setting.layout == 'lt': - if setting.variant == 'us': - return KeyboardSetting(layout='lt,lt', variant='us,') - else: - return KeyboardSetting(layout='lt,lt', - variant=setting.variant + ',us') - elif setting.layout == 'me': - if setting.variant == 'basic' or setting.variant.startswith('latin'): - return setting - else: - return KeyboardSetting(layout='me,me', - variant=setting.variant + ',us') - elif setting.layout in standard_non_latin_layouts: - return KeyboardSetting(layout='us,' + setting.layout, - variant=',' + setting.variant) - else: - return setting - - -def for_ui(setting): - """ - Attempt to guess a setting the user chose which resulted in the - current config. Basically the inverse of latinizable(). - """ - if ',' in setting.layout: - layout1, layout2 = setting.layout.split(',', 1) - else: - layout1, layout2 = setting.layout, '' - if ',' in setting.variant: - variant1, variant2 = setting.variant.split(',', 1) - else: - variant1, variant2 = setting.variant, '' - if setting.layout == 'lt,lt': - layout = layout1 - variant = variant1 - elif setting.layout in ('rs,rs', 'us,rs', 'jp,jp', 'us,jp'): - layout = layout2 - variant = variant2 - elif layout1 == 'us' and layout2 in standard_non_latin_layouts: - layout = layout2 - variant = variant2 - elif ',' in setting.layout: - # Something unrecognized - layout = 'us' - variant = '' - else: - return setting - return KeyboardSetting(layout=layout, variant=variant) - - class KeyboardList: def __init__(self): diff --git a/subiquity/common/apidef.py b/subiquity/common/apidef.py index e2bea25b..18d51ec0 100644 --- a/subiquity/common/apidef.py +++ b/subiquity/common/apidef.py @@ -94,6 +94,9 @@ class API: def GET() -> KeyboardSetting: ... def POST(data: Payload[KeyboardSetting]): ... + class needs_toggle: + def GET(layout_code: str, variant_code: str) -> bool: ... + class steps: def GET(index: Optional[str]) -> AnyStep: ... diff --git a/subiquity/server/controllers/keyboard.py b/subiquity/server/controllers/keyboard.py index 5b133881..85922ab9 100644 --- a/subiquity/server/controllers/keyboard.py +++ b/subiquity/server/controllers/keyboard.py @@ -33,6 +33,85 @@ from subiquity.server.controller import SubiquityController log = logging.getLogger('subiquity.server.controllers.keyboard') +# Non-latin keyboard layouts that are handled in a uniform way +standard_non_latin_layouts = set( + ('af', 'am', 'ara', 'ben', 'bd', 'bg', 'bt', 'by', 'et', 'ge', + 'gh', 'gr', 'guj', 'guru', 'il', 'in', 'iq', 'ir', 'iku', 'kan', + 'kh', 'kz', 'la', 'lao', 'lk', 'kg', 'ma', 'mk', 'mm', 'mn', 'mv', + 'mal', 'np', 'ori', 'pk', 'ru', 'scc', 'sy', 'syr', 'tel', 'th', + 'tj', 'tam', 'tib', 'ua', 'ug', 'uz') +) + + +def latinizable(layout_code, variant_code): + """ + If this setting does not allow the typing of latin characters, + return a setting that can be switched to one that can. + """ + if layout_code == 'rs': + if variant_code.startswith('latin'): + return None + else: + if variant_code == 'yz': + new_variant_code = 'latinyz' + elif variant_code == 'alternatequotes': + new_variant_code = 'latinalternatequotes' + else: + new_variant_code = 'latin' + return 'rs,rs', new_variant_code + ',' + variant_code + elif layout_code == 'jp': + if variant_code in ('106', 'common', 'OADG109A', 'nicola_f_bs', ''): + return None + else: + return 'jp,jp', ',' + variant_code + elif layout_code == 'lt': + if variant_code == 'us': + return 'lt,lt', 'us,' + else: + return 'lt,lt', variant_code + ',us' + elif layout_code == 'me': + if variant_code == 'basic' or variant_code.startswith('latin'): + return None + else: + return 'me,me', variant_code + ',us' + elif layout_code in standard_non_latin_layouts: + return 'us,' + layout_code, ',' + variant_code + else: + return None + + +def for_ui(setting): + """ + Attempt to guess a setting the user chose which resulted in the + current config. Basically the inverse of latinizable(). + """ + if ',' in setting.layout: + layout1, layout2 = setting.layout.split(',', 1) + else: + layout1, layout2 = setting.layout, '' + if ',' in setting.variant: + variant1, variant2 = setting.variant.split(',', 1) + else: + variant1, variant2 = setting.variant, '' + if setting.layout == 'lt,lt': + layout = layout1 + variant = variant1 + elif setting.layout in ('rs,rs', 'us,rs', 'jp,jp', 'us,jp'): + layout = layout2 + variant = variant2 + elif layout1 == 'us' and layout2 in standard_non_latin_layouts: + layout = layout2 + variant = variant2 + elif ',' in setting.layout: + # Something unrecognized + layout = 'us' + variant = '' + else: + return setting + return KeyboardSetting( + layout=layout, variant=variant, toggle=setting.toggle) + + class KeyboardController(SubiquityController): endpoint = API.keyboard @@ -74,12 +153,19 @@ class KeyboardController(SubiquityController): return attr.asdict(self.model.setting) async def GET(self) -> KeyboardSetting: - return self.model.setting + return for_ui(self.model.setting) async def POST(self, data: KeyboardSetting): + new = latinizable(data.layout, data.variant) + if new is not None: + data = KeyboardSetting(new[0], new[1], data.toggle) self.model.setting = data self.configured() + async def needs_toggle_GET(self, layout_code: str, + variant_code: str) -> bool: + return latinizable(layout_code, variant_code) is not None + async def steps_GET(self, index: Optional[str]) -> AnyStep: if self.pc105_steps is None: path = os.path.join(self._kbds_dir, 'pc105.json') diff --git a/subiquity/ui/views/keyboard.py b/subiquity/ui/views/keyboard.py index 0998bf0b..a9ea6b8d 100644 --- a/subiquity/ui/views/keyboard.py +++ b/subiquity/ui/views/keyboard.py @@ -45,7 +45,6 @@ from subiquitycore.ui.stretchy import ( from subiquitycore.ui.utils import button_pile, Color, Padding, screen from subiquitycore.view import BaseView -from subiquity.client.keyboard import for_ui, latinizable from subiquity.common.types import ( KeyboardSetting, StepKeyPresent, @@ -378,10 +377,10 @@ class KeyboardView(BaseView): title = _("Keyboard configuration") - def __init__(self, controller, initial_setting): + def __init__(self, controller, setting): self.controller = controller self.keyboard_list = controller.keyboard_list - self.initial_setting = initial_setting + self.initial_setting = setting self.form = KeyboardForm() opts = [] @@ -392,7 +391,6 @@ class KeyboardView(BaseView): connect_signal(self.form, 'cancel', self.cancel) connect_signal(self.form.layout.widget, "select", self.select_layout) self.form.layout.widget.options = opts - setting = for_ui(initial_setting) layout, variant = self.lookup(setting.layout, setting.variant) self.set_values(layout, variant) @@ -427,16 +425,20 @@ class KeyboardView(BaseView): self.set_values(layout, variant) self._w.base_widget.focus_position = 4 + async def _check_toggle(self, setting): + needs_toggle = await self.controller.app.wait_with_text_dialog( + self.controller.needs_toggle(setting), "...") + if needs_toggle: + self.show_stretchy_overlay(ToggleQuestion(self, setting)) + else: + self.really_done(setting) + def done(self, result): data = result.as_data() layout = data['layout'] variant = data.get('variant', layout.variants[0]) setting = KeyboardSetting(layout=layout.code, variant=variant.code) - other = latinizable(setting) - if setting != other: - self.show_stretchy_overlay(ToggleQuestion(self, other)) - else: - self.really_done(setting) + self.controller.app.aio_loop.create_task(self._check_toggle(setting)) def really_done(self, setting): apply = False