mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-02-06 07:23:37 +08:00
Merge pull request #141 from Dliwk/meta-tag-keyboard
New meta-tag '# ba_meta export keyboard'
This commit is contained in:
commit
c098087521
7
.idea/dictionaries/roman.xml
generated
Normal file
7
.idea/dictionaries/roman.xml
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="roman">
|
||||||
|
<words>
|
||||||
|
<w>maxlen</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
@ -1,4 +1,5 @@
|
|||||||
[
|
[
|
||||||
|
"ba_data/python/__pycache__/keyboards.cpython-38.opt-1.pyc",
|
||||||
"ba_data/python/ba/__init__.py",
|
"ba_data/python/ba/__init__.py",
|
||||||
"ba_data/python/ba/__pycache__/__init__.cpython-38.opt-1.pyc",
|
"ba_data/python/ba/__pycache__/__init__.cpython-38.opt-1.pyc",
|
||||||
"ba_data/python/ba/__pycache__/_account.cpython-38.opt-1.pyc",
|
"ba_data/python/ba/__pycache__/_account.cpython-38.opt-1.pyc",
|
||||||
@ -28,6 +29,7 @@
|
|||||||
"ba_data/python/ba/__pycache__/_general.cpython-38.opt-1.pyc",
|
"ba_data/python/ba/__pycache__/_general.cpython-38.opt-1.pyc",
|
||||||
"ba_data/python/ba/__pycache__/_hooks.cpython-38.opt-1.pyc",
|
"ba_data/python/ba/__pycache__/_hooks.cpython-38.opt-1.pyc",
|
||||||
"ba_data/python/ba/__pycache__/_input.cpython-38.opt-1.pyc",
|
"ba_data/python/ba/__pycache__/_input.cpython-38.opt-1.pyc",
|
||||||
|
"ba_data/python/ba/__pycache__/_keyboard.cpython-38.opt-1.pyc",
|
||||||
"ba_data/python/ba/__pycache__/_lang.cpython-38.opt-1.pyc",
|
"ba_data/python/ba/__pycache__/_lang.cpython-38.opt-1.pyc",
|
||||||
"ba_data/python/ba/__pycache__/_level.cpython-38.opt-1.pyc",
|
"ba_data/python/ba/__pycache__/_level.cpython-38.opt-1.pyc",
|
||||||
"ba_data/python/ba/__pycache__/_lobby.cpython-38.opt-1.pyc",
|
"ba_data/python/ba/__pycache__/_lobby.cpython-38.opt-1.pyc",
|
||||||
@ -87,6 +89,7 @@
|
|||||||
"ba_data/python/ba/_general.py",
|
"ba_data/python/ba/_general.py",
|
||||||
"ba_data/python/ba/_hooks.py",
|
"ba_data/python/ba/_hooks.py",
|
||||||
"ba_data/python/ba/_input.py",
|
"ba_data/python/ba/_input.py",
|
||||||
|
"ba_data/python/ba/_keyboard.py",
|
||||||
"ba_data/python/ba/_lang.py",
|
"ba_data/python/ba/_lang.py",
|
||||||
"ba_data/python/ba/_level.py",
|
"ba_data/python/ba/_level.py",
|
||||||
"ba_data/python/ba/_lobby.py",
|
"ba_data/python/ba/_lobby.py",
|
||||||
@ -497,6 +500,7 @@
|
|||||||
"ba_data/python/efro/json.py",
|
"ba_data/python/efro/json.py",
|
||||||
"ba_data/python/efro/terminal.py",
|
"ba_data/python/efro/terminal.py",
|
||||||
"ba_data/python/efro/util.py",
|
"ba_data/python/efro/util.py",
|
||||||
|
"ba_data/python/keyboards.py",
|
||||||
"server/__pycache__/ballisticacore_server.cpython-38.opt-1.pyc",
|
"server/__pycache__/ballisticacore_server.cpython-38.opt-1.pyc",
|
||||||
"server/ballisticacore_server.py"
|
"server/ballisticacore_server.py"
|
||||||
]
|
]
|
||||||
@ -177,6 +177,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
|||||||
build/ba_data/python/ba/_general.py \
|
build/ba_data/python/ba/_general.py \
|
||||||
build/ba_data/python/ba/_hooks.py \
|
build/ba_data/python/ba/_hooks.py \
|
||||||
build/ba_data/python/ba/_input.py \
|
build/ba_data/python/ba/_input.py \
|
||||||
|
build/ba_data/python/ba/_keyboard.py \
|
||||||
build/ba_data/python/ba/_lang.py \
|
build/ba_data/python/ba/_lang.py \
|
||||||
build/ba_data/python/ba/_level.py \
|
build/ba_data/python/ba/_level.py \
|
||||||
build/ba_data/python/ba/_lobby.py \
|
build/ba_data/python/ba/_lobby.py \
|
||||||
@ -380,6 +381,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
|||||||
build/ba_data/python/bastd/ui/trophies.py \
|
build/ba_data/python/bastd/ui/trophies.py \
|
||||||
build/ba_data/python/bastd/ui/url.py \
|
build/ba_data/python/bastd/ui/url.py \
|
||||||
build/ba_data/python/bastd/ui/watch.py \
|
build/ba_data/python/bastd/ui/watch.py \
|
||||||
|
build/ba_data/python/keyboards.py \
|
||||||
build/server/ballisticacore_server.py
|
build/server/ballisticacore_server.py
|
||||||
|
|
||||||
SCRIPT_TARGETS_PYC_PUBLIC = \
|
SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||||
@ -411,6 +413,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
|||||||
build/ba_data/python/ba/__pycache__/_general.cpython-38.opt-1.pyc \
|
build/ba_data/python/ba/__pycache__/_general.cpython-38.opt-1.pyc \
|
||||||
build/ba_data/python/ba/__pycache__/_hooks.cpython-38.opt-1.pyc \
|
build/ba_data/python/ba/__pycache__/_hooks.cpython-38.opt-1.pyc \
|
||||||
build/ba_data/python/ba/__pycache__/_input.cpython-38.opt-1.pyc \
|
build/ba_data/python/ba/__pycache__/_input.cpython-38.opt-1.pyc \
|
||||||
|
build/ba_data/python/ba/__pycache__/_keyboard.cpython-38.opt-1.pyc \
|
||||||
build/ba_data/python/ba/__pycache__/_lang.cpython-38.opt-1.pyc \
|
build/ba_data/python/ba/__pycache__/_lang.cpython-38.opt-1.pyc \
|
||||||
build/ba_data/python/ba/__pycache__/_level.cpython-38.opt-1.pyc \
|
build/ba_data/python/ba/__pycache__/_level.cpython-38.opt-1.pyc \
|
||||||
build/ba_data/python/ba/__pycache__/_lobby.cpython-38.opt-1.pyc \
|
build/ba_data/python/ba/__pycache__/_lobby.cpython-38.opt-1.pyc \
|
||||||
@ -614,6 +617,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
|||||||
build/ba_data/python/bastd/ui/__pycache__/trophies.cpython-38.opt-1.pyc \
|
build/ba_data/python/bastd/ui/__pycache__/trophies.cpython-38.opt-1.pyc \
|
||||||
build/ba_data/python/bastd/ui/__pycache__/url.cpython-38.opt-1.pyc \
|
build/ba_data/python/bastd/ui/__pycache__/url.cpython-38.opt-1.pyc \
|
||||||
build/ba_data/python/bastd/ui/__pycache__/watch.cpython-38.opt-1.pyc \
|
build/ba_data/python/bastd/ui/__pycache__/watch.cpython-38.opt-1.pyc \
|
||||||
|
build/ba_data/python/__pycache__/keyboards.cpython-38.opt-1.pyc \
|
||||||
build/server/__pycache__/ballisticacore_server.cpython-38.opt-1.pyc
|
build/server/__pycache__/ballisticacore_server.cpython-38.opt-1.pyc
|
||||||
|
|
||||||
# Rule to copy src asset scripts to dst.
|
# Rule to copy src asset scripts to dst.
|
||||||
|
|||||||
@ -78,6 +78,7 @@ from ba._gameutils import (GameTip, animate, animate_array, show_damage_count,
|
|||||||
timestring, cameraflash)
|
timestring, cameraflash)
|
||||||
from ba._general import (WeakCall, Call, existing, Existable,
|
from ba._general import (WeakCall, Call, existing, Existable,
|
||||||
verify_object_death, storagename)
|
verify_object_death, storagename)
|
||||||
|
from ba._keyboard import Keyboard
|
||||||
from ba._level import Level
|
from ba._level import Level
|
||||||
from ba._lobby import Lobby, Chooser
|
from ba._lobby import Lobby, Chooser
|
||||||
from ba._math import normalized_color, is_point_in_box, vec3validate
|
from ba._math import normalized_color, is_point_in_box, vec3validate
|
||||||
|
|||||||
53
assets/src/ba_data/python/ba/_keyboard.py
Normal file
53
assets/src/ba_data/python/ba/_keyboard.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Copyright (c) 2011-2020 Eric Froemling
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
"""On-screen Keyboard related functionality."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import List, Tuple, Dict
|
||||||
|
|
||||||
|
|
||||||
|
class Keyboard:
|
||||||
|
"""Chars definitions for on-screen keyboard.
|
||||||
|
|
||||||
|
Category: App Classes
|
||||||
|
|
||||||
|
Keyboards are discoverable by the meta-tag system
|
||||||
|
and the user can select which one they want to use.
|
||||||
|
On-screen keyboard uses chars from active ba.Keyboard.
|
||||||
|
Attributes:
|
||||||
|
name
|
||||||
|
Displays when user selecting this keyboard.
|
||||||
|
chars
|
||||||
|
Used for row/column lengths.
|
||||||
|
pages
|
||||||
|
Extra chars like emojis.
|
||||||
|
nums
|
||||||
|
The 'num' page.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
chars: List[Tuple[str, ...]]
|
||||||
|
pages: Dict[str, Tuple[str, ...]]
|
||||||
|
nums: Tuple[str, ...]
|
||||||
@ -46,6 +46,7 @@ class ScanResults:
|
|||||||
"""Final results from a metadata scan."""
|
"""Final results from a metadata scan."""
|
||||||
games: List[str] = field(default_factory=list)
|
games: List[str] = field(default_factory=list)
|
||||||
plugins: List[str] = field(default_factory=list)
|
plugins: List[str] = field(default_factory=list)
|
||||||
|
keyboards: List[str] = field(default_factory=list)
|
||||||
errors: str = ''
|
errors: str = ''
|
||||||
warnings: str = ''
|
warnings: str = ''
|
||||||
|
|
||||||
@ -290,6 +291,8 @@ class DirectoryScan:
|
|||||||
self.results.games.append(classname)
|
self.results.games.append(classname)
|
||||||
elif exporttype == 'plugin':
|
elif exporttype == 'plugin':
|
||||||
self.results.plugins.append(classname)
|
self.results.plugins.append(classname)
|
||||||
|
elif exporttype == 'keyboard':
|
||||||
|
self.results.keyboards.append(classname)
|
||||||
else:
|
else:
|
||||||
self.results.warnings += (
|
self.results.warnings += (
|
||||||
'Warning: ' + str(subpath) +
|
'Warning: ' + str(subpath) +
|
||||||
|
|||||||
@ -37,8 +37,6 @@ class OnScreenKeyboardWindow(ba.Window):
|
|||||||
"""Simple built-in on-screen keyboard."""
|
"""Simple built-in on-screen keyboard."""
|
||||||
|
|
||||||
def __init__(self, textwidget: ba.Widget, label: str, max_chars: int):
|
def __init__(self, textwidget: ba.Widget, label: str, max_chars: int):
|
||||||
# pylint: disable=too-many-locals
|
|
||||||
# pylint: disable=too-many-statements
|
|
||||||
self._target_text = textwidget
|
self._target_text = textwidget
|
||||||
self._width = 700
|
self._width = 700
|
||||||
self._height = 400
|
self._height = 400
|
||||||
@ -88,22 +86,44 @@ class OnScreenKeyboardWindow(ba.Window):
|
|||||||
force_internal_editing=True,
|
force_internal_editing=True,
|
||||||
always_show_carat=True)
|
always_show_carat=True)
|
||||||
|
|
||||||
self._shift_button = None
|
|
||||||
self._double_press_shift = False
|
|
||||||
self._num_mode_button = None
|
|
||||||
self._emoji_button = None
|
|
||||||
self._char_keys: List[ba.Widget] = []
|
|
||||||
self._mode = 'normal'
|
|
||||||
self._last_mode = 'normal'
|
|
||||||
|
|
||||||
v = self._height - 180
|
|
||||||
key_width = 46
|
|
||||||
key_height = 46
|
|
||||||
self._key_color_lit = (1.4, 1.2, 1.4)
|
self._key_color_lit = (1.4, 1.2, 1.4)
|
||||||
self._key_color = key_color = (0.69, 0.6, 0.74)
|
self._key_color = (0.69, 0.6, 0.74)
|
||||||
self._key_color_dark = key_color_dark = (0.55, 0.55, 0.71)
|
self._key_color_dark = (0.55, 0.55, 0.71)
|
||||||
|
|
||||||
|
self._shift_button: Optional[ba.Widget] = None
|
||||||
|
self._backspace_button: Optional[ba.Widget] = None
|
||||||
|
self._space_button: Optional[ba.Widget] = None
|
||||||
|
self._double_press_shift = False
|
||||||
|
self._num_mode_button: Optional[ba.Widget] = None
|
||||||
|
self._emoji_button: Optional[ba.Widget] = None
|
||||||
|
self._char_keys: List[ba.Widget] = []
|
||||||
|
self._keyboard_index = 0
|
||||||
|
self._last_space_press = 0.0
|
||||||
|
self._double_space_interval = 0.3
|
||||||
|
|
||||||
|
self._keyboard: ba.Keyboard
|
||||||
|
self._chars: List[str]
|
||||||
|
self._modes: List[str]
|
||||||
|
self._mode: str
|
||||||
|
self._mode_index: int
|
||||||
|
self._load_keyboard()
|
||||||
|
|
||||||
|
def _load_keyboard(self) -> None:
|
||||||
|
# pylint: disable=too-many-locals
|
||||||
|
self._keyboard = self._get_keyboard()
|
||||||
|
# We want to get just chars without column data, etc.
|
||||||
|
self._chars = [j for i in self._keyboard.chars for j in i]
|
||||||
|
self._modes = ['normal'] + list(self._keyboard.pages)
|
||||||
|
self._mode_index = 0
|
||||||
|
self._mode = self._modes[self._mode_index]
|
||||||
|
|
||||||
|
v = self._height - 180.0
|
||||||
|
key_width = 46 * 10 / len(self._keyboard.chars[0])
|
||||||
|
key_height = 46 * 3 / len(self._keyboard.chars)
|
||||||
key_textcolor = (1, 1, 1)
|
key_textcolor = (1, 1, 1)
|
||||||
row_starts = (69, 95, 151)
|
row_starts = (69.0, 95.0, 151.0)
|
||||||
|
key_color = self._key_color
|
||||||
|
key_color_dark = self._key_color_dark
|
||||||
|
|
||||||
self._click_sound = ba.getsound('click01')
|
self._click_sound = ba.getsound('click01')
|
||||||
|
|
||||||
@ -114,16 +134,12 @@ class OnScreenKeyboardWindow(ba.Window):
|
|||||||
|
|
||||||
# dummy data just used for row/column lengths... we don't actually
|
# dummy data just used for row/column lengths... we don't actually
|
||||||
# set things until refresh
|
# set things until refresh
|
||||||
chars: List[Tuple[str, ...]] = [
|
chars: List[Tuple[str, ...]] = self._keyboard.chars
|
||||||
('q', 'u', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'),
|
|
||||||
('a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'),
|
|
||||||
('z', 'x', 'c', 'v', 'b', 'n', 'm')
|
|
||||||
]
|
|
||||||
|
|
||||||
for row_num, row in enumerate(chars):
|
for row_num, row in enumerate(chars):
|
||||||
h = row_starts[row_num]
|
h = row_starts[row_num]
|
||||||
# shift key before row 3
|
# shift key before row 3
|
||||||
if row_num == 2:
|
if row_num == 2 and self._shift_button is None:
|
||||||
self._shift_button = ba.buttonwidget(
|
self._shift_button = ba.buttonwidget(
|
||||||
parent=self._root_widget,
|
parent=self._root_widget,
|
||||||
position=(h - key_width * 2.0, v),
|
position=(h - key_width * 2.0, v),
|
||||||
@ -155,17 +171,21 @@ class OnScreenKeyboardWindow(ba.Window):
|
|||||||
|
|
||||||
# Add delete key at end of third row.
|
# Add delete key at end of third row.
|
||||||
if row_num == 2:
|
if row_num == 2:
|
||||||
ba.buttonwidget(parent=self._root_widget,
|
if self._backspace_button is not None:
|
||||||
position=(h + 4, v),
|
self._backspace_button.delete()
|
||||||
size=(key_width * 1.8, key_height),
|
|
||||||
autoselect=True,
|
self._backspace_button = ba.buttonwidget(
|
||||||
enable_sound=False,
|
parent=self._root_widget,
|
||||||
repeat=True,
|
position=(h + 4, v),
|
||||||
textcolor=key_textcolor,
|
size=(key_width * 1.8, key_height),
|
||||||
color=key_color_dark,
|
autoselect=True,
|
||||||
label=charstr(SpCh.DELETE),
|
enable_sound=False,
|
||||||
button_type='square',
|
repeat=True,
|
||||||
on_activate_call=self._del)
|
textcolor=key_textcolor,
|
||||||
|
color=key_color_dark,
|
||||||
|
label=charstr(SpCh.DELETE),
|
||||||
|
button_type='square',
|
||||||
|
on_activate_call=self._del)
|
||||||
v -= (key_height + 9)
|
v -= (key_height + 9)
|
||||||
# Do space bar and stuff.
|
# Do space bar and stuff.
|
||||||
if row_num == 2:
|
if row_num == 2:
|
||||||
@ -196,17 +216,25 @@ class OnScreenKeyboardWindow(ba.Window):
|
|||||||
button_type='square',
|
button_type='square',
|
||||||
)
|
)
|
||||||
btn1 = self._num_mode_button
|
btn1 = self._num_mode_button
|
||||||
btn2 = ba.buttonwidget(parent=self._root_widget,
|
if self._space_button is None:
|
||||||
position=(210, v - 12),
|
self._space_button = ba.buttonwidget(
|
||||||
size=(key_width * 6.1, key_height + 15),
|
parent=self._root_widget,
|
||||||
extra_touch_border_scale=0.3,
|
position=(210, v - 12),
|
||||||
enable_sound=False,
|
size=(key_width * 6.1, key_height + 15),
|
||||||
autoselect=True,
|
extra_touch_border_scale=0.3,
|
||||||
textcolor=key_textcolor,
|
enable_sound=False,
|
||||||
color=key_color_dark,
|
autoselect=True,
|
||||||
label=ba.Lstr(resource='spaceKeyText'),
|
textcolor=key_textcolor,
|
||||||
on_activate_call=ba.Call(
|
color=key_color_dark,
|
||||||
self._type_char, ' '))
|
label=ba.Lstr(resource='spaceKeyText'),
|
||||||
|
on_activate_call=ba.Call(self._type_char, ' '))
|
||||||
|
ba.textwidget(parent=self._root_widget,
|
||||||
|
h_align='center',
|
||||||
|
position=(210, v - 70),
|
||||||
|
size=(key_width * 6.1, key_height + 15),
|
||||||
|
text='Double press space to change keyboard',
|
||||||
|
scale=0.75)
|
||||||
|
btn2 = self._space_button
|
||||||
btn3 = self._emoji_button
|
btn3 = self._emoji_button
|
||||||
ba.widget(edit=btn1, right_widget=btn2, left_widget=btn3)
|
ba.widget(edit=btn1, right_widget=btn2, left_widget=btn3)
|
||||||
ba.widget(edit=btn2,
|
ba.widget(edit=btn2,
|
||||||
@ -220,14 +248,19 @@ class OnScreenKeyboardWindow(ba.Window):
|
|||||||
|
|
||||||
self._refresh()
|
self._refresh()
|
||||||
|
|
||||||
|
def _get_keyboard(self) -> ba.Keyboard:
|
||||||
|
assert ba.app.metascan is not None
|
||||||
|
path = ba.app.metascan.keyboards[self._keyboard_index]
|
||||||
|
classname = path.split('.')[-1]
|
||||||
|
module = path[:-len(classname) - 1]
|
||||||
|
keyboard = getattr(__import__(module), classname)()
|
||||||
|
assert isinstance(keyboard, ba.Keyboard)
|
||||||
|
return keyboard
|
||||||
|
|
||||||
def _refresh(self) -> None:
|
def _refresh(self) -> None:
|
||||||
chars: Optional[List[str]] = None
|
chars: Optional[List[str]] = None
|
||||||
if self._mode in ['normal', 'caps']:
|
if self._mode in ['normal', 'caps']:
|
||||||
chars = [
|
chars = list(self._chars)
|
||||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'a', 's',
|
|
||||||
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b',
|
|
||||||
'n', 'm'
|
|
||||||
]
|
|
||||||
if self._mode == 'caps':
|
if self._mode == 'caps':
|
||||||
chars = [c.upper() for c in chars]
|
chars = [c.upper() for c in chars]
|
||||||
ba.buttonwidget(edit=self._shift_button,
|
ba.buttonwidget(edit=self._shift_button,
|
||||||
@ -241,13 +274,12 @@ class OnScreenKeyboardWindow(ba.Window):
|
|||||||
ba.buttonwidget(edit=self._emoji_button,
|
ba.buttonwidget(edit=self._emoji_button,
|
||||||
color=self._key_color_dark,
|
color=self._key_color_dark,
|
||||||
label=charstr(SpCh.LOGO_FLAT),
|
label=charstr(SpCh.LOGO_FLAT),
|
||||||
on_activate_call=self._emoji_mode)
|
on_activate_call=self._next_mode)
|
||||||
elif self._mode == 'num':
|
else:
|
||||||
chars = [
|
if self._mode == 'num':
|
||||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '/',
|
chars = list(self._keyboard.nums)
|
||||||
':', ';', '(', ')', '$', '&', '@', '"', '.', ',', '?', '!',
|
else:
|
||||||
'\'', '_'
|
chars = list(self._keyboard.pages[self._mode])
|
||||||
]
|
|
||||||
ba.buttonwidget(edit=self._shift_button,
|
ba.buttonwidget(edit=self._shift_button,
|
||||||
color=self._key_color_dark,
|
color=self._key_color_dark,
|
||||||
label='',
|
label='',
|
||||||
@ -258,29 +290,7 @@ class OnScreenKeyboardWindow(ba.Window):
|
|||||||
ba.buttonwidget(edit=self._emoji_button,
|
ba.buttonwidget(edit=self._emoji_button,
|
||||||
color=self._key_color_dark,
|
color=self._key_color_dark,
|
||||||
label=charstr(SpCh.LOGO_FLAT),
|
label=charstr(SpCh.LOGO_FLAT),
|
||||||
on_activate_call=self._emoji_mode)
|
on_activate_call=self._next_mode)
|
||||||
|
|
||||||
elif self._mode in ['emoji', 'emoji2']:
|
|
||||||
chars = [
|
|
||||||
'💣', '💥', '🙂', '😄', '😆', '😅', '😂', '☺', '😀', '😉', '😇', '😎',
|
|
||||||
'😰', '😠', '😈', '😨', '😛', '😜', '😝', '😐', '😑', '😵', '😬', '😡',
|
|
||||||
'😌', '😍'
|
|
||||||
]
|
|
||||||
if self._mode == 'emoji2':
|
|
||||||
chars = [
|
|
||||||
'😔', '😥', '😭', '😖', '😓', '😉', '😴', '😷', '👋', '💯', '🙏', '💪',
|
|
||||||
'👀', '💬', '💀', '☠', '💩', '👻', '👽', '👾', '❤', '💛', '💚', '💙',
|
|
||||||
'💜', '💔'
|
|
||||||
]
|
|
||||||
ba.buttonwidget(edit=self._shift_button,
|
|
||||||
color=self._key_color_lit if self._mode == 'emoji2'
|
|
||||||
else self._key_color_dark,
|
|
||||||
label=charstr(SpCh.SHIFT),
|
|
||||||
on_activate_call=self._emoji_mode_2)
|
|
||||||
ba.buttonwidget(edit=self._emoji_button,
|
|
||||||
color=self._key_color_lit,
|
|
||||||
label=charstr(SpCh.LOGO_FLAT),
|
|
||||||
on_activate_call=self._emoji_mode)
|
|
||||||
|
|
||||||
for i, btn in enumerate(self._char_keys):
|
for i, btn in enumerate(self._char_keys):
|
||||||
assert chars is not None
|
assert chars is not None
|
||||||
@ -302,22 +312,23 @@ class OnScreenKeyboardWindow(ba.Window):
|
|||||||
self._mode = 'num'
|
self._mode = 'num'
|
||||||
self._refresh()
|
self._refresh()
|
||||||
|
|
||||||
def _emoji_mode(self) -> None:
|
def _next_mode(self) -> None:
|
||||||
ba.playsound(self._click_sound)
|
ba.playsound(self._click_sound)
|
||||||
if self._mode in ['normal', 'caps', 'num']:
|
self._mode_index = (self._mode_index + 1) % len(self._modes)
|
||||||
self._last_mode = self._mode
|
self._mode = self._modes[self._mode_index]
|
||||||
self._mode = 'emoji'
|
|
||||||
elif self._mode == 'emoji' or self._mode == 'emoji2':
|
|
||||||
self._mode = self._last_mode
|
|
||||||
self._refresh()
|
self._refresh()
|
||||||
|
|
||||||
def _emoji_mode_2(self) -> None:
|
def _next_keyboard(self) -> None:
|
||||||
ba.playsound(self._click_sound)
|
assert ba.app.metascan is not None
|
||||||
if self._mode == 'emoji':
|
self._keyboard_index = (self._keyboard_index + 1) % len(
|
||||||
self._mode = 'emoji2'
|
ba.app.metascan.keyboards)
|
||||||
elif self._mode == 'emoji2':
|
self._load_keyboard()
|
||||||
self._mode = 'emoji'
|
if len(ba.app.metascan.keyboards) < 2:
|
||||||
self._refresh()
|
ba.playsound(ba.getsound('error'))
|
||||||
|
ba.screenmessage('No other keyboards available', color=(1, 0, 0))
|
||||||
|
else:
|
||||||
|
ba.screenmessage(f'Switching keyboard to "{self._keyboard.name}"',
|
||||||
|
color=(0, 1, 0))
|
||||||
|
|
||||||
def _shift(self) -> None:
|
def _shift(self) -> None:
|
||||||
ba.playsound(self._click_sound)
|
ba.playsound(self._click_sound)
|
||||||
@ -340,6 +351,15 @@ class OnScreenKeyboardWindow(ba.Window):
|
|||||||
|
|
||||||
def _type_char(self, char: str) -> None:
|
def _type_char(self, char: str) -> None:
|
||||||
ba.playsound(self._click_sound)
|
ba.playsound(self._click_sound)
|
||||||
|
if char.isspace():
|
||||||
|
if (ba.time(ba.TimeType.REAL) - self._last_space_press <
|
||||||
|
self._double_space_interval):
|
||||||
|
self._last_space_press = 0
|
||||||
|
self._next_keyboard()
|
||||||
|
self._del() # We typed unneeded space around 1s ago.
|
||||||
|
return
|
||||||
|
self._last_space_press = ba.time(ba.TimeType.REAL)
|
||||||
|
|
||||||
# operate in unicode so we don't do anything funky like chop utf-8
|
# operate in unicode so we don't do anything funky like chop utf-8
|
||||||
# chars in half
|
# chars in half
|
||||||
txt = cast(str, ba.textwidget(query=self._text_field))
|
txt = cast(str, ba.textwidget(query=self._text_field))
|
||||||
|
|||||||
73
assets/src/ba_data/python/keyboards.py
Normal file
73
assets/src/ba_data/python/keyboards.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# Copyright (c) 2011-2020 Eric Froemling
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
"""Defines a default keyboards."""
|
||||||
|
|
||||||
|
# ba_meta require api 6
|
||||||
|
# (see https://ballistica.net/wiki/meta-tag-system)
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import ba
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Iterable, List, Tuple, Dict
|
||||||
|
|
||||||
|
|
||||||
|
def split(chars: Iterable[str], maxlen: int) -> List[List[str]]:
|
||||||
|
"""Returns char groups with a fixed number of elements"""
|
||||||
|
result = []
|
||||||
|
shatter: List[str] = []
|
||||||
|
for i in chars:
|
||||||
|
if len(shatter) < maxlen:
|
||||||
|
shatter.append(i)
|
||||||
|
else:
|
||||||
|
result.append(shatter)
|
||||||
|
shatter = [i]
|
||||||
|
if shatter:
|
||||||
|
while len(shatter) < maxlen:
|
||||||
|
shatter.append('')
|
||||||
|
result.append(shatter)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def generate_emojis(maxlen: int) -> List[List[str]]:
|
||||||
|
"""Generates a lot of UTF8 emojis prepared for ba.Keyboard pages"""
|
||||||
|
all_emojis = split([chr(i) for i in range(0x1F601, 0x1F650)], maxlen)
|
||||||
|
all_emojis += split([chr(i) for i in range(0x2702, 0x27B1)], maxlen)
|
||||||
|
all_emojis += split([chr(i) for i in range(0x1F680, 0x1F6C1)], maxlen)
|
||||||
|
return all_emojis
|
||||||
|
|
||||||
|
|
||||||
|
# ba_meta export keyboard
|
||||||
|
class EnglishKeyboard(ba.Keyboard):
|
||||||
|
"""Default English keyboard."""
|
||||||
|
name = 'English'
|
||||||
|
chars = [('q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'),
|
||||||
|
('a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'),
|
||||||
|
('z', 'x', 'c', 'v', 'b', 'n', 'm')]
|
||||||
|
nums = ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '/', ':',
|
||||||
|
';', '(', ')', '$', '&', '@', '"', '.', ',', '?', '!', '\'', '_')
|
||||||
|
pages: Dict[str, Tuple[str, ...]] = {
|
||||||
|
f'emoji{i}': tuple(page)
|
||||||
|
for i, page in enumerate(generate_emojis(len(nums)))
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user