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

View file

@ -16,7 +16,7 @@ mod drawableVec;
mod fps_counter;
mod algorithm;
mod tui;
mod pages;
#[tokio::main]
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 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 crossterm::event::{Event, KeyCode, KeyEventKind, MouseEventKind};
use rand::seq::SliceRandom;
use rand::{rand_core::impls, seq::SliceRandom};
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::{
sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
@ -18,6 +29,12 @@ pub enum Message {
StartSimulation,
SwapEvent(usize, usize),
SetEvent(usize, u32),
SwapGui,
SimulationFinished
}
pub struct State {
pub vec: Vec<u32>,
}
pub struct Tui {
@ -26,16 +43,12 @@ pub struct Tui {
frame_rate: f64,
event_rx: UnboundedReceiver<Message>,
event_tx: UnboundedSender<Message>,
sorting_tx: UnboundedSender<SortingMessage>,
sorting_tx: Option<UnboundedSender<SortingMessage>>,
should_exit: bool,
vec: Vec<u32>,
fps_counter: FpsCounter,
state: State,
page: Pages,
}
use crate::{
drawableVec::{self, DrawableVec, SortingMessage},
fps_counter::FpsCounter,
graphics::vertical_barchart,
};
#[derive(Clone, Debug)]
pub enum UpdateCommand {
@ -48,12 +61,10 @@ impl Tui {
let terminal = ratatui::init();
let (event_tx, event_rx): (UnboundedSender<Message>, UnboundedReceiver<Message>) =
mpsc::unbounded_channel();
let (sorting_tx, sorting_rx) = mpsc::unbounded_channel();
let mut vec = (Vec::from_iter(1..50));
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 {
terminal,
@ -61,10 +72,11 @@ impl Tui {
frame_rate: 60.,
event_rx,
event_tx,
sorting_tx,
sorting_tx: None,
should_exit: false,
vec: vec,
fps_counter: FpsCounter::new(),
page: Pages::Main(page),
state: State { vec },
})
}
@ -77,7 +89,10 @@ impl Tui {
tokio::select! {
_tick = tick_interval.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() => {
self.event_tx.send(Message::Render)?
@ -114,13 +129,40 @@ impl Tui {
self.fps_counter.tick();
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) => {
self.vec.swap(a, b);
self.state.vec.swap(a, b);
Ok(UpdateCommand::None)
}
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)
}
}
@ -128,18 +170,20 @@ impl Tui {
fn view(&mut self) -> Result<()> {
self.terminal.draw(|frame| {
let [title, vertical] =
Layout::vertical([Constraint::Length(1), Constraint::Fill(1)])
.spacing(1)
.areas(frame.area());
let [title, vertical] = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)])
.spacing(1)
.areas(frame.area());
frame.render_widget("Sorting!".bold().into_centered_line(), title);
frame.render_widget(
vertical_barchart(&self.vec, vertical.inner(Margin::new(10, 0)).width).block(
Block::bordered()
.title(format!(" Esc to Quit. FPS: {} ", self.fps_counter.fps)),
),
vertical,
);
match &self.page {
Pages::Main(main_view) => {
let _ = main_view.render(frame, &self.state, &vertical, self.fps_counter.fps);
}
Pages::SortingView(sorting_view) => {
let _ =
sorting_view.render(frame, &self.state, &vertical, self.fps_counter.fps);
}
};
})?;
Ok(())
@ -151,7 +195,12 @@ impl Tui {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Esc {
self.event_tx.send(Message::Quit)?;
} 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)?;
}
}
_ => {}