Skip to content

Commit

Permalink
Merge pull request #9 from ziriuz84/7-add-basical-tui
Browse files Browse the repository at this point in the history
7 add basical tui
  • Loading branch information
ziriuz84 authored May 2, 2024
2 parents 4e70d9d + 8597be7 commit 5cdb72b
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 49 deletions.
14 changes: 14 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
use crate::settings::Settings;
use crate::weather::prepare_data;
use crate::weather::ForecastResponse;
use std::error;

/// Application result type.
pub type AppResult<T> = std::result::Result<T, Box<dyn error::Error>>;

#[derive(Debug)]
pub enum CurrentScreen {
MainMenu,
SchedulingMenu,
WeatherForecast,
}

/// Application.
#[derive(Debug)]
pub struct App {
/// Is the application running?
pub running: bool,
/// counter
pub counter: u8,
pub current_screen: CurrentScreen,
pub weather_requested: ForecastResponse,
}

impl Default for App {
fn default() -> Self {
let weather_data: ForecastResponse = prepare_data().unwrap();
Self {
running: true,
counter: 0,
current_screen: CurrentScreen::MainMenu,
weather_requested: weather_data,
}
}
}
Expand Down
15 changes: 14 additions & 1 deletion src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::app::{App, AppResult};
use crate::app::{App, AppResult, CurrentScreen};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

/// Handles the key events and updates the state of [`App`].
Expand All @@ -14,6 +14,19 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
app.quit();
}
}
KeyCode::Char('s') | KeyCode::Char('S') => match app.current_screen {
CurrentScreen::MainMenu => app.current_screen = CurrentScreen::SchedulingMenu,
_ => {}
},
KeyCode::Char('b') | KeyCode::Char('B') => match app.current_screen {
CurrentScreen::SchedulingMenu => app.current_screen = CurrentScreen::MainMenu,
CurrentScreen::WeatherForecast => app.current_screen = CurrentScreen::SchedulingMenu,
_ => {}
},
KeyCode::Char('w') | KeyCode::Char('W') => match app.current_screen {
CurrentScreen::SchedulingMenu => app.current_screen = CurrentScreen::WeatherForecast,
_ => {}
},
// Counter handlers
KeyCode::Right => {
app.increment_counter();
Expand Down
258 changes: 242 additions & 16 deletions src/ui.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,260 @@
use ratatui::{
layout::Alignment,
style::{Color, Style},
widgets::{Block, BorderType, Paragraph},
widgets::{Block, BorderType, Cell, Paragraph, Row, Table},
Frame,
};
use serde::{Deserialize, Serialize};
use serde_json::{Error, Result};

use ratatui::prelude::*;

use crate::app::App;
use crate::app::CurrentScreen;
use crate::weather::prepare_data;
use crate::weather::ForecastResponse;

/// Renders the user interface widgets.
pub fn render(app: &mut App, frame: &mut Frame) {
// This is where you add new widgets.
// See the following resources:
// - https://docs.rs/ratatui/latest/ratatui/widgets/index.html
// - https://github.com/ratatui-org/ratatui/tree/master/examples
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![
Constraint::Length(3),
Constraint::Length(3),
Constraint::Min(1),
Constraint::Length(3),
])
.split(frame.size());
match app.current_screen {
CurrentScreen::MainMenu => {
render_main_menu(app, frame, layout);
}
CurrentScreen::SchedulingMenu => {
render_scheduling_menu(app, frame, layout);
}
CurrentScreen::WeatherForecast => {
render_weather_forecast(app, frame, layout);
}
}
}

/// Centers a rect inside an area
///
/// * `r`: The area where to insert resulting rect
/// * `percent_x`: width of resulting rect in percent of `r.width()`
/// * `percent_y`: height of resulting rect in percent of `r.height()`
fn centered_rect(r: Rect, percent_x: u16, percent_y: u16) -> Rect {
let popup_layout = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
])
.split(r);

Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
])
.split(popup_layout[1])[1]
}

fn render_main_menu(
app: &mut App,
frame: &mut Frame,
layout: std::rc::Rc<[ratatui::layout::Rect]>,
) {
frame.render_widget(
Paragraph::new("AsteroidTUI")
.block(
Block::bordered()
//.title("Template")
//.title_alignment(Alignment::Center)
.border_type(BorderType::Rounded),
)
.style(Style::default().fg(Color::Red).bg(Color::Black))
.centered(),
layout[0],
);
frame.render_widget(
Paragraph::new(format!(
"This is a tui template.\n\
Press `Esc`, `Ctrl-C` or `q` to stop running.\n\
Press left and right to increment and decrement the counter respectively.\n\
Counter: {}",
app.counter
))
.block(
Block::bordered()
.title("Template")
.title_alignment(Alignment::Center)
.border_type(BorderType::Rounded),
Paragraph::new("")
.block(Block::default())
.style(Style::default().bg(Color::Black)),
layout[1],
);
frame.render_widget(
Paragraph::new(
"Main Menu\n\
\n\n\
c - Configuration\n\
s - Scheduling\n\
q - quit",
)
.style(Style::default().fg(Color::Cyan).bg(Color::Black))
.style(Style::default().fg(Color::Red).bg(Color::Black))
.centered(),
frame.size(),
)
layout[2],
);
frame.render_widget(
Paragraph::new("Press q or Ctrl+C to quit")
.block(Block::bordered().border_type(BorderType::Rounded))
.style(Style::default().fg(Color::Red).bg(Color::Black))
.centered(),
layout[3],
);
}

