Search code examples
user-interfacerustegui

Egui ScrollArea part of a Rect


I'm trying to create a ScrollArea that will have bounds that start at a certain (x, y) screen coordinates and end at a (z, w) screen coordinates. In other words, I want to create an arbitrary <div> (in html terms) that has fixed width and height and is placed in an arbitrary spot on the screen.

What I have right now is this:

Text that is outside of the container, (but with what seems to be correct bounds)

It appears the text is outside of where the container is, but it seems to have the correct bounds.

Here is what I want the result to look like: The text is at the same position as the container, and is scrolling with respect to the bounds of the container

Here's the code I have tried at the moment:

let rect = Rect::from_min_max(pos2(80.0, 80.0), pos2(280.0, 280.0));
let frame = egui::Frame::none()
    .fill(Color32::from_rgb(232, 12, 232))
    .paint(rect);

ui.painter().add(frame);

egui::ScrollArea::vertical()
    .max_height(280.0 - 80.0)
    .max_width(280.0 - 80.0)
    .show(ui, |ui| {
        ui.add(Label::new("long text".repeat(99)));
    });

Full code:

use eframe::egui::{self, *};

fn main() {
    let native_options = eframe::NativeOptions::default();

    eframe::run_native(
        "Arbitrary Scroll",
        native_options,
        Box::new(|_| Box::new(MyEguiApp)),
    )
    .unwrap();
}

struct MyEguiApp;

impl eframe::App for MyEguiApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            let rect = Rect::from_min_max(pos2(80.0, 80.0), pos2(280.0, 280.0));
            let frame = egui::Frame::none()
                .fill(Color32::from_rgb(232, 12, 232))
                .paint(rect);

            ui.painter().add(frame);

            egui::ScrollArea::vertical()
                .max_height(280.0 - 80.0)
                .max_width(280.0 - 80.0)
                .show(ui, |ui| {
                    ui.add(Label::new("long text".repeat(99)));
                });
        });
    }
}

Any advice?


Solution

  • egui uses a sort of cursor to remember where to draw next, but painting does not advance that cursor or influence it in any way. You have to Ui::allocate_* to advance the cursor to the correct position. Specifically you can use Ui::allocate_ui_at_rect to allocate a specific rect of the remaining surface. And then just render to that.

    impl eframe::App for MyEguiApp {
        fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
            egui::CentralPanel::default().show(ctx, |ui| {
                let rect = Rect::from_min_max(pos2(80.0, 80.0), pos2(280.0, 280.0));
                let frame = egui::Frame::none()
                    .fill(Color32::from_rgb(232, 12, 232))
                    .paint(rect);
    
                ui.painter().add(frame);
                ui.allocate_ui_at_rect(rect, |ui| {
                    egui::ScrollArea::vertical()
                        .max_height(280.0 - 80.0)
                        .max_width(280.0 - 80.0)
                        .show(ui, |ui| {
                            ui.add(Label::new("long text".repeat(99)));
                        });
                });
            });
        }
    }