bytestring-net / bevy_lunex

Blazingly fast path based retained layout engine for Bevy entities, built around vanilla Bevy ECS.
https://bytestring-net.github.io/bevy_lunex/
Apache License 2.0
478 stars 18 forks source link

DSL thoughts #10

Open UkoeHB opened 9 months ago

UkoeHB commented 9 months ago

I have been thinking a bit about a DSL syntax for bevy_lunex. This syntax could easily be translated to pure-rust without too much additional verbosity, but I wanted to see if I could figure out a nice DSL.

Here is an example:

#[derive(LnxStyle)]
struct PlainTextStyle
{
    font      : &'static str,
    font_size : usize,
    color     : Color,
}

/// Deriving `LnxParams` transforms the text fields into lnx 'param methods' (see the example).
#[derive(LnxParams, Default)]
struct PlainTextParams
{
    justify: Justification,
    width: Option<f32>,
    height: Option<f32>,
    depth: f32,
}

impl Into<TextParams> for PlainTextParams
{
    fn into(self) -> TextParams
    {
        let params = match self.justify
        {
            Justification::Center => TextParams::center(),
            _ => unimplemented!()
        }
        params.set_width(self.width);
        params.set_height(self.height);
        params.depth(depth);
    }
}

#[lnx_prefab]
fn plain_text(lnx: &mut LnxContext, text: &str, params: PlainTextParams)
{
    let style = lnx.get_style::<PlainTextStyle>().unwrap();
    let text_style = TextStyle {
            font      : lnx.asset_server().load(style.font),
            font_size : style.font_size,
            color     : style.color,
        };

    let text_params: TextParams = params
        .into()
        .with_style(&text_style);
    lnx.commands().spawn(TextElementBundle::new(lnx.widget().clone(), text_params, text)); 
}

#[LnxStyleBundle]
struct BaseStyle
{
    plain: PlainTextStyle,
}

impl Default for BaseStyle
{
    //define the style
}

#[LnxStyleBundle]
struct DisplayStyle
{
    plain: PlainTextStyle,
}

impl Default for PlainTextStyle
{
    //define the style
}

let ui =
lnx!{
    // import rust bindings or lnx scripts
    use ui::prefabs::{plain_text, PlainTextParams};
    use ui::prefabs::text_params::Center;

    // registers style handles that can be referenced in the widget tree
    // - style bundles are default-initialize, unpacked, and stored in Arcs; copies are minimized in the widget tree
    styles[
        base_style: BaseStyle
        display_style: DisplayStyle
    ]

    // [r] creates a relative widget and implicitly sets it in the lnx context widget stack
    // - when entering a {} block, a new empty widget stack entry is added, then popped when leaving the block
    // - when a new widget is created within a block, the previous entry in the stack is over-written (but the other widget
    //   handle remains valid until leaving the block where it is created)
    [r] root: rx(0, 100) ry(0, 100)
    {
        // sets the current style in the lnx context style stack
        // - all previous styles in the stack will be hidden
        [style] set(base_style)

        // this nameless widget is implicitly a child of `root`
        [r] _: rx(20, 80), ry(42.5, 57.5)
        {
            // layers `display_style` on top of the previous style in the style stack
            // - the parent style will be used whenever the child style cannot satisfy a prefab's requirements
            [style] add(display_style)

            // style and parent widget are implicitly passed to the prefab
            // - we use 'param methods' to define parameters in the prefab
            // - if there is a param method name conflict, you must resolve the conflict with the names of the 
            //   target structs
            [inject] plain_text("Hello, World!", justify(Center), height(100), PlainTextParams::width(100))
        }
    }
}