cached image loading
This commit is contained in:
@@ -4,7 +4,7 @@ use wgui::{
|
|||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::{Layout, WidgetID},
|
layout::{Layout, WidgetID},
|
||||||
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
renderer_vk::text::custom_glyph::CustomGlyphData,
|
||||||
taffy::{self, prelude::length},
|
taffy::{self, prelude::length},
|
||||||
widget::{
|
widget::{
|
||||||
label::{WidgetLabel, WidgetLabelParams},
|
label::{WidgetLabel, WidgetLabelParams},
|
||||||
@@ -62,7 +62,7 @@ pub fn mount_simple_sprite_square(
|
|||||||
layout.add_child(
|
layout.add_child(
|
||||||
parent_id,
|
parent_id,
|
||||||
WidgetSprite::create(WidgetSpriteParams {
|
WidgetSprite::create(WidgetSpriteParams {
|
||||||
glyph_data: Some(CustomGlyphData::new(CustomGlyphContent::from_assets(globals, path)?)),
|
glyph_data: Some(CustomGlyphData::from_assets(globals, path)?),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
taffy::Style {
|
taffy::Style {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use wgui::{
|
|||||||
parser::{Fetchable, ParseDocumentParams, ParserState},
|
parser::{Fetchable, ParseDocumentParams, ParserState},
|
||||||
renderer_vk::text::{
|
renderer_vk::text::{
|
||||||
FontWeight, HorizontalAlign, TextShadow, TextStyle,
|
FontWeight, HorizontalAlign, TextShadow, TextStyle,
|
||||||
custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
custom_glyph::{ CustomGlyphData},
|
||||||
},
|
},
|
||||||
taffy::{
|
taffy::{
|
||||||
self, AlignItems, AlignSelf, JustifyContent, JustifySelf,
|
self, AlignItems, AlignSelf, JustifyContent, JustifySelf,
|
||||||
@@ -413,10 +413,10 @@ impl View {
|
|||||||
|
|
||||||
fn get_placeholder_image(&mut self) -> anyhow::Result<&CustomGlyphData> {
|
fn get_placeholder_image(&mut self) -> anyhow::Result<&CustomGlyphData> {
|
||||||
if self.img_placeholder.is_none() {
|
if self.img_placeholder.is_none() {
|
||||||
let c = CustomGlyphData::new(CustomGlyphContent::from_assets(
|
let c = CustomGlyphData::from_assets(
|
||||||
&self.globals,
|
&self.globals,
|
||||||
AssetPath::BuiltIn("dashboard/placeholder_cover.png"),
|
AssetPath::BuiltIn("dashboard/placeholder_cover.png"),
|
||||||
)?);
|
)?;
|
||||||
self.img_placeholder = Some(c);
|
self.img_placeholder = Some(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,7 +515,8 @@ impl View {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let glyph = match CustomGlyphContent::from_bin_raster(&cover_art.compressed_image_data) {
|
let path = format!("app:{app_id:?}");
|
||||||
|
let glyph = match CustomGlyphData::from_bytes_raster(&self.globals, &path ,&cover_art.compressed_image_data) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
@@ -526,7 +527,7 @@ impl View {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
View::mount_image(layout, cell, &CustomGlyphData::new(glyph))?;
|
View::mount_image(layout, cell, &glyph)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,24 +1,22 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
animation::{Animation, AnimationEasing},
|
animation::{Animation, AnimationEasing},
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
components::{self, Component, ComponentBase, ComponentTrait, RefreshData, tooltip::ComponentTooltip},
|
components::{self, tooltip::ComponentTooltip, Component, ComponentBase, ComponentTrait, RefreshData},
|
||||||
drawing::{self, Boundary, Color},
|
drawing::{self, Boundary, Color},
|
||||||
event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind},
|
event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind},
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::{LayoutTask, WidgetID, WidgetPair},
|
layout::{LayoutTask, WidgetID, WidgetPair},
|
||||||
renderer_vk::{
|
renderer_vk::{
|
||||||
text::{
|
text::{custom_glyph::CustomGlyphData, FontWeight, TextStyle},
|
||||||
FontWeight, TextStyle,
|
|
||||||
custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
|
||||||
},
|
|
||||||
util::centered_matrix,
|
util::centered_matrix,
|
||||||
},
|
},
|
||||||
widget::{
|
widget::{
|
||||||
self, ConstructEssentials, EventResult, WidgetData,
|
self,
|
||||||
label::{WidgetLabel, WidgetLabelParams},
|
label::{WidgetLabel, WidgetLabelParams},
|
||||||
rectangle::{WidgetRectangle, WidgetRectangleParams},
|
rectangle::{WidgetRectangle, WidgetRectangleParams},
|
||||||
sprite::{WidgetSprite, WidgetSpriteParams},
|
sprite::{WidgetSprite, WidgetSpriteParams},
|
||||||
util::WLength,
|
util::WLength,
|
||||||
|
ConstructEssentials, EventResult, WidgetData,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use glam::{Mat4, Vec3};
|
use glam::{Mat4, Vec3};
|
||||||
@@ -27,7 +25,7 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use taffy::{AlignItems, JustifyContent, prelude::length};
|
use taffy::{prelude::length, AlignItems, JustifyContent};
|
||||||
|
|
||||||
pub struct Params<'a> {
|
pub struct Params<'a> {
|
||||||
pub text: Option<Translation>, // if unset, label will not be populated
|
pub text: Option<Translation>, // if unset, label will not be populated
|
||||||
@@ -461,10 +459,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||||||
|
|
||||||
if let Some(sprite_path) = params.sprite_src {
|
if let Some(sprite_path) = params.sprite_src {
|
||||||
let sprite = WidgetSprite::create(WidgetSpriteParams {
|
let sprite = WidgetSprite::create(WidgetSpriteParams {
|
||||||
glyph_data: Some(CustomGlyphData::new(CustomGlyphContent::from_assets(
|
glyph_data: Some(CustomGlyphData::from_assets(&globals, sprite_path)?),
|
||||||
&globals,
|
|
||||||
sprite_path,
|
|
||||||
)?)),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use crate::{
|
|||||||
assets_internal, drawing,
|
assets_internal, drawing,
|
||||||
font_config::{WguiFontConfig, WguiFontSystem},
|
font_config::{WguiFontConfig, WguiFontSystem},
|
||||||
i18n::I18n,
|
i18n::I18n,
|
||||||
|
renderer_vk::text::custom_glyph::CustomGlyphCache,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -52,6 +53,7 @@ pub struct Globals {
|
|||||||
pub i18n_builtin: I18n,
|
pub i18n_builtin: I18n,
|
||||||
pub defaults: Defaults,
|
pub defaults: Defaults,
|
||||||
pub font_system: WguiFontSystem,
|
pub font_system: WguiFontSystem,
|
||||||
|
pub custom_glyph_cache: CustomGlyphCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -74,6 +76,7 @@ impl WguiGlobals {
|
|||||||
defaults,
|
defaults,
|
||||||
asset_folder,
|
asset_folder,
|
||||||
font_system: WguiFontSystem::new(font_config),
|
font_system: WguiFontSystem::new(font_config),
|
||||||
|
custom_glyph_cache: CustomGlyphCache::new(),
|
||||||
}))))
|
}))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ use crate::{
|
|||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{
|
||||||
AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, print_invalid_attrib,
|
parse_children, parse_widget_universal, print_invalid_attrib,
|
||||||
style::{parse_color, parse_round, parse_style},
|
style::{parse_color, parse_round, parse_style},
|
||||||
|
AttribPair, ParserContext, ParserFile,
|
||||||
},
|
},
|
||||||
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
renderer_vk::text::custom_glyph::CustomGlyphData,
|
||||||
widget::image::{WidgetImage, WidgetImageParams},
|
widget::image::{WidgetImage, WidgetImageParams},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ pub fn parse_widget_image<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !value.is_empty() {
|
if !value.is_empty() {
|
||||||
glyph = match CustomGlyphContent::from_assets(&mut ctx.layout.state.globals, asset_path) {
|
glyph = match CustomGlyphData::from_assets(&mut ctx.layout.state.globals, asset_path) {
|
||||||
Ok(glyph) => Some(glyph),
|
Ok(glyph) => Some(glyph),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("failed to load {value}: {e}");
|
log::warn!("failed to load {value}: {e}");
|
||||||
@@ -63,7 +64,7 @@ pub fn parse_widget_image<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(glyph) = glyph {
|
if let Some(glyph) = glyph {
|
||||||
params.glyph_data = Some(CustomGlyphData::new(glyph));
|
params.glyph_data = Some(glyph);
|
||||||
} else {
|
} else {
|
||||||
log::warn!("No source for image node!");
|
log::warn!("No source for image node!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, style::parse_style},
|
parser::{parse_children, parse_widget_universal, style::parse_style, AttribPair, ParserContext, ParserFile},
|
||||||
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
renderer_vk::text::custom_glyph::CustomGlyphData,
|
||||||
widget::sprite::{WidgetSprite, WidgetSpriteParams},
|
widget::sprite::{WidgetSprite, WidgetSpriteParams},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ pub fn parse_widget_sprite<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !value.is_empty() {
|
if !value.is_empty() {
|
||||||
glyph = match CustomGlyphContent::from_assets(&ctx.layout.state.globals, asset_path) {
|
glyph = match CustomGlyphData::from_assets(&ctx.layout.state.globals, asset_path) {
|
||||||
Ok(glyph) => Some(glyph),
|
Ok(glyph) => Some(glyph),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("failed to load {value}: {e}");
|
log::warn!("failed to load {value}: {e}");
|
||||||
@@ -53,7 +53,7 @@ pub fn parse_widget_sprite<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(glyph) = glyph {
|
if let Some(glyph) = glyph {
|
||||||
params.glyph_data = Some(CustomGlyphData::new(glyph));
|
params.glyph_data = Some(glyph);
|
||||||
} else {
|
} else {
|
||||||
log::warn!("No source for sprite node!");
|
log::warn!("No source for sprite node!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
f32,
|
f32,
|
||||||
|
hash::{DefaultHasher, Hasher},
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc, Weak,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -14,15 +16,53 @@ use crate::{assets::AssetPath, globals::WguiGlobals};
|
|||||||
|
|
||||||
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
#[derive(Hash, PartialEq, Eq)]
|
||||||
|
pub struct HashedAsset {
|
||||||
|
path: String,
|
||||||
|
hash: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CustomGlyphCache {
|
||||||
|
inner: HashMap<HashedAsset, CustomGlyphDataWeak>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomGlyphCache {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self { inner: HashMap::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, path: &str, bytes: &[u8]) -> Result<CustomGlyphData, HashedAsset> {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
hasher.write(bytes);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
|
||||||
|
let hashed_asset = HashedAsset {
|
||||||
|
path: path.to_string(),
|
||||||
|
hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
self
|
||||||
|
.inner
|
||||||
|
.get(&hashed_asset)
|
||||||
|
.and_then(|a| a.upgrade())
|
||||||
|
.inspect(|_| log::debug!("Glyph cache hit on: '{path}'"))
|
||||||
|
.ok_or(hashed_asset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, hashed_asset: HashedAsset, data: &CustomGlyphData) {
|
||||||
|
self.inner.insert(hashed_asset, data.clone_weak());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The raw content of a glyph.
|
/// The raw content of a glyph.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum CustomGlyphContent {
|
pub(crate) enum CustomGlyphContent {
|
||||||
Svg(Box<Tree>),
|
Svg(Box<Tree>),
|
||||||
Image(RgbaImage),
|
Image(RgbaImage),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomGlyphContent {
|
impl CustomGlyphContent {
|
||||||
pub fn from_bin_svg(data: &[u8]) -> anyhow::Result<Self> {
|
fn from_bin_svg(data: &[u8]) -> anyhow::Result<Self> {
|
||||||
let options = Options {
|
let options = Options {
|
||||||
style_sheet: Some("svg { color: white }".into()),
|
style_sheet: Some("svg { color: white }".into()),
|
||||||
..Options::default()
|
..Options::default()
|
||||||
@@ -32,19 +72,23 @@ impl CustomGlyphContent {
|
|||||||
Ok(Self::Svg(Box::new(tree)))
|
Ok(Self::Svg(Box::new(tree)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bin_raster(data: &[u8]) -> anyhow::Result<Self> {
|
fn from_bin_raster(data: &[u8]) -> anyhow::Result<Self> {
|
||||||
let image = image::load_from_memory(data)?.into_rgba8();
|
let image = image::load_from_memory(data)?.into_rgba8();
|
||||||
Ok(Self::Image(image))
|
Ok(Self::Image(image))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::case_sensitive_file_extension_comparisons)]
|
struct CustomGlyphDataWeak {
|
||||||
pub fn from_assets(globals: &WguiGlobals, path: AssetPath) -> anyhow::Result<Self> {
|
id: usize,
|
||||||
let path_str = path.get_str();
|
content: Weak<CustomGlyphContent>,
|
||||||
let data = globals.get_asset(path)?;
|
}
|
||||||
if path_str.ends_with(".svg") || path_str.ends_with(".svgz") {
|
|
||||||
Ok(Self::from_bin_svg(&data)?)
|
impl CustomGlyphDataWeak {
|
||||||
|
fn upgrade(&self) -> Option<CustomGlyphData> {
|
||||||
|
if let Some(content) = self.content.upgrade() {
|
||||||
|
Some(CustomGlyphData { id: self.id, content })
|
||||||
} else {
|
} else {
|
||||||
Ok(Self::from_bin_raster(&data)?)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,13 +102,20 @@ pub struct CustomGlyphData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CustomGlyphData {
|
impl CustomGlyphData {
|
||||||
pub fn new(content: CustomGlyphContent) -> Self {
|
fn new(content: CustomGlyphContent) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed),
|
id: AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed),
|
||||||
content: Arc::new(content),
|
content: Arc::new(content),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clone_weak(&self) -> CustomGlyphDataWeak {
|
||||||
|
CustomGlyphDataWeak {
|
||||||
|
id: self.id,
|
||||||
|
content: Arc::downgrade(&self.content),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dim_for_cache_key(&self, width: u16, height: u16) -> (u16, u16) {
|
pub fn dim_for_cache_key(&self, width: u16, height: u16) -> (u16, u16) {
|
||||||
const MAX_RASTER_DIM: u16 = 256;
|
const MAX_RASTER_DIM: u16 = 256;
|
||||||
match self.content.as_ref() {
|
match self.content.as_ref() {
|
||||||
@@ -75,6 +126,41 @@ impl CustomGlyphData {
|
|||||||
CustomGlyphContent::Image(image) => (image.width() as _, image.height() as _),
|
CustomGlyphContent::Image(image) => (image.width() as _, image.height() as _),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_assets(globals: &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") {
|
||||||
|
Self::from_bytes_svg(globals, path_str, &data)
|
||||||
|
} else {
|
||||||
|
Self::from_bytes_raster(globals, path_str, &data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes_raster(globals: &WguiGlobals, path: &str, data: &[u8]) -> anyhow::Result<Self> {
|
||||||
|
let globals_borrow = &mut globals.get();
|
||||||
|
match globals_borrow.custom_glyph_cache.get(path, data) {
|
||||||
|
Ok(data) => return Ok(data),
|
||||||
|
Err(hashed_asset) => {
|
||||||
|
let data = Self::new(CustomGlyphContent::from_bin_raster(data)?);
|
||||||
|
globals_borrow.custom_glyph_cache.insert(hashed_asset, &data);
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes_svg(globals: &WguiGlobals, path: &str, data: &[u8]) -> anyhow::Result<Self> {
|
||||||
|
let globals_borrow = &mut globals.get();
|
||||||
|
match globals_borrow.custom_glyph_cache.get(path, data) {
|
||||||
|
Ok(data) => return Ok(data),
|
||||||
|
Err(hashed_asset) => {
|
||||||
|
let data = Self::new(CustomGlyphContent::from_bin_svg(data)?);
|
||||||
|
globals_borrow.custom_glyph_cache.insert(hashed_asset, &data);
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for CustomGlyphData {
|
impl PartialEq for CustomGlyphData {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use wgui::{
|
|||||||
event::{CallbackDataCommon, EventAlterables},
|
event::{CallbackDataCommon, EventAlterables},
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
parser::{Fetchable, parse_color_hex},
|
parser::{Fetchable, parse_color_hex},
|
||||||
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
renderer_vk::text::custom_glyph::CustomGlyphData,
|
||||||
taffy,
|
taffy,
|
||||||
widget::{
|
widget::{
|
||||||
image::WidgetImage, label::WidgetLabel, rectangle::WidgetRectangle, sprite::WidgetSprite,
|
image::WidgetImage, label::WidgetLabel, rectangle::WidgetRectangle, sprite::WidgetSprite,
|
||||||
@@ -117,12 +117,11 @@ fn apply_custom_command(
|
|||||||
.parser_state
|
.parser_state
|
||||||
.fetch_widget(&panel.layout.state, element)
|
.fetch_widget(&panel.layout.state, element)
|
||||||
{
|
{
|
||||||
let content = CustomGlyphContent::from_assets(
|
let data = CustomGlyphData::from_assets(
|
||||||
&app.wgui_globals,
|
&app.wgui_globals,
|
||||||
wgui::assets::AssetPath::File(path),
|
wgui::assets::AssetPath::File(path),
|
||||||
)
|
)
|
||||||
.context("Could not load content from supplied path.")?;
|
.context("Could not load content from supplied path.")?;
|
||||||
let data = CustomGlyphData::new(content);
|
|
||||||
|
|
||||||
if let Some(mut sprite) = pair.widget.get_as::<WidgetSprite>() {
|
if let Some(mut sprite) = pair.widget.get_as::<WidgetSprite>() {
|
||||||
sprite.set_content(&mut com, Some(data));
|
sprite.set_content(&mut com, Some(data));
|
||||||
|
|||||||
Reference in New Issue
Block a user