Added a main menu

This commit is contained in:
Rolf Martin Glomsrud 2025-05-03 02:08:21 +02:00
parent 908fe2f166
commit ca9b0a7e0b
6 changed files with 210 additions and 52 deletions

View file

@ -1,6 +1,6 @@
use std::time::Duration; use std::time::Duration;
use color_eyre::{eyre::Ok, Result}; use color_eyre::{Result, eyre::Ok};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use ratatui::{ use ratatui::{
DefaultTerminal, Frame, DefaultTerminal, Frame,
@ -10,37 +10,53 @@ use ratatui::{
text::Line, text::Line,
widgets::{Bar, BarChart, BarGroup, Block}, widgets::{Bar, BarChart, BarGroup, Block},
}; };
use tokio::{sync::mpsc::{UnboundedReceiver, UnboundedSender}, time::sleep}; use tokio::{
sync::mpsc::{UnboundedReceiver, UnboundedSender},
time::sleep,
};
use crate::{ use crate::{
algorithm::{self}, algorithm::{self},
tui::Message, tui::Message,
}; };
pub enum SortingMessage{ pub enum SortingMessage {
Tick Tick,
Finished,
Start,
} }
pub struct DrawableVec { pub struct DrawableVec {
vec: Vec<u32>, vec: Vec<u32>,
event_rx: UnboundedReceiver<SortingMessage>, event_rx: UnboundedReceiver<SortingMessage>,
event_tx: UnboundedSender<Message>, event_tx: UnboundedSender<Message>,
algo: String algo: String,
} }
impl DrawableVec { impl DrawableVec {
pub fn new(event_tx: UnboundedSender<Message>, event_rx: UnboundedReceiver<SortingMessage>, vec: Vec<u32>) -> DrawableVec { pub fn new(
event_tx: UnboundedSender<Message>,
event_rx: UnboundedReceiver<SortingMessage>,
vec: Vec<u32>,
) -> DrawableVec {
return DrawableVec { return DrawableVec {
vec, vec,
event_rx, event_rx,
event_tx: event_tx.clone(), event_tx: event_tx.clone(),
algo: "radixSortMSD".to_string(), algo: "insertSort".to_string(),
}; };
} }
pub async fn run(mut self) -> Result<()> { pub async fn run(mut self) -> Result<()> {
loop {
let event = self.event_rx.recv().await;
match event {
Some(SortingMessage::Start) => break,
_ => (),
}
}
algorithm::sort(self.algo.clone(), &mut self).await?; algorithm::sort(self.algo.clone(), &mut self).await?;
sleep(Duration::from_secs(100000)).await; self.event_tx.send(Message::SimulationFinished)?;
Ok(()) Ok(())
} }
@ -52,17 +68,27 @@ impl DrawableVec {
self.vec.swap(a, b); self.vec.swap(a, b);
self.event_tx.send(Message::SwapEvent(a, b))?; self.event_tx.send(Message::SwapEvent(a, b))?;
self.event_rx.recv().await; self.wait_for_next_tick().await;
Ok(()) Ok(())
} }
pub async fn set(&mut self, i:usize, elem:u32) -> Result<()>{ pub async fn set(&mut self, i: usize, elem: u32) -> Result<()> {
self.vec[i] = elem; self.vec[i] = elem;
self.event_tx.send(Message::SetEvent(i, elem))?; self.event_tx.send(Message::SetEvent(i, elem))?;
self.event_rx.recv().await; self.wait_for_next_tick().await;
Ok(())
Ok(())
}
async fn wait_for_next_tick(&mut self) {
loop {
match self.event_rx.recv().await {
Some(SortingMessage::Tick) => break,
_ => (),
};
}
} }
pub fn lessThan(&self, a: usize, b: usize) -> bool { pub fn lessThan(&self, a: usize, b: usize) -> bool {
@ -73,30 +99,30 @@ impl DrawableVec {
self.vec.iter() self.vec.iter()
} }
pub fn get(&mut self, i:usize)-> u32{ pub fn get(&mut self, i: usize) -> u32 {
*self.vec.get(i).unwrap() *self.vec.get(i).unwrap()
} }
pub fn randomize(&mut self){ pub fn randomize(&mut self) {
self.vec.shuffle(&mut rand::rng()); self.vec.shuffle(&mut rand::rng());
} }
pub fn isSorted(&mut self) -> bool{ pub fn isSorted(&mut self) -> bool {
let mut prev:u32 = 0; let mut prev: u32 = 0;
for bar in self.elements() { for bar in self.elements() {
if *bar < prev{ if *bar < prev {
return false; return false;
}else{ } else {
prev = *bar; prev = *bar;
} }
} }
true true
} }
pub fn lessThanEqual(&mut self, a:usize, b:u32) -> bool{ pub fn lessThanEqual(&mut self, a: usize, b: u32) -> bool {
return self.get(a) <= b return self.get(a) <= b;
} }
pub fn getListClone(&self) -> Vec<u32>{ pub fn getListClone(&self) -> Vec<u32> {
self.vec.clone() self.vec.clone()
} }
} }

View file

@ -16,7 +16,7 @@ mod drawableVec;
mod fps_counter; mod fps_counter;
mod algorithm; mod algorithm;
mod tui; mod tui;
mod pages;
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {

35
src/pages/main.rs Normal file
View file

@ -0,0 +1,35 @@
use crate::tui::State;
use color_eyre::eyre::Ok;
use ratatui::{Frame, Terminal};
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::prelude::CrosstermBackend;
use ratatui::widgets::{Block, Paragraph, Widget};
use color_eyre::Result;
pub struct MainView {}
impl MainView {
pub fn new() -> Self {
Self {}
}
pub fn render(&self,frame:&mut Frame<'_>, state: & State, area: &Rect, fps: u64) -> Result<()> {
let [layout] = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Fill(1)
])
.areas(*area);
let widget = Paragraph::new("HELLO")
.block(Block::bordered().title(format!(" Esc to Quit. FPS: {} ", fps)));
frame.render_widget(widget, layout);
Ok(())
}
}

