Back to Blog

egui TopBottomPanel Tutorial - Recipe Viewer App | Learn Rust GUI Ep 11

Sandy LaneSandy Lane

Video: egui TopBottomPanel Tutorial - Recipe Viewer App | Learn Rust GUI Ep 11 by Taught by Celeste AI - AI Coding Coach

Watch full page →

egui TopBottomPanel Tutorial - Recipe Viewer App

In this tutorial, we build a simple Recipe Viewer desktop app using Rust's egui framework, demonstrating how to use top and bottom panels for navigation and status bars. You'll learn to create a three-panel layout with a menu bar at the top, a status bar at the bottom, and a central content area with selectable tabs for switching views.

Code

use eframe::{egui, epi};

struct Recipe {
  name: String,
  ingredients: Vec<String>,
}

struct RecipeApp {
  recipes: Vec<Recipe>,
  selected_tab: usize,
}

impl Default for RecipeApp {
  fn default() -> Self {
    Self {
      recipes: vec![
        Recipe {
          name: "Pancakes".to_owned(),
          ingredients: vec!["Flour".to_owned(), "Eggs".to_owned(), "Milk".to_owned()],
        },
        Recipe {
          name: "Salad".to_owned(),
          ingredients: vec!["Lettuce".to_owned(), "Tomatoes".to_owned(), "Cucumber".to_owned()],
        },
      ],
      selected_tab: 0,
    }
  }
}

impl epi::App for RecipeApp {
  fn name(&self) -> &str {
    "Recipe Viewer"
  }

  fn update(&mut self, ctx: &egui::CtxRef, _frame: &epi::Frame) {
    // Top panel for menu/navigation bar
    egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
      ui.horizontal(|ui| {
        ui.label("Recipes:");
        for (i, recipe) in self.recipes.iter().enumerate() {
          // selectable_label creates clickable tabs
          if ui.selectable_label(self.selected_tab == i, &recipe.name).clicked() {
            self.selected_tab = i;
          }
        }
      });
    });

    // Bottom panel for status bar
    egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| {
      ui.label(format!("{} recipes loaded", self.recipes.len()));
    });

    // Central panel for main content
    egui::CentralPanel::default().show(ctx, |ui| {
      if let Some(recipe) = self.recipes.get(self.selected_tab) {
        ui.heading(&recipe.name);
        ui.separator();
        ui.label("Ingredients:");
        for ingredient in &recipe.ingredients {
          ui.label(format!("- {}", ingredient));
        }
      } else {
        ui.label("Select a recipe from the top menu.");
      }
    });
  }
}

fn main() {
  let app = RecipeApp::default();
  let native_options = eframe::NativeOptions::default();
  eframe::run_native(Box::new(app), native_options);
}

Key Points

  • Use TopBottomPanel::top() to create a menu or navigation bar at the top of the window.
  • TopBottomPanel::bottom() is ideal for status bars or footers showing app info.
  • selectable_label() enables clickable tabs to switch views or content dynamically.
  • CentralPanel provides the main content area where detailed information is displayed.
  • Structs and Vec collections organize app data, such as recipes and their ingredients.