quill icon indicating copy to clipboard operation
quill copied to clipboard

How to pass a font asset handle to a style builder?

Open musjj opened this issue 1 year ago • 4 comments

I'm trying to pass a handle:

#[derive(Clone, PartialEq)]
pub struct MyUi;

impl ViewTemplate for MyUi {
    type View = impl View;

    fn create(&self, cx: &mut Cx) -> Self::View {
        let font_assets = cx.use_resource::<MyFontAssets>();
        let font = font_assets.comic_sans.clone();

        Element::<NodeBundle>::new().style(move |ss: &mut StyleBuilder| {
            ss.font(font);
        })
    }
}

But it doesn't work because the style builder function must be Fn:

expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
required for `{closure@src/my_ui.rs:26:44: 26:72}` to implement `bevy_mod_stylebuilder::StyleTuple`

Is there a way to make font handles work with quill?

musjj avatar Sep 09 '24 21:09 musjj

Hmmm, not sure why this doesn't work. That being said, there's a few things you can do right now:

  1. Use style_dyn, and pass in the font handle as an explicit dependency.

  2. Instead of passing in a handle, ss.font() should be able to accept the asset path string directly, e.g. .font("embedded://bevy_quill_obsidian/assets/fonts/Open_Sans/static/OpenSans-Medium.ttf").

viridia avatar Sep 09 '24 22:09 viridia

Thanks, but I ended up inserting InheritableFontStyles at the root node instead:

Element::<NodeBundle>::new()
    .insert(InheritableFontStyles {
        ...
    })

Use style_dyn, and pass in the font handle as an explicit dependency.

I'll try this if I ever need to override the font on a specific node.

Instead of passing in a handle, ss.font() should be able to accept the asset path string directly, e.g. .font("embedded://bevy_quill_obsidian/assets/fonts/Open_Sans/static/OpenSans-Medium.ttf").

This won't work well for me, because I want to have the font fully loaded before the game starts (I'm using bevy_asset_loader)

EDIT: It looks like even when passing it as dep with style_dyn you still need to call .clone() on the resource or you'll get a lifetime error.

To clarify, this is what my resource looks like:

#[derive(AssetCollection, Clone, Resource, PartialEq, PartialOrd, Eq)]
pub struct MyAssets {
    #[asset(path = "foobar.png")]
    pub foobar: Handle<Image>,
    // ...and tens of other asset fields
}

I wonder if there's a more efficient approach that avoids cloning.

musjj avatar Sep 10 '24 07:09 musjj

Ok, I figured it out:

#[derive(Clone, PartialEq)]
pub struct MyUi;

impl ViewTemplate for MyUi {
    type View = impl View;

    fn create(&self, cx: &mut Cx) -> Self::View {
        let font_assets = cx.use_resource::<MyFontAssets>();
        let font = font_assets.comic_sans.clone();

        Element::<NodeBundle>::new().style(move |ss: &mut StyleBuilder| {
            ss.font(font.clone()); // <- you need to clone it so that the closure becomes `Fn`
                                   // See: https://doc.rust-lang.org/std/keyword.move.html
        })
    }
}

It seems that this is more of a Rust issue, so I think I'll close this. But before that, would it be possible for .style() and other callbacks to be a FnOnce instead? Do they need to be called multiple times?

musjj avatar Sep 10 '24 17:09 musjj

FnOnce: Certainly worth investigating.

viridia avatar Sep 10 '24 19:09 viridia