fn render_scheduling_menu(
app: &mut App,
frame: &mut Frame,
layout: std::rc::Rc<[ratatui::layout::Rect]>,
) {
frame.render_widget(
Paragraph::new("AsteroidTUI")
.block(
Block::bordered()
//.title("Template")
//.title_alignment(Alignment::Center)
.border_type(BorderType::Rounded),
)
.style(Style::default().fg(Color::Red).bg(Color::Black))
.centered(),
layout[0],
);
frame.render_widget(
Paragraph::new("")
.block(Block::default())
.style(Style::default().bg(Color::Black)),
layout[1],
);
frame.render_widget(
Paragraph::new(
"Scheduling Menu\n\
\n\n\
w - Weather Forecast\n\
b - Back to Main Menu\n\
q - Quit",
)
.style(Style::default().fg(Color::Red).bg(Color::Black))
.centered(),
layout[2],
);
frame.render_widget(
Paragraph::new("Press q or Ctrl+C to quit")
.block(Block::bordered().border_type(BorderType::Rounded))
.style(Style::default().fg(Color::Red).bg(Color::Black))
.centered(),
layout[3],
);
}

fn create_table(data: &ForecastResponse) -> Table {
// Create the table header
let header = vec![
"Timepoint",
"Cloud Cover",
"Seeing",
"Transparency",
"Lifted Index",
"RH2m",
"Wind10m",
"Temp2m",
"Prec Type",
]
.into_iter()
.map(Cell::from)
.collect::<Row>()
.style(Style::default().add_modifier(Modifier::BOLD));

// Create table rows
let rows = data
.dataseries
.iter()
.map(|forecast| {
Row::new(vec![
Cell::from(forecast.timepoint.to_string()),
Cell::from(forecast.cloud_cover.to_string()), // Assuming CloudCover, Seeing, Transparency, Wind10m have a to_string() method
Cell::from(forecast.seeing.to_string()),
Cell::from(forecast.transparency.to_string()),
Cell::from(forecast.lifted_index.to_string()),
Cell::from(forecast.rh2m.to_string()),
Cell::from(forecast.wind10m.direction.to_string()),
Cell::from(forecast.wind10m.speed.to_string()),
Cell::from(forecast.temp2m.to_string()),
Cell::from(forecast.prec_type.clone()),
])
})
.collect::<Vec<Row>>();
let widths = [
Constraint::Percentage(10),
Constraint::Percentage(10),
Constraint::Percentage(10),
Constraint::Percentage(15),
Constraint::Percentage(10),
Constraint::Percentage(10),
Constraint::Percentage(10),
Constraint::Percentage(15),
Constraint::Percentage(10),
Constraint::Percentage(15),
];

// Configure the table
Table::new(rows, widths).header(header)
}

fn render_weather_forecast(
app: &mut App,
frame: &mut Frame,
layout: std::rc::Rc<[ratatui::layout::Rect]>,
) {
let header = Row::new(vec![
Cell::from("Time"),
Cell::from("Cloud Cover"),
Cell::from("Seeing"),
Cell::from("Transparency"),
Cell::from("Lifted Index"),
Cell::from("RH (2m)"),
Cell::from("Wind Dir (10m)"),
Cell::from("Wind Speed (10m)"),
Cell::from("Temp (2m)"),
Cell::from("Prec Type"),
]);
frame.render_widget(
Paragraph::new("AsteroidTUI")
.block(
Block::bordered()
//.title("Template")
//.title_alignment(Alignment::Center)
.border_type(BorderType::Rounded),
)
.style(Style::default().fg(Color::Red).bg(Color::Black))
.centered(),
layout[0],
);
frame.render_widget(
Paragraph::new("")
.block(Block::default())
.style(Style::default().bg(Color::Black)),
layout[1],
);
frame.render_widget(
create_table(&app.weather_requested)
.header(header)
.style(Style::default().bg(Color::Black).fg(Color::Red)),
layout[2],
);
frame.render_widget(
Paragraph::new("Press q or Ctrl+C to quit")
.block(Block::bordered().border_type(BorderType::Rounded))
.style(Style::default().fg(Color::Red).bg(Color::Black))
.centered(),
layout[3],
);
}
Loading

0 comments on commit 5cdb72b

Please sign in to comment.