youtui 0.0.37

A simple TUI YouTube Music player
use super::action::AppAction;
use crate::app::component::actionhandler::{
    Action, ActionHandler, ComponentEffect, KeyRouter, TextHandler, YoutuiEffect,
};
use crate::app::ui::AppCallback;
use crate::app::view::Drawable;
use crate::config::Config;
use crate::config::keymap::Keymap;
use async_callback_manager::AsyncTask;
use draw::draw_logger;
use ratatui::Frame;
use ratatui::prelude::Rect;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use tui_logger::TuiWidgetEvent;

#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LoggerAction {
    ToggleTargetSelector,
    ToggleTargetFocus,
    ToggleHideFiltered,
    Up,
    Down,
    PageUp,
    PageDown,
    ReduceShown,
    IncreaseShown,
    ReduceCaptured,
    IncreaseCaptured,
    ExitPageMode,
    ViewBrowser,
}
impl Action for LoggerAction {
    fn context(&self) -> Cow<'_, str> {
        "Logger".into()
    }
    fn describe(&self) -> Cow<'_, str> {
        match self {
            LoggerAction::ViewBrowser => "View Browser".into(),
            LoggerAction::ToggleTargetSelector => "Toggle Target Selector Widget".into(),
            LoggerAction::ToggleTargetFocus => "Toggle Focus Selected Target".into(),
            LoggerAction::ToggleHideFiltered => "Toggle Hide Filtered Targets".into(),
            LoggerAction::Up => "Up - Selector".into(),
            LoggerAction::Down => "Down - Selector".into(),
            LoggerAction::PageUp => "Enter Page Mode, Scroll History Up".into(),
            LoggerAction::PageDown => "In Page Mode: Scroll History Down".into(),
            LoggerAction::ReduceShown => "Reduce SHOWN (!) Messages".into(),
            LoggerAction::IncreaseShown => "Increase SHOWN (!) Messages".into(),
            LoggerAction::ReduceCaptured => "Reduce CAPTURED (!) Messages".into(),
            LoggerAction::IncreaseCaptured => "Increase CAPTURED (!) Messages".into(),
            LoggerAction::ExitPageMode => "Exit Page Mode".into(),
        }
    }
}
pub struct Logger {
    logger_state: tui_logger::TuiWidgetState,
}
impl_youtui_component!(Logger);

impl ActionHandler<LoggerAction> for Logger {
    fn apply_action(&mut self, action: LoggerAction) -> impl Into<YoutuiEffect<Self>> {
        match action {
            LoggerAction::ToggleTargetSelector => self.handle_toggle_target_selector(),
            LoggerAction::ToggleTargetFocus => self.handle_toggle_target_focus(),
            LoggerAction::ToggleHideFiltered => self.handle_toggle_hide_filtered(),
            LoggerAction::Up => self.handle_up(),
            LoggerAction::Down => self.handle_down(),
            LoggerAction::PageUp => self.handle_pgup(),
            LoggerAction::PageDown => self.handle_pgdown(),
            LoggerAction::ReduceShown => self.handle_reduce_shown(),
            LoggerAction::IncreaseShown => self.handle_increase_shown(),
            LoggerAction::ReduceCaptured => self.handle_reduce_captured(),
            LoggerAction::IncreaseCaptured => self.handle_increase_captured(),
            LoggerAction::ExitPageMode => self.handle_exit_page_mode(),
            LoggerAction::ViewBrowser => return self.handle_view_browser(),
        }
        AsyncTask::new_no_op().into()
    }
}
impl Drawable for Logger {
    fn draw_chunk(&self, f: &mut Frame, chunk: Rect, selected: bool) {
        draw_logger(f, self, chunk, selected)
    }
}

impl KeyRouter<AppAction> for Logger {
    fn get_active_keybinds<'a>(
        &self,
        config: &'a Config,
    ) -> impl Iterator<Item = &'a Keymap<AppAction>> + 'a {
        std::iter::once(&config.keybinds.log)
    }
    fn get_all_keybinds<'a>(
        &self,
        config: &'a Config,
    ) -> impl Iterator<Item = &'a Keymap<AppAction>> + 'a {
        self.get_active_keybinds(config)
    }
}

impl TextHandler for Logger {
    fn is_text_handling(&self) -> bool {
        false
    }
    fn get_text(&self) -> std::option::Option<&str> {
        None
    }
    fn replace_text(&mut self, _text: impl Into<String>) {}
    fn clear_text(&mut self) -> bool {
        false
    }
    fn handle_text_event_impl(
        &mut self,
        _event: &crossterm::event::Event,
    ) -> Option<ComponentEffect<Self>> {
        None
    }
}

impl Logger {
    pub fn new() -> Self {
        Self {
            logger_state: tui_logger::TuiWidgetState::default(),
        }
    }
    fn handle_view_browser(&mut self) -> YoutuiEffect<Self> {
        (
            AsyncTask::new_no_op(),
            Some(AppCallback::ChangeContext(super::WindowContext::Browser)),
        )
            .into()
    }
    fn handle_toggle_hide_filtered(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::SpaceKey);
    }
    fn handle_down(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::DownKey);
    }
    fn handle_up(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::UpKey);
    }
    fn handle_pgdown(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::NextPageKey);
    }
    fn handle_pgup(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::PrevPageKey);
    }
    fn handle_reduce_shown(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::LeftKey);
    }
    fn handle_increase_shown(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::RightKey);
    }
    fn handle_exit_page_mode(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::EscapeKey);
    }
    fn handle_increase_captured(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::PlusKey);
    }
    fn handle_reduce_captured(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::MinusKey);
    }
    fn handle_toggle_target_focus(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::FocusKey);
    }
    fn handle_toggle_target_selector(&mut self) {
        self.logger_state.transition(TuiWidgetEvent::HideKey);
    }
}

pub mod draw {
    use super::Logger;
    use crate::drawutils::{DESELECTED_BORDER_COLOUR, SELECTED_BORDER_COLOUR};
    use ratatui::Frame;
    use ratatui::prelude::Rect;
    use ratatui::style::{Color, Style};

    pub fn draw_logger(f: &mut Frame, l: &Logger, chunk: Rect, selected: bool) {
        let border_colour = if selected {
            SELECTED_BORDER_COLOUR
        } else {
            DESELECTED_BORDER_COLOUR
        };
        let log = tui_logger::TuiLoggerSmartWidget::default()
            .style_error(Style::default().fg(Color::Red))
            .style_debug(Style::default().fg(Color::Green))
            .style_warn(Style::default().fg(Color::Yellow))
            .style_trace(Style::default().fg(Color::Magenta))
            .style_info(Style::default().fg(Color::Cyan))
            .border_style(Style::default().fg(border_colour))
            .state(&l.logger_state)
            .output_timestamp(Some("%H:%M:%S:%3f".to_string()));
        f.render_widget(log, chunk);
    }
}