Search code examples
user-interfacerustwidgeteguieframe

How can I change the value of a label in egui after creating it?


I am trying to change the string inside an egui label using a function. I made a reference to the content of the label and passed it along to the function but it doesn't change what is displayed in the application.

let mut label_text = "Change this".to_string();
egui::CentralPanel::default().show(ctx, |ui| {
    ui.add(egui::Label::new(&label_text));
    if ui.add(egui::Button::new("Press me")).clicked() {
        do_stuff(&mut label_text, &ctx);
    }
});

and the function do_stuff:

fn do_stuff(labeltext: &mut String, ctx: &egui::Context){
    labeltext.clear();
    labeltext.push_str("Changed");    
    ctx.request_repaint();
}

I am using the following dependencies in my toml:

eframe = "0.21.3"
egui = "0.21.0"

Also on the same topic how can I get a reference id for an egui widget so I can call that id from whatever function later on? From what I saw most widgets don't have an id or unique_id attribute anymore. Sorry if it's a noob question I am just starting out with Rust and Egui/Eframe, and it is all very confusing to me as in C++ every element of an UI would have an id which you could reference anywhere assign it to a handle and then change anything about that element.


Solution

  • Egui is an immediate-mode gui, which means that when you create a widget and call ui.add(widget), instead of that widget being added to some dom and then the whole scene being re-rendered, the rendering actually happens at that moment. This drawing code is then called again on each frame that needs to be rendered.

    This means that your label_text variable actually only existing during drawing, and any changes made to it will be lost once the drawing is finished. Then when drawing again, a new label_text will be created.

    In order to avoid this, you can add label_text as a field of the struct you're implementing eframe::App on. You can then refer to this variable within eframe::App::update. This way it will live for as long as the app itself.

    Here is a mcve with the changes:

    struct MyApp {
        label_text: String,
    }
    
    impl MyApp {
        fn new() -> Self {
            // Now you define `label_text` here
            Self { label_text: "Change this".to_string() }
        }
    }
    
    impl eframe::App for MyApp {
        fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
            // And you access it with `self.label_text` here        
            egui::CentralPanel::default().show(ctx, |ui| {
                ui.add(egui::Label::new(&self.label_text));
                if ui.add(egui::Button::new("Press me")).clicked() {
                    do_stuff(&mut self.label_text, &ctx);
                }
            });
        }
    }
    
    fn do_stuff(label_text: &mut String, ctx: &egui::Context) {
        label_text.clear();
        label_text.push_str("Changed");    
        ctx.request_repaint();
    }