system/ui: add caps lock to keyboard (#35277)

* add new icons shift-fill and capslock-fill, rm capslock

* SHIFT_KEY_ON, SHIFT_KEY_OFF

* capslock

* rm arrow-down

* a lot simpler

* only one

* just use time

* layout name

* rename shift

* CONSTANT
pull/35281/head
Cameron Clough 4 months ago committed by GitHub
parent 6640986622
commit 0c013f6186
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      selfdrive/assets/icons/arrow-down.png
  2. 3
      selfdrive/assets/icons/capslock-fill.png
  3. 3
      selfdrive/assets/icons/capslock.png
  4. 3
      selfdrive/assets/icons/shift-fill.png
  5. 4
      selfdrive/assets/prep-svg.sh
  6. 53
      system/ui/widgets/keyboard.py

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7e12a3cf36fdef107d237457e20b44e16320414f9de8a1791aff1ec5fd85ccef
size 2390

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6872a1047f1a534a037be7b1367640fe1bfb205a6e1c50420a2d1a946cda78ed
size 4397

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c6621d000d9514df1d636e9953941eadec415ff950724f26a61e0fcbaa7a3818
size 5403

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e625ca991746abaaac375b095aa9a586601982232f4aae0fc2b17b2a524e9ce9
size 3946

@ -6,11 +6,11 @@ ICONS_DIR="$DIR/icons"
BOOTSTRAP_SVG="$DIR/../../third_party/bootstrap/bootstrap-icons.svg" BOOTSTRAP_SVG="$DIR/../../third_party/bootstrap/bootstrap-icons.svg"
ICON_IDS=( ICON_IDS=(
arrow-down
arrow-right arrow-right
backspace backspace
capslock capslock-fill
shift shift
shift-fill
) )
ICON_FILL_COLOR="#fff" ICON_FILL_COLOR="#fff"

@ -1,3 +1,5 @@
import time
from typing import Literal
import pyray as rl import pyray as rl
from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.application import gui_app, FontWeight
from openpilot.system.ui.lib.button import ButtonStyle, gui_button from openpilot.system.ui.lib.button import ButtonStyle, gui_button
@ -5,30 +7,32 @@ from openpilot.system.ui.lib.inputbox import InputBox
from openpilot.system.ui.lib.label import gui_label from openpilot.system.ui.lib.label import gui_label
KEY_FONT_SIZE = 96 KEY_FONT_SIZE = 96
DOUBLE_CLICK_THRESHOLD = 0.5 # seconds
# Constants for special keys # Constants for special keys
CONTENT_MARGIN = 50 CONTENT_MARGIN = 50
BACKSPACE_KEY = "<-" BACKSPACE_KEY = "<-"
ENTER_KEY = "->" ENTER_KEY = "->"
SPACE_KEY = " " SPACE_KEY = " "
SHIFT_KEY = "" SHIFT_INACTIVE_KEY = "SHIFT_OFF"
SHIFT_DOWN_KEY = "" SHIFT_ACTIVE_KEY = "SHIFT_ON"
CAPS_LOCK_KEY = "CAPS"
NUMERIC_KEY = "123" NUMERIC_KEY = "123"
SYMBOL_KEY = "#+=" SYMBOL_KEY = "#+="
ABC_KEY = "ABC" ABC_KEY = "ABC"
# Define keyboard layouts as a dictionary for easier access # Define keyboard layouts as a dictionary for easier access
keyboard_layouts = { KEYBOARD_LAYOUTS = {
"lowercase": [ "lowercase": [
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"], ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
["a", "s", "d", "f", "g", "h", "j", "k", "l"], ["a", "s", "d", "f", "g", "h", "j", "k", "l"],
[SHIFT_KEY, "z", "x", "c", "v", "b", "n", "m", BACKSPACE_KEY], [SHIFT_INACTIVE_KEY, "z", "x", "c", "v", "b", "n", "m", BACKSPACE_KEY],
[NUMERIC_KEY, "/", "-", SPACE_KEY, ".", ENTER_KEY], [NUMERIC_KEY, "/", "-", SPACE_KEY, ".", ENTER_KEY],
], ],
"uppercase": [ "uppercase": [
["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
["A", "S", "D", "F", "G", "H", "J", "K", "L"], ["A", "S", "D", "F", "G", "H", "J", "K", "L"],
[SHIFT_DOWN_KEY, "Z", "X", "C", "V", "B", "N", "M", BACKSPACE_KEY], [SHIFT_ACTIVE_KEY, "Z", "X", "C", "V", "B", "N", "M", BACKSPACE_KEY],
[NUMERIC_KEY, "/", "-", SPACE_KEY, ".", ENTER_KEY], [NUMERIC_KEY, "/", "-", SPACE_KEY, ".", ENTER_KEY],
], ],
"numbers": [ "numbers": [
@ -48,7 +52,10 @@ keyboard_layouts = {
class Keyboard: class Keyboard:
def __init__(self, max_text_size: int = 255, min_text_size: int = 0, password_mode: bool = False, show_password_toggle: bool = False): def __init__(self, max_text_size: int = 255, min_text_size: int = 0, password_mode: bool = False, show_password_toggle: bool = False):
self._layout = keyboard_layouts["lowercase"] self._layout_name: Literal["lowercase", "uppercase", "numbers", "specials"] = "lowercase"
self._caps_lock = False
self._last_shift_press_time = 0
self._max_text_size = max_text_size self._max_text_size = max_text_size
self._min_text_size = min_text_size self._min_text_size = min_text_size
self._input_box = InputBox(max_text_size) self._input_box = InputBox(max_text_size)
@ -59,8 +66,9 @@ class Keyboard:
self._eye_closed_texture = gui_app.texture("icons/eye_closed.png", 81, 54) self._eye_closed_texture = gui_app.texture("icons/eye_closed.png", 81, 54)
self._key_icons = { self._key_icons = {
BACKSPACE_KEY: gui_app.texture("icons/backspace.png", 80, 80), BACKSPACE_KEY: gui_app.texture("icons/backspace.png", 80, 80),
SHIFT_KEY: gui_app.texture("icons/shift.png", 80, 80), SHIFT_INACTIVE_KEY: gui_app.texture("icons/shift.png", 80, 80),
SHIFT_DOWN_KEY: gui_app.texture("icons/arrow-down.png", 80, 80), SHIFT_ACTIVE_KEY: gui_app.texture("icons/shift-fill.png", 80, 80),
CAPS_LOCK_KEY: gui_app.texture("icons/capslock-fill.png", 80, 80),
ENTER_KEY: gui_app.texture("icons/arrow-right.png", 80, 80), ENTER_KEY: gui_app.texture("icons/arrow-right.png", 80, 80),
} }
@ -84,13 +92,15 @@ class Keyboard:
input_box_rect = rl.Rectangle(rect.x + input_margin, rect.y + 160, rect.width - input_margin, 100) input_box_rect = rl.Rectangle(rect.x + input_margin, rect.y + 160, rect.width - input_margin, 100)
self._render_input_area(input_box_rect) self._render_input_area(input_box_rect)
layout = KEYBOARD_LAYOUTS[self._layout_name]
h_space, v_space = 15, 15 h_space, v_space = 15, 15
row_y_start = rect.y + 300 # Starting Y position for the first row row_y_start = rect.y + 300 # Starting Y position for the first row
key_height = (rect.height - 300 - 3 * v_space) / 4 key_height = (rect.height - 300 - 3 * v_space) / 4
key_max_width = (rect.width - (len(self._layout[2]) - 1) * h_space) / len(self._layout[2]) key_max_width = (rect.width - (len(layout[2]) - 1) * h_space) / len(layout[2])
# Iterate over the rows of keys in the current layout # Iterate over the rows of keys in the current layout
for row, keys in enumerate(self._layout): for row, keys in enumerate(layout):
key_width = min((rect.width - (180 if row == 1 else 0) - h_space * (len(keys) - 1)) / len(keys), key_max_width) key_width = min((rect.width - (180 if row == 1 else 0) - h_space * (len(keys) - 1)) / len(keys), key_max_width)
start_x = rect.x + (90 if row == 1 else 0) start_x = rect.x + (90 if row == 1 else 0)
@ -105,6 +115,8 @@ class Keyboard:
is_enabled = key != ENTER_KEY or len(self._input_box.text) >= self._min_text_size is_enabled = key != ENTER_KEY or len(self._input_box.text) >= self._min_text_size
result = -1 result = -1
if key in self._key_icons: if key in self._key_icons:
if key == SHIFT_ACTIVE_KEY and self._caps_lock:
key = CAPS_LOCK_KEY
texture = self._key_icons[key] texture = self._key_icons[key]
result = gui_button(key_rect, "", icon=texture, button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.NORMAL, is_enabled=is_enabled) result = gui_button(key_rect, "", icon=texture, button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.NORMAL, is_enabled=is_enabled)
else: else:
@ -148,18 +160,27 @@ class Keyboard:
) )
def handle_key_press(self, key): def handle_key_press(self, key):
if key in (SHIFT_DOWN_KEY, ABC_KEY): if key in (CAPS_LOCK_KEY, ABC_KEY):
self._layout = keyboard_layouts["lowercase"] self._caps_lock = False
elif key == SHIFT_KEY: self._layout_name = "lowercase"
self._layout = keyboard_layouts["uppercase"] elif key == SHIFT_INACTIVE_KEY:
self._last_shift_press_time = time.monotonic()
self._layout_name = "uppercase"
elif key == SHIFT_ACTIVE_KEY:
if time.monotonic() - self._last_shift_press_time < DOUBLE_CLICK_THRESHOLD:
self._caps_lock = True
else:
self._layout_name = "lowercase"
elif key == NUMERIC_KEY: elif key == NUMERIC_KEY:
self._layout = keyboard_layouts["numbers"] self._layout_name = "numbers"
elif key == SYMBOL_KEY: elif key == SYMBOL_KEY:
self._layout = keyboard_layouts["specials"] self._layout_name = "specials"
elif key == BACKSPACE_KEY: elif key == BACKSPACE_KEY:
self._input_box.delete_char_before_cursor() self._input_box.delete_char_before_cursor()
else: else:
self._input_box.add_char_at_cursor(key) self._input_box.add_char_at_cursor(key)
if not self._caps_lock and self._layout_name == "uppercase":
self._layout_name = "lowercase"
if __name__ == "__main__": if __name__ == "__main__":

Loading…
Cancel
Save