mirror of
https://github.com/kristoferssolo/cipher-workshop.git
synced 2025-12-20 11:04:38 +00:00
chore: remove tui module
This commit is contained in:
parent
262c7decfe
commit
bfa93c095a
@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["aes", "cipher-core", "cli", "des", "tui"]
|
||||
members = ["aes", "cipher-core", "cli", "des"]
|
||||
|
||||
[workspace.dependencies]
|
||||
aes = { path = "aes" }
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "tui"
|
||||
version = "0.1.0"
|
||||
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
aes.workspace = true
|
||||
cipher-core.workspace = true
|
||||
color-eyre.workspace = true
|
||||
crossterm = "0.28"
|
||||
des.workspace = true
|
||||
ratatui = "0.29"
|
||||
thiserror.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@ -1,96 +0,0 @@
|
||||
use crate::event::{AppEvent, Event, EventHandler};
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
DefaultTerminal,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||
};
|
||||
|
||||
/// Application.
|
||||
#[derive(Debug)]
|
||||
pub struct App {
|
||||
/// Is the application running?
|
||||
pub running: bool,
|
||||
/// Counter.
|
||||
pub counter: u8,
|
||||
/// Event handler.
|
||||
pub events: EventHandler,
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
running: true,
|
||||
counter: 0,
|
||||
events: EventHandler::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Constructs a new instance of [`App`].
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Run the application's main loop.
|
||||
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
|
||||
while self.running {
|
||||
terminal.draw(|frame| frame.render_widget(&self, frame.area()))?;
|
||||
self.handle_events()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle_events(&mut self) -> Result<()> {
|
||||
match self.events.next()? {
|
||||
Event::Tick => self.tick(),
|
||||
Event::Crossterm(event) => match event {
|
||||
crossterm::event::Event::Key(key_event)
|
||||
if key_event.kind == crossterm::event::KeyEventKind::Press =>
|
||||
{
|
||||
self.handle_key_event(key_event);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Event::App(app_event) => match app_event {
|
||||
AppEvent::Increment => self.increment_counter(),
|
||||
AppEvent::Decrement => self.decrement_counter(),
|
||||
AppEvent::Quit => self.quit(),
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles the key events and updates the state of [`App`].
|
||||
pub fn handle_key_event(&self, key_event: KeyEvent) {
|
||||
match key_event.code {
|
||||
KeyCode::Esc | KeyCode::Char('q') => self.events.send(AppEvent::Quit),
|
||||
KeyCode::Char('c' | 'C') if key_event.modifiers == KeyModifiers::CONTROL => {
|
||||
self.events.send(AppEvent::Quit);
|
||||
}
|
||||
KeyCode::Right => self.events.send(AppEvent::Increment),
|
||||
KeyCode::Left => self.events.send(AppEvent::Decrement),
|
||||
// Other handlers you could add here.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the tick event of the terminal.
|
||||
///
|
||||
/// The tick event is where you can update the state of your application with any logic that
|
||||
/// needs to be updated at a fixed frame rate. E.g. polling a server, updating an animation.
|
||||
pub const fn tick(&self) {}
|
||||
|
||||
/// Set running to false to quit the application.
|
||||
pub const fn quit(&mut self) {
|
||||
self.running = false;
|
||||
}
|
||||
|
||||
pub const fn increment_counter(&mut self) {
|
||||
self.counter = self.counter.saturating_add(1);
|
||||
}
|
||||
|
||||
pub const fn decrement_counter(&mut self) {
|
||||
self.counter = self.counter.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
131
tui/src/event.rs
131
tui/src/event.rs
@ -1,131 +0,0 @@
|
||||
use color_eyre::eyre::WrapErr;
|
||||
use ratatui::crossterm::event::{self, Event as CrosstermEvent};
|
||||
use std::{
|
||||
sync::mpsc,
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
/// The frequency at which tick events are emitted.
|
||||
const TICK_FPS: f64 = 30.0;
|
||||
|
||||
/// Representation of all possible events.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
/// An event that is emitted on a regular schedule.
|
||||
///
|
||||
/// Use this event to run any code which has to run outside of being a direct response to a user
|
||||
/// event. e.g. polling exernal systems, updating animations, or rendering the UI based on a
|
||||
/// fixed frame rate.
|
||||
Tick,
|
||||
/// Crossterm events.
|
||||
///
|
||||
/// These events are emitted by the terminal.
|
||||
Crossterm(CrosstermEvent),
|
||||
/// Application events.
|
||||
///
|
||||
/// Use this event to emit custom events that are specific to your application.
|
||||
App(AppEvent),
|
||||
}
|
||||
|
||||
/// Application events.
|
||||
///
|
||||
/// You can extend this enum with your own custom events.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AppEvent {
|
||||
/// Increment the counter.
|
||||
Increment,
|
||||
/// Decrement the counter.
|
||||
Decrement,
|
||||
/// Quit the application.
|
||||
Quit,
|
||||
}
|
||||
|
||||
/// Terminal event handler.
|
||||
#[derive(Debug)]
|
||||
pub struct EventHandler {
|
||||
/// Event sender channel.
|
||||
sender: mpsc::Sender<Event>,
|
||||
/// Event receiver channel.
|
||||
receiver: mpsc::Receiver<Event>,
|
||||
}
|
||||
|
||||
impl EventHandler {
|
||||
/// Constructs a new instance of [`EventHandler`] and spawns a new thread to handle events.
|
||||
pub fn new() -> Self {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let actor = EventThread::new(sender.clone());
|
||||
thread::spawn(|| actor.run());
|
||||
Self { sender, receiver }
|
||||
}
|
||||
|
||||
/// Receives an event from the sender.
|
||||
///
|
||||
/// This function blocks until an event is received.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function returns an error if the sender channel is disconnected. This can happen if an
|
||||
/// error occurs in the event thread. In practice, this should not happen unless there is a
|
||||
/// problem with the underlying terminal.
|
||||
pub fn next(&self) -> color_eyre::Result<Event> {
|
||||
Ok(self.receiver.recv()?)
|
||||
}
|
||||
|
||||
/// Queue an app event to be sent to the event receiver.
|
||||
///
|
||||
/// This is useful for sending events to the event handler which will be processed by the next
|
||||
/// iteration of the application's event loop.
|
||||
pub fn send(&self, app_event: AppEvent) {
|
||||
// Ignore the result as the reciever cannot be dropped while this struct still has a
|
||||
// reference to it
|
||||
let _ = self.sender.send(Event::App(app_event));
|
||||
}
|
||||
}
|
||||
|
||||
/// A thread that handles reading crossterm events and emitting tick events on a regular schedule.
|
||||
struct EventThread {
|
||||
/// Event sender channel.
|
||||
sender: mpsc::Sender<Event>,
|
||||
}
|
||||
|
||||
impl EventThread {
|
||||
/// Constructs a new instance of [`EventThread`].
|
||||
const fn new(sender: mpsc::Sender<Event>) -> Self {
|
||||
Self { sender }
|
||||
}
|
||||
|
||||
/// Runs the event thread.
|
||||
///
|
||||
/// This function emits tick events at a fixed rate and polls for crossterm events in between.
|
||||
fn run(self) -> color_eyre::Result<()> {
|
||||
let tick_interval = Duration::from_secs_f64(1.0 / TICK_FPS);
|
||||
let mut last_tick = Instant::now();
|
||||
loop {
|
||||
// emit tick events at a fixed rate
|
||||
let timeout = tick_interval.saturating_sub(last_tick.elapsed());
|
||||
if timeout == Duration::ZERO {
|
||||
last_tick = Instant::now();
|
||||
self.send(Event::Tick);
|
||||
}
|
||||
// poll for crossterm events, ensuring that we don't block the tick interval
|
||||
if event::poll(timeout).wrap_err("failed to poll for crossterm events")? {
|
||||
let event = event::read().wrap_err("failed to read crossterm event")?;
|
||||
self.send(Event::Crossterm(event));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends an event to the receiver.
|
||||
fn send(&self, event: Event) {
|
||||
// Ignores the result because shutting down the app drops the receiver, which causes the send
|
||||
// operation to fail. This is expected behavior and should not panic.
|
||||
let _ = self.sender.send(event);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EventHandler {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
use crate::app::App;
|
||||
|
||||
mod app;
|
||||
mod event;
|
||||
mod ui;
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = App::new().run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Alignment, Rect},
|
||||
style::{Color, Stylize},
|
||||
widgets::{Block, BorderType, Paragraph, Widget},
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
|
||||
impl Widget for &App {
|
||||
/// Renders the user interface widgets.
|
||||
///
|
||||
// This is where you add new widgets.
|
||||
// See the following resources:
|
||||
// - https://docs.rs/ratatui/latest/ratatui/widgets/index.html
|
||||
// - https://github.com/ratatui/ratatui/tree/master/examples
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let block = Block::bordered()
|
||||
.title("event-driven-generated")
|
||||
.title_alignment(Alignment::Center)
|
||||
.border_type(BorderType::Rounded);
|
||||
|
||||
let text = 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: {}",
|
||||
self.counter
|
||||
);
|
||||
|
||||
let paragraph = Paragraph::new(text).block(block).fg(Color::Cyan).centered();
|
||||
|
||||
paragraph.render(area, buf);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user