Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add save and record messages on file #69

Merged
merged 5 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,36 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
override: true
- name: Install libudev-sys
run: sudo apt-get install -y libudev-dev
- name: Build
run: cargo build --release --verbose
run: cargo +nightly build --release --verbose
build-windows:
name: Check on Windows
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
override: true
- name: Build
run: cargo build --release --verbose
run: cargo +nightly build --release --verbose
build-macos:
name: Check on MacOS
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
override: true
- name: Build
run: cargo build --release --verbose
run: cargo +nightly build --release --verbose
71 changes: 71 additions & 0 deletions src/blink_color.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use ratatui::style::Color;
use std::time::Duration;
use tokio::time::Instant;

pub struct BlinkColor {
color: Color,
duration: Duration,
blinks: usize,
action: BlinkAction,
}

enum BlinkAction {
None,
On { timeout: Instant, blink: usize },
Off { timeout: Instant, blink: usize },
}

impl BlinkColor {
pub fn new(color: Color, duration: Duration, blinks: usize) -> Self {
Self {
color,
duration,
blinks,
action: BlinkAction::None,
}
}

pub fn start(&mut self) {
self.action = BlinkAction::On {
timeout: Instant::now() + self.duration,
blink: 1,
}
}

pub fn get_color(&self) -> Option<Color> {
match self.action {
BlinkAction::None | BlinkAction::Off { .. } => None,
BlinkAction::On { .. } => Some(self.color),
}
}

pub fn update(&mut self) {
match self.action {
BlinkAction::None => {}
BlinkAction::On { timeout, blink } => {
if Instant::now() >= timeout {
if self.blinks <= blink {
self.action = BlinkAction::None;
} else {
self.action = BlinkAction::Off {
timeout: Instant::now() + self.duration,
blink,
};
}
}
}
BlinkAction::Off { timeout, blink } => {
if Instant::now() >= timeout {
if self.blinks <= blink {
self.action = BlinkAction::None;
} else {
self.action = BlinkAction::On {
timeout: Instant::now() + self.duration,
blink: blink + 1,
};
}
}
}
}
}
}
86 changes: 76 additions & 10 deletions src/command_bar.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::blink_color::BlinkColor;
use crate::command_bar::InputEvent::{HorizontalScroll, Key, VerticalScroll};
use crate::error_pop_up::ErrorPopUp;
use crate::messages::{SerialRxData, UserTxData};
Expand Down Expand Up @@ -40,12 +41,13 @@ pub struct CommandBar {
current_hint: Option<&'static str>,
hints: Vec<&'static str>,
plugin_manager: PluginManager,
blink_color: BlinkColor,
}

