WIP: TUI actor
All checks were successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful

This commit is contained in:
Paul Campbell 2024-08-10 08:30:23 +01:00
parent 08d2377404
commit f0a1d43e96
7 changed files with 144 additions and 1 deletions

View file

@ -5,10 +5,13 @@ mod forge;
mod init;
mod repo;
mod server;
mod webhook;
#[cfg(feature = "tui")]
mod tui;
#[cfg(test)]
mod tests;
mod webhook;
use git_next_core::git;

View file

@ -65,6 +65,15 @@ pub fn start(
let fw_shutdown = watch_file("git-next-server.toml".into(), server.clone().recipient())
.expect("file watcher");
// info!("Start Terminal...");
// #[cfg(feature = "tui")]
// {
// info!("Start Terminal...");
// let tui = crate::tui::Tui::default();
// let tui_addr = tui.start();
// tui_addr.do_send(crate::tui::Start);
// }
info!("Server running - Press Ctrl-C to stop...");
let _ = actix_rt::signal::ctrl_c().await;
info!("Ctrl-C received, shutting down...");

View file

@ -0,0 +1,2 @@
//
mod start;

View file

@ -0,0 +1,58 @@
use std::io::Stderr;
//
use actix::{ContextFutureSpawner, Handler, WrapFuture};
use ratatui::{
crossterm::event::{self, KeyCode, KeyEventKind},
prelude::CrosstermBackend,
style::Stylize,
widgets::Paragraph,
Terminal,
};
use tracing::Instrument;
use crate::tui::actor::{messages::Start, Tui};
impl Handler<Start> for Tui {
type Result = ();
fn handle(&mut self, _msg: Start, ctx: &mut Self::Context) -> Self::Result {
let Some(mut terminal) = self.terminal.take() else {
tracing::error!("Unable to start without terminal");
return;
};
async move { terminal_loop_wrapper(&mut terminal) }
.in_current_span()
.into_actor(self)
.wait(ctx);
}
}
fn terminal_loop_wrapper(terminal: &mut Terminal<CrosstermBackend<Stderr>>) {
match terminal_loop(terminal) {
Ok(()) => tracing::info!("Terminal closed"),
Err(err) => tracing::error!(?err, "Terminal closed"),
}
}
fn terminal_loop(terminal: &mut Terminal<CrosstermBackend<Stderr>>) -> std::io::Result<()> {
loop {
terminal.draw(|frame| {
let area = frame.area();
frame.render_widget(
Paragraph::new("Hello Ratatui! (press 'q' to quit)")
.white()
.on_blue(),
area,
);
})?;
if event::poll(std::time::Duration::from_millis(16))? {
if let event::Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
break;
}
}
}
}
Ok(())
}

View file

@ -0,0 +1,4 @@
//
use git_next_core::message;
message!(Start, "Start the TUI");

View file

@ -0,0 +1,62 @@
//
mod handlers;
pub mod messages;
use std::io::{stderr, Stderr};
use actix::{Actor, Context};
use ratatui::{
crossterm::{
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
prelude::CrosstermBackend,
Terminal,
};
#[derive(Debug, Default)]
pub struct Tui {
terminal: Option<Terminal<CrosstermBackend<Stderr>>>,
}
impl Actor for Tui {
type Context = Context<Self>;
fn started(&mut self, _ctx: &mut Self::Context) {
match init() {
Ok(terminal) => {
self.terminal.replace(terminal);
}
Err(err) => tracing::error!(?err, "Failed to start terminal UI"),
}
}
fn stopped(&mut self, _ctx: &mut Self::Context) {
if let Err(err) = restore() {
match std::env::consts::OS {
"linux" | "macos" => {
tracing::error!(
?err,
"Failed to restore terminal: Type `reset` to restore terminal"
);
}
"windows" => {
tracing::error!(?err, "Failed to restore terminal: Reopen a new terminal");
}
_ => tracing::error!(?err, "Failed to restore terminal"),
}
}
}
}
fn init() -> std::io::Result<Terminal<CrosstermBackend<Stderr>>> {
execute!(stderr(), EnterAlternateScreen)?;
enable_raw_mode()?;
Terminal::new(CrosstermBackend::new(stderr()))
}
fn restore() -> std::io::Result<()> {
execute!(stderr(), LeaveAlternateScreen)?;
disable_raw_mode()
}

View file

@ -0,0 +1,5 @@
//
mod actor;
// pub use actor::messages::Start;
// pub use actor::Tui;