egui: source GUI strings from Fluent files

This commit is contained in:
Mikolaj Wielgus 2024-08-11 00:13:30 +02:00
parent 0257568410
commit 4e37f5715b
8 changed files with 106 additions and 38 deletions

View File

@ -1 +1,23 @@
reference = simple text with a reference: { -something } menu-file = File
menu-debug = Debug
action-open-dsn = Open
action-export-ses = Export session file
action-import-cmd = Import history
action-export-cmd = Export history
action-quit = Quit
action-autoroute = Autoroute
action-place-via = Place Via
action-remove-bands = Remove Bands
action-compare-detours = Compare Detours
action-undo = Undo
action-redo = Redo
show-ratsnest = Show Ratsnest
show-navmesh = Show Navmesh
show-bboxes = Show BBoxes
show-origin-destination = Show OriginDestination
specctra-session-file = Specctra session file

View File

@ -4,9 +4,9 @@ pub struct Action {
} }
impl Action { impl Action {
pub fn new(name: &str, modifiers: egui::Modifiers, key: egui::Key) -> Self { pub fn new(name: String, modifiers: egui::Modifiers, key: egui::Key) -> Self {
Self { Self {
name: String::from(name), name,
shortcut: egui::KeyboardShortcut::new(modifiers, key), shortcut: egui::KeyboardShortcut::new(modifiers, key),
} }
} }

View File

@ -43,14 +43,14 @@ use topola::{
use crate::{ use crate::{
bottom::Bottom, file_receiver::FileReceiver, layers::Layers, overlay::Overlay, bottom::Bottom, file_receiver::FileReceiver, layers::Layers, overlay::Overlay,
painter::Painter, top::Top, viewport::Viewport, painter::Painter, top::Top, translator::Translator, viewport::Viewport,
}; };
/// Deserialize/Serialize is needed to persist app state between restarts. /// Deserialize/Serialize is needed to persist app state between restarts.
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(default)] #[serde(default)]
pub struct App { pub struct App {
langid: LanguageIdentifier, translator: Translator,
#[serde(skip)] #[serde(skip)]
maybe_overlay: Option<Overlay>, maybe_overlay: Option<Overlay>,
@ -89,7 +89,7 @@ pub struct App {
impl Default for App { impl Default for App {
fn default() -> Self { fn default() -> Self {
Self { Self {
langid: langid!("en-US"), translator: Translator::new(langid!("en-US")),
maybe_overlay: None, maybe_overlay: None,
arc_mutex_maybe_invoker: Arc::new(Mutex::new(None)), arc_mutex_maybe_invoker: Arc::new(Mutex::new(None)),
maybe_execute: None, maybe_execute: None,
@ -111,14 +111,14 @@ impl App {
// Load previous app state if one exists. // Load previous app state if one exists.
if let Some(storage) = cc.storage { if let Some(storage) = cc.storage {
let this = Self { let this = Self {
langid, translator: Translator::new(langid),
..eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default() ..eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default()
}; };
return this; return this;
} }
Self { Self {
langid, translator: Translator::new(langid),
..Default::default() ..Default::default()
} }
} }
@ -176,6 +176,7 @@ impl eframe::App for App {
self.top.update( self.top.update(
ctx, ctx,
&self.translator,
self.content_channel.0.clone(), self.content_channel.0.clone(),
self.history_channel.0.clone(), self.history_channel.0.clone(),
self.arc_mutex_maybe_invoker.clone(), self.arc_mutex_maybe_invoker.clone(),
@ -199,8 +200,13 @@ impl eframe::App for App {
&self.maybe_layers, &self.maybe_layers,
); );
self.bottom self.bottom.update(
.update(ctx, &self.viewport, viewport_rect, &self.maybe_execute); ctx,
&self.translator,
&self.viewport,
viewport_rect,
&self.maybe_execute,
);
if ctx.input(|i| i.key_pressed(egui::Key::Escape)) { if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
ctx.send_viewport_cmd(egui::ViewportCommand::Close); ctx.send_viewport_cmd(egui::ViewportCommand::Close);

View File

@ -1,6 +1,6 @@
use topola::autorouter::invoker::{Execute, ExecuteWithStatus, InvokerStatus}; use topola::autorouter::invoker::{Execute, ExecuteWithStatus, InvokerStatus};
use crate::viewport::Viewport; use crate::{translator::Translator, viewport::Viewport};
pub struct Bottom {} pub struct Bottom {}
@ -12,6 +12,7 @@ impl Bottom {
pub fn update( pub fn update(
&mut self, &mut self,
ctx: &egui::Context, ctx: &egui::Context,
tr: &Translator,
viewport: &Viewport, viewport: &Viewport,
viewport_rect: egui::Rect, viewport_rect: egui::Rect,
maybe_execute: &Option<ExecuteWithStatus>, maybe_execute: &Option<ExecuteWithStatus>,

View File

@ -9,19 +9,14 @@ mod layers;
mod overlay; mod overlay;
mod painter; mod painter;
mod top; mod top;
mod translator;
mod viewport; mod viewport;
use app::App; use app::App;
use fluent_templates::static_loader; use fluent_templates::static_loader;
use sys_locale::get_locale; use sys_locale::get_locale;
use unic_langid::{langid, LanguageIdentifier}; use unic_langid::{langid, LanguageIdentifier};
static_loader! {
static LOCALES = {
locales: "./locales",
fallback_language: "en-US",
};
}
// Build to native. // Build to native.
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
fn main() -> eframe::Result<()> { fn main() -> eframe::Result<()> {

View File

@ -16,6 +16,7 @@ use crate::{
app::{channel_text, execute}, app::{channel_text, execute},
file_sender::FileSender, file_sender::FileSender,
overlay::Overlay, overlay::Overlay,
translator::Translator,
}; };
pub struct Top { pub struct Top {
@ -40,6 +41,7 @@ impl Top {
pub fn update( pub fn update(
&mut self, &mut self,
ctx: &egui::Context, ctx: &egui::Context,
tr: &Translator,
content_sender: Sender<String>, content_sender: Sender<String>,
history_sender: Sender<String>, history_sender: Sender<String>,
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>, arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
@ -47,51 +49,66 @@ impl Top {
maybe_overlay: &mut Option<Overlay>, maybe_overlay: &mut Option<Overlay>,
maybe_design: &Option<SpecctraDesign>, maybe_design: &Option<SpecctraDesign>,
) -> Result<(), InvokerError> { ) -> Result<(), InvokerError> {
let mut open_design = let mut open_design = Trigger::new(Action::new(
Trigger::new(Action::new("Open", egui::Modifiers::CTRL, egui::Key::O)); tr.text("action-open-dsn"),
egui::Modifiers::CTRL,
egui::Key::O,
));
let mut export_session = Trigger::new(Action::new( let mut export_session = Trigger::new(Action::new(
"Export session file", tr.text("action-export-ses"),
egui::Modifiers::CTRL, egui::Modifiers::CTRL,
egui::Key::S, egui::Key::S,
)); ));
let mut import_history = Trigger::new(Action::new( let mut import_history = Trigger::new(Action::new(
"Import history", tr.text("action-import-cmd"),
egui::Modifiers::CTRL, egui::Modifiers::CTRL,
egui::Key::I, egui::Key::I,
)); ));
let mut export_history = Trigger::new(Action::new( let mut export_history = Trigger::new(Action::new(
"Export history", tr.text("action-export-cmd"),
egui::Modifiers::CTRL, egui::Modifiers::CTRL,
egui::Key::E, egui::Key::E,
)); ));
let mut quit = Trigger::new(Action::new("Quit", egui::Modifiers::CTRL, egui::Key::V)); let mut quit = Trigger::new(Action::new(
tr.text("action-quit"),
egui::Modifiers::CTRL,
egui::Key::V,
));
let mut autoroute = Trigger::new(Action::new( let mut autoroute = Trigger::new(Action::new(
"Autoroute", tr.text("action-autoroute"),
egui::Modifiers::CTRL, egui::Modifiers::CTRL,
egui::Key::A, egui::Key::A,
)); ));
let mut place_via = Switch::new(Action::new( let mut place_via = Switch::new(Action::new(
"Place Via", tr.text("action-place-via"),
egui::Modifiers::CTRL, egui::Modifiers::CTRL,
egui::Key::P, egui::Key::P,
)); ));
let mut remove_bands = Trigger::new(Action::new( let mut remove_bands = Trigger::new(Action::new(
"Remove Selected Bands", tr.text("action-remove-bands"),
egui::Modifiers::NONE, egui::Modifiers::NONE,
egui::Key::Delete, egui::Key::Delete,
)); ));
let mut compare_detours = Trigger::new(Action::new( let mut compare_detours = Trigger::new(Action::new(
"Compare Detours", tr.text("action-compare-detours"),
egui::Modifiers::NONE, egui::Modifiers::NONE,
egui::Key::Minus, egui::Key::Minus,
)); ));
let mut undo = Trigger::new(Action::new("Undo", egui::Modifiers::CTRL, egui::Key::Z)); let mut undo = Trigger::new(Action::new(
let mut redo = Trigger::new(Action::new("Redo", egui::Modifiers::CTRL, egui::Key::Y)); tr.text("action-undo"),
egui::Modifiers::CTRL,
egui::Key::Z,
));
let mut redo = Trigger::new(Action::new(
tr.text("action-redo"),
egui::Modifiers::CTRL,
egui::Key::Y,
));
egui::TopBottomPanel::top("top_panel") egui::TopBottomPanel::top("top_panel")
.show(ctx, |ui| { .show(ctx, |ui| {
egui::menu::bar(ui, |ui| { egui::menu::bar(ui, |ui| {
ui.menu_button("File", |ui| { ui.menu_button(tr.text("menu-file"), |ui| {
open_design.button(ctx, ui); open_design.button(ctx, ui);
export_session.button(ctx, ui); export_session.button(ctx, ui);
@ -123,11 +140,14 @@ impl Top {
ui.separator(); ui.separator();
ui.menu_button("Debug", |ui| { ui.menu_button(tr.text("menu-debug"), |ui| {
ui.checkbox(&mut self.show_ratsnest, "Show Ratsnest"); ui.checkbox(&mut self.show_ratsnest, tr.text("show-ratsnest"));
ui.checkbox(&mut self.show_navmesh, "Show Navmesh"); ui.checkbox(&mut self.show_navmesh, tr.text("show-navmesh"));
ui.checkbox(&mut self.show_bboxes, "Show BBoxes"); ui.checkbox(&mut self.show_bboxes, tr.text("show-bboxes"));
ui.checkbox(&mut self.show_origin_destination, "Show OriginDestination"); ui.checkbox(
&mut self.show_origin_destination,
tr.text("show-origin-destination"),
);
ui.separator(); ui.separator();
compare_detours.button(ctx, ui); compare_detours.button(ctx, ui);
@ -170,7 +190,7 @@ impl Top {
} }
} }
let task = dialog let task = dialog
.add_filter("Specctra session file", &["ses"]) .add_filter(tr.text("specctra-session-file"), &["ses"])
.save_file(); .save_file();
execute(async move { execute(async move {

View File

@ -0,0 +1,25 @@
use fluent_templates::{static_loader, Loader};
use serde::{Deserialize, Serialize};
use unic_langid::LanguageIdentifier;
static_loader! {
static LOCALES = {
locales: "./locales",
fallback_language: "en-US",
};
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Translator {
langid: LanguageIdentifier,
}
impl Translator {
pub fn new(langid: LanguageIdentifier) -> Self {
Self { langid }
}
pub fn text(&self, fluent_id: &str) -> String {
LOCALES.lookup(&self.langid, fluent_id)
}
}

View File

@ -1,5 +1,4 @@
use clap::{Error, Parser}; use clap::{Error, Parser};
use fluent_templates::static_loader;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::BufReader; use std::io::BufReader;