Skip to content

Commit 9b2f734

Browse files
authored
raylib: wrap text for multilang (#36410)
* fix multilang dialog height * split to file * stash * Revert "stash" This reverts commit deb4239. * add updater * add files * stuff * try rev * stash * works! * works! * this should be the flow? * cursor wrapping -- it missed entire sections, changed formatting, and didn't use trn properly!!!!!!!!!!!!!!!!! * update translations * learned my lesson * this should be the one thing it's good at * update trans * onroad wrap * spanish * rename * clean up * load all * Revert "load all" This reverts commit 6f2a458. * jp translations * try jp * Revert "try jp" This reverts commit d0524b1. * remove languages we can't add rn * tr * pt and fr * ai cannot be trusted * ai cannot be trusted * missing trans * add fonts * Revert "remove languages we can't add rn" This reverts commit 73dc75f. * painfully slow to startup * only load what we need * Reapply "remove languages we can't add rn" This reverts commit 52cb48f. * stash! * rm * Revert "stash!" This reverts commit 31d7c36. * revert this * revert that * make this dynamic! * device * revert * firehose * stuff * revert application * back * full revert * clean up * network * more system * fix dat * fixy
1 parent 650946c commit 9b2f734

26 files changed

+238
-199
lines changed

selfdrive/ui/layouts/home.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from openpilot.selfdrive.ui.widgets.setup import SetupWidget
1010
from openpilot.system.ui.lib.text_measure import measure_text_cached
1111
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
12+
from openpilot.system.ui.lib.multilang import tr, trn
1213
from openpilot.system.ui.widgets.label import gui_label
1314
from openpilot.system.ui.widgets import Widget
1415

@@ -151,7 +152,7 @@ def _render_header(self):
151152
highlight_color = rl.Color(75, 95, 255, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(54, 77, 239, 255)
152153
rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color)
153154

154-
text = "UPDATE"
155+
text = tr("UPDATE")
155156
text_size = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE)
156157
text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_size.x) // 2
157158
text_y = self.update_notif_rect.y + (self.update_notif_rect.height - text_size.y) // 2
@@ -165,7 +166,7 @@ def _render_header(self):
165166
highlight_color = rl.Color(255, 70, 70, 255) if self.current_state == HomeLayoutState.ALERTS else rl.Color(226, 44, 44, 255)
166167
rl.draw_rectangle_rounded(self.alert_notif_rect, 0.3, 10, highlight_color)
167168

168-
alert_text = f"{self.alert_count} ALERT{'S' if self.alert_count > 1 else ''}"
169+
alert_text = trn("{} ALERT", "{} ALERTS", self.alert_count).format(self.alert_count)
169170
text_size = measure_text_cached(font, alert_text, HEAD_BUTTON_FONT_SIZE)
170171
text_x = self.alert_notif_rect.x + (self.alert_notif_rect.width - text_size.x) // 2
171172
text_y = self.alert_notif_rect.y + (self.alert_notif_rect.height - text_size.y) // 2

selfdrive/ui/layouts/onboarding.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pyray as rl
77
from openpilot.common.basedir import BASEDIR
88
from openpilot.system.ui.lib.application import FontWeight, gui_app
9+
from openpilot.system.ui.lib.multilang import tr
910
from openpilot.system.ui.widgets import Widget
1011
from openpilot.system.ui.widgets.button import Button, ButtonStyle
1112
from openpilot.system.ui.widgets.label import Label
@@ -107,12 +108,12 @@ def __init__(self, on_accept=None, on_decline=None):
107108
self._on_accept = on_accept
108109
self._on_decline = on_decline
109110

