0

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.

  • 2
    Egui is immediate-mode, which means that all (most anyway) widgets aren't kept around after drawing. You create them, draw them, then they get destroyed, that's why they don't have ids. If you're familiar with the C++ gui library [imgui](https://github.com/ocornut/imgui), it's very similar to that. – Filipe Rodrigues Mar 12 '23 at 19:43
  • Ok so I can't reference them after creation. Does that also mean I can't change the value of a label because it is destroyed after creation? And should I use another widget that supports text such as a text edit which has a longer lifetime to do what I need (I am trying to print messages to the label in real time)? – Lucien A. Jones Mar 12 '23 at 20:36
  • 1
    No, you just need to re-create the widgets on each draw and store the label text somewhere "outside" the draw code. You should take a look at the [eframe/egui hello world](https://github.com/emilk/egui/blob/f94187ab718e8513084e591fcef73356a4971d6b/examples/hello_world/src/main.rs). Both `name` and `age` are examples of this. `name` fits your use case better – Filipe Rodrigues Mar 12 '23 at 20:43
  • Ok I made the label strings as part of the App struct so I can pass a reference to the struct to any function then I can change the strings and it did update and redraw the text. Thank you for your assistance. If you would submit your comment as an answer I will pick it and close the question. Thanks again. – Lucien A. Jones Mar 12 '23 at 22:35

1 Answers1

3

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();
}
Filipe Rodrigues
  • 1,843
  • 2
  • 12
  • 21