17
src/pages/mod.rs Normal file
View file

@ -0,0 +1,17 @@
use main::MainView;
use ratatui::{layout::Rect, prelude::CrosstermBackend, widgets::Widget, Terminal};
use color_eyre::Result;
use sorting::SortingView;
use crate::tui::State;
pub(crate) mod main;
pub (crate) mod sorting;
pub enum Pages {
Main(MainView),
SortingView(SortingView)
}

31
src/pages/sorting.rs Normal file
View file

@ -0,0 +1,31 @@
use crate::graphics::vertical_barchart;
use crate::tui::State;
use color_eyre::eyre::Ok;
use ratatui::{Frame, Terminal};
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::prelude::CrosstermBackend;
use ratatui::widgets::{Block, Widget};
use color_eyre::Result;
pub struct SortingView {}
impl SortingView {
pub fn new() -> Self {
Self {}
}
pub fn render(&self, frame: &mut Frame<'_>, state: &State , area: &Rect, fps: u64) -> Result<()> {
let [layout] = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Fill(1)
])
.areas(*area);
let widget = vertical_barchart(&state.vec, area.width)
.block(Block::bordered().title(format!(" Esc to Quit. FPS: {} ", fps)));
frame.render_widget(widget, layout);
Ok(())
}
}

View file

