load keyboard layouts on the server side

this means decoding a fairly large (40k) blob of JSON in the client, but
it seems to be OK.
This commit is contained in:
Michael Hudson-Doyle 2021-03-16 14:26:53 +13:00
parent 697949c1d2
commit 622aec8abf
7 changed files with 64 additions and 85 deletions

View File

@ -15,7 +15,6 @@ subiquity/client/controllers/ssh.py
subiquity/client/controllers/welcome.py
subiquity/client/controllers/zdev.py
subiquity/client/__init__.py
subiquity/client/keyboard.py
subiquity/client/keycodes.py
subiquity/cmd/common.py
subiquity/cmd/__init__.py

View File

@ -17,7 +17,6 @@
import logging
from subiquity.client.controller import SubiquityTuiController
from subiquity.client.keyboard import KeyboardList
from subiquity.common.types import KeyboardSetting
from subiquity.ui.views import KeyboardView
@ -28,29 +27,9 @@ class KeyboardController(SubiquityTuiController):
endpoint_name = 'keyboard'
signals = [
('l10n:language-selected', 'language_selected'),
]
def __init__(self, app):
super().__init__(app)
self.keyboard_list = KeyboardList()
def language_selected(self, code):
log.debug("language_selected %s", code)
if not self.keyboard_list.has_language(code):
code = code.split('_')[0]
if not self.keyboard_list.has_language(code):
code = 'C'
log.debug("loading language %s", code)
self.keyboard_list.load_language(code)
async def make_ui(self):
if self.keyboard_list.current_lang is None:
self.keyboard_list.load_language('C')
initial_setting = await self.endpoint.GET()
view = KeyboardView(self, initial_setting)
return view
setup = await self.endpoint.GET()
return KeyboardView(self, setup)
async def run_answers(self):
if 'layout' in self.answers:

View File

@ -1,52 +0,0 @@
# Copyright 2020 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from subiquity.common.serialize import Serializer
from subiquity.common.types import (
KeyboardLayout,
)
class KeyboardList:
def __init__(self):
self._kbnames_dir = os.path.join(os.environ.get("SNAP", '.'), 'kbds')
self.serializer = Serializer(compact=True)
self._clear()
def _file_for_lang(self, code):
return os.path.join(self._kbnames_dir, code + '.jsonl')
def has_language(self, code):
return os.path.exists(self._file_for_lang(code))
def load_language(self, code):
if code == self.current_lang:
return
self._clear()
with open(self._file_for_lang(code)) as kbdnames:
self.layouts = [
self.serializer.from_json(KeyboardLayout, line)
for line in kbdnames
]
self.current_lang = code
def _clear(self):
self.current_lang = None
self.layouts = []

View File

@ -29,6 +29,7 @@ from subiquity.common.types import (
ApplicationStatus,
ErrorReportRef,
KeyboardSetting,
KeyboardSetup,
IdentityData,
RefreshStatus,
SnapInfo,
@ -91,7 +92,7 @@ class API:
def GET(change_id: str) -> dict: ...
class keyboard:
def GET() -> KeyboardSetting: ...
def GET() -> KeyboardSetup: ...
def POST(data: Payload[KeyboardSetting]): ...
class needs_toggle:

View File

@ -137,6 +137,12 @@ class KeyboardLayout:
variants: List[KeyboardVariant]
@attr.s(auto_attribs=True)
class KeyboardSetup:
setting: KeyboardSetting
layouts: List[KeyboardLayout]
@attr.s(auto_attribs=True)
class ZdevInfo:
id: str

View File

@ -26,7 +26,9 @@ from subiquity.common.apidef import API
from subiquity.common.serialize import Serializer
from subiquity.common.types import (
AnyStep,
KeyboardLayout,
KeyboardSetting,
KeyboardSetup,
)
from subiquity.server.controller import SubiquityController
@ -112,6 +114,44 @@ def for_ui(setting):
layout=layout, variant=variant, toggle=setting.toggle)
class KeyboardList:
def __init__(self):
self._kbnames_dir = os.path.join(os.environ.get("SNAP", '.'), 'kbds')
self.serializer = Serializer(compact=True)
self._clear()
def _file_for_lang(self, code):
return os.path.join(self._kbnames_dir, code + '.jsonl')
def _has_language(self, code):
return os.path.exists(self._file_for_lang(code))
def load_language(self, code):
if '.' in code:
code = code.split('.')[0]
if not self._has_language(code):
code = code.split('_')[0]
if not self._has_language(code):
code = 'C'
if code == self.current_lang:
return
self._clear()
with open(self._file_for_lang(code)) as kbdnames:
self.layouts = [
self.serializer.from_json(KeyboardLayout, line)
for line in kbdnames
]
self.current_lang = code
def _clear(self):
self.current_lang = None
self.layouts = []
class KeyboardController(SubiquityController):
endpoint = API.keyboard
@ -133,6 +173,7 @@ class KeyboardController(SubiquityController):
self.serializer = Serializer(compact=True)
self.pc105_steps = None
self.needs_set_keyboard = False
self.keyboard_list = KeyboardList()
super().__init__(app)
def load_autoinstall_data(self, data):
@ -166,8 +207,12 @@ class KeyboardController(SubiquityController):
for cmd in cmds:
await arun_command(cmd)
async def GET(self) -> KeyboardSetting:
return for_ui(self.model.setting)
async def GET(self) -> KeyboardSetup:
self.keyboard_list.load_language(
self.app.base_model.locale.selected_language)
return KeyboardSetup(
setting=for_ui(self.model.setting),
layouts=self.keyboard_list.layouts)
async def POST(self, data: KeyboardSetting):
new = latinizable(data.layout, data.variant)

View File

@ -360,21 +360,22 @@ class KeyboardView(BaseView):
title = _("Keyboard configuration")
def __init__(self, controller, setting):
def __init__(self, controller, setup):
self.controller = controller
self.keyboard_list = controller.keyboard_list
self.initial_setting = setting
self.initial_setting = setup.setting
self.layouts = setup.layouts
self.form = KeyboardForm()
opts = []
for layout in self.keyboard_list.layouts:
for layout in self.layouts:
opts.append(Option((layout.name, True, layout)))
opts.sort(key=lambda o: locale.strxfrm(o.label.text))
connect_signal(self.form, 'submit', self.done)
connect_signal(self.form, 'cancel', self.cancel)
connect_signal(self.form.layout.widget, "select", self.select_layout)
self.form.layout.widget.options = opts
layout, variant = self.lookup(setting.layout, setting.variant)
layout, variant = self.lookup(
setup.setting.layout, setup.setting.variant)
self.set_values(layout, variant)
if self.controller.opts.run_on_serial:
@ -448,7 +449,7 @@ class KeyboardView(BaseView):
self.form.variant.enabled = len(opts) > 1
def lookup(self, layout_code, variant_code):
for layout in self.keyboard_list.layouts:
for layout in self.layouts:
if layout.code == layout_code:
break
if layout.code == "us":