[rust/egui] (8) Use panels to divide your application into functional blocks

Say it in front

  • New to rust, I can’t find any tutorials on egui. I’ll record the learning process here.
  • Environment: windows11 22H2
  • rust version: rustc 1.71.1
  • egui version: 0.22.0
  • eframe version: 0.22.0
  • Previous article: here

What is panel

  • panel is an area on the UI. For example, if we open CSDN’s markdown editor, it can be roughly divided into four (five) blocks (of course, in actual implementation, these four areas may not be parallel ), then we can use four panels to implement it:

    • top-level article title
    • sub-top menu bar
    • Editing area on the left
    • Preview area on the right
  • panel is somewhat similar to the div element in html, but its function is not as powerful as div (preliminary Feeling haha)

How to use

  • In the previous sections, we have had a preliminary understanding of the basic use of panel. Here we look at a comprehensive use case.

  • Suppose we want to implement the layout of vscode, how should we implement it? Let’s first take a look at the functional area of vscode. Of course, there is also a menu bar at the top.

  • Now let’s try to implement it (you can do it directly on the template in the previous sections)

    fn update( & amp;mut self, ctx: & amp;egui::Context, _frame: & amp;mut eframe::Frame) {<!-- -->
        egui::TopBottomPanel::top("Menu Bar").show(ctx, |ui| {<!-- -->
            ui.vertical_centered(|ui|{<!-- -->
                ui.heading("Menu Bar");
            });
        });
    
        egui::TopBottomPanel::bottom("Status Bar").show(ctx, |ui| {<!-- -->
            ui.vertical_centered(|ui|{<!-- -->
                ui.heading("Status Bar");
            });
        });
    
        egui::SidePanel::left("Activity Bar").show(ctx, |ui| {<!-- -->
            ui.horizontal_centered(|ui|{<!-- -->
                ui.label("Activity Bar");
            });
        });
    
        egui::SidePanel::left("Side Bar").show(ctx, |ui| {<!-- -->
            ui.horizontal_centered(|ui|{<!-- -->
                ui.label("Side Bar");
            });
        });
    
        egui::TopBottomPanel::bottom("Panel").show(ctx, |ui| {<!-- -->
            ui.vertical_centered(|ui|{<!-- -->
                ui.heading("Panel");
            });
        });
    
        egui::CentralPanel::default().show(ctx, |ui|{<!-- -->
            ui.vertical_centered(|ui|{<!-- -->
                ui.heading("Editor");
            });
        });
    }
    

    The result is:

  • The general division of areas is almost the same, but the size of each area is not quite right. We can adjust it slightly.

    fn update( & amp;mut self, ctx: & amp;egui::Context, _frame: & amp;mut eframe::Frame) {<!-- -->
        egui::TopBottomPanel::top("Menu Bar").show(ctx, |ui| {<!-- -->
            ui.vertical_centered(|ui| {<!-- -->
                ui.heading("Menu Bar");
            });
        });
    
        egui::TopBottomPanel::bottom("Status Bar").show(ctx, |ui| {<!-- -->
            ui.vertical_centered(|ui| {<!-- -->
                ui.heading("Status Bar");
            });
        });
    
        egui::SidePanel::left("Activity Bar")
            .max_width(40.0)
            .resizable(false)
            .show(ctx, |ui| ui.add(egui::Label::new("Activity Bar")));
    
        egui::SidePanel::left("Side Bar")
            .default_width(1000.0)
            .width_range(200.0..=2000.0)
            .resizable(true)
            .show(ctx, |ui| {<!-- -->
                ui.text_edit_singleline( & amp;mut "hi");
            });
    
        egui::TopBottomPanel::bottom("Panel")
            .default_height(200.0)
            .resizable(false)
            .show(ctx, |ui| {<!-- -->
                ui.add(egui::TextEdit::multiline( & amp;mut "Panel").desired_rows(10));
            });
    
        egui::CentralPanel::default().show(ctx, |ui| {<!-- -->
            ui.heading("Editor");
        });
    }
    

    The result is:

    In the code, in addition to defining the width and height of the panel, some text_edit is also added; this is because the actual width and height of the panel are related to its internal elements.
    For example, if there is only one label inside a SidePanel, its width cannot change dynamically even if you set the resizable attribute.

    pub fn resizable(self, resizable: bool) -> Self

    • Can panel be resized by dragging the edge of it?
    • Default is true.
    • If you want your panel to be resizable you also need a widget in it that takes up more space as you resize it

window decorations

  • Comparing vscode we can see that there is a small difference:

  • The icons and menu bar in vscode are all together, but our demo app is divided into two parts

  • If we want to be consistent with vscode, how should we achieve it?

  • First remove the decorations of eframe

    let mut native_options = eframe::NativeOptions::default();
    native_options.decorated = false;
    
    let ret = eframe::run_native(
        "demo app",
        native_options,
        Box::new(|cc| Box::new(demo_app::TemplateApp::new(cc))),
    );
    
  • Then you need to implement the entire frame yourself. You can refer to here for details. It is relatively troublesome. The effect is as shown below:

  • I feel that the effect is not very good, so I won’t expand on it here.

Dividing areas within the panel

  • When using a panel, you may encounter a situation where the interior of the panel needs to be further divided. How should you deal with this situation?
    egui::SidePanel::left("Side Bar")
    .default_width(1000.0)
        .width_range(200.0..=2000.0)
        .resizable(true)
        .show(ctx, |ui| {<!-- -->
            egui::TopBottomPanel::bottom("AB bottom").show_inside(ui, |ui| {<!-- -->
                ui.label("bottom");
            });
            ui.text_edit_singleline( & amp;mut "Side Bar");
        });
    

    Use the show_inside method

Reference

  • panel