wgui: Separate user and wgui assets, topmost widgets (poc)
This commit is contained in:
@@ -1,5 +1,77 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AssetPath<'a> {
|
||||
WguiInternal(&'a str), // tied to internal wgui AssetProvider. Used internally
|
||||
BuiltIn(&'a str), // tied to user AssetProvider
|
||||
Filesystem(&'a str), // tied to filesystem path
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AssetPathOwned {
|
||||
WguiInternal(PathBuf),
|
||||
BuiltIn(PathBuf),
|
||||
Filesystem(PathBuf),
|
||||
}
|
||||
|
||||
impl AssetPath<'_> {
|
||||
pub const fn get_str(&self) -> &str {
|
||||
match &self {
|
||||
AssetPath::WguiInternal(path) => path,
|
||||
AssetPath::BuiltIn(path) => path,
|
||||
AssetPath::Filesystem(path) => path,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> AssetPathOwned {
|
||||
match self {
|
||||
AssetPath::WguiInternal(path) => AssetPathOwned::WguiInternal(PathBuf::from(path)),
|
||||
AssetPath::BuiltIn(path) => AssetPathOwned::BuiltIn(PathBuf::from(path)),
|
||||
AssetPath::Filesystem(path) => AssetPathOwned::Filesystem(PathBuf::from(path)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AssetPathOwned {
|
||||
pub fn as_ref(&'_ self) -> AssetPath<'_> {
|
||||
match self {
|
||||
AssetPathOwned::WguiInternal(buf) => AssetPath::WguiInternal(buf.to_str().unwrap()),
|
||||
AssetPathOwned::BuiltIn(buf) => AssetPath::BuiltIn(buf.to_str().unwrap()),
|
||||
AssetPathOwned::Filesystem(buf) => AssetPath::Filesystem(buf.to_str().unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn get_path_buf(&self) -> &PathBuf {
|
||||
match self {
|
||||
AssetPathOwned::WguiInternal(buf) => buf,
|
||||
AssetPathOwned::BuiltIn(buf) => buf,
|
||||
AssetPathOwned::Filesystem(buf) => buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AssetPathOwned {
|
||||
#[must_use]
|
||||
pub fn push_include(&self, include: &str) -> AssetPathOwned {
|
||||
let buf = self.get_path_buf();
|
||||
let mut new_path = buf.parent().unwrap_or_else(|| Path::new("/")).to_path_buf();
|
||||
new_path.push(include);
|
||||
let new_path = normalize_path(&new_path);
|
||||
|
||||
match self {
|
||||
AssetPathOwned::WguiInternal(_) => AssetPathOwned::WguiInternal(new_path),
|
||||
AssetPathOwned::BuiltIn(_) => AssetPathOwned::BuiltIn(new_path),
|
||||
AssetPathOwned::Filesystem(_) => AssetPathOwned::Filesystem(new_path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AssetPathOwned {
|
||||
fn default() -> Self {
|
||||
Self::WguiInternal(PathBuf::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AssetProvider {
|
||||
fn load_from_path(&mut self, path: &str) -> anyhow::Result<Vec<u8>>;
|
||||
}
|
||||
|
||||
12
wgui/src/assets_internal.rs
Normal file
12
wgui/src/assets_internal.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#[derive(rust_embed::Embed)]
|
||||
#[folder = "assets/"]
|
||||
pub struct AssetInternal;
|
||||
|
||||
impl crate::assets::AssetProvider for AssetInternal {
|
||||
fn load_from_path(&mut self, path: &str) -> anyhow::Result<Vec<u8>> {
|
||||
match AssetInternal::get(path) {
|
||||
Some(data) => Ok(data.data.to_vec()),
|
||||
None => anyhow::bail!("internal file {path} not found"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,6 +146,7 @@ pub struct PrimitiveExtent {
|
||||
}
|
||||
|
||||
pub enum RenderPrimitive {
|
||||
NewPass,
|
||||
Rectangle(PrimitiveExtent, Rectangle),
|
||||
Text(PrimitiveExtent, Rc<RefCell<Buffer>>, Option<TextShadow>),
|
||||
Sprite(PrimitiveExtent, Option<CustomGlyph>), //option because we want as_slice
|
||||
@@ -276,7 +277,7 @@ fn draw_widget(
|
||||
|
||||
widget_state.draw_all(state, &draw_params);
|
||||
|
||||
draw_children(params, state, node_id);
|
||||
draw_children(params, state, node_id, false);
|
||||
|
||||
if scissor_pushed {
|
||||
state.scissor_stack.pop();
|
||||
@@ -296,7 +297,7 @@ fn draw_widget(
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taffy::NodeId) {
|
||||
fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taffy::NodeId, is_topmost: bool) {
|
||||
let layout = ¶ms.layout;
|
||||
|
||||
for node_id in layout.state.tree.child_ids(parent_node_id) {
|
||||
@@ -316,6 +317,10 @@ fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taf
|
||||
};
|
||||
|
||||
draw_widget(params, state, node_id, style, widget);
|
||||
|
||||
if is_topmost {
|
||||
state.primitives.push(RenderPrimitive::NewPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,14 +329,6 @@ pub fn draw(params: &mut DrawParams) -> anyhow::Result<Vec<RenderPrimitive>> {
|
||||
let mut transform_stack = TransformStack::new();
|
||||
let mut scissor_stack = ScissorStack::new();
|
||||
|
||||
let Some(root_widget) = params.layout.state.widgets.get(params.layout.root_widget) else {
|
||||
panic!();
|
||||
};
|
||||
|
||||
let Ok(style) = params.layout.state.tree.style(params.layout.root_node) else {
|
||||
panic!();
|
||||
};
|
||||
|
||||
let mut alterables = EventAlterables::default();
|
||||
|
||||
let mut state = DrawState {
|
||||
@@ -342,7 +339,8 @@ pub fn draw(params: &mut DrawParams) -> anyhow::Result<Vec<RenderPrimitive>> {
|
||||
alterables: &mut alterables,
|
||||
};
|
||||
|
||||
draw_widget(params, &mut state, params.layout.root_node, style, root_widget);
|
||||
draw_children(params, &mut state, params.layout.tree_root_node, true);
|
||||
|
||||
params.layout.process_alterables(alterables)?;
|
||||
|
||||
Ok(primitives)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::{marker::PhantomData, ops::Range, sync::Arc};
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use vulkano::{
|
||||
buffer::{
|
||||
allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo},
|
||||
BufferContents, BufferUsage, Subbuffer,
|
||||
allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo},
|
||||
},
|
||||
descriptor_set::{
|
||||
layout::{DescriptorBindingFlags, DescriptorSetLayoutCreateFlags},
|
||||
DescriptorSet, WriteDescriptorSet,
|
||||
layout::{DescriptorBindingFlags, DescriptorSetLayoutCreateFlags},
|
||||
},
|
||||
format::Format,
|
||||
image::{
|
||||
@@ -17,8 +17,9 @@ use vulkano::{
|
||||
},
|
||||
memory::allocator::MemoryTypeFilter,
|
||||
pipeline::{
|
||||
DynamicState, GraphicsPipeline, Pipeline, PipelineLayout,
|
||||
graphics::{
|
||||
self,
|
||||
self, GraphicsPipelineCreateInfo,
|
||||
color_blend::{AttachmentBlend, ColorBlendAttachmentState, ColorBlendState},
|
||||
input_assembly::{InputAssemblyState, PrimitiveTopology},
|
||||
multisample::MultisampleState,
|
||||
@@ -26,15 +27,13 @@ use vulkano::{
|
||||
subpass::PipelineRenderingCreateInfo,
|
||||
vertex_input::{Vertex, VertexDefinition, VertexInputState},
|
||||
viewport::ViewportState,
|
||||
GraphicsPipelineCreateInfo,
|
||||
},
|
||||
layout::PipelineDescriptorSetLayoutCreateInfo,
|
||||
DynamicState, GraphicsPipeline, Pipeline, PipelineLayout,
|
||||
},
|
||||
shader::{EntryPoint, ShaderModule},
|
||||
};
|
||||
|
||||
use super::{pass::WGfxPass, WGfx};
|
||||
use super::{WGfx, pass::WGfxPass};
|
||||
|
||||
pub struct WGfxPipeline<V> {
|
||||
pub graphics: Arc<WGfx>,
|
||||
@@ -47,6 +46,7 @@ impl<V> WGfxPipeline<V>
|
||||
where
|
||||
V: Sized,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new_from_stages(
|
||||
graphics: Arc<WGfx>,
|
||||
format: Format,
|
||||
@@ -207,21 +207,25 @@ impl WPipelineCreateInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_blend(mut self, blend: AttachmentBlend) -> Self {
|
||||
#[must_use]
|
||||
pub const fn use_blend(mut self, blend: AttachmentBlend) -> Self {
|
||||
self.blend = Some(blend);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn use_topology(mut self, topology: PrimitiveTopology) -> Self {
|
||||
#[must_use]
|
||||
pub const fn use_topology(mut self, topology: PrimitiveTopology) -> Self {
|
||||
self.topology = topology;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn use_instanced(mut self) -> Self {
|
||||
#[must_use]
|
||||
pub const fn use_instanced(mut self) -> Self {
|
||||
self.instanced = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn use_updatable_descriptors(mut self, updatable_sets: SmallVec<[usize; 8]>) -> Self {
|
||||
self.updatable_sets = updatable_sets;
|
||||
self
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
io::Read,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use crate::{assets::AssetProvider, drawing, i18n::I18n};
|
||||
use crate::{
|
||||
assets::{AssetPath, AssetProvider},
|
||||
assets_internal, drawing,
|
||||
i18n::I18n,
|
||||
};
|
||||
|
||||
pub struct Defaults {
|
||||
pub dark_mode: bool,
|
||||
@@ -22,8 +27,9 @@ impl Default for Defaults {
|
||||
}
|
||||
|
||||
pub struct Globals {
|
||||
pub assets: Box<dyn AssetProvider>,
|
||||
pub i18n: I18n,
|
||||
pub assets_internal: Box<dyn AssetProvider>,
|
||||
pub assets_builtin: Box<dyn AssetProvider>,
|
||||
pub i18n_builtin: I18n,
|
||||
pub defaults: Defaults,
|
||||
}
|
||||
|
||||
@@ -31,10 +37,33 @@ pub struct Globals {
|
||||
pub struct WguiGlobals(Rc<RefCell<Globals>>);
|
||||
|
||||
impl WguiGlobals {
|
||||
pub fn new(mut assets: Box<dyn AssetProvider>, defaults: Defaults) -> anyhow::Result<Self> {
|
||||
let i18n = I18n::new(&mut assets)?;
|
||||
pub fn new(mut assets_builtin: Box<dyn AssetProvider>, defaults: Defaults) -> anyhow::Result<Self> {
|
||||
let i18n_builtin = I18n::new(&mut assets_builtin)?;
|
||||
let assets_internal = Box::new(assets_internal::AssetInternal {});
|
||||
|
||||
Ok(Self(Rc::new(RefCell::new(Globals { assets, i18n, defaults }))))
|
||||
Ok(Self(Rc::new(RefCell::new(Globals {
|
||||
assets_internal,
|
||||
assets_builtin,
|
||||
i18n_builtin,
|
||||
defaults,
|
||||
}))))
|
||||
}
|
||||
|
||||
pub fn get_asset(&self, asset_path: AssetPath) -> anyhow::Result<Vec<u8>> {
|
||||
match asset_path {
|
||||
AssetPath::WguiInternal(path) => self.assets_internal().load_from_path(path),
|
||||
AssetPath::BuiltIn(path) => self.assets_builtin().load_from_path(path),
|
||||
AssetPath::Filesystem(path) => {
|
||||
let mut file = std::fs::File::open(path)?;
|
||||
/* 16 MiB safeguard */
|
||||
if file.metadata()?.len() > 16 * 1024 * 1024 {
|
||||
anyhow::bail!("Too large file size");
|
||||
}
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)?;
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> RefMut<'_, Globals> {
|
||||
@@ -42,10 +71,14 @@ impl WguiGlobals {
|
||||
}
|
||||
|
||||
pub fn i18n(&self) -> RefMut<'_, I18n> {
|
||||
RefMut::map(self.0.borrow_mut(), |x| &mut x.i18n)
|
||||
RefMut::map(self.0.borrow_mut(), |x| &mut x.i18n_builtin)
|
||||
}
|
||||
|
||||
pub fn assets(&self) -> RefMut<'_, Box<dyn AssetProvider>> {
|
||||
RefMut::map(self.0.borrow_mut(), |x| &mut x.assets)
|
||||
pub fn assets_internal(&self) -> RefMut<'_, Box<dyn AssetProvider>> {
|
||||
RefMut::map(self.0.borrow_mut(), |x| &mut x.assets_internal)
|
||||
}
|
||||
|
||||
pub fn assets_builtin(&self) -> RefMut<'_, Box<dyn AssetProvider>> {
|
||||
RefMut::map(self.0.borrow_mut(), |x| &mut x.assets_builtin)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ use std::{
|
||||
use crate::{
|
||||
animation::Animations,
|
||||
components::{Component, InitData},
|
||||
drawing::{push_scissor_stack, push_transform_stack, Boundary},
|
||||
drawing::{Boundary, push_scissor_stack, push_transform_stack},
|
||||
event::{self, CallbackDataCommon, EventAlterables, EventListenerCollection},
|
||||
globals::WguiGlobals,
|
||||
widget::{self, div::WidgetDiv, EventParams, WidgetObj, WidgetState},
|
||||
widget::{self, EventParams, WidgetObj, WidgetState, div::WidgetDiv},
|
||||
};
|
||||
|
||||
use glam::{vec2, Vec2};
|
||||
use slotmap::{new_key_type, HopSlotMap, SecondaryMap};
|
||||
use glam::{Vec2, vec2};
|
||||
use slotmap::{HopSlotMap, SecondaryMap, new_key_type};
|
||||
use taffy::{NodeId, TaffyTree, TraversePartialTree};
|
||||
|
||||
new_key_type! {
|
||||
@@ -113,8 +113,15 @@ pub struct Layout {
|
||||
pub components_to_init: Vec<Component>,
|
||||
pub widgets_to_tick: Vec<WidgetID>,
|
||||
|
||||
pub root_widget: WidgetID,
|
||||
pub root_node: taffy::NodeId,
|
||||
// *Main root*
|
||||
// contains content_root_widget and topmost widgets
|
||||
pub tree_root_widget: WidgetID,
|
||||
pub tree_root_node: taffy::NodeId,
|
||||
|
||||
// *Main topmost widget*
|
||||
// main topmost widget, always present, parent of `tree_root_widget`
|
||||
pub content_root_widget: WidgetID,
|
||||
pub content_root_node: taffy::NodeId,
|
||||
|
||||
pub prev_size: Vec2,
|
||||
pub content_size: Vec2,
|
||||
@@ -165,6 +172,22 @@ impl Layout {
|
||||
Rc::new(RefCell::new(self))
|
||||
}
|
||||
|
||||
pub fn add_topmost_child(
|
||||
&mut self,
|
||||
widget: WidgetState,
|
||||
style: taffy::Style,
|
||||
) -> anyhow::Result<(WidgetPair, taffy::NodeId)> {
|
||||
self.mark_redraw();
|
||||
add_child_internal(
|
||||
&mut self.state.tree,
|
||||
&mut self.state.widgets,
|
||||
&mut self.state.nodes,
|
||||
Some(self.tree_root_node),
|
||||
widget,
|
||||
style,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_child(
|
||||
&mut self,
|
||||
parent_widget_id: WidgetID,
|
||||
@@ -217,7 +240,7 @@ impl Layout {
|
||||
self.needs_redraw = true;
|
||||
}
|
||||
|
||||
fn process_pending_components(&mut self, alterables: &mut EventAlterables) -> anyhow::Result<()> {
|
||||
fn process_pending_components(&mut self, alterables: &mut EventAlterables) {
|
||||
for comp in &self.components_to_init {
|
||||
let mut common = CallbackDataCommon {
|
||||
state: &self.state,
|
||||
@@ -227,7 +250,6 @@ impl Layout {
|
||||
comp.0.init(&mut InitData { common: &mut common });
|
||||
}
|
||||
self.components_to_init.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_pending_widget_ticks(&mut self, alterables: &mut EventAlterables) {
|
||||
@@ -358,9 +380,7 @@ impl Layout {
|
||||
mut user_data: (&mut U1, &mut U2),
|
||||
) -> anyhow::Result<()> {
|
||||
let mut alterables = EventAlterables::default();
|
||||
|
||||
self.push_event_widget(listeners, self.root_node, event, &mut alterables, &mut user_data)?;
|
||||
|
||||
self.push_event_widget(listeners, self.tree_root_node, event, &mut alterables, &mut user_data)?;
|
||||
self.process_alterables(alterables)?;
|
||||
|
||||
listeners.gc();
|
||||
@@ -376,7 +396,7 @@ impl Layout {
|
||||
globals,
|
||||
};
|
||||
|
||||
let (root_widget, root_node) = add_child_internal(
|
||||
let (tree_root_widget, tree_root_node) = add_child_internal(
|
||||
&mut state.tree,
|
||||
&mut state.widgets,
|
||||
&mut state.nodes,
|
||||
@@ -392,12 +412,23 @@ impl Layout {
|
||||
},
|
||||
)?;
|
||||
|
||||
let (content_root_widget, content_root_node) = add_child_internal(
|
||||
&mut state.tree,
|
||||
&mut state.widgets,
|
||||
&mut state.nodes,
|
||||
Some(tree_root_node),
|
||||
WidgetDiv::create(),
|
||||
taffy::Style::default(),
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
prev_size: Vec2::default(),
|
||||
content_size: Vec2::default(),
|
||||
root_node,
|
||||
root_widget: root_widget.id,
|
||||
tree_root_node,
|
||||
tree_root_widget: tree_root_widget.id,
|
||||
content_root_node,
|
||||
content_root_widget: content_root_widget.id,
|
||||
needs_redraw: true,
|
||||
haptics_triggered: false,
|
||||
animations: Animations::default(),
|
||||
@@ -407,7 +438,7 @@ impl Layout {
|
||||
}
|
||||
|
||||
fn try_recompute_layout(&mut self, size: Vec2) -> anyhow::Result<()> {
|
||||
if !self.state.tree.dirty(self.root_node)? && self.prev_size == size {
|
||||
if !self.state.tree.dirty(self.tree_root_node)? && self.prev_size == size {
|
||||
// Nothing to do
|
||||
return Ok(());
|
||||
}
|
||||
@@ -417,7 +448,7 @@ impl Layout {
|
||||
self.prev_size = size;
|
||||
|
||||
self.state.tree.compute_layout_with_measure(
|
||||
self.root_node,
|
||||
self.tree_root_node,
|
||||
taffy::Size {
|
||||
width: taffy::AvailableSpace::Definite(size.x),
|
||||
height: taffy::AvailableSpace::Definite(size.y),
|
||||
@@ -443,7 +474,7 @@ impl Layout {
|
||||
}
|
||||
},
|
||||
)?;
|
||||
let root_size = self.state.tree.layout(self.root_node).unwrap().size;
|
||||
let root_size = self.state.tree.layout(self.tree_root_node).unwrap().size;
|
||||
if self.content_size.x != root_size.width || self.content_size.y != root_size.height {
|
||||
log::debug!(
|
||||
"content size changed: {:.0}x{:.0} → {:.0}x{:.0}",
|
||||
@@ -470,7 +501,7 @@ impl Layout {
|
||||
pub fn tick(&mut self) -> anyhow::Result<()> {
|
||||
let mut alterables = EventAlterables::default();
|
||||
self.animations.tick(&self.state, &mut alterables);
|
||||
self.process_pending_components(&mut alterables)?;
|
||||
self.process_pending_components(&mut alterables);
|
||||
self.process_pending_widget_ticks(&mut alterables);
|
||||
self.process_alterables(alterables)?;
|
||||
Ok(())
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
pub mod animation;
|
||||
pub mod any;
|
||||
pub mod assets;
|
||||
mod assets_internal;
|
||||
pub mod components;
|
||||
pub mod drawing;
|
||||
pub mod event;
|
||||
|
||||
@@ -8,7 +8,7 @@ mod widget_rectangle;
|
||||
mod widget_sprite;
|
||||
|
||||
use crate::{
|
||||
assets::{self, AssetProvider},
|
||||
assets::{AssetPath, AssetPathOwned, normalize_path},
|
||||
components::{Component, ComponentWeak},
|
||||
drawing::{self},
|
||||
event::EventListenerCollection,
|
||||
@@ -22,12 +22,7 @@ use crate::{
|
||||
};
|
||||
use ouroboros::self_referencing;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
cell::RefMut,
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
};
|
||||
use std::{cell::RefMut, collections::HashMap, path::Path, rc::Rc};
|
||||
|
||||
#[self_referencing]
|
||||
struct XmlDocument {
|
||||
@@ -44,7 +39,7 @@ pub struct Template {
|
||||
}
|
||||
|
||||
struct ParserFile {
|
||||
path: PathBuf,
|
||||
path: AssetPathOwned,
|
||||
document: Rc<XmlDocument>,
|
||||
template_parameters: HashMap<Rc<str>, Rc<str>>,
|
||||
}
|
||||
@@ -201,7 +196,7 @@ impl Fetchable for ParserData {
|
||||
#[derive(Default)]
|
||||
pub struct ParserState {
|
||||
pub data: ParserData,
|
||||
pub path: PathBuf,
|
||||
pub path: AssetPathOwned,
|
||||
}
|
||||
|
||||
impl ParserState {
|
||||
@@ -558,11 +553,22 @@ fn parse_tag_include<U1, U2>(
|
||||
#[allow(clippy::single_match)]
|
||||
match pair.attrib.as_ref() {
|
||||
"src" => {
|
||||
let mut new_path = file.path.parent().unwrap_or_else(|| Path::new("/")).to_path_buf();
|
||||
new_path.push(pair.value.as_ref());
|
||||
let new_path = assets::normalize_path(&new_path);
|
||||
let new_path = {
|
||||
let this = &file.path.clone();
|
||||
let include: &str = &pair.value;
|
||||
let buf = this.get_path_buf();
|
||||
let mut new_path = buf.parent().unwrap_or_else(|| Path::new("/")).to_path_buf();
|
||||
new_path.push(include);
|
||||
let new_path = normalize_path(&new_path);
|
||||
|
||||
let (new_file, node_layout) = get_doc_from_path(ctx, &new_path)?;
|
||||
match this {
|
||||
AssetPathOwned::WguiInternal(_) => AssetPathOwned::WguiInternal(new_path),
|
||||
AssetPathOwned::BuiltIn(_) => AssetPathOwned::BuiltIn(new_path),
|
||||
AssetPathOwned::Filesystem(_) => AssetPathOwned::Filesystem(new_path),
|
||||
}
|
||||
};
|
||||
let new_path_ref = new_path.as_ref();
|
||||
let (new_file, node_layout) = get_doc_from_asset_path(ctx, new_path_ref)?;
|
||||
parse_document_root(&new_file, ctx, parent_id, node_layout)?;
|
||||
|
||||
return Ok(());
|
||||
@@ -992,7 +998,7 @@ pub struct ParseDocumentExtra {
|
||||
// filled-in by you in `new_layout_from_assets` function
|
||||
pub struct ParseDocumentParams<'a> {
|
||||
pub globals: WguiGlobals, // mandatory field
|
||||
pub path: &'a str, // mandatory field
|
||||
pub path: AssetPath<'a>, // mandatory field
|
||||
pub extra: ParseDocumentExtra, // optional field, can be Default-ed
|
||||
}
|
||||
|
||||
@@ -1002,18 +1008,15 @@ pub fn parse_from_assets<U1, U2>(
|
||||
listeners: &mut EventListenerCollection<U1, U2>,
|
||||
parent_id: WidgetID,
|
||||
) -> anyhow::Result<ParserState> {
|
||||
let path = PathBuf::from(doc_params.path);
|
||||
|
||||
let parser_data = ParserData::default();
|
||||
let mut ctx = create_default_context(doc_params, layout, listeners, &parser_data);
|
||||
|
||||
let (file, node_layout) = get_doc_from_path(&ctx, &path)?;
|
||||
let (file, node_layout) = get_doc_from_asset_path(&ctx, doc_params.path)?;
|
||||
parse_document_root(&file, &mut ctx, parent_id, node_layout)?;
|
||||
|
||||
// move everything essential to the result
|
||||
let result = ParserState {
|
||||
data: std::mem::take(&mut ctx.data_local),
|
||||
path,
|
||||
path: doc_params.path.to_owned(),
|
||||
};
|
||||
|
||||
drop(ctx);
|
||||
@@ -1027,21 +1030,18 @@ pub fn new_layout_from_assets<U1, U2>(
|
||||
layout_params: &LayoutParams,
|
||||
) -> anyhow::Result<(Layout, ParserState)> {
|
||||
let mut layout = Layout::new(doc_params.globals.clone(), layout_params)?;
|
||||
let widget = layout.root_widget;
|
||||
let widget = layout.content_root_widget;
|
||||
let state = parse_from_assets(doc_params, &mut layout, listeners, widget)?;
|
||||
Ok((layout, state))
|
||||
}
|
||||
|
||||
fn assets_path_to_xml(assets: &mut Box<dyn AssetProvider>, path: &Path) -> anyhow::Result<String> {
|
||||
let data = assets.load_from_path(&path.to_string_lossy())?;
|
||||
Ok(String::from_utf8(data)?)
|
||||
}
|
||||
|
||||
fn get_doc_from_path<U1, U2>(
|
||||
fn get_doc_from_asset_path<U1, U2>(
|
||||
ctx: &ParserContext<U1, U2>,
|
||||
path: &Path,
|
||||
asset_path: AssetPath,
|
||||
) -> anyhow::Result<(ParserFile, roxmltree::NodeId)> {
|
||||
let xml = assets_path_to_xml(&mut ctx.layout.state.globals.assets(), path)?;
|
||||
let data = ctx.layout.state.globals.get_asset(asset_path)?;
|
||||
let xml = String::from_utf8(data)?;
|
||||
|
||||
let document = Rc::new(XmlDocument::new(xml, |xml| {
|
||||
let opt = roxmltree::ParsingOptions {
|
||||
allow_dtd: true,
|
||||
@@ -1054,7 +1054,7 @@ fn get_doc_from_path<U1, U2>(
|
||||
let tag_layout = require_tag_by_name(&root, "layout")?;
|
||||
|
||||
let file = ParserFile {
|
||||
path: PathBuf::from(path),
|
||||
path: asset_path.to_owned(),
|
||||
document: document.clone(),
|
||||
template_parameters: Default::default(),
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
assets::AssetPath,
|
||||
layout::WidgetID,
|
||||
parser::{AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, style::parse_style},
|
||||
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
||||
@@ -21,9 +22,16 @@ pub fn parse_widget_sprite<'a, U1, U2>(
|
||||
for pair in attribs {
|
||||
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||
match key {
|
||||
"src" => {
|
||||
"src" | "src_ext" | "src_internal" => {
|
||||
let asset_path = match key {
|
||||
"src" => AssetPath::BuiltIn(value),
|
||||
"src_ext" => AssetPath::Filesystem(value),
|
||||
"src_internal" => AssetPath::WguiInternal(value),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if !value.is_empty() {
|
||||
glyph = match CustomGlyphContent::from_assets(&mut ctx.layout.state.globals.assets(), value) {
|
||||
glyph = match CustomGlyphContent::from_assets(&mut ctx.layout.state.globals, asset_path) {
|
||||
Ok(glyph) => Some(glyph),
|
||||
Err(e) => {
|
||||
log::warn!("failed to load {value}: {e}");
|
||||
@@ -32,11 +40,6 @@ pub fn parse_widget_sprite<'a, U1, U2>(
|
||||
}
|
||||
}
|
||||
}
|
||||
"src_ext" => {
|
||||
if !value.is_empty() && std::fs::exists(value).unwrap_or(false) {
|
||||
glyph = CustomGlyphContent::from_file(value).ok();
|
||||
}
|
||||
}
|
||||
"color" => {
|
||||
if let Some(color) = parse_color_hex(value) {
|
||||
params.color = Some(color);
|
||||
|
||||
@@ -243,6 +243,9 @@ impl Context {
|
||||
let pass = passes.last_mut().unwrap(); // always safe
|
||||
|
||||
match &primitive {
|
||||
drawing::RenderPrimitive::NewPass => {
|
||||
needs_new_pass = true;
|
||||
}
|
||||
drawing::RenderPrimitive::Rectangle(extent, rectangle) => {
|
||||
pass
|
||||
.rect_renderer
|
||||
|
||||
@@ -10,7 +10,7 @@ use cosmic_text::SubpixelBin;
|
||||
use image::RgbaImage;
|
||||
use resvg::usvg::{Options, Tree};
|
||||
|
||||
use crate::assets::AssetProvider;
|
||||
use crate::{assets::AssetPath, globals::WguiGlobals};
|
||||
|
||||
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
@@ -32,19 +32,10 @@ impl CustomGlyphContent {
|
||||
}
|
||||
|
||||
#[allow(clippy::case_sensitive_file_extension_comparisons)]
|
||||
pub fn from_assets(provider: &mut Box<dyn AssetProvider>, path: &str) -> anyhow::Result<Self> {
|
||||
let data = provider.load_from_path(path)?;
|
||||
if path.ends_with(".svg") || path.ends_with(".svgz") {
|
||||
Ok(Self::from_bin_svg(&data)?)
|
||||
} else {
|
||||
Ok(Self::from_bin_raster(&data)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::case_sensitive_file_extension_comparisons)]
|
||||
pub fn from_file(path: &str) -> anyhow::Result<Self> {
|
||||
let data = std::fs::read(path)?;
|
||||
if path.ends_with(".svg") || path.ends_with(".svgz") {
|
||||
pub fn from_assets(globals: &mut WguiGlobals, path: AssetPath) -> anyhow::Result<Self> {
|
||||
let path_str = path.get_str();
|
||||
let data = globals.get_asset(path)?;
|
||||
if path_str.ends_with(".svg") || path_str.ends_with(".svgz") {
|
||||
Ok(Self::from_bin_svg(&data)?)
|
||||
} else {
|
||||
Ok(Self::from_bin_raster(&data)?)
|
||||
@@ -165,11 +156,7 @@ impl RasterizedCustomGlyph {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn validate(
|
||||
&self,
|
||||
input: &RasterizeCustomGlyphRequest,
|
||||
expected_type: Option<ContentType>,
|
||||
) {
|
||||
pub(super) fn validate(&self, input: &RasterizeCustomGlyphRequest, expected_type: Option<ContentType>) {
|
||||
if let Some(expected_type) = expected_type {
|
||||
assert_eq!(
|
||||
self.content_type, expected_type,
|
||||
@@ -222,10 +209,7 @@ impl ContentType {
|
||||
}
|
||||
}
|
||||
|
||||
fn rasterize_svg(
|
||||
tree: &Tree,
|
||||
input: &RasterizeCustomGlyphRequest,
|
||||
) -> Option<RasterizedCustomGlyph> {
|
||||
fn rasterize_svg(tree: &Tree, input: &RasterizeCustomGlyphRequest) -> Option<RasterizedCustomGlyph> {
|
||||
// Calculate the scale based on the "glyph size".
|
||||
let svg_size = tree.size();
|
||||
let scale_x = f32::from(input.width) / svg_size.width();
|
||||
|
||||
@@ -46,7 +46,7 @@ impl WidgetLabel {
|
||||
buffer.set_wrap(wrap);
|
||||
|
||||
buffer.set_rich_text(
|
||||
[(params.content.generate(&mut globals.i18n).as_ref(), attrs)],
|
||||
[(params.content.generate(&mut globals.i18n_builtin).as_ref(), attrs)],
|
||||
&Attrs::new(),
|
||||
Shaping::Advanced,
|
||||
params.style.align.map(Into::into),
|
||||
|
||||
Reference in New Issue
Block a user