feat(web): unify fonts and refine viewcube

Preload one language-aware font resource across the interface, drawing text, and navigation labels.\n\nRender the navigation cube, ring, outlines, and labels through a 4x MSAA target.
This commit is contained in:
Hakan Seven committed 2026-08-03 15:28:51 +03:00
1 parent 8dc642d40c
commit 53cf31f33d
26 files changed
+1337 -522

No files matched your search

+15 -1
View File
@@ -1550,6 +1550,13 @@ pub enum Message {
crate::scene::text::web_font::Script,
Result<Vec<u8>, String>,
),
/// Register a font already held by the shared web store with the UI renderer.
ApplyWebFont(crate::scene::text::web_font::Script),
/// Completion of the UI renderer's runtime font registration.
WebUiFontLoaded(
crate::scene::text::web_font::Script,
Result<(), String>,
),
/// Ctrl+V. Routed by `update`: into the open text/MText editor (via an async
/// system-clipboard read, which is the only paste path that works on the
/// web) or, with no editor open, the entity paste command.
@@ -3309,6 +3316,13 @@ impl OpenCADStudio {
#[cfg_attr(target_arch = "wasm32", allow(unused_mut))]
let mut s = Self::new();
let focus = s.focus_cmd_input();
let primary_font = crate::scene::text::web_font::preload_language(
&crate::i18n::active_language_tag(),
);
let fonts = Task::batch([
Task::done(Message::PollWebFonts),
Task::done(Message::ApplyWebFont(primary_font)),
]);
// Web can't reach the Patreon API directly (CORS); fetch the CI-built
// supporters.json served on the same origin instead.
let patrons = Task::perform(
@@ -3328,7 +3342,7 @@ impl OpenCADStudio {
let thumbs_fetch = s.refresh_recent_thumbs();
(
s,
Task::batch([focus, patrons, videos, discussions, thumbs_fetch]),
Task::batch([focus, fonts, patrons, videos, discussions, thumbs_fetch]),
)
}
}
+41 -6
View File
@@ -255,9 +255,8 @@ impl OpenCADStudio {
fn update_inner(&mut self, msg: Message) -> Task<Message> {
match msg {
// Web: a drawing referenced a script whose Noto subset isn't loaded
// yet (recorded during text tessellation). Kick off one fetch per
// pending script; the result comes back as `WebFontLoaded`. (#141)
// Web: fetch every script queued by startup language selection or
// drawing text discovery. Each script has one shared store entry.
Message::PollWebFonts => {
let pending = crate::scene::text::web_font::take_pending();
if pending.is_empty() {
@@ -270,9 +269,8 @@ impl OpenCADStudio {
}))
}
// Web: a per-script font arrived. Store it, drop the stale fallback
// glyph cache (entries that resolved to nothing while it loaded),
// and re-tessellate so the text appears. (#141)
// Web: a per-script font arrived. The same bytes feed drawing text,
// the UI renderer, and the navigation-cube label atlas.
Message::WebFontLoaded(script, res) => {
match res {
Ok(bytes) => {
@@ -281,6 +279,7 @@ impl OpenCADStudio {
for tab in self.tabs.iter_mut() {
tab.scene.invalidate_text_geometry_dependencies();
}
return Task::done(Message::ApplyWebFont(script));
}
Err(e) => {
crate::scene::text::web_font::insert(script, None);
@@ -291,6 +290,32 @@ impl OpenCADStudio {
Task::none()
}
Message::ApplyWebFont(script) => {
let Some(bytes) = crate::scene::text::web_font::loaded(script) else {
return Task::none();
};
iced::font::load((*bytes).clone()).map(move |result| {
Message::WebUiFontLoaded(
script,
result.map_err(|error| format!("{error:?}")),
)
})
}
Message::WebUiFontLoaded(script, result) => {
if let Err(error) = result {
self.command_line.push_error(
crate::tf!("Font load failed ({script:?}): {error}").as_ref(),
);
return Task::none();
}
if script == crate::scene::text::web_font::primary_script() {
let family = iced::font::Family::name(script.family());
return iced::font::set_defaults(iced::Font::with_family(family), 16.0);
}
Task::none()
}
Message::Tick(t) => self.on_tick(t),
Message::OpenFile => self.on_open_file(),
@@ -4436,6 +4461,16 @@ impl OpenCADStudio {
Ok(()) => {
self.language = language;
self.persist_settings_if_changed();
#[cfg(target_arch = "wasm32")]
{
let script = crate::scene::text::web_font::preload_language(
&crate::i18n::active_language_tag(),
);
return Task::batch([
Task::done(Message::PollWebFonts),
Task::done(Message::ApplyWebFont(script)),
]);
}
}
Err(error) => self
.command_line