wgui: basic i18n support, refactoring: use LayoutState, translation framework (LLM-based generator)

This commit is contained in:
Aleksander
2025-08-02 23:31:23 +02:00
parent 4e46c45bcf
commit eaa81450b5
45 changed files with 916 additions and 223 deletions

View File

@@ -20,9 +20,9 @@
<TopButton id="delete" src="bar/delete.svg" />
</rectangle>
<rectangle padding="8" gap="8" round="100%" color="~bg_color_active" justify_content="center" align_items="center">
<label size="18" text="Opacity" color="~text_color" />
<label size="18" translation="BAR.OPACITY" color="~text_color" />
<slider width="150" height="24" min_value="0" max_value="100" value="100" />
<label size="18" text="Additive:" color="~text_color" />
<label size="18" translation="BAR.ADDITIVE" color="~text_color" />
<sprite color="~device_color" width="20" height="20" src="bar/checkbox-checked.svg" />
</rectangle>
</div>

View File

@@ -0,0 +1 @@
{}

View File

@@ -44,11 +44,8 @@ impl<S> GuiPanel<S> {
pub fn new_from_template(app: &mut AppState, path: &str, state: S) -> anyhow::Result<Self> {
let mut listeners = EventListenerCollection::<AppState, S>::default();
let (layout, parser_state) = wgui::parser::new_layout_from_assets(
Box::new(gui::asset::GuiAsset {}),
&mut listeners,
path,
)?;
let (layout, parser_state) =
wgui::parser::new_layout_from_assets(app.wgui_globals.clone(), &mut listeners, path)?;
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
let mut timestep = Timestep::new();
@@ -68,7 +65,7 @@ impl<S> GuiPanel<S> {
}
pub fn new_blank(app: &mut AppState, state: S) -> anyhow::Result<Self> {
let layout = Layout::new(Box::new(GuiAsset {}))?;
let layout = Layout::new(app.wgui_globals.clone())?;
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
let mut timestep = Timestep::new();
timestep.set_tps(60.0);

View File

@@ -16,7 +16,7 @@ use wgui::{
use crate::{
backend::overlay::{OverlayData, OverlayState, Positioning},
gui::{self, panel::GuiPanel},
gui::panel::GuiPanel,
state::AppState,
subsystem::hid::{ALT, CTRL, META, SHIFT, SUPER, XkbKeymap},
};
@@ -76,7 +76,7 @@ where
}
let (_, mut gui_state_key) = wgui::parser::new_layout_from_assets(
Box::new(gui::asset::GuiAsset {}),
app.wgui_globals.clone(),
&mut panel.listeners,
"gui/keyboard.xml",
)?;
@@ -169,7 +169,8 @@ where
let key_state = {
let rect = panel
.layout
.widget_map
.state
.widgets
.get_as::<Rectangle>(*widget_id)
.unwrap(); // want panic

View File

@@ -9,6 +9,7 @@ use glam::{Quat, vec3a};
use idmap_derive::IntegerId;
use serde::{Deserialize, Serialize};
use wgui::{
i18n::Translation,
parser::parse_color_hex,
renderer_vk::text::{FontWeight, TextStyle},
taffy::{
@@ -168,6 +169,9 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
let mut panel = GuiPanel::new_blank(app, ()).ok()?;
let globals = panel.layout.state.globals.clone();
let mut i18n = globals.i18n();
let (rect, _) = panel
.layout
.add_child(
@@ -192,13 +196,16 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
let _ = panel.layout.add_child(
rect,
TextLabel::create(TextParams {
content: title,
style: TextStyle {
color: parse_color_hex("#ffffff"),
..Default::default()
TextLabel::create(
&mut i18n,
TextParams {
content: Translation::from_raw_text(&title),
style: TextStyle {
color: parse_color_hex("#ffffff"),
..Default::default()
},
},
})
)
.unwrap(),
taffy::Style {
size: taffy::Size {
@@ -212,14 +219,17 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
let _ = panel.layout.add_child(
rect,
TextLabel::create(TextParams {
content: toast.body,
style: TextStyle {
weight: Some(FontWeight::Bold),
color: parse_color_hex("#eeeeee"),
..Default::default()
TextLabel::create(
&mut i18n,
TextParams {
content: Translation::from_raw_text(&toast.body),
style: TextStyle {
weight: Some(FontWeight::Bold),
color: parse_color_hex("#eeeeee"),
..Default::default()
},
},
})
)
.unwrap(),
taffy::Style {
size: taffy::Size {

View File

@@ -6,6 +6,7 @@ use glam::Vec3A;
use regex::Regex;
use wgui::{
event::{self, EventListenerKind},
i18n::Translation,
widget::text::TextLabel,
};
@@ -43,18 +44,20 @@ where
let mut label = panel
.layout
.widget_map
.state
.widgets
.get_as::<TextLabel>(*widget_id)
.unwrap();
let format = match role {
"tz" => {
let mut i18n = panel.layout.state.globals.i18n();
if let Some(s) =
tz_str.and_then(|tz| tz.split('/').next_back().map(|x| x.replace('_', " ")))
{
label.set_text(&s);
label.set_text(&mut i18n, Translation::from_raw_text(&s));
} else {
label.set_text("Local");
label.set_text(&mut i18n, Translation::from_raw_text("Local"));
}
continue;
@@ -69,7 +72,8 @@ where
}
}
_ => {
label.set_text("ERR");
let mut i18n = panel.layout.state.globals.i18n();
label.set_text(&mut i18n, Translation::from_raw_text("ERR"));
continue;
}
};
@@ -87,8 +91,8 @@ where
&mut panel.listener_handles,
*widget_id,
EventListenerKind::InternalStateChange,
Box::new(move |_common, data, _, _| {
clock_on_tick(&clock, data);
Box::new(move |common, data, _, _| {
clock_on_tick(&clock, common, data);
}),
);
}
@@ -151,12 +155,16 @@ struct ClockState {
format: Rc<str>,
}
fn clock_on_tick(clock: &ClockState, data: &mut event::CallbackData) {
fn clock_on_tick(
clock: &ClockState,
common: &event::CallbackDataCommon,
data: &mut event::CallbackData,
) {
let date_time = clock.timezone.as_ref().map_or_else(
|| format!("{}", Local::now().format(&clock.format)),
|tz| format!("{}", Local::now().with_timezone(tz).format(&clock.format)),
);
let label = data.obj.get_as_mut::<TextLabel>();
label.set_text(&date_time);
label.set_text(&mut common.i18n(), Translation::from_raw_text(&date_time));
}

View File

@@ -3,7 +3,9 @@ use idmap::IdMap;
use serde::{Deserialize, Serialize};
use smallvec::{SmallVec, smallvec};
use std::sync::Arc;
use wgui::{gfx::WGfx, renderer_vk::context::SharedContext as WSharedContext};
use wgui::{
gfx::WGfx, globals::WguiGlobals, renderer_vk::context::SharedContext as WSharedContext,
};
#[cfg(feature = "wayvr")]
use {
@@ -20,6 +22,7 @@ use crate::{
config::GeneralConfig,
config_io,
graphics::WGfxExtras,
gui,
overlays::toast::{DisplayMethod, ToastTopic},
subsystem::{audio::AudioOutput, input::HidWrapper},
};
@@ -40,6 +43,8 @@ pub struct AppState {
pub anchor: Affine3A,
pub toast_sound: &'static [u8],
pub wgui_globals: WguiGlobals,
#[cfg(feature = "osc")]
pub osc_sender: Option<OscSender>,
@@ -85,6 +90,7 @@ impl AppState {
screens: smallvec![],
anchor: Affine3A::IDENTITY,
toast_sound: toast_sound_wav,
wgui_globals: WguiGlobals::new(Box::new(gui::asset::GuiAsset {}))?,
#[cfg(feature = "osc")]
osc_sender,