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:
parent
697949c1d2
commit
622aec8abf
|
@ -15,7 +15,6 @@ subiquity/client/controllers/ssh.py
|
||||||
subiquity/client/controllers/welcome.py
|
subiquity/client/controllers/welcome.py
|
||||||
subiquity/client/controllers/zdev.py
|
subiquity/client/controllers/zdev.py
|
||||||
subiquity/client/__init__.py
|
subiquity/client/__init__.py
|
||||||
subiquity/client/keyboard.py
|
|
||||||
subiquity/client/keycodes.py
|
subiquity/client/keycodes.py
|
||||||
subiquity/cmd/common.py
|
subiquity/cmd/common.py
|
||||||
subiquity/cmd/__init__.py
|
subiquity/cmd/__init__.py
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from subiquity.client.controller import SubiquityTuiController
|
from subiquity.client.controller import SubiquityTuiController
|
||||||
from subiquity.client.keyboard import KeyboardList
|
|
||||||
from subiquity.common.types import KeyboardSetting
|
from subiquity.common.types import KeyboardSetting
|
||||||
from subiquity.ui.views import KeyboardView
|
from subiquity.ui.views import KeyboardView
|
||||||
|
|
||||||
|
@ -28,29 +27,9 @@ class KeyboardController(SubiquityTuiController):
|
||||||
|
|
||||||
endpoint_name = 'keyboard'
|
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):
|
async def make_ui(self):
|
||||||
if self.keyboard_list.current_lang is None:
|
setup = await self.endpoint.GET()
|
||||||
self.keyboard_list.load_language('C')
|
return KeyboardView(self, setup)
|
||||||
initial_setting = await self.endpoint.GET()
|
|
||||||
view = KeyboardView(self, initial_setting)
|
|
||||||
return view
|
|
||||||
|
|
||||||
async def run_answers(self):
|
async def run_answers(self):
|
||||||
if 'layout' in self.answers:
|
if 'layout' in self.answers:
|
||||||
|
|
|
@ -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 = []
|
|
|
@ -29,6 +29,7 @@ from subiquity.common.types import (
|
||||||
ApplicationStatus,
|
ApplicationStatus,
|
||||||
ErrorReportRef,
|
ErrorReportRef,
|
||||||
KeyboardSetting,
|
KeyboardSetting,
|
||||||
|
KeyboardSetup,
|
||||||
IdentityData,
|
IdentityData,
|
||||||
RefreshStatus,
|
RefreshStatus,
|
||||||
SnapInfo,
|
SnapInfo,
|
||||||
|
@ -91,7 +92,7 @@ class API:
|
||||||
def GET(change_id: str) -> dict: ...
|
def GET(change_id: str) -> dict: ...
|
||||||
|
|
||||||
class keyboard:
|
class keyboard:
|
||||||
def GET() -> KeyboardSetting: ...
|
def GET() -> KeyboardSetup: ...
|
||||||
def POST(data: Payload[KeyboardSetting]): ...
|
def POST(data: Payload[KeyboardSetting]): ...
|
||||||
|
|
||||||
class needs_toggle:
|
class needs_toggle:
|
||||||
|
|
|
@ -137,6 +137,12 @@ class KeyboardLayout:
|
||||||
variants: List[KeyboardVariant]
|
variants: List[KeyboardVariant]
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(auto_attribs=True)
|
||||||
|
class KeyboardSetup:
|
||||||
|
setting: KeyboardSetting
|
||||||
|
layouts: List[KeyboardLayout]
|
||||||
|
|
||||||
|
|
||||||
@attr.s(auto_attribs=True)
|
@attr.s(auto_attribs=True)
|
||||||
class ZdevInfo:
|
class ZdevInfo:
|
||||||
id: str
|
id: str
|
||||||
|
|
|
@ -26,7 +26,9 @@ from subiquity.common.apidef import API
|
||||||
from subiquity.common.serialize import Serializer
|
from subiquity.common.serialize import Serializer
|
||||||
from subiquity.common.types import (
|
from subiquity.common.types import (
|
||||||
AnyStep,
|
AnyStep,
|
||||||
|
KeyboardLayout,
|
||||||
KeyboardSetting,
|
KeyboardSetting,
|
||||||
|
KeyboardSetup,
|
||||||
)
|
)
|
||||||
from subiquity.server.controller import SubiquityController
|
from subiquity.server.controller import SubiquityController
|
||||||
|
|
||||||
|
@ -112,6 +114,44 @@ def for_ui(setting):
|
||||||
layout=layout, variant=variant, toggle=setting.toggle)
|
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):
|
class KeyboardController(SubiquityController):
|
||||||
|
|
||||||
endpoint = API.keyboard
|
endpoint = API.keyboard
|
||||||
|
@ -133,6 +173,7 @@ class KeyboardController(SubiquityController):
|
||||||
self.serializer = Serializer(compact=True)
|
self.serializer = Serializer(compact=True)
|
||||||
self.pc105_steps = None
|
self.pc105_steps = None
|
||||||
self.needs_set_keyboard = False
|
self.needs_set_keyboard = False
|
||||||
|
self.keyboard_list = KeyboardList()
|
||||||
super().__init__(app)
|
super().__init__(app)
|
||||||
|
|
||||||
def load_autoinstall_data(self, data):
|
def load_autoinstall_data(self, data):
|
||||||
|
@ -166,8 +207,12 @@ class KeyboardController(SubiquityController):
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
await arun_command(cmd)
|
await arun_command(cmd)
|
||||||
|
|
||||||
async def GET(self) -> KeyboardSetting:
|
async def GET(self) -> KeyboardSetup:
|
||||||
return for_ui(self.model.setting)
|
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):
|
async def POST(self, data: KeyboardSetting):
|
||||||
new = latinizable(data.layout, data.variant)
|
new = latinizable(data.layout, data.variant)
|
||||||
|
|
|
@ -360,21 +360,22 @@ class KeyboardView(BaseView):
|
||||||
|
|
||||||
title = _("Keyboard configuration")
|
title = _("Keyboard configuration")
|
||||||
|
|
||||||
def __init__(self, controller, setting):
|
def __init__(self, controller, setup):
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.keyboard_list = controller.keyboard_list
|
self.initial_setting = setup.setting
|
||||||
self.initial_setting = setting
|
self.layouts = setup.layouts
|
||||||
|
|
||||||
self.form = KeyboardForm()
|
self.form = KeyboardForm()
|
||||||
opts = []
|
opts = []
|
||||||
for layout in self.keyboard_list.layouts:
|
for layout in self.layouts:
|
||||||
opts.append(Option((layout.name, True, layout)))
|
opts.append(Option((layout.name, True, layout)))
|
||||||
opts.sort(key=lambda o: locale.strxfrm(o.label.text))
|
opts.sort(key=lambda o: locale.strxfrm(o.label.text))
|
||||||
connect_signal(self.form, 'submit', self.done)
|
connect_signal(self.form, 'submit', self.done)
|
||||||
connect_signal(self.form, 'cancel', self.cancel)
|
connect_signal(self.form, 'cancel', self.cancel)
|
||||||
connect_signal(self.form.layout.widget, "select", self.select_layout)
|
connect_signal(self.form.layout.widget, "select", self.select_layout)
|
||||||
self.form.layout.widget.options = opts
|
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)
|
self.set_values(layout, variant)
|
||||||
|
|
||||||
if self.controller.opts.run_on_serial:
|
if self.controller.opts.run_on_serial:
|
||||||
|
@ -448,7 +449,7 @@ class KeyboardView(BaseView):
|
||||||
self.form.variant.enabled = len(opts) > 1
|
self.form.variant.enabled = len(opts) > 1
|
||||||
|
|
||||||
def lookup(self, layout_code, variant_code):
|
def lookup(self, layout_code, variant_code):
|
||||||
for layout in self.keyboard_list.layouts:
|
for layout in self.layouts:
|
||||||
if layout.code == layout_code:
|
if layout.code == layout_code:
|
||||||
break
|
break
|
||||||
if layout.code == "us":
|
if layout.code == "us":
|
||||||
|
|
Loading…
Reference in New Issue