add WguiFontSystem, remove FONT_SYSTEM singleton, custom fonts, add Light font weight

there are a few gzip-compressed ttf as for now, looks like variable fonts aren't parsed properly by cosmic_text. Not sure why. Also, we probably need to have a fallback for CJK characters in the future, or just fallback to the built-in ones in the OS.
This commit is contained in:
Aleksander
2025-11-07 22:21:57 +01:00
parent d2c23ac6a9
commit 71898056f3
33 changed files with 202 additions and 65 deletions

5
Cargo.lock generated
View File

@@ -1852,9 +1852,9 @@ checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.1.2" version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide",
@@ -6278,6 +6278,7 @@ dependencies = [
"anyhow", "anyhow",
"cosmic-text", "cosmic-text",
"etagere", "etagere",
"flate2",
"glam", "glam",
"image", "image",
"log", "log",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -100,6 +100,8 @@
<rectangle <rectangle
width="100%" width="100%"
height="48" height="48"
min_height="48"
max_height="48"
box_sizing="border_box" box_sizing="border_box"
round="8" round="8"
flex_direction="row" flex_direction="row"
@@ -130,7 +132,7 @@
<!-- Right bottom side --> <!-- Right bottom side -->
<div margin_right="16"> <div margin_right="16">
<label id="label_time" size="16" /> <label id="label_time" size="16" weight="light" />
</div> </div>
</rectangle> </rectangle>
</div> </div>

View File

@@ -3,9 +3,9 @@ use std::{cell::RefCell, collections::VecDeque, rc::Rc};
use chrono::Timelike; use chrono::Timelike;
use glam::Vec2; use glam::Vec2;
use wgui::{ use wgui::{
assets::AssetPath, assets::{AssetPath, AssetProvider},
components::button::ComponentButton, components::button::ComponentButton,
event::{CallbackDataCommon, EventAlterables}, font_config::WguiFontConfig,
globals::WguiGlobals, globals::WguiGlobals,
i18n::Translation, i18n::Translation,
layout::{LayoutParams, RcLayout, WidgetID}, layout::{LayoutParams, RcLayout, WidgetID},
@@ -54,7 +54,22 @@ pub enum FrontendTask {
impl Frontend { impl Frontend {
pub fn new(params: InitParams) -> anyhow::Result<(RcFrontend, RcLayout)> { pub fn new(params: InitParams) -> anyhow::Result<(RcFrontend, RcLayout)> {
let globals = WguiGlobals::new(Box::new(assets::Asset {}), wgui::globals::Defaults::default())?; let mut assets = Box::new(assets::Asset {});
let font_binary_bold = assets.load_from_path_gzip("Quicksand-Bold.ttf.gz")?;
let font_binary_regular = assets.load_from_path_gzip("Quicksand-Regular.ttf.gz")?;
let font_binary_light = assets.load_from_path_gzip("Quicksand-Light.ttf.gz")?;
let globals = WguiGlobals::new(
assets,
wgui::globals::Defaults::default(),
&WguiFontConfig {
binaries: vec![&font_binary_regular, &font_binary_bold, &font_binary_light],
family_name_sans_serif: "Quicksand",
family_name_serif: "Quicksand",
family_name_monospace: "",
},
)?;
let (layout, state) = wgui::parser::new_layout_from_assets( let (layout, state) = wgui::parser::new_layout_from_assets(
&ParseDocumentParams { &ParseDocumentParams {

View File

@@ -1,7 +1,9 @@
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
components::button::ComponentButton, components::button::ComponentButton,
event::CallbackDataCommon,
i18n::Translation, i18n::Translation,
layout::Widget,
parser::{Fetchable, ParseDocumentParams, ParserState}, parser::{Fetchable, ParseDocumentParams, ParserState},
widget::label::WidgetLabel, widget::label::WidgetLabel,
}; };
@@ -23,7 +25,7 @@ impl Tab for TabHome {
} }
} }
fn configure_label_hello(label_hello: &mut WidgetLabel, i18n: &mut wgui::i18n::I18n, settings: &settings::Settings) { fn configure_label_hello(common: &mut CallbackDataCommon, label_hello: Widget, settings: &settings::Settings) {
let mut username = various::get_username(); let mut username = various::get_username();
// first character as uppercase // first character as uppercase
if let Some(first) = username.chars().next() { if let Some(first) = username.chars().next() {
@@ -32,12 +34,13 @@ fn configure_label_hello(label_hello: &mut WidgetLabel, i18n: &mut wgui::i18n::I
} }
let translated = if !settings.home_screen.hide_username { let translated = if !settings.home_screen.hide_username {
i18n.translate_and_replace("HELLO_USER", ("{USER}", &username)) common.i18n().translate_and_replace("HELLO_USER", ("{USER}", &username))
} else { } else {
i18n.translate("HELLO").to_string() common.i18n().translate("HELLO").to_string()
}; };
label_hello.set_text_simple(i18n, Translation::from_raw_text(&translated)); let mut label_hello = label_hello.get_as_mut::<WidgetLabel>().unwrap();
label_hello.set_text(common, Translation::from_raw_text(&translated));
} }
impl TabHome { impl TabHome {
@@ -52,8 +55,9 @@ impl TabHome {
params.parent_id, params.parent_id,
)?; )?;
let mut label_hello = state.fetch_widget_as::<WidgetLabel>(&params.layout.state, "label_hello")?; let mut c = params.layout.start_common();
configure_label_hello(&mut label_hello, &mut params.globals.i18n(), params.settings); let widget_label = state.fetch_widget(&c.layout.state, "label_hello")?.widget;
configure_label_hello(&mut c.common(), widget_label, params.settings);
let btn_apps = state.fetch_component_as::<ComponentButton>("btn_apps")?; let btn_apps = state.fetch_component_as::<ComponentButton>("btn_apps")?;
let btn_games = state.fetch_component_as::<ComponentButton>("btn_games")?; let btn_games = state.fetch_component_as::<ComponentButton>("btn_games")?;

View File

@@ -2,7 +2,6 @@ use std::rc::Rc;
use wgui::{ use wgui::{
components::button::ComponentButton, components::button::ComponentButton,
event::CallbackDataCommon,
globals::WguiGlobals, globals::WguiGlobals,
layout::{Layout, WidgetID}, layout::{Layout, WidgetID},
}; };

View File

@@ -1,26 +1,26 @@
use glam::{vec2, Vec2}; use glam::{Vec2, vec2};
use std::sync::Arc; use std::sync::Arc;
use testbed::{testbed_any::TestbedAny, Testbed}; use testbed::{Testbed, testbed_any::TestbedAny};
use timestep::Timestep; use timestep::Timestep;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::filter::LevelFilter; use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::EnvFilter;
use vulkan::init_window; use vulkan::init_window;
use vulkano::{ use vulkano::{
Validated, VulkanError,
command_buffer::CommandBufferUsage, command_buffer::CommandBufferUsage,
format::Format, format::Format,
image::{view::ImageView, ImageUsage}, image::{ImageUsage, view::ImageView},
swapchain::{ swapchain::{
acquire_next_image, CompositeAlpha, PresentMode, Surface, SurfaceInfo, Swapchain, CompositeAlpha, PresentMode, Surface, SurfaceInfo, Swapchain, SwapchainCreateInfo,
SwapchainCreateInfo, SwapchainPresentInfo, SwapchainPresentInfo, acquire_next_image,
}, },
sync::GpuFuture, sync::GpuFuture,
Validated, VulkanError,
}; };
use wgui::{ use wgui::{
event::{MouseButtonIndex, MouseDownEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent}, event::{MouseButtonIndex, MouseDownEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent},
gfx::{cmd::WGfxClearMode, WGfx}, gfx::{WGfx, cmd::WGfxClearMode},
renderer_vk::{self}, renderer_vk::{self},
}; };
use winit::{ use winit::{
@@ -32,7 +32,7 @@ use winit::{
use crate::{ use crate::{
rate_limiter::RateLimiter, rate_limiter::RateLimiter,
testbed::{ testbed::{
testbed_dashboard::TestbedDashboard, testbed_generic::TestbedGeneric, TestbedUpdateParams, TestbedUpdateParams, testbed_dashboard::TestbedDashboard, testbed_generic::TestbedGeneric,
}, },
}; };
@@ -337,7 +337,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.unwrap(); .unwrap();
let mut layout = testbed.layout().borrow_mut(); let mut layout = testbed.layout().borrow_mut();
let globals = layout.state.globals.clone();
let mut globals = globals.get();
let mut draw_params = wgui::drawing::DrawParams { let mut draw_params = wgui::drawing::DrawParams {
globals: &mut globals,
layout: &mut layout, layout: &mut layout,
debug_draw: debug_draw_enabled, debug_draw: debug_draw_enabled,
alpha: timestep.alpha, alpha: timestep.alpha,
@@ -347,7 +351,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
drop(layout); drop(layout);
let draw_result = render_context let draw_result = render_context
.draw(&mut shared_context, &mut cmd_buf, &primitives) .draw(
&globals.font_system,
&mut shared_context,
&mut cmd_buf,
&primitives,
)
.unwrap(); .unwrap();
if debug_draw_enabled { if debug_draw_enabled {

View File

@@ -5,6 +5,7 @@ use crate::{
use glam::Vec2; use glam::Vec2;
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
font_config::WguiFontConfig,
globals::WguiGlobals, globals::WguiGlobals,
layout::{LayoutParams, RcLayout}, layout::{LayoutParams, RcLayout},
parser::{ParseDocumentParams, ParserState}, parser::{ParseDocumentParams, ParserState},
@@ -24,6 +25,7 @@ impl TestbedAny {
let globals = WguiGlobals::new( let globals = WguiGlobals::new(
Box::new(assets::Asset {}), Box::new(assets::Asset {}),
wgui::globals::Defaults::default(), wgui::globals::Defaults::default(),
&WguiFontConfig::default(),
)?; )?;
let (layout, state) = wgui::parser::new_layout_from_assets( let (layout, state) = wgui::parser::new_layout_from_assets(

View File

@@ -8,11 +8,12 @@ use glam::Vec2;
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
components::{ components::{
Component,
button::{ButtonClickCallback, ComponentButton}, button::{ButtonClickCallback, ComponentButton},
checkbox::ComponentCheckbox, checkbox::ComponentCheckbox,
Component,
}, },
drawing::Color, drawing::Color,
font_config::WguiFontConfig,
globals::WguiGlobals, globals::WguiGlobals,
i18n::Translation, i18n::Translation,
layout::{Layout, LayoutParams, RcLayout, Widget}, layout::{Layout, LayoutParams, RcLayout, Widget},
@@ -76,6 +77,7 @@ impl TestbedGeneric {
let globals = WguiGlobals::new( let globals = WguiGlobals::new(
Box::new(assets::Asset {}), Box::new(assets::Asset {}),
wgui::globals::Defaults::default(), wgui::globals::Defaults::default(),
&WguiFontConfig::default(),
)?; )?;
let extra = ParseDocumentExtra { let extra = ParseDocumentExtra {

View File

@@ -30,3 +30,4 @@ taffy = "0.9.1"
vulkano = { workspace = true } vulkano = { workspace = true }
vulkano-shaders = { workspace = true } vulkano-shaders = { workspace = true }
rust-embed = { workspace = true } rust-embed = { workspace = true }
flate2 = "1.1.5"

View File

@@ -1,3 +1,5 @@
use flate2::read::GzDecoder;
use std::io::Read;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@@ -74,6 +76,13 @@ impl Default for AssetPathOwned {
pub trait AssetProvider { pub trait AssetProvider {
fn load_from_path(&mut self, path: &str) -> anyhow::Result<Vec<u8>>; fn load_from_path(&mut self, path: &str) -> anyhow::Result<Vec<u8>>;
fn load_from_path_gzip(&mut self, path: &str) -> anyhow::Result<Vec<u8>> {
let compressed = self.load_from_path(path)?;
let mut gz = GzDecoder::new(&compressed[..]);
let mut out = Vec::new();
gz.read_to_end(&mut out)?;
Ok(out)
}
} }
// replace "./foo/bar/../file.txt" with "./foo/file.txt" // replace "./foo/bar/../file.txt" with "./foo/file.txt"

View File

@@ -8,9 +8,9 @@ use crate::{
animation::{Animation, AnimationEasing}, animation::{Animation, AnimationEasing},
components::{Component, ComponentBase, ComponentTrait, InitData}, components::{Component, ComponentBase, ComponentTrait, InitData},
drawing::Color, drawing::Color,
event::{CallbackDataCommon, EventAlterables, EventListenerCollection, EventListenerID, EventListenerKind}, event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind},
i18n::Translation, i18n::Translation,
layout::{self, LayoutState, WidgetID, WidgetPair}, layout::{self, WidgetID, WidgetPair},
renderer_vk::text::{FontWeight, TextStyle}, renderer_vk::text::{FontWeight, TextStyle},
widget::{ widget::{
ConstructEssentials, EventResult, ConstructEssentials, EventResult,
@@ -53,6 +53,7 @@ struct State {
#[allow(clippy::struct_field_names)] #[allow(clippy::struct_field_names)]
struct Data { struct Data {
#[allow(dead_code)]
id_container: WidgetID, // Rectangle, transparent if not hovered id_container: WidgetID, // Rectangle, transparent if not hovered
//id_outer_box: WidgetID, // Rectangle, parent of container //id_outer_box: WidgetID, // Rectangle, parent of container

View File

@@ -17,6 +17,7 @@ pub struct InitData<'a> {
// common component data // common component data
#[derive(Default)] #[derive(Default)]
pub struct ComponentBase { pub struct ComponentBase {
#[allow(dead_code)]
lhandles: Vec<EventListenerID>, lhandles: Vec<EventListenerID>,
} }

View File

@@ -53,7 +53,8 @@ struct State {
} }
struct Data { struct Data {
body: WidgetID, // Div #[allow(dead_code)]
body: WidgetID, // Div
slider_handle_rect_id: WidgetID, // Rectangle slider_handle_rect_id: WidgetID, // Rectangle
slider_text_id: WidgetID, // Text slider_text_id: WidgetID, // Text
slider_handle_node: taffy::NodeId, slider_handle_node: taffy::NodeId,

View File

@@ -7,6 +7,7 @@ use taffy::TraversePartialTree;
use crate::{ use crate::{
drawing, drawing,
event::EventAlterables, event::EventAlterables,
globals::Globals,
layout::Widget, layout::Widget,
renderer_vk::text::{TextShadow, custom_glyph::CustomGlyph}, renderer_vk::text::{TextShadow, custom_glyph::CustomGlyph},
stack::{self, ScissorBoundary, ScissorStack, TransformStack}, stack::{self, ScissorBoundary, ScissorStack, TransformStack},
@@ -169,6 +170,7 @@ pub enum RenderPrimitive {
} }
pub struct DrawParams<'a> { pub struct DrawParams<'a> {
pub globals: &'a Globals,
pub layout: &'a mut Layout, pub layout: &'a mut Layout,
pub debug_draw: bool, pub debug_draw: bool,
pub alpha: f32, // timestep alpha, 0.0 - 1.0, used for motion interpolation if rendering above tick rate: smoother animations or scrolling pub alpha: f32, // timestep alpha, 0.0 - 1.0, used for motion interpolation if rendering above tick rate: smoother animations or scrolling
@@ -347,6 +349,7 @@ pub fn draw(params: &mut DrawParams) -> anyhow::Result<Vec<RenderPrimitive>> {
let mut alterables = EventAlterables::default(); let mut alterables = EventAlterables::default();
let mut state = DrawState { let mut state = DrawState {
globals: params.globals,
primitives: &mut primitives, primitives: &mut primitives,
transform_stack: &mut transform_stack, transform_stack: &mut transform_stack,
scissor_stack: &mut scissor_stack, scissor_stack: &mut scissor_stack,

View File

@@ -2,7 +2,6 @@ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
cell::RefMut, cell::RefMut,
collections::HashSet, collections::HashSet,
ops::Deref,
}; };
use glam::Vec2; use glam::Vec2;

45
wgui/src/font_config.rs Normal file
View File

@@ -0,0 +1,45 @@
use parking_lot::Mutex;
#[derive(Default)]
pub struct WguiFontConfig<'a> {
pub binaries: Vec<&'a [u8]>,
pub family_name_sans_serif: &'a str,
pub family_name_serif: &'a str,
pub family_name_monospace: &'a str,
}
pub struct WguiFontSystem {
pub system: Mutex<cosmic_text::FontSystem>,
}
impl WguiFontSystem {
pub fn new(config: &WguiFontConfig) -> Self {
let mut db = cosmic_text::fontdb::Database::new();
let system = if config.binaries.is_empty() {
cosmic_text::FontSystem::new()
} else {
for binary in &config.binaries {
// binary data is copied and preserved here
db.load_font_data(binary.to_vec());
}
if !config.family_name_sans_serif.is_empty() {
db.set_sans_serif_family(config.family_name_sans_serif);
}
if !config.family_name_serif.is_empty() {
db.set_serif_family(config.family_name_serif);
}
// we don't require anything special, at least for now
let locale = String::from("C");
cosmic_text::FontSystem::new_with_locale_and_db(locale, db)
};
Self {
system: Mutex::new(system),
}
}
}

View File

@@ -7,6 +7,7 @@ use std::{
use crate::{ use crate::{
assets::{AssetPath, AssetProvider}, assets::{AssetPath, AssetProvider},
assets_internal, drawing, assets_internal, drawing,
font_config::{WguiFontConfig, WguiFontSystem},
i18n::I18n, i18n::I18n,
}; };
@@ -31,13 +32,18 @@ pub struct Globals {
pub assets_builtin: Box<dyn AssetProvider>, pub assets_builtin: Box<dyn AssetProvider>,
pub i18n_builtin: I18n, pub i18n_builtin: I18n,
pub defaults: Defaults, pub defaults: Defaults,
pub font_system: WguiFontSystem,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct WguiGlobals(Rc<RefCell<Globals>>); pub struct WguiGlobals(Rc<RefCell<Globals>>);
impl WguiGlobals { impl WguiGlobals {
pub fn new(mut assets_builtin: Box<dyn AssetProvider>, defaults: Defaults) -> anyhow::Result<Self> { pub fn new(
mut assets_builtin: Box<dyn AssetProvider>,
defaults: Defaults,
font_config: &WguiFontConfig,
) -> anyhow::Result<Self> {
let i18n_builtin = I18n::new(&mut assets_builtin)?; let i18n_builtin = I18n::new(&mut assets_builtin)?;
let assets_internal = Box::new(assets_internal::AssetInternal {}); let assets_internal = Box::new(assets_internal::AssetInternal {});
@@ -46,6 +52,7 @@ impl WguiGlobals {
assets_builtin, assets_builtin,
i18n_builtin, i18n_builtin,
defaults, defaults,
font_system: WguiFontSystem::new(font_config),
})))) }))))
} }
@@ -81,4 +88,8 @@ impl WguiGlobals {
pub fn assets_builtin(&self) -> RefMut<'_, Box<dyn AssetProvider>> { pub fn assets_builtin(&self) -> RefMut<'_, Box<dyn AssetProvider>> {
RefMut::map(self.0.borrow_mut(), |x| &mut x.assets_builtin) RefMut::map(self.0.borrow_mut(), |x| &mut x.assets_builtin)
} }
pub fn font_system(&self) -> RefMut<'_, WguiFontSystem> {
RefMut::map(self.0.borrow_mut(), |x| &mut x.font_system)
}
} }

View File

@@ -75,7 +75,7 @@ impl I18n {
log::info!("Guessed system language: {lang}"); log::info!("Guessed system language: {lang}");
match lang.as_str() { match lang.as_str() {
"en" | "pl" | "it" | "ja" | "es" => {} "en" | "pl" | "it" | "ja" | "es" | "de" => {}
_ => { _ => {
log::warn!("Unsupported language \"{}\", defaulting to \"en\".", lang.as_str()); log::warn!("Unsupported language \"{}\", defaulting to \"en\".", lang.as_str());

View File

@@ -198,7 +198,7 @@ fn add_child_internal(
pub struct LayoutCommon<'a> { pub struct LayoutCommon<'a> {
alterables: EventAlterables, alterables: EventAlterables,
layout: &'a mut Layout, pub layout: &'a mut Layout,
} }
impl LayoutCommon<'_> { impl LayoutCommon<'_> {
@@ -564,6 +564,8 @@ impl Layout {
log::debug!("re-computing layout, size {}x{}", size.x, size.y); log::debug!("re-computing layout, size {}x{}", size.x, size.y);
self.prev_size = size; self.prev_size = size;
let globals = self.state.globals.get();
self.state.tree.compute_layout_with_measure( self.state.tree.compute_layout_with_measure(
self.tree_root_node, self.tree_root_node,
taffy::Size { taffy::Size {
@@ -583,7 +585,10 @@ impl Layout {
None => taffy::Size::ZERO, None => taffy::Size::ZERO,
Some(h) => { Some(h) => {
if let Some(w) = self.state.widgets.get(*h) { if let Some(w) = self.state.widgets.get(*h) {
w.0.borrow_mut().obj.measure(known_dimensions, available_space) w.0
.borrow_mut()
.obj
.measure(&globals, known_dimensions, available_space)
} else { } else {
taffy::Size::ZERO taffy::Size::ZERO
} }

View File

@@ -27,6 +27,7 @@ mod assets_internal;
pub mod components; pub mod components;
pub mod drawing; pub mod drawing;
pub mod event; pub mod event;
pub mod font_config;
pub mod gfx; pub mod gfx;
pub mod globals; pub mod globals;
pub mod i18n; pub mod i18n;

View File

@@ -8,7 +8,7 @@ mod widget_rectangle;
mod widget_sprite; mod widget_sprite;
use crate::{ use crate::{
assets::{normalize_path, AssetPath, AssetPathOwned}, assets::{AssetPath, AssetPathOwned, normalize_path},
components::{Component, ComponentWeak}, components::{Component, ComponentWeak},
drawing::{self}, drawing::{self},
globals::WguiGlobals, globals::WguiGlobals,
@@ -624,7 +624,7 @@ pub fn replace_vars(input: &str, vars: &HashMap<Rc<str>, Rc<str>>) -> Rc<str> {
if let Some(replacement) = vars.get(input_var) { if let Some(replacement) = vars.get(input_var) {
replacement.clone() replacement.clone()
} else { } else {
log::warn!("failed to replace var named \"{input_var}\" (not found)"); // failed to find var, return an empty string
Rc::from("") Rc::from("")
} }
}); });

View File

@@ -65,6 +65,7 @@ pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle {
} }
}, },
"weight" => match value { "weight" => match value {
"light" => style.weight = Some(FontWeight::Light),
"normal" => style.weight = Some(FontWeight::Normal), "normal" => style.weight = Some(FontWeight::Normal),
"bold" => style.weight = Some(FontWeight::Bold), "bold" => style.weight = Some(FontWeight::Bold),
_ => { _ => {

View File

@@ -7,13 +7,14 @@ use vulkano::pipeline::graphics::viewport;
use crate::{ use crate::{
drawing::{self}, drawing::{self},
font_config,
gfx::{WGfx, cmd::GfxCommandBuffer}, gfx::{WGfx, cmd::GfxCommandBuffer},
}; };
use super::{ use super::{
rect::{RectPipeline, RectRenderer}, rect::{RectPipeline, RectRenderer},
text::{ text::{
DEFAULT_METRICS, FONT_SYSTEM, SWASH_CACHE, TextArea, TextBounds, DEFAULT_METRICS, SWASH_CACHE, TextArea, TextBounds,
text_atlas::{TextAtlas, TextPipeline}, text_atlas::{TextAtlas, TextPipeline},
text_renderer::TextRenderer, text_renderer::TextRenderer,
}, },
@@ -51,6 +52,7 @@ impl RendererPass<'_> {
fn submit( fn submit(
&mut self, &mut self,
font_system: &font_config::WguiFontSystem,
gfx: &Arc<WGfx>, gfx: &Arc<WGfx>,
viewport: &mut Viewport, viewport: &mut Viewport,
cmd_buf: &mut GfxCommandBuffer, cmd_buf: &mut GfxCommandBuffer,
@@ -90,7 +92,7 @@ impl RendererPass<'_> {
self.rect_renderer.render(gfx, viewport, &vk_scissor, cmd_buf)?; self.rect_renderer.render(gfx, viewport, &vk_scissor, cmd_buf)?;
{ {
let mut font_system = FONT_SYSTEM.lock(); let mut font_system = font_system.system.lock();
let mut swash_cache = SWASH_CACHE.lock(); let mut swash_cache = SWASH_CACHE.lock();
self.text_renderer.prepare( self.text_renderer.prepare(
@@ -217,6 +219,7 @@ impl Context {
pub fn draw( pub fn draw(
&mut self, &mut self,
font_system: &font_config::WguiFontSystem,
shared: &mut SharedContext, shared: &mut SharedContext,
cmd_buf: &mut GfxCommandBuffer, cmd_buf: &mut GfxCommandBuffer,
primitives: &[drawing::RenderPrimitive], primitives: &[drawing::RenderPrimitive],
@@ -302,7 +305,13 @@ impl Context {
}; };
for mut pass in passes { for mut pass in passes {
pass.submit(&shared.gfx, &mut self.viewport, cmd_buf, &mut atlas.text_atlas)?; pass.submit(
font_system,
&shared.gfx,
&mut self.viewport,
cmd_buf,
&mut atlas.text_atlas,
)?;
} }
Ok(res) Ok(res)

View File

@@ -13,7 +13,6 @@ use parking_lot::Mutex;
use crate::drawing::{self}; use crate::drawing::{self};
pub static FONT_SYSTEM: LazyLock<Mutex<FontSystem>> = LazyLock::new(|| Mutex::new(FontSystem::new()));
pub static SWASH_CACHE: LazyLock<Mutex<SwashCache>> = LazyLock::new(|| Mutex::new(SwashCache::new())); pub static SWASH_CACHE: LazyLock<Mutex<SwashCache>> = LazyLock::new(|| Mutex::new(SwashCache::new()));
/// Used in case no `font_size` is defined /// Used in case no `font_size` is defined
@@ -102,6 +101,7 @@ impl From<FontStyle> for Style {
#[derive(Default, Debug, Clone, Copy)] #[derive(Default, Debug, Clone, Copy)]
pub enum FontWeight { pub enum FontWeight {
Light,
#[default] #[default]
Normal, Normal,
Bold, Bold,
@@ -110,6 +110,7 @@ pub enum FontWeight {
impl From<FontWeight> for Weight { impl From<FontWeight> for Weight {
fn from(value: FontWeight) -> Self { fn from(value: FontWeight) -> Self {
match value { match value {
FontWeight::Light => Self::LIGHT,
FontWeight::Normal => Self::NORMAL, FontWeight::Normal => Self::NORMAL,
FontWeight::Bold => Self::BOLD, FontWeight::Bold => Self::BOLD,
} }

View File

@@ -8,9 +8,9 @@ use crate::{
drawing::{self, Boundary, PrimitiveExtent}, drawing::{self, Boundary, PrimitiveExtent},
event::CallbackDataCommon, event::CallbackDataCommon,
globals::Globals, globals::Globals,
i18n::{I18n, Translation}, i18n::Translation,
layout::WidgetID, layout::WidgetID,
renderer_vk::text::{TextStyle, FONT_SYSTEM}, renderer_vk::text::TextStyle,
}; };
use super::{WidgetObj, WidgetState}; use super::{WidgetObj, WidgetState};
@@ -41,7 +41,7 @@ impl WidgetLabel {
let mut buffer = Buffer::new_empty(metrics); let mut buffer = Buffer::new_empty(metrics);
{ {
let mut font_system = FONT_SYSTEM.lock(); let mut font_system = globals.font_system.system.lock();
let mut buffer = buffer.borrow_with(&mut font_system); let mut buffer = buffer.borrow_with(&mut font_system);
buffer.set_wrap(wrap); buffer.set_wrap(wrap);
@@ -63,19 +63,19 @@ impl WidgetLabel {
// set text without layout/re-render update. // set text without layout/re-render update.
// Not recommended unless the widget wasn't rendered yet (first init). // Not recommended unless the widget wasn't rendered yet (first init).
pub fn set_text_simple(&mut self, i18n: &mut I18n, translation: Translation) -> bool { pub fn set_text_simple(&mut self, globals: &mut Globals, translation: Translation) -> bool {
if self.params.content == translation { if self.params.content == translation {
return false; return false;
} }
self.params.content = translation; self.params.content = translation;
let attrs = Attrs::from(&self.params.style); let attrs = Attrs::from(&self.params.style);
let mut font_system = FONT_SYSTEM.lock(); let mut font_system = globals.font_system.system.lock();
let mut buffer = self.buffer.borrow_mut(); let mut buffer = self.buffer.borrow_mut();
buffer.set_rich_text( buffer.set_rich_text(
&mut font_system, &mut font_system,
[(self.params.content.generate(i18n).as_ref(), attrs)], [(self.params.content.generate(&mut globals.i18n_builtin).as_ref(), attrs)],
&Attrs::new(), &Attrs::new(),
Shaping::Advanced, Shaping::Advanced,
self.params.style.align.map(Into::into), self.params.style.align.map(Into::into),
@@ -93,7 +93,9 @@ impl WidgetLabel {
// set text and check if it needs to be re-rendered/re-layouted // set text and check if it needs to be re-rendered/re-layouted
pub fn set_text(&mut self, common: &mut CallbackDataCommon, translation: Translation) { pub fn set_text(&mut self, common: &mut CallbackDataCommon, translation: Translation) {
if self.set_text_simple(&mut common.i18n(), translation) { let mut globals = common.state.globals.get();
if self.set_text_simple(&mut globals, translation) {
common.mark_widget_dirty(self.id); common.mark_widget_dirty(self.id);
} }
} }
@@ -113,7 +115,7 @@ impl WidgetObj for WidgetLabel {
if self.last_boundary != boundary { if self.last_boundary != boundary {
self.last_boundary = boundary; self.last_boundary = boundary;
let mut font_system = FONT_SYSTEM.lock(); let mut font_system = state.globals.font_system.system.lock();
let mut buffer = self.buffer.borrow_mut(); let mut buffer = self.buffer.borrow_mut();
buffer.set_size(&mut font_system, Some(boundary.size.x), Some(boundary.size.y)); buffer.set_size(&mut font_system, Some(boundary.size.x), Some(boundary.size.y));
} }
@@ -130,6 +132,7 @@ impl WidgetObj for WidgetLabel {
fn measure( fn measure(
&mut self, &mut self,
globals: &Globals,
known_dimensions: taffy::Size<Option<f32>>, known_dimensions: taffy::Size<Option<f32>>,
available_space: taffy::Size<taffy::AvailableSpace>, available_space: taffy::Size<taffy::AvailableSpace>,
) -> taffy::Size<f32> { ) -> taffy::Size<f32> {
@@ -140,7 +143,8 @@ impl WidgetObj for WidgetLabel {
AvailableSpace::Definite(width) => Some(width), AvailableSpace::Definite(width) => Some(width),
}); });
let mut font_system = FONT_SYSTEM.lock(); let wgui_font_system = &globals.font_system;
let mut font_system = wgui_font_system.system.lock();
let mut buffer = self.buffer.borrow_mut(); let mut buffer = self.buffer.borrow_mut();
buffer.set_size(&mut font_system, width_constraint, None); buffer.set_size(&mut font_system, width_constraint, None);

View File

@@ -10,6 +10,7 @@ use crate::{
EventListenerKind::{self, InternalStateChange, MouseLeave}, EventListenerKind::{self, InternalStateChange, MouseLeave},
MouseWheelEvent, MouseWheelEvent,
}, },
globals::Globals,
layout::{Layout, LayoutState, WidgetID}, layout::{Layout, LayoutState, WidgetID},
stack::{ScissorStack, TransformStack}, stack::{ScissorStack, TransformStack},
}; };
@@ -108,6 +109,7 @@ impl WidgetState {
// global draw params // global draw params
pub struct DrawState<'a> { pub struct DrawState<'a> {
pub globals: &'a Globals,
pub layout: &'a Layout, pub layout: &'a Layout,
pub primitives: &'a mut Vec<RenderPrimitive>, pub primitives: &'a mut Vec<RenderPrimitive>,
pub transform_stack: &'a mut TransformStack, pub transform_stack: &'a mut TransformStack,
@@ -151,6 +153,7 @@ pub trait WidgetObj: AnyTrait {
fn measure( fn measure(
&mut self, &mut self,
_globals: &Globals,
_known_dimensions: taffy::Size<Option<f32>>, _known_dimensions: taffy::Size<Option<f32>>,
_available_space: taffy::Size<taffy::AvailableSpace>, _available_space: taffy::Size<taffy::AvailableSpace>,
) -> taffy::Size<f32> { ) -> taffy::Size<f32> {
@@ -181,11 +184,7 @@ impl EventResult {
#[must_use] #[must_use]
pub fn merge(self, other: Self) -> Self { pub fn merge(self, other: Self) -> Self {
if self > other { if self > other { self } else { other }
self
} else {
other
}
} }
} }

View File

@@ -5,10 +5,11 @@ use slotmap::Key;
use crate::{ use crate::{
drawing::{self, PrimitiveExtent}, drawing::{self, PrimitiveExtent},
globals::Globals,
layout::WidgetID, layout::WidgetID,
renderer_vk::text::{ renderer_vk::text::{
DEFAULT_METRICS,
custom_glyph::{CustomGlyph, CustomGlyphData}, custom_glyph::{CustomGlyph, CustomGlyphData},
DEFAULT_METRICS, FONT_SYSTEM,
}, },
}; };
@@ -67,7 +68,7 @@ impl WidgetObj for WidgetSprite {
let mut buffer = Buffer::new_empty(DEFAULT_METRICS); let mut buffer = Buffer::new_empty(DEFAULT_METRICS);
{ {
let mut font_system = FONT_SYSTEM.lock(); let mut font_system = state.globals.font_system.system.lock();
let mut buffer = buffer.borrow_with(&mut font_system); let mut buffer = buffer.borrow_with(&mut font_system);
let attrs = Attrs::new().color(Color::rgb(255, 0, 255)).weight(Weight::BOLD); let attrs = Attrs::new().color(Color::rgb(255, 0, 255)).weight(Weight::BOLD);
@@ -88,6 +89,7 @@ impl WidgetObj for WidgetSprite {
fn measure( fn measure(
&mut self, &mut self,
_globals: &Globals,
_known_dimensions: taffy::Size<Option<f32>>, _known_dimensions: taffy::Size<Option<f32>>,
_available_space: taffy::Size<taffy::AvailableSpace>, _available_space: taffy::Size<taffy::AvailableSpace>,
) -> taffy::Size<f32> { ) -> taffy::Size<f32> {

View File

@@ -15,8 +15,8 @@ use wgui::{
event::{self, EventCallback}, event::{self, EventCallback},
i18n::Translation, i18n::Translation,
layout::Layout, layout::Layout,
parser::{parse_color_hex, CustomAttribsInfoOwned}, parser::{CustomAttribsInfoOwned, parse_color_hex},
widget::{label::WidgetLabel, EventResult}, widget::{EventResult, label::WidgetLabel},
}; };
use crate::state::AppState; use crate::state::AppState;
@@ -123,13 +123,13 @@ pub(super) fn setup_custom_label<S: 'static>(
let pretty_tz = maybe_pretty_tz.as_ref().map_or("Local", |x| x.as_str()); let pretty_tz = maybe_pretty_tz.as_ref().map_or("Local", |x| x.as_str());
let mut i18n = layout.state.globals.i18n(); let mut globals = layout.state.globals.get();
layout layout
.state .state
.widgets .widgets
.get_as::<WidgetLabel>(attribs.widget_id) .get_as::<WidgetLabel>(attribs.widget_id)
.unwrap() .unwrap()
.set_text_simple(&mut i18n, Translation::from_raw_text(pretty_tz)); .set_text_simple(&mut globals, Translation::from_raw_text(pretty_tz));
// does not need to be dynamic // does not need to be dynamic
return; return;

View File

@@ -1,7 +1,7 @@
use std::{cell::RefCell, rc::Rc, sync::Arc}; use std::{cell::RefCell, rc::Rc, sync::Arc};
use button::setup_custom_button; use button::setup_custom_button;
use glam::{vec2, Affine2, Vec2}; use glam::{Affine2, Vec2, vec2};
use label::setup_custom_label; use label::setup_custom_label;
use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView}; use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView};
use wgui::{ use wgui::{
@@ -15,14 +15,14 @@ use wgui::{
layout::{Layout, LayoutParams, WidgetID}, layout::{Layout, LayoutParams, WidgetID},
parser::ParserState, parser::ParserState,
renderer_vk::context::Context as WguiContext, renderer_vk::context::Context as WguiContext,
widget::{label::WidgetLabel, rectangle::WidgetRectangle, EventResult}, widget::{EventResult, label::WidgetLabel, rectangle::WidgetRectangle},
}; };
use crate::{ use crate::{
backend::input::{Haptics, HoverResult, PointerHit, PointerMode}, backend::input::{Haptics, HoverResult, PointerHit, PointerMode},
graphics::{CommandBuffers, ExtentExt}, graphics::{CommandBuffers, ExtentExt},
state::AppState, state::AppState,
windowing::backend::{ui_transform, FrameMeta, OverlayBackend, ShouldRender}, windowing::backend::{FrameMeta, OverlayBackend, ShouldRender, ui_transform},
}; };
use super::{timer::GuiTimer, timestep::Timestep}; use super::{timer::GuiTimer, timestep::Timestep};
@@ -237,13 +237,21 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
wgui::gfx::cmd::WGfxClearMode::Clear([0.0, 0.0, 0.0, 0.0]), wgui::gfx::cmd::WGfxClearMode::Clear([0.0, 0.0, 0.0, 0.0]),
)?; )?;
let globals = self.layout.state.globals.clone(); // sorry
let mut globals = globals.get();
let primitives = wgui::drawing::draw(&mut wgui::drawing::DrawParams { let primitives = wgui::drawing::draw(&mut wgui::drawing::DrawParams {
globals: &mut globals,
layout: &mut self.layout, layout: &mut self.layout,
debug_draw: false, debug_draw: false,
alpha, alpha,
})?; })?;
self.context self.context.draw(
.draw(&mut app.wgui_shared, &mut cmd_buf, &primitives)?; &globals.font_system,
&mut app.wgui_shared,
&mut cmd_buf,
&primitives,
)?;
cmd_buf.end_rendering()?; cmd_buf.end_rendering()?;
buf.push(cmd_buf.build()?); buf.push(cmd_buf.build()?);

View File

@@ -1,10 +1,11 @@
use glam::Affine3A; use glam::Affine3A;
use idmap::IdMap; use idmap::IdMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::{smallvec, SmallVec}; use smallvec::{SmallVec, smallvec};
use std::sync::Arc; use std::sync::Arc;
use wgui::{ use wgui::{
gfx::WGfx, globals::WguiGlobals, renderer_vk::context::SharedContext as WSharedContext, font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals,
renderer_vk::context::SharedContext as WSharedContext,
}; };
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
@@ -97,6 +98,7 @@ impl AppState {
wgui_globals: WguiGlobals::new( wgui_globals: WguiGlobals::new(
Box::new(gui::asset::GuiAsset {}), Box::new(gui::asset::GuiAsset {}),
wgui::globals::Defaults::default(), wgui::globals::Defaults::default(),
&WguiFontConfig::default(),
)?, )?,
#[cfg(feature = "osc")] #[cfg(feature = "osc")]