mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-10-08 18:56:42 +02:00
207 lines
6.9 KiB
Rust
207 lines
6.9 KiB
Rust
use crate::*;
|
|
|
|
/// Collection of UI modes.
|
|
///
|
|
/// ```
|
|
/// # use tek::*;
|
|
///
|
|
/// // Empty mode registry:
|
|
/// let state: Config = Default::default();
|
|
/// assert_eq!(state.modes_count(), 0);
|
|
/// assert!(state.modes_get("Foo").is_none());
|
|
///
|
|
/// // Add a mode:
|
|
/// let _ = state.modes_add(&"Foo (name Foo) (info Bar)").unwrap();
|
|
/// assert_eq!(state.modes_count(), 1);
|
|
/// assert!(state.modes_get("Foo").is_some());
|
|
/// ```
|
|
pub trait HasModes {
|
|
fn modes (&self) -> &RwLock<BTreeMap<Arc<str>, Arc<Mode>>>;
|
|
|
|
/// Register a mode.
|
|
///
|
|
/// ```
|
|
/// # use ::{std::sync::Arc, tek::*};
|
|
/// let config = Config::default();
|
|
/// config.modes_add(stringify!(Menu
|
|
/// (name Menu) (info Mode selector.) (axis Y Index) (bind Confirm)
|
|
/// (view (bg (g 30) (bar/s 7 Logo (full/xy Dialog))))
|
|
/// )).unwrap();
|
|
/// let _: Arc<Mode> = config.modes_get("Menu").unwrap();
|
|
/// ```
|
|
fn modes_add <'a> (&self, expr: impl Language) -> UsuallyRef<'a, ()> {
|
|
let name = expr.head()?.ok_or("mode: missing name")?;
|
|
let body = expr.tail()?.ok_or("mode: missing body")?;
|
|
let mut mode = Mode::default();
|
|
body.each(&mut mode, |c, s|c.add(s))?;
|
|
self.modes().write().insert(name.into(), Arc::new(mode));
|
|
Ok(())
|
|
}
|
|
/// Get a mode by name.
|
|
fn modes_get (&self, name: impl AsRef<str>) -> Option<Arc<Mode>> {
|
|
self.modes().read().get(name.as_ref()).cloned()
|
|
}
|
|
/// Run something for each mode.
|
|
fn modes_for_each <T> (&self, mut ator: impl FnMut(&str, &Mode)->T) {
|
|
for (k, v) in self.modes().read().iter() {
|
|
let _ = ator(k.as_ref(), v.as_ref());
|
|
}
|
|
}
|
|
/// Count modes.
|
|
fn modes_count (&self) -> usize {
|
|
self.modes().read().len()
|
|
}
|
|
/// Clear modes.
|
|
fn modes_clear (&self) {
|
|
*self.modes().write() = Default::default();
|
|
}
|
|
}
|
|
impl HasModes for App {
|
|
fn modes (&self) -> &RwLock<BTreeMap<Arc<str>, Arc<Mode>>> {
|
|
&self.watch.config.modes
|
|
}
|
|
}
|
|
impl HasModes for Config {
|
|
fn modes (&self) -> &RwLock<BTreeMap<Arc<str>, Arc<Mode>>> {
|
|
&self.modes
|
|
}
|
|
}
|
|
impl HasModes for Mode {
|
|
fn modes (&self) -> &RwLock<BTreeMap<Arc<str>, Arc<Mode>>> {
|
|
&self.modes
|
|
}
|
|
}
|
|
|
|
impl_as_mut!(Mode: |self: Mode| self);
|
|
|
|
/// Group of view and keys definitions.
|
|
///
|
|
/// ```
|
|
/// let mode = tek::Mode::default();
|
|
/// ```
|
|
#[derive(Default, Debug)]
|
|
pub struct Mode {
|
|
pub path: PathBuf,
|
|
pub name: Vec<Arc<str>>,
|
|
pub info: Vec<Arc<str>>,
|
|
pub view: Vec<Arc<View<Tui, App>>>,
|
|
pub keys: Vec<Arc<str>>,
|
|
pub axis: BTreeMap<Arc<str>, Arc<str>>,
|
|
pub data: BTreeMap<Arc<str>, usize>,
|
|
pub modes: Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode>>>>,
|
|
}
|
|
|
|
impl Mode {
|
|
/// Add a definition to the mode.
|
|
///
|
|
/// Supported definitions:
|
|
///
|
|
/// - (name ...) -> name
|
|
/// - (info ...) -> description
|
|
/// - (keys ...) -> key bindings
|
|
/// - (mode ...) -> submode
|
|
/// - ... -> view
|
|
///
|
|
/// ```
|
|
/// let mut mode = tek::Mode::default().add("(name hello)").unwrap();
|
|
/// ```
|
|
pub fn add (&mut self, dsl: impl Language) -> Usually<&mut Self> {
|
|
Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() {
|
|
let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
|
|
match head {
|
|
"self" => self.add_mode(tail)?,
|
|
"bind" => self.add_bind(tail)?,
|
|
"axis" => self.add_axis(tail)?,
|
|
"data" => self.add_data(tail)?,
|
|
"name" => { self.name.push(tail.into()); self },
|
|
"info" => { self.info.push(tail.into()); self },
|
|
"view" => { self.view.push(View::new(tail)?.into()); self },
|
|
_ => { self.view.push(View::new(tail)?.into()); self },
|
|
}
|
|
} else if let Ok(Some(word)) = dsl.word() {
|
|
self.view.push(View::new(word)?.into());
|
|
self
|
|
} else {
|
|
return Err(format!("Mode::add: unexpected: {dsl:?}").into());
|
|
})
|
|
}
|
|
|
|
fn add_mode (&mut self, tail: &str) -> Usually<&mut Self> {
|
|
let name = tail.head()?.ok_or("submode: missing name")?;
|
|
let body = tail.tail()?.ok_or("submode: missing body")?;
|
|
let mut submode = Mode::default();
|
|
body.each(&mut submode, |c, s|c.add(s))?;
|
|
self.modes.write().insert(name.into(), Arc::new(submode));
|
|
Ok(self)
|
|
}
|
|
|
|
fn add_bind (&mut self, tail: &str) -> Usually<&mut Self> {
|
|
tail.each(self, |mode: &mut Self, expr: &str|{
|
|
mode.keys.push(expr.trim().into());
|
|
Ok(mode)
|
|
})
|
|
}
|
|
|
|
fn add_axis (&mut self, tail: &str) -> Usually<&mut Self> {
|
|
let mut axis: Option<Arc<str>> = None;
|
|
let mut param: Option<Arc<str>> = None;
|
|
tail.each(0, |index: usize, src: &str|if index == 0 {
|
|
axis = Some(src.into());
|
|
Ok(1)
|
|
} else if index == 1 {
|
|
param = Some(src.into());
|
|
Ok(2)
|
|
} else {
|
|
err!("mode: expected (axis Name Param), got: {tail}")
|
|
})?;
|
|
if let Some(axis) = axis && let Some(param) = param {
|
|
self.axis.insert(axis.into(), param.into());
|
|
Ok(self)
|
|
} else {
|
|
err!("mode: expected (axis Name Param), got: {tail}")
|
|
}
|
|
}
|
|
|
|
fn add_data (&mut self, tail: &str) -> Usually<&mut Self> {
|
|
let mut param: Option<Arc<str>> = None;
|
|
let mut value: Option<usize> = None;
|
|
tail.each(0, |index: usize, src: &str|if index == 0 {
|
|
param = Some(src.into());
|
|
Ok(1)
|
|
} else if index == 1 {
|
|
value = Some(src.parse::<usize>()?);
|
|
Ok(2)
|
|
} else {
|
|
err!("mode: expected (data Param Value), got: {tail}")
|
|
})?;
|
|
if let Some(param) = param && let Some(value) = value {
|
|
self.data.insert(param.into(), value.into());
|
|
Ok(self)
|
|
} else {
|
|
err!("mode: expected (data Param Value), got: {tail}")
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
pub fn print_config_modes (config: &Config) {
|
|
use ::ansi_term::Color::*;
|
|
config.modes_for_each(|k, v|{
|
|
println!();
|
|
for v in v.name.iter() { print!("{}", Green.bold().paint(format!("{v} "))); }
|
|
for v in v.info.iter() { print!("\n{}", Green.paint(format!("{v}"))); }
|
|
print!("\n{} {}", Blue.paint("TOOL"), Green.bold().paint(format!("{k:<16}")));
|
|
print!("\n{}", Blue.paint("KEYS"));
|
|
for v in v.keys.iter() { print!("{}", Green.paint(format!(" {v}"))); }
|
|
println!();
|
|
v.modes_for_each(|k, v|{
|
|
print!("{} {} {:?}", Blue.paint("MODE"), Green.bold().paint(format!("{k:<16}")), v.name);
|
|
print!( " INFO={:?}", v.info);
|
|
print!( " VIEW={:?}", v.view);
|
|
println!(" KEYS={:?}", v.keys);
|
|
});
|
|
print!("{}", Blue.paint("VIEW"));
|
|
for v in v.view.iter() { print!("{}", Green.paint(format!(" {v}"))); }
|
|
println!();
|
|
});
|
|
}
|