lingora-tui 0.4.7

Lingora is a free and open-source localization management program that analyses fluent translation files highlighting discrepancies between reference and target languages. This application provides a terminal user interface; run as `lingora-tui`.
Documentation
use crossterm::event::{Event, KeyCode, KeyEvent};
use rat_event::{HandleEvent, Outcome, Regular};
use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
use ratatui::{
    buffer::Buffer,
    layout::Rect,
    text::{Line, Span},
    widgets::{List, ListState, StatefulWidget},
};

use crate::theme::LingoraTheme;

#[derive(Debug)]
pub struct LineNumberedTextViewState {
    focus_flag: FocusFlag,
    list_state: ListState,
    content: String,
    area: Rect,
}

impl LineNumberedTextViewState {
    pub fn new(content: String) -> Self {
        Self {
            focus_flag: FocusFlag::default(),
            list_state: ListState::default(),
            content,
            area: Rect::default(),
        }
    }

    fn handle_key_event(&mut self, event: &KeyEvent) -> Outcome {
        match &event.code {
            KeyCode::Up => {
                self.list_state.select_previous();
                Outcome::Unchanged
            }
            KeyCode::Down => {
                self.list_state.select_next();
                Outcome::Unchanged
            }
            _ => Outcome::Continue,
        }
    }
}

impl HasFocus for LineNumberedTextViewState {
    fn build(&self, builder: &mut FocusBuilder) {
        builder.leaf_widget(self);
    }

    fn focus(&self) -> FocusFlag {
        self.focus_flag.clone()
    }

    fn area(&self) -> Rect {
        self.area
    }
}

impl HandleEvent<Event, Regular, Outcome> for LineNumberedTextViewState {
    fn handle(&mut self, event: &Event, _qualifier: Regular) -> Outcome {
        match event {
            Event::Key(event) => self.handle_key_event(event),
            _ => Outcome::Continue,
        }
    }
}

pub struct LineNumberedTextView<'a> {
    theme: &'a LingoraTheme,
}

impl<'a> LineNumberedTextView<'a> {
    pub fn new(theme: &'a LingoraTheme) -> Self {
        Self { theme }
    }
}

impl StatefulWidget for LineNumberedTextView<'_> {
    type State = LineNumberedTextViewState;

    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        state.area = area;

        let area = Rect::new(area.x, area.y + 1, area.width, area.height - 2);

        let rows = state
            .content
            .lines()
            .enumerate()
            .map(|(i, line)| {
                Line::from(vec![
                    Span::styled(format!("{i:>7}   "), self.theme.muted()),
                    Span::from(line),
                ])
            })
            .collect::<Vec<_>>();

        let list = List::new(rows).highlight_style(self.theme.selection());
        list.render(area, buf, &mut state.list_state);
    }
}