use std::sync::Arc;
use std::time::Duration;
use rich::{Console, Renderable, Segment, Text};
pub type ComponentFactory = Arc<dyn Fn() -> Box<dyn PluginComponent> + Send + Sync>;
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ComponentEvent {
Key(String),
Paste(String),
Resize { columns: u16, rows: u16 },
Tick,
Mouse {
kind: String,
column: u16,
row: u16,
},
Link(String),
}
impl ComponentEvent {
pub fn key(&self) -> Option<&str> {
match self {
ComponentEvent::Key(name) => Some(name),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ComponentFlow {
Continue,
Done(String),
Cancel,
Ignored,
}
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct ComponentView {
pub lines: Vec<Vec<Segment>>,
pub cursor: Option<(usize, usize)>,
}
impl ComponentView {
pub fn new(lines: Vec<Vec<Segment>>) -> ComponentView {
ComponentView {
lines,
cursor: None,
}
}
pub fn with_cursor(mut self, row: usize, column: usize) -> ComponentView {
self.cursor = Some((row, column));
self
}
}
#[non_exhaustive]
pub struct ComponentContext<'a> {
pub console: &'a Console,
pub width: usize,
pub height: usize,
}
impl<'a> ComponentContext<'a> {
pub fn new(console: &'a Console, width: usize, height: usize) -> ComponentContext<'a> {
ComponentContext {
console,
width,
height,
}
}
pub fn lines(&self, renderable: &dyn Renderable) -> Vec<Vec<Segment>> {
let options = self.console.options().update_width(self.width.max(1));
self.console.render_lines(renderable, &options, false)
}
pub fn markup(&self, markup: &str) -> Vec<Vec<Segment>> {
let text = Text::from_markup(markup).unwrap_or_else(|_| Text::new(markup));
self.lines(&text)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct ComponentBinding {
pub action: String,
pub keys: Vec<String>,
pub description: String,
}
impl ComponentBinding {
pub fn new(
action: impl Into<String>,
keys: impl IntoIterator<Item = impl Into<String>>,
description: impl Into<String>,
) -> ComponentBinding {
ComponentBinding {
action: action.into(),
keys: keys.into_iter().map(Into::into).collect(),
description: description.into(),
}
}
}
pub trait PluginComponent: Send {
fn handle(&mut self, event: &ComponentEvent, context: &ComponentContext<'_>) -> ComponentFlow;
fn render(&self, context: &ComponentContext<'_>) -> ComponentView;
fn bindings(&self) -> Vec<ComponentBinding> {
Vec::new()
}
fn focusable(&self) -> bool {
true
}
fn tick(&self) -> Option<Duration> {
None
}
fn mouse(&self) -> bool {
false
}
}