Build a Playlist Manager with egui — Vec State & CRUD | Rust GUI Ep 27
Video: Build a Playlist Manager with egui — Vec State & CRUD | Rust GUI Ep 27 by Taught by Celeste AI - AI Coding Coach
Watch full page →Build a Playlist Manager with egui — Vec State & CRUD
Learn how to create a simple playlist manager GUI in Rust using the egui framework. This example demonstrates managing a dynamic list of songs with add, remove, and favorite features, leveraging a Vec for state management and common egui widgets like TextEdit, Buttons, Checkboxes, and ScrollArea for a smooth user experience.
Code
use eframe::{egui, epi};
#[derive(Default)]
struct Song {
title: String,
artist: String,
favorite: bool,
}
struct MyApp {
songs: Vec<Song>,
new_title: String,
new_artist: String,
remove_index: Option<usize>,
}
impl Default for MyApp {
fn default() -> Self {
Self {
songs: Vec::new(),
new_title: String::new(),
new_artist: String::new(),
remove_index: None,
}
}
}
impl epi::App for MyApp {
fn name(&self) -> &str {
"Playlist Manager"
}
fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
egui::TopBottomPanel::bottom("add_song_panel").show(ctx, |ui| {
ui.horizontal(|ui| {
// Fixed width inputs for title and artist
ui.add(egui::TextEdit::singleline(&mut self.new_title).desired_width(150.0));
ui.add(egui::TextEdit::singleline(&mut self.new_artist).desired_width(150.0));
// Add button enabled only if both fields are filled
let add_enabled = !self.new_title.trim().is_empty() && !self.new_artist.trim().is_empty();
if ui.add_enabled(add_enabled, egui::Button::new("Add")).clicked() {
self.songs.push(Song {
title: self.new_title.trim().to_owned(),
artist: self.new_artist.trim().to_owned(),
favorite: false,
});
self.new_title.clear();
self.new_artist.clear();
}
});
});
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Playlist");
// Scrollable list of songs
egui::ScrollArea::vertical().show(ui, |ui| {
for (i, song) in self.songs.iter_mut().enumerate() {
ui.horizontal(|ui| {
// Favorite checkbox
ui.checkbox(&mut song.favorite, "");
// Song info with bold title
ui.label(egui::RichText::new(&song.title).strong());
ui.label(format!("by {}", &song.artist));
// Right-aligned remove button
ui.with_layout(egui::Layout::right_to_left(), |ui| {
if ui.button("Remove").clicked() {
self.remove_index = Some(i);
}
});
});
ui.separator();
}
});
});
// Deferred removal to avoid borrow conflicts
if let Some(index) = self.remove_index {
if index < self.songs.len() {
self.songs.remove(index);
}
self.remove_index = None;
}
}
}
fn main() {
let app = MyApp::default();
let native_options = eframe::NativeOptions::default();
eframe::run_native(Box::new(app), native_options);
}
Key Points
- Use a Vec<Song> to store and manage the playlist state dynamically.
- TextEdit::singleline with desired_width creates fixed-width input fields for consistent layout.
- add_enabled() disables the Add button until both title and artist fields are non-empty.
- ScrollArea::vertical() enables scrolling through a potentially long list of songs.
- Deferred removal with an Option<usize> avoids mutable borrow conflicts during iteration.