@ -1,10 +1,21 @@
use std::{fmt::format, time::Duration}; use std::{fmt::format, time::Duration};
use crate::pages::Pages;
use crate::pages::{main::MainView, sorting::SortingView};
use crate::{
drawableVec::{self, DrawableVec, SortingMessage},
fps_counter::FpsCounter,
graphics::vertical_barchart,
};
use color_eyre::{Result, eyre::Ok}; use color_eyre::{Result, eyre::Ok};
use crossterm::event::{Event, KeyCode, KeyEventKind, MouseEventKind}; use crossterm::event::{Event, KeyCode, KeyEventKind, MouseEventKind};
use rand::seq::SliceRandom; use rand::{rand_core::impls, seq::SliceRandom};
use ratatui::{ use ratatui::{
layout::{Constraint, Layout, Margin}, prelude::CrosstermBackend, style::Stylize, widgets::Block, Terminal Terminal,
layout::{Constraint, Layout, Margin},
prelude::CrosstermBackend,
style::Stylize,
widgets::Block,
}; };
use tokio::{ use tokio::{
sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
@ -18,6 +29,12 @@ pub enum Message {
StartSimulation, StartSimulation,
SwapEvent(usize, usize), SwapEvent(usize, usize),
SetEvent(usize, u32), SetEvent(usize, u32),
SwapGui,
SimulationFinished
}
pub struct State {
pub vec: Vec<u32>,
} }
pub struct Tui { pub struct Tui {
@ -26,16 +43,12 @@ pub struct Tui {
frame_rate: f64, frame_rate: f64,
event_rx: UnboundedReceiver<Message>, event_rx: UnboundedReceiver<Message>,
event_tx: UnboundedSender<Message>, event_tx: UnboundedSender<Message>,
sorting_tx: UnboundedSender<SortingMessage>, sorting_tx: Option<UnboundedSender<SortingMessage>>,
should_exit: bool, should_exit: bool,
vec: Vec<u32>,
fps_counter: FpsCounter, fps_counter: FpsCounter,
state: State,
page: Pages,
} }
use crate::{
drawableVec::{self, DrawableVec, SortingMessage},
fps_counter::FpsCounter,
graphics::vertical_barchart,
};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum UpdateCommand { pub enum UpdateCommand {
@ -48,12 +61,10 @@ impl Tui {
let terminal = ratatui::init(); let terminal = ratatui::init();
let (event_tx, event_rx): (UnboundedSender<Message>, UnboundedReceiver<Message>) = let (event_tx, event_rx): (UnboundedSender<Message>, UnboundedReceiver<Message>) =
mpsc::unbounded_channel(); mpsc::unbounded_channel();
let (sorting_tx, sorting_rx) = mpsc::unbounded_channel();
let mut vec = (Vec::from_iter(1..50)); let mut vec = (Vec::from_iter(1..50));
vec.shuffle(&mut rand::rng()); vec.shuffle(&mut rand::rng());
let drawableVec = DrawableVec::new(event_tx.clone(), sorting_rx, vec.clone());
let sorter = tokio::spawn(drawableVec.run()); let page = MainView::new();
Ok(Self { Ok(Self {
terminal, terminal,
@ -61,10 +72,11 @@ impl Tui {
frame_rate: 60., frame_rate: 60.,
event_rx, event_rx,
event_tx, event_tx,
sorting_tx, sorting_tx: None,
should_exit: false, should_exit: false,
vec: vec,
fps_counter: FpsCounter::new(), fps_counter: FpsCounter::new(),
page: Pages::Main(page),
state: State { vec },
}) })
} }
@ -77,7 +89,10 @@ impl Tui {
tokio::select! { tokio::select! {
_tick = tick_interval.tick() => { _tick = tick_interval.tick() => {
self.event_tx.send(Message::Tick)?; self.event_tx.send(Message::Tick)?;
self.sorting_tx.send(SortingMessage::Tick)? match &self.sorting_tx {
Some(tx) => tx.send(SortingMessage::Tick)?,
None => (),
}
} }
_frame = frame_interval.tick() => { _frame = frame_interval.tick() => {
self.event_tx.send(Message::Render)? self.event_tx.send(Message::Render)?
@ -114,13 +129,40 @@ impl Tui {
self.fps_counter.tick(); self.fps_counter.tick();
Ok(UpdateCommand::None) Ok(UpdateCommand::None)
} }
Message::StartSimulation => todo!(), Message::StartSimulation => {
match &self.sorting_tx {
Some(tx) => tx.send(SortingMessage::Start)?,
None => (),
}
Ok(UpdateCommand::None)
}
Message::SwapEvent(a, b) => { Message::SwapEvent(a, b) => {
self.vec.swap(a, b); self.state.vec.swap(a, b);
Ok(UpdateCommand::None) Ok(UpdateCommand::None)
} }
Message::SetEvent(i, e) => { Message::SetEvent(i, e) => {
self.vec.insert(i, e); self.state.vec.insert(i, e);
Ok(UpdateCommand::None)
}
Message::SwapGui => {
self.page = match self.page {
Pages::Main(_) => {
let (sorting_tx, sorting_rx) = mpsc::unbounded_channel();
self.sorting_tx = Some(sorting_tx);
let drawable_vec = DrawableVec::new(
self.event_tx.clone(),
sorting_rx,
self.state.vec.clone(),
);
tokio::spawn(drawable_vec.run());
Pages::SortingView(SortingView::new())
}
Pages::SortingView(_) => Pages::Main(MainView::new()),
};
Ok(UpdateCommand::None)
}
Message::SimulationFinished => {
self.sorting_tx = None;
Ok(UpdateCommand::None) Ok(UpdateCommand::None)
} }
} }
@ -128,18 +170,20 @@ impl Tui {
fn view(&mut self) -> Result<()> { fn view(&mut self) -> Result<()> {
self.terminal.draw(|frame| { self.terminal.draw(|frame| {
let [title, vertical] = let [title, vertical] = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)])
Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]) .spacing(1)
.spacing(1) .areas(frame.area());
.areas(frame.area());
frame.render_widget("Sorting!".bold().into_centered_line(), title); frame.render_widget("Sorting!".bold().into_centered_line(), title);
frame.render_widget(
vertical_barchart(&self.vec, vertical.inner(Margin::new(10, 0)).width).block( match &self.page {
Block::bordered() Pages::Main(main_view) => {
.title(format!(" Esc to Quit. FPS: {} ", self.fps_counter.fps)), let _ = main_view.render(frame, &self.state, &vertical, self.fps_counter.fps);
), }
vertical, Pages::SortingView(sorting_view) => {
); let _ =
sorting_view.render(frame, &self.state, &vertical, self.fps_counter.fps);
}
};
})?; })?;
Ok(()) Ok(())
@ -151,7 +195,12 @@ impl Tui {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Esc { if key.kind == KeyEventKind::Press && key.code == KeyCode::Esc {
self.event_tx.send(Message::Quit)?; self.event_tx.send(Message::Quit)?;
} else if key.kind == KeyEventKind::Press && key.code == KeyCode::Enter { } else if key.kind == KeyEventKind::Press && key.code == KeyCode::Enter {
self.event_tx.send(Message::StartSimulation)?; match &self.sorting_tx {
Some(tx) => tx.send(SortingMessage::Start)?,
None => (),
};
} else if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('a') {
self.event_tx.send(Message::SwapGui)?;
} }
} }
_ => {} _ => {}