impl CommandBar {
const HEIGHT: u16 = 3;

pub fn new(interface: SerialIF, view_capacity: usize) -> Self {
pub fn new(interface: SerialIF, view_capacity: usize, save_filename: String) -> Self {
let (key_sender, key_receiver) = tokio::sync::mpsc::unbounded_channel();

tokio::spawn(async move {
Expand All @@ -59,7 +61,7 @@ impl CommandBar {
];

let interface = Arc::new(Mutex::new(interface));
let text_view = Arc::new(Mutex::new(TextView::new(view_capacity)));
let text_view = Arc::new(Mutex::new(TextView::new(view_capacity, save_filename)));

let plugin_manager = PluginManager::new(interface.clone(), text_view.clone());

Expand All @@ -78,6 +80,7 @@ impl CommandBar {
hints: hints.clone(),
current_hint: Some(hints.choose(&mut rand::thread_rng()).unwrap()),
plugin_manager,
blink_color: BlinkColor::new(Color::Black, Duration::from_millis(200), 2),
}
}

Expand Down Expand Up @@ -137,7 +140,7 @@ impl CommandBar {
)
.split(f.size());

text_view.draw(f, chunks[0]);
text_view.draw(f, chunks[0], self.blink_color.get_color());

let (description, is_connected) = (interface.description(), interface.is_connected());

Expand Down Expand Up @@ -257,6 +260,65 @@ impl CommandBar {
KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => {
self.plugin_manager.stop_process().await;
}
KeyCode::Char('s') if key.modifiers == KeyModifiers::CONTROL => {
let is_recording = {
let mut text_view = self.text_view.lock().await;
text_view.get_mut_recorder().is_recording()
};

if is_recording {
self.set_error_pop_up("Cannot save file while recording.".to_string());
return Ok(());
}

self.blink_color.start();
let mut text_view = self.text_view.lock().await;
let typewriter = text_view.get_mut_typewriter();
let filename = typewriter.get_filename();
let save_result = typewriter.flush().await;
text_view
.add_data_out(SerialRxData::Plugin {
plugin_name: "SAVE".to_string(),
timestamp: Local::now(),
content: if let Err(err) = &save_result {
format!("Cannot save on \"{}\": {}", filename, err)
} else {
format!("Content saved on \"{}\"", filename)
},
is_successful: save_result.is_ok(),
})
.await;
}
KeyCode::Char('r') if key.modifiers == KeyModifiers::CONTROL => {
let mut text_view = self.text_view.lock().await;
let recorder = text_view.get_mut_recorder();
let filename = recorder.get_filename();
let record_msg = if !recorder.is_recording() {
let record_result = recorder.start_record().await;
SerialRxData::Plugin {
plugin_name: "REC".to_string(),
timestamp: Local::now(),
content: if let Err(err) = &record_result {
format!(
"Cannot start content recording on \"{}\": {}",
filename, err
)
} else {
format!("Recording content on \"{}\"...", filename)
},
is_successful: record_result.is_ok(),
}
} else {
recorder.stop_record();
SerialRxData::Plugin {
plugin_name: "REC".to_string(),
timestamp: Local::now(),
content: format!("Content recorded on \"{}\"", filename),
is_successful: true,
}
};
text_view.add_data_out(record_msg).await;
}
KeyCode::Char(c) => {
self.clear_hint();

Expand Down Expand Up @@ -446,12 +508,14 @@ impl CommandBar {
msg_lut.insert("reload".to_string(), "Plugin reloaded!");

let mut text_view = self.text_view.lock().await;
text_view.add_data_out(SerialRxData::Plugin {
timestamp: Local::now(),
plugin_name,
content: msg_lut[&cmd].to_string(),
is_successful: true,
})
text_view
.add_data_out(SerialRxData::Plugin {
timestamp: Local::now(),
plugin_name,
content: msg_lut[&cmd].to_string(),
is_successful: true,
})
.await;
}
Err(err_msg) => {
self.set_error_pop_up(err_msg);
Expand Down Expand Up @@ -514,11 +578,13 @@ impl CommandBar {
}
}

self.blink_color.update();

{
let mut interface = self.interface.lock().await;
if let Ok(data_out) = interface.try_recv() {
let mut text_view = self.text_view.lock().await;
text_view.add_data_out(data_out.clone());
text_view.add_data_out(data_out.clone()).await;

self.plugin_manager.call_plugins_serial_rx(data_out);
}
Expand Down
8 changes: 7 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#![deny(warnings)]
#![feature(utf8_chunks)]

extern crate core;

use crate::command_bar::CommandBar;
use crate::plugin_installer::PluginInstaller;
use crate::serial::SerialIF;
use chrono::Local;
use clap::Parser;
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
use crossterm::execute;
Expand All @@ -17,6 +19,7 @@ use std::io;
use std::io::Stdout;
use std::path::PathBuf;

mod blink_color;
mod command_bar;
mod error_pop_up;
mod messages;
Expand All @@ -27,6 +30,8 @@ mod process;
mod rich_string;
mod serial;
mod text;
mod typewriter;
mod recorder;

pub type ConcreteBackend = CrosstermBackend<Stdout>;

Expand Down Expand Up @@ -68,7 +73,8 @@ async fn app() -> Result<(), String> {
let mut terminal =
Terminal::new(backend).map_err(|_| "Cannot create terminal backend".to_string())?;

let mut command_bar = CommandBar::new(interface, view_length)
let datetime = Local::now().format("%Y%m%d_%H%M%S");
let mut command_bar = CommandBar::new(interface, view_length, format!("{}.txt", datetime))
.with_command_file(cmd_file.as_path().to_str().unwrap());

'main: loop {
Expand Down
Loading
Loading