emilk / egui

egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native
https://www.egui.rs/
Apache License 2.0
21.72k stars 1.57k forks source link

Improve popup menu resizing with dynamic content #5138

Open abey79 opened 1 day ago

abey79 commented 1 day ago

Consider this example, which (maybe naively) tries to display a popup menu with content that can change over time:

use eframe::egui;

fn main() -> eframe::Result {
    env_logger::init();

    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),
        ..Default::default()
    };

    let mut flag = false;
    let mut row_count = 5;

    eframe::run_simple_native("My egui App", options, move |ctx, _frame| {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.label(format!("Showing {} rows", row_count));
            if egui::menu::menu_button(ui, "BTN", |ui| {
                ui.radio_value(&mut flag, false, "False");
                ui.radio_value(&mut flag, true, "True");

                if flag {
                    egui::ScrollArea::vertical().show(ui, |ui| {
                        for _ in 0..row_count {
                            ui.add_space(30.0);
                            ui.label("Veeeeeeeeeeeery long text.");
                        }
                    });
                }
            })
            .response
            .clicked()
            {
                if row_count % 2 == 1 {
                    row_count -= 3;
                } else {
                    row_count += 5;
                }
            }
        });
    })
}

It results in something along these lines:

https://github.com/user-attachments/assets/170f482b-f458-48a3-834a-5912d31c477e

The popup menu/scroll bar combination needs a way to react (entire automatically or at least by way of an API) to the change of content size and resize if appropriate.

abey79 commented 1 day ago

This diff proposed by @emilk improves the situation a bit. However, it means the the text will no longer wrap should its length grow, which can be an issue.

  egui::menu::menu_button(ui, "BTN", |ui| {
+     ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
+         ui.set_max_height(400.0);
          ui.radio_value(&mut self.b, false, "False");
          ui.radio_value(&mut self.b, true, "True");

          if self.b {
              egui::ScrollArea::vertical().show(ui, |ui| {
+                 ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
                  for _ in row_count {
                      ui.add_space(30.0);
                      ui.label("Veeeeeeeeeeeery long text.");
                  }
              });
          }
+     });
  })
lucasmerlin commented 1 day ago

I'm having the same problem in my app, my workaround is to call this at the beginning of the Area:

    let screen_size = ui.ctx().screen_rect().size();
    ui.set_max_size(screen_size);

But I think this is pretty ugly, maybe we can add a Area::remember_size(bool) or Area::limit_size(bool) option? I think this changed with the 0.28 release, maybe in https://github.com/emilk/egui/pull/4557?