110-
self._title = Label("Welcome to openpilot", font_size=90, font_weight=FontWeight.BOLD, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
111-
self._desc = Label("You must accept the Terms and Conditions to use openpilot. Read the latest terms at https://comma.ai/terms before continuing.",
111+
self._title = Label(tr("Welcome to openpilot"), font_size=90, font_weight=FontWeight.BOLD, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
112+
self._desc = Label(tr("You must accept the Terms and Conditions to use openpilot. Read the latest terms at https://comma.ai/terms before continuing."),
112113
font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
113114

114-
self._decline_btn = Button("Decline", click_callback=on_decline)
115-
self._accept_btn = Button("Agree", button_style=ButtonStyle.PRIMARY, click_callback=on_accept)
115+
self._decline_btn = Button(tr("Decline"), click_callback=on_decline)
116+
self._accept_btn = Button(tr("Agree"), button_style=ButtonStyle.PRIMARY, click_callback=on_accept)
116117

117118
def _render(self, _):
118119
welcome_x = self._rect.x + 165
@@ -141,10 +142,10 @@ def _render(self, _):
141142
class DeclinePage(Widget):
142143
def __init__(self, back_callback=None):
143144
super().__init__()
144-
self._text = Label("You must accept the Terms and Conditions in order to use openpilot.",
145+
self._text = Label(tr("You must accept the Terms and Conditions in order to use openpilot."),
145146
font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
146-
self._back_btn = Button("Back", click_callback=back_callback)
147-
self._uninstall_btn = Button("Decline, uninstall openpilot", button_style=ButtonStyle.DANGER,
147+
self._back_btn = Button(tr("Back"), click_callback=back_callback)
148+
self._uninstall_btn = Button(tr("Decline, uninstall openpilot"), button_style=ButtonStyle.DANGER,
148149
click_callback=self._on_uninstall_clicked)
149150

150151
def _on_uninstall_clicked(self):

selfdrive/ui/layouts/settings/developer.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,20 @@
66
from openpilot.system.ui.widgets.scroller import Scroller
77
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
88
from openpilot.system.ui.lib.application import gui_app
9+
from openpilot.system.ui.lib.multilang import tr
910
from openpilot.system.ui.widgets import DialogResult
1011

1112
# Description constants
1213
DESCRIPTIONS = {
13-
'enable_adb': (
14+
'enable_adb': tr(
1415
"ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. " +
1516
"See https://docs.comma.ai/how-to/connect-to-comma for more info."
1617
),
17-
'ssh_key': (
18+
'ssh_key': tr(
1819
"Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " +
1920
"other than your own. A comma employee will NEVER ask you to add their GitHub username."
2021
),
21-
'alpha_longitudinal': (
22+
'alpha_longitudinal': tr(
2223
"<b>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</b><br><br>" +
2324
"On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. " +
2425
"Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha."
@@ -34,7 +35,7 @@ def __init__(self):
3435

3536
# Build items and keep references for callbacks/state updates
3637
self._adb_toggle = toggle_item(
37-
"Enable ADB",
38+
tr("Enable ADB"),
3839
description=DESCRIPTIONS["enable_adb"],
3940
initial_state=self._params.get_bool("AdbEnabled"),
4041
callback=self._on_enable_adb,
@@ -43,30 +44,30 @@ def __init__(self):
4344

4445
# SSH enable toggle + SSH key management
4546
self._ssh_toggle = toggle_item(
46-
"Enable SSH",
47+
tr("Enable SSH"),
4748
description="",
4849
initial_state=self._params.get_bool("SshEnabled"),
4950
callback=self._on_enable_ssh,
5051
)
5152
self._ssh_keys = ssh_key_item("SSH Keys", description=DESCRIPTIONS["ssh_key"])
5253

5354
self._joystick_toggle = toggle_item(
54-
"Joystick Debug Mode",
55+
tr("Joystick Debug Mode"),
5556
description="",
5657
initial_state=self._params.get_bool("JoystickDebugMode"),
5758
callback=self._on_joystick_debug_mode,
5859
enabled=ui_state.is_offroad,
5960
)
6061

6162
self._long_maneuver_toggle = toggle_item(
62-
"Longitudinal Maneuver Mode",
63+
tr("Longitudinal Maneuver Mode"),
6364
description="",
6465
initial_state=self._params.get_bool("LongitudinalManeuverMode"),
6566
callback=self._on_long_maneuver_mode,
6667
)
6768

6869
self._alpha_long_toggle = toggle_item(
69-
"openpilot Longitudinal Control (Alpha)",
70+
tr("openpilot Longitudinal Control (Alpha)"),
7071
description=DESCRIPTIONS["alpha_longitudinal"],
7172
initial_state=self._params.get_bool("AlphaLongitudinalEnabled"),
7273
callback=self._on_alpha_long_enabled,
@@ -163,7 +164,7 @@ def confirm_callback(result: int):
163164
content = (f"<h1>{self._alpha_long_toggle.title}</h1><br>" +
164165
f"<p>{self._alpha_long_toggle.description}</p>")
165166

166-
dlg = ConfirmDialog(content, "Enable", rich=True)
167+
dlg = ConfirmDialog(content, tr("Enable"), rich=True)
167168
gui_app.set_modal_overlay(dlg, callback=confirm_callback)
168169

169170
else:

selfdrive/ui/layouts/settings/device.py

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog
1313
from openpilot.system.hardware import TICI
1414
from openpilot.system.ui.lib.application import gui_app
15+
from openpilot.system.ui.lib.multilang import tr
1516
from openpilot.system.ui.widgets import Widget, DialogResult
1617
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog
1718
from openpilot.system.ui.widgets.html_render import HtmlModal
@@ -21,10 +22,10 @@
2122

2223
# Description constants
2324
DESCRIPTIONS = {
24-
'pair_device': "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.",
25-
'driver_camera': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)",
26-
'reset_calibration': "openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down.",
27-
'review_guide': "Review the rules, features, and limitations of openpilot",
25+
'pair_device': tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."),
26+
'driver_camera': tr("Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)"),
27+
'reset_calibration': tr("openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down."),
28+
'review_guide': tr("Review the rules, features, and limitations of openpilot"),
2829
}
2930

3031

@@ -45,27 +46,27 @@ def __init__(self):
4546
ui_state.add_offroad_transition_callback(self._offroad_transition)
4647

4748
def _initialize_items(self):
48-
dongle_id = self._params.get("DongleId") or "N/A"
49-
serial = self._params.get("HardwareSerial") or "N/A"
49+
dongle_id = self._params.get("DongleId") or tr("N/A")
50+
serial = self._params.get("HardwareSerial") or tr("N/A")
5051

51-
self._pair_device_btn = button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device)
52+
self._pair_device_btn = button_item(tr("Pair Device"), tr("PAIR"), DESCRIPTIONS['pair_device'], callback=self._pair_device)
5253
self._pair_device_btn.set_visible(lambda: not ui_state.prime_state.is_paired())
5354

54-
self._reset_calib_btn = button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt)
55+
self._reset_calib_btn = button_item(tr("Reset Calibration"), tr("RESET"), DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt)
5556
self._reset_calib_btn.set_description_opened_callback(self._update_calib_description)
5657

57-
self._power_off_btn = dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt)
58+
self._power_off_btn = dual_button_item(tr("Reboot"), tr("Power Off"), left_callback=self._reboot_prompt, right_callback=self._power_off_prompt)
5859

5960
items = [
60-
text_item("Dongle ID", dongle_id),
61-
text_item("Serial", serial),
61+
text_item(tr("Dongle ID"), dongle_id),
62+
text_item(tr("Serial"), serial),
6263
self._pair_device_btn,
63-
button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad),
64+
button_item(tr("Driver Camera"), tr("PREVIEW"), DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad),
6465
self._reset_calib_btn,
65-
button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide, enabled=ui_state.is_offroad),
66-
regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory, enabled=ui_state.is_offroad),
66+
button_item(tr("Review Training Guide"), tr("REVIEW"), DESCRIPTIONS['review_guide'], self._on_review_training_guide, enabled=ui_state.is_offroad),
67+
regulatory_btn := button_item(tr("Regulatory"), tr("VIEW"), callback=self._on_regulatory, enabled=ui_state.is_offroad),
6768
# TODO: implement multilang
68-
# button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad),
69+
# button_item(tr("Change Language"), tr("CHANGE"), callback=self._show_language_selection, enabled=ui_state.is_offroad),
6970
self._power_off_btn,
7071
]
7172
regulatory_btn.set_visible(TICI)
@@ -106,7 +107,7 @@ def _show_driver_camera(self):
106107

107108
def _reset_calibration_prompt(self):
108109
if ui_state.engaged:
109-
gui_app.set_modal_overlay(alert_dialog("Disengage to Reset Calibration"))
110+
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Reset Calibration")))
110111
return
111112

112113
def reset_calibration(result: int):
@@ -122,7 +123,7 @@ def reset_calibration(result: int):
122123
self._params.put_bool("OnroadCycleRequested", True)
123124
self._update_calib_description()
124125

125-
dialog = ConfirmDialog("Are you sure you want to reset calibration?", "Reset")
126+
dialog = ConfirmDialog(tr("Are you sure you want to reset calibration?"), tr("Reset"))
126127
gui_app.set_modal_overlay(dialog, callback=reset_calibration)
127128

128129
def _update_calib_description(self):
@@ -136,7 +137,8 @@ def _update_calib_description(self):
136137
if calib.calStatus != log.LiveCalibrationData.Status.uncalibrated:
137138
pitch = math.degrees(calib.rpyCalib[1])
138139
yaw = math.degrees(calib.rpyCalib[2])
139-
desc += f" Your device is pointed {abs(pitch):.1f}° {'down' if pitch > 0 else 'up'} and {abs(yaw):.1f}° {'left' if yaw > 0 else 'right'}."
140+
desc += tr(" Your device is pointed {:.1f}° {} and {:.1f}° {}.").format(abs(pitch), tr("down") if pitch > 0 else tr("up"),
141+
abs(yaw), tr("left") if yaw > 0 else tr("right"))
140142
except Exception:
141143
cloudlog.exception("invalid CalibrationParams")
142144

@@ -148,9 +150,9 @@ def _update_calib_description(self):
148150
except Exception:
149151
cloudlog.exception("invalid LiveDelay")
150152
if lag_perc < 100:
151-
desc += f"<br><br>Steering lag calibration is {lag_perc}% complete."
153+
desc += tr("<br><br>Steering lag calibration is {}% complete.").format(lag_perc)
152154
else:
153-
desc += "<br><br>Steering lag calibration is complete."
155+
desc += tr("<br><br>Steering lag calibration is complete.")
154156

155157
torque_bytes = self._params.get("LiveTorqueParameters")
156158
if torque_bytes:
@@ -160,24 +162,24 @@ def _update_calib_description(self):
160162
if torque.useParams:
161163
torque_perc = torque.calPerc
162164
if torque_perc < 100:
163-
desc += f" Steering torque response calibration is {torque_perc}% complete."
165+
desc += tr(" Steering torque response calibration is {}% complete.").format(torque_perc)
164166
else:
165-
desc += " Steering torque response calibration is complete."
167+
desc += tr(" Steering torque response calibration is complete.")
166168
except Exception:
167169
cloudlog.exception("invalid LiveTorqueParameters")
168170

169171
desc += "<br><br>"
170-
desc += ("openpilot is continuously calibrating, resetting is rarely required. " +
171-
"Resetting calibration will restart openpilot if the car is powered on.")
172+
desc += tr("openpilot is continuously calibrating, resetting is rarely required. " +
173+
"Resetting calibration will restart openpilot if the car is powered on.")
172174

173175
self._reset_calib_btn.set_description(desc)
174176

175177
def _reboot_prompt(self):
176178
if ui_state.engaged:
177-
gui_app.set_modal_overlay(alert_dialog("Disengage to Reboot"))
179+
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Reboot")))
178180
return
179181

180-
dialog = ConfirmDialog("Are you sure you want to reboot?", "Reboot")
182+
dialog = ConfirmDialog(tr("Are you sure you want to reboot?"), tr("Reboot"))
181183
gui_app.set_modal_overlay(dialog, callback=self._perform_reboot)
182184

183185
def _perform_reboot(self, result: int):
@@ -186,10 +188,10 @@ def _perform_reboot(self, result: int):
186188

187189
def _power_off_prompt(self):
188190
if ui_state.engaged:
189-
gui_app.set_modal_overlay(alert_dialog("Disengage to Power Off"))
191+
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Power Off")))
190192
return
191193

192-
dialog = ConfirmDialog("Are you sure you want to power off?", "Power Off")
194+
dialog = ConfirmDialog(tr("Are you sure you want to power off?"), tr("Power Off"))
193195
gui_app.set_modal_overlay(dialog, callback=self._perform_power_off)
194196

195197
def _perform_power_off(self, result: int):
@@ -210,5 +212,6 @@ def _on_review_training_guide(self):
210212
if not self._training_guide:
211213
def completed_callback():
212214
gui_app.set_modal_overlay(None)
215+
213216
self._training_guide = TrainingGuide(completed_callback=completed_callback)
214217
gui_app.set_modal_overlay(self._training_guide)

selfdrive/ui/layouts/settings/firehose.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@
88
from openpilot.selfdrive.ui.ui_state import ui_state
99
from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID
1010
from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE
11+
from openpilot.system.ui.lib.multilang import tr, trn
1112
from openpilot.system.ui.lib.text_measure import measure_text_cached
1213
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
1314
from openpilot.system.ui.lib.wrap_text import wrap_text
1415
from openpilot.system.ui.widgets import Widget
1516
from openpilot.selfdrive.ui.lib.api_helpers import get_token
1617

17-
TITLE = "Firehose Mode"
18-
DESCRIPTION = (
18+
TITLE = tr("Firehose Mode")
19+
DESCRIPTION = tr(
1920
"openpilot learns to drive by watching humans, like you, drive.\n\n"
2021
+ "Firehose Mode allows you to maximize your training data uploads to improve "
2122
+ "openpilot's driving models. More data means bigger models, which means better Experimental Mode."
2223
)
23-
INSTRUCTIONS = (
24+
INSTRUCTIONS = tr(
2425
"For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.\n\n"
2526
+ "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.\n\n\n"
2627
+ "Frequently Asked Questions\n\n"
@@ -106,7 +107,8 @@ def _render_content(self, rect: rl.Rectangle, scroll_offset: float) -> int:
106107

107108
# Contribution count (if available)
108109
if self.segment_count > 0:
109-
contrib_text = f"{self.segment_count} segment(s) of your driving is in the training dataset so far."
110+
contrib_text = trn("{} segment of your driving is in the training dataset so far.",
111+
"{} segments of your driving is in the training dataset so far.", self.segment_count).format(self.segment_count)
110112
y = self._draw_wrapped_text(x, y, w, contrib_text, gui_app.font(FontWeight.BOLD), 52, rl.WHITE)
111113
y += 20 + 20
112114

@@ -132,9 +134,9 @@ def _get_status(self) -> tuple[str, rl.Color]:
132134
network_metered = ui_state.sm["deviceState"].networkMetered
133135

134136
if not network_metered and network_type != 0: # Not metered and connected
135-
return "ACTIVE", self.GREEN
137+
return tr("ACTIVE"), self.GREEN
136138
else:
137-
return "INACTIVE: connect to an unmetered network", self.RED
139+
return tr("INACTIVE: connect to an unmetered network"), self.RED
138140

139141
def _fetch_firehose_stats(self):
140142
try:

selfdrive/ui/layouts/settings/settings.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout
99
from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout
1010
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
11+
from openpilot.system.ui.lib.multilang import tr
1112
from openpilot.system.ui.lib.text_measure import measure_text_cached
1213
from openpilot.system.ui.lib.wifi_manager import WifiManager
1314
from openpilot.system.ui.widgets import Widget
@@ -58,12 +59,12 @@ def __init__(self):
5859
wifi_manager.set_active(False)
5960

6061
self._panels = {
61-
PanelType.DEVICE: PanelInfo("Device", DeviceLayout()),
62-
PanelType.NETWORK: PanelInfo("Network", NetworkUI(wifi_manager)),
63-
PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout()),
64-
PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout()),
65-
PanelType.FIREHOSE: PanelInfo("Firehose", FirehoseLayout()),
66-
PanelType.DEVELOPER: PanelInfo("Developer", DeveloperLayout()),
62+
PanelType.DEVICE: PanelInfo(tr("Device"), DeviceLayout()),
63+
PanelType.NETWORK: PanelInfo(tr("Network"), NetworkUI(wifi_manager)),
64+
PanelType.TOGGLES: PanelInfo(tr("Toggles"), TogglesLayout()),
65+
PanelType.SOFTWARE: PanelInfo(tr("Software"), SoftwareLayout()),
66+
PanelType.FIREHOSE: PanelInfo(tr("Firehose"), FirehoseLayout()),
67+
PanelType.DEVELOPER: PanelInfo(tr("Developer"), DeveloperLayout()),
6768
}
6869

6970
self._font_medium = gui_app.font(FontWeight.MEDIUM)

0 commit comments

Comments
 (0)