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

Implement ability to move within text box - closes #154 #182

Merged
merged 13 commits into from
Nov 22, 2024
1,097 changes: 764 additions & 333 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions async-callback-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ repository = "https://github.com/nick42d/youtui"
readme = "README.md"

[dependencies]
futures = "0.3.30"
reqwest = "0.12.7"
tokio = { version = "1.40.0", default-features = false, features = ["macros"] }
futures = "0.3.31"
reqwest = "0.12.9"
tokio = { version = "1.41.1", default-features = false, features = ["macros"] }
tracing = "0.1.40"
# For Then combinator
tokio-stream = "0.1.16"

[dev-dependencies]
tokio-stream = "0.1.16"
tokio = { version = "1.40.0", features = ["full"] }
ratatui = "0.28.1"
tokio = { version = "1.41.1", features = ["full"] }
ratatui = "0.29.0"
crossterm = { version = "0.28.1", features = ["event-stream"] }
reqwest = { version = "0.12.7", features = [
reqwest = { version = "0.12.9", features = [
"http2",
"charset",
"rustls-tls"
Expand Down
4 changes: 2 additions & 2 deletions json-crawler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ repository = "https://github.com/nick42d/youtui"
readme = "README.md"

[dependencies]
serde = "1.0.209"
serde_json = "1.0.127"
serde = "1.0.215"
serde_json = "1.0.133"

[lints]
workspace = true
18 changes: 10 additions & 8 deletions youtui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ categories = ["multimedia::audio"]
rust-version = "1.79"

[dependencies]
clap = { version = "4.5.16", features = ["derive"] }
clap = { version = "4.5.21", features = ["derive"] }
crossterm = { version = "0.28.1", features = ["event-stream"] }
futures = "0.3.30"
ratatui = { version = "0.28.0", features = ["all-widgets"] }
serde = "1.0.209"
serde_json = "1.0.127"
tokio = "1.39.3"
futures = "0.3.31"
rat-text = "0.29.0"
ratatui = { version = "0.29.0", features = ["all-widgets"] }
tui-logger = { version = "0.14.0", features = ["tracing-support"] }
serde = "1.0.215"
serde_json = "1.0.133"
tokio = "1.41.1"
gag = "1.0.0"
ytmapi-rs = { path = "../ytmapi-rs", version = "0.0.16", default-features = false, features = [
"rustls-tls",
Expand All @@ -29,15 +31,15 @@ ytmapi-rs = { path = "../ytmapi-rs", version = "0.0.16", default-features = fals
async-callback-manager = { path = "../async-callback-manager", version = "0.0.2" }
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
tui-logger = { version = "0.12.0", features = ["tracing-support"] }
directories = "5.0.1"
toml = "0.8.19"
# For intersperse feature. RFC in progress to bring to std
# https://github.com/rust-lang/rust/issues/79524
itertools = "0.13.0"
tokio-stream = "0.1.16"
async_cell = "0.2.2"
bytes = "1.7.2"
bytes = "1.8.0"
log = "0.4.22"

[dependencies.rusty_ytdl]
# version = "0.7.4"
Expand Down
9 changes: 7 additions & 2 deletions youtui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crossterm::{
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use log::LevelFilter;
use ratatui::{backend::CrosstermBackend, Terminal};
use server::{Server, TaskMetadata};
use std::borrow::Cow;
Expand Down Expand Up @@ -164,7 +165,7 @@ impl Youtui {
async fn handle_event(&mut self, event: AppEvent) {
match event {
AppEvent::Tick => self.window_state.handle_tick().await,
AppEvent::Crossterm(e) => self.window_state.handle_event(e).await,
AppEvent::Crossterm(e) => self.window_state.handle_initial_event(e).await,
AppEvent::QuitSignal => self.status = AppStatus::Exiting("Quit signal received".into()),
}
}
Expand Down Expand Up @@ -210,15 +211,19 @@ fn init_tracing(debug: bool) -> Result<()> {
.with(tui_logger_layer.and_then(log_file_layer))
.with(context_layer)
.init();
tui_logger::init_logger(LevelFilter::Debug)
.expect("Expected logger to initialise succesfully");
info!("Started in debug mode, logging to {:?}.", log_file_name);
} else {
// TODO: Confirm if this filter is correct.
let context_layer =
tracing_subscriber::filter::Targets::new().with_target("youtui", tracing::Level::TRACE);
tracing_subscriber::filter::Targets::new().with_target("youtui", tracing::Level::INFO);
tracing_subscriber::registry()
.with(tui_logger_layer)
.with(context_layer)
.init();
tui_logger::init_logger(LevelFilter::Info)
.expect("Expected logger to initialise succesfully");
}
Ok(())
}
45 changes: 17 additions & 28 deletions youtui/src/app/component/actionhandler.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::app::keycommand::{CommandVisibility, DisplayableCommand, KeyCommand, Keymap};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent};
use crossterm::event::{Event, KeyEvent, MouseEvent};
use std::borrow::Cow;
use ytmapi_rs::common::SearchSuggestion;

Expand Down Expand Up @@ -72,38 +72,27 @@ pub trait KeyDisplayer {
&'a self,
) -> Box<dyn Iterator<Item = DisplayableCommand<'a>> + 'a>;
}
/// A component of the application that handles text entry.
// TODO: Cursor position and movement.
/// A component of the application that handles text entry, currently designed
/// to wrap rat_text::TextInputState.
pub trait TextHandler {
// TODO: cursor manipulation
fn push_text(&mut self, c: char);
fn pop_text(&mut self);
// Assume internal representation is a String.
fn take_text(&mut self) -> String;
// Assume internal representation is a String and we'll simply replace it with
// text. Into<String> may also work.
fn replace_text(&mut self, text: String);
/// Get a reference to the text.
fn get_text(&self) -> &str;
/// Clear text, returning false if it was already clear.
fn clear_text(&mut self) -> bool;
/// Replace all text
fn replace_text(&mut self, text: impl Into<String>);
/// Text handling could be a subset of the component. Return true if the
/// text handling subset is active.
fn is_text_handling(&self) -> bool;
fn handle_text_entry(&mut self, key_event: KeyEvent) -> bool {
/// Handle a crossterm event, returning true if an event was handled.
fn handle_event_repr(&mut self, event: &Event) -> bool;
/// Default behaviour is to only handle an event if is_text_handling() ==
/// true.
fn handle_event(&mut self, event: &Event) -> bool {
if !self.is_text_handling() {
return false;
}
// The only accepted modifier is shift - if pressing another set of modifiers,
// we won't handle it. Somewhere else should instead.
if !key_event.modifiers.is_empty() && key_event.modifiers != KeyModifiers::SHIFT {
return false;
}
match key_event.code {
KeyCode::Char(c) => {
self.push_text(c);
true
}
KeyCode::Backspace => {
self.pop_text();
true
}
_ => false,
}
self.handle_event_repr(event)
}
}
// A text handler that can receive suggestions
Expand Down
46 changes: 23 additions & 23 deletions youtui/src/app/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,41 +261,41 @@ impl Action for UIAction {
}

impl TextHandler for YoutuiWindow {
fn push_text(&mut self, c: char) {
match self.context {
WindowContext::Browser => self.browser.push_text(c),
WindowContext::Playlist => self.playlist.push_text(c),
WindowContext::Logs => self.logger.push_text(c),
}
}
fn pop_text(&mut self) {
match self.context {
WindowContext::Browser => self.browser.pop_text(),
WindowContext::Playlist => self.playlist.pop_text(),
WindowContext::Logs => self.logger.pop_text(),
}
}
fn is_text_handling(&self) -> bool {
match self.context {
WindowContext::Browser => self.browser.is_text_handling(),
WindowContext::Playlist => self.playlist.is_text_handling(),
WindowContext::Logs => self.logger.is_text_handling(),
}
}
fn take_text(&mut self) -> String {
fn get_text(&self) -> &str {
match self.context {
WindowContext::Browser => self.browser.take_text(),
WindowContext::Playlist => self.playlist.take_text(),
WindowContext::Logs => self.logger.take_text(),
WindowContext::Browser => self.browser.get_text(),
WindowContext::Playlist => self.playlist.get_text(),
WindowContext::Logs => self.logger.get_text(),
}
}
fn replace_text(&mut self, text: String) {
fn replace_text(&mut self, text: impl Into<String>) {
match self.context {
WindowContext::Browser => self.browser.replace_text(text),
WindowContext::Playlist => self.playlist.replace_text(text),
WindowContext::Logs => self.logger.replace_text(text),
}
}
fn clear_text(&mut self) -> bool {
match self.context {
WindowContext::Browser => self.browser.clear_text(),
WindowContext::Playlist => self.playlist.clear_text(),
WindowContext::Logs => self.logger.clear_text(),
}
}
fn handle_event_repr(&mut self, event: &Event) -> bool {
match self.context {
WindowContext::Browser => self.browser.handle_event_repr(event),
WindowContext::Playlist => self.playlist.handle_event_repr(event),
WindowContext::Logs => self.logger.handle_event_repr(event),
}
}
}

impl YoutuiWindow {
Expand Down Expand Up @@ -328,7 +328,10 @@ impl YoutuiWindow {
.apply(self)
}
// Splitting out event types removes one layer of indentation.
pub async fn handle_event(&mut self, event: crossterm::event::Event) {
pub async fn handle_initial_event(&mut self, event: crossterm::event::Event) {
if self.handle_event(&event) {
return;
}
match event {
Event::Key(k) => self.handle_key_event(k).await,
Event::Mouse(m) => self.handle_mouse_event(m),
Expand All @@ -339,9 +342,6 @@ impl YoutuiWindow {
self.playlist.handle_tick().await;
}
async fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) {
if self.handle_text_entry(key_event) {
return;
}
self.key_stack.push(key_event);
self.global_handle_key_stack().await;
}
Expand Down
Loading