Using rust to update text

We will be using rust to modify the contents of a text node at runtime (no hot reloading).

Modify the cob file

Let's setup the cob file as below.

#![allow(unused)]
fn main() {
"scene"
    AbsoluteNode{left:40%}
    "cell"
        Animated<BackgroundColor>{
            idle:#FF0000 // You can input colours in other formats
            hover:Hsla{ hue:120 saturation:1.0 lightness:0.50 alpha:1.0 }
            press:Hsla{ hue:240 saturation:1.0 lightness:0.50 alpha:1.0 }
        }
        NodeShadow{color:#FF0000 spread_radius:10px blur_radius:5px}
        "text"
            TextLine{text:"Hello, World!, I am writing using cobweb "} // <-- will be overwritten

}

We split position logic into a child node called cell which holds most of the positioning and styling logic. cell has a child called text. Text is a minimal node responsible for just text stuff.

Why we split text from styling

This is more of an html/CSS pattern then anything particular with cobweb but it is worth mentioning here.

It just turns out to be easier to position nodes than it is to position text.

Rust

Updating text at runtime

Let's change the rust code to be as below.

fn build_ui(mut c: Commands, mut s: ResMut<SceneLoader>) {
    c.spawn(Camera2d);
    c.ui_root()
        .load_scene_and_edit(("main.cob", "main_scene"), &mut s, |loaded_scene| {
            loaded_scene
                .get("cell::text")
                .update_text("My runtime text");
        });
}

We have changed load_scene to be load_scene_and_edit.

When we load "main_scene" in the cob file, we automatically load all the child nodes recursively. The second argument is a closure where we can use loaded_scene similar to commands along with extension methods provided by cobweb.

Inside the closure we call get(cell::text) which is basically a path syntax to go straight to the text node. It also possible to call edit on "cell" then call update_text inside the resulting closure.

Recompile and run the program. You will see your text has changed to reflect the rust code.

Spawning new nodes

Cobweb can also spawn new scenes inside other scenes. Let's start with an example.

Below we have our new scene called number_text.

If the concept of scenes was a bit confusing before, this should clarify it a bit more.

#![allow(unused)]
fn main() {
scenes
"scene"
    AbsoluteNode{left:40% flex_direction:Column}
    "cell"
        Animated<BackgroundColor>{
            idle:#FF0000 // You can input colours in other formats
            hover:Hsla{ hue:120 saturation:1.0 lightness:0.50 alpha:1.0 }
            press:Hsla{ hue:240 saturation:1.0 lightness:0.50 alpha:1.0 }
        }
        NodeShadow{color:#FF0000 spread_radius:10px blur_radius:5px}
        "text"
            TextLine{text:"Hello, World!, I am writing using cobweb "}


"number_text"
    "cell"
        "text"
            TextLine{text:"placeholder"}
}

Now let's change our rust code to spawn some scenes.

fn build_ui(mut c: Commands, mut s: ResMut<SceneLoader>) {
    c.spawn(Camera2d);
    c.ui_root()
        .load_scene_and_edit(("main.cob", "main_scene"), &mut s, |loaded_scene| {
            loaded_scene
                .get("cell::text")
                .update_text("My runtime text");

            // Spawning new ui nodes inside our main scene
            for i in (0..=10).into_iter() {
                loaded_scene.load_scene_and_edit(("main.cob", "number_text"), |loaded_scene| {
                    loaded_scene.get("cell::text").update_text(i.to_string());
                });
            }
        });
}

We now have some numbers that appear based on your code. We can still modify the cob files and change styling:

#![allow(unused)]
fn main() {
"number_text"
    "cell"
        "text"
            TextLine{text:"placeholder"}
            TextLineColor(Hsla{hue:45 saturation:1.0 lightness:0.5 alpha:1.0}) // <-- add this
}

Making nodes interactive

Setting our UI to react to the user is essential, and easy. First we need to add the Interactive loadable.

#![allow(unused)]
fn main() {
"number_text"
    "cell"
        "text"
            TextLine{text:"placeholder"}
            TextLineColor(Hsla{hue:45 saturation:1.0 lightness:0.5 alpha:1.0})
            Interactive // Sets up the node for user interaction

}

Now we can use on_pressed:

fn build_ui(mut c: Commands, mut s: ResMut<SceneLoader>) {
    c.spawn(Camera2d);
    c.ui_root()
        .load_scene_and_edit(("main.cob", "main_scene"), &mut s, |loaded_scene| {
            loaded_scene
                .get("cell::text")
                .update_text("My runtime text");

            for i in (0..=10).into_iter() {
                loaded_scene.load_scene_and_edit(("main.cob", "number_text"), |loaded_scene| {
                    loaded_scene.edit("cell::text", |loaded_scene| {
                        loaded_scene.update_text(i.to_string());
                        loaded_scene.on_pressed(move|/* We can write arbitrary bevy parameters here*/|{
                            println!("You clicked {}", i);
                        });
                    });
                });
            }
        });
}