bizzle
Some checks are pending
/ build (push) Waiting to run

This commit is contained in:
i do not exist 2026-08-19 18:21:42 +03:00
parent e50c4a5eef
commit e6984915e4
22 changed files with 1002 additions and 294 deletions

View file

@ -257,11 +257,11 @@ mod config {
/// tek::Config::default().add(&source).unwrap().get_mode(":menu").unwrap();
/// ```
#[derive(Default, Debug)]
pub struct Config {
pub struct Config<'a> {
/// XDG base directories of running user.
pub dirs: BaseDirectories,
/// Active collection of interaction modes.
pub modes: Modes,
pub modes: Modes<'a>,
/// Active collection of event bindings.
pub binds: Binds,
/// Active collection of view definitions.
@ -286,7 +286,7 @@ mod config {
/// assert_eq!(modes.len(), 1);
/// ```
#[derive(Default, Debug, Clone)]
pub struct Modes(Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode<Arc<str>>>>>>);
pub struct Modes<'a>(Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode<'a, Arc<str>>>>>>);
/// Group of view and keys definitions.
///
@ -294,16 +294,16 @@ mod config {
/// let mode = tek::Mode::<std::sync::Arc<str>>::default();
/// ```
#[derive(Default, Debug)]
pub struct Mode<D: Language + Ord> {
pub struct Mode<'a, D: Language + Ord> {
pub path: PathBuf,
pub name: Vec<D>,
pub info: Vec<D>,
pub view: Vec<D>,
pub keys: Vec<D>,
pub modes: Modes,
pub modes: Modes<'a>,
}
impl Config {
impl<'a> Config<'a> {
/// Default configuration directory.
const CONFIG_DIR: &'static str = "tek";
/// Default configuration subdirectory.
@ -389,7 +389,7 @@ mod config {
/// Write initial contents of a configuration file.
pub fn load <F: FnMut(&Self, &str)->Usually<()>> (
&self, path: &str, defaults: &str, mut each: F
) -> Usually<()> {
) -> UsuallyRef<()> {
self.stamp.store(quanta::Clock::new().raw(), Relaxed);
if self.find_file().is_none() {
//println!("Creating {path:?}");
@ -415,27 +415,27 @@ mod config {
*self.binds.write().unwrap() = Default::default();
}
/// Add one entry to the configuration.
fn add_one (&self, item: impl Language) -> Usually<()> {
if let Some(expr) = item.expr()? {
let head = expr.head()?;
let tail = expr.tail()?;
let name = tail.head()?;
let body = tail.tail()?;
match head {
Some("mode") if let Some(name) = name => self.modes.add(&name, &body)?,
Some("keys") if let Some(name) = name => load_bind(&self.binds, &name, &body)?,
Some("view") if let Some(name) = name => load_view(&self.views, &name, &body)?,
_ => return Err(
format!("Config::load: expected view/keys/mode, got: {item:?}").into()
)
}
Ok(())
} else {
return Err(format!("Config::load: expected expr, got: {item:?}").into())
}
fn add_one (&'a self, item: &'a (impl Language + ?Sized)) -> UsuallyRef<'a, ()> {
item.expr()?
.map(|expr|{
let head = expr.head()?;
let tail = expr.tail()?;
let name = tail.head()?;
let body = tail.tail()?;
match head {
Some("mode") if let Some(name) = name => self.modes.add(name, &body)?,
Some("keys") if let Some(name) = name => load_bind(&self.binds, &name, &body)?,
Some("view") if let Some(name) = name => load_view(&self.views, &name, &body)?,
_ => return Ok::<Option<()>, Box<dyn Error>>(None)
}
Ok(Some(()))
})
.transpose()?
.flatten()
.ok_or_else(||format!("Config::load: expected view/keys/mode expr, got: {item:?}").into())
}
/// Get a mode by name.
pub fn get_mode (&self, mode: impl AsRef<str>) -> Option<Arc<Mode<Arc<str>>>> {
pub fn get_mode (&'a self, mode: impl AsRef<str>) -> Option<Arc<Mode<'a, Arc<str>>>> {
self.modes.get(mode)
}
/// Print the configuration.
@ -444,16 +444,22 @@ mod config {
}
}
impl Modes {
impl<'a> Modes<'a> {
/// Register a mode.
pub fn add (&self, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
pub fn add (
&self,
name: &'a (impl AsRef<str> + ?Sized),
body: &(impl Language + ?Sized)
)
-> UsuallyRef<'a, ()>
{
let mut mode = Mode::default();
body.each(|item|mode.add(item))?;
self.0.write().unwrap().insert(name.as_ref().into(), Arc::new(mode));
Ok(())
}
/// Get a mode by name.
pub fn get (&self, name: impl AsRef<str>) -> Option<Arc<Mode<Arc<str>>>> {
pub fn get (&self, name: impl AsRef<str>) -> Option<Arc<Mode<'a, Arc<str>>>> {
self.0.read().unwrap().get(name.as_ref()).cloned()
}
/// Run something for each mode.
@ -468,7 +474,7 @@ mod config {
}
}
impl Mode<Arc<str>> {
impl<'a> Mode<'a, Arc<str>> {
/// Add a definition to the mode.
///
/// Supported definitions:
@ -483,7 +489,7 @@ mod config {
/// let mut mode: tek::Mode<std::sync::Arc<str>> = Default::default();
/// mode.add("(name hello)").unwrap();
/// ```
pub fn add (&mut self, dsl: impl Language) -> Usually<()> {
pub fn add (&mut self, dsl: &'a (impl Language + ?Sized)) -> UsuallyRef<'a, ()> {
Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() {
//println!("Mode::add: {head} {:?}", expr.tail());
let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
@ -501,23 +507,26 @@ mod config {
})
}
/// Add a name to the mode.
fn add_name (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.name.push(src.into())))
fn add_name (&mut self, dsl: &'a (impl Language + ?Sized)) -> PerhapsRef<'a, ()> {
Ok(dsl.text()?.map(|text|self.name.push(text.into())))
}
/// Add a description to the mode.
fn add_info (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.info.push(src.into())))
fn add_info (&mut self, dsl: &'a (impl Language + ?Sized)) -> PerhapsRef<'a, ()> {
Ok(dsl.text()?.map(|text|self.info.push(text.into())))
}
/// Add a view definition to the mode.
fn add_view (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.view.push(src.into())))
fn add_view (&mut self, dsl: &'a (impl Language + ?Sized)) -> PerhapsRef<'a, ()> {
Ok(dsl.text()?.map(|text|self.view.push(text.into())))
}
/// Add a keyboard input bindin to the mode.
fn add_keys (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(Some(dsl.each(|expr|{ self.keys.push(expr.trim().into()); Ok(()) })?))
fn add_keys (&mut self, dsl: &'a (impl Language + ?Sized)) -> PerhapsRef<'a, ()> {
Ok(Some(dsl.src()?.each(|expr|{
self.keys.push(expr.trim().into());
Ok::<(), Box<dyn Error>>(())
})?))
}
/// Add a submode to the mode.
fn add_mode (&mut self, dsl: impl Language) -> Perhaps<()> {
fn add_mode (&mut self, dsl: &'a (impl Language + ?Sized)) -> PerhapsRef<'a, ()> {
Ok(Some(if let Some(id) = dsl.head()? {
self.modes.add(&id, &dsl.tail())?;
} else {
@ -599,7 +608,7 @@ mod app {
/// Performance counter
pub perf: PerfModel,
/// Available view modes and input bindings
pub config: Arc<Config>,
pub config: Arc<Config<'static>>,
/// Currently selected mode
pub mode: Option<Arc<str>>,
/// Undo history
@ -640,17 +649,17 @@ mod app {
}
}
fn get_arc_str (_: &App, src: impl Language) -> Perhaps<Arc<str>> {
fn get_arc_str <'a> (_: &App, src: impl Language) -> Perhaps<Arc<str>> {
Ok(src.src()?.map(|x|x.into()))
}
fn get_u16 (state: &App, src: impl Language) -> Perhaps<u16> {
fn get_u16 <'a> (state: &App, src: impl Language) -> Perhaps<u16> {
Ok(Some(match src.word()? {
Some(":w/sidebar") => state.project.w_sidebar(state.editor().is_some()),
Some(":h/sample-detail") => 6.max(state.size.h() as u16 * 3 / 9),
_ => return try_to_u16(src)
}))
}
fn get_usize (state: &App, src: impl Language) -> Perhaps<usize> {
fn get_usize <'a> (state: &App, src: impl Language) -> Perhaps<usize> {
Ok(Some(match src.word()? {
Some(":scene-count") => state.scenes().len(),
Some(":track-count") => state.tracks().len(),
@ -660,7 +669,7 @@ mod app {
_ => return try_to_usize(src)
}))
}
fn get_bool (state: &App, src: impl Language) -> Perhaps<bool> {
fn get_bool <'a> (state: &App, src: impl Language) -> Perhaps<bool> {
src.word()?.map(|word|Ok(match word {
"Y" => true,
"N" => false,
@ -681,7 +690,7 @@ mod app {
})).transpose()
}
#[allow(unused)]
fn get_selection (state: &App, src: impl Language) -> Perhaps<Selection> {
fn get_selection <'a> (state: &App, src: impl Language) -> Perhaps<Selection> {
src.word()?.map(|word|Ok(match word {
":select/scene" => state.selection().select_scene(state.tracks().len()),
":select/scene/next" => state.selection().select_scene_next(state.scenes().len()),
@ -692,21 +701,21 @@ mod app {
_ => return Err(format!("not selection: {word}").into())
})).transpose()
}
fn get_color (_: &App, src: impl Language) -> Perhaps<Color> {
fn get_color <'a> (_: &App, src: impl Language) -> Perhaps<Color> {
if let Some(expr) = src.expr()? {
match (expr.head()?, expr.tail()?) {
(Some("g"), Some(tail)) => {
let n = try_to_u8(tail.head().map_err(Into::into))?
.ok_or(LanguageError::Domain("not gray"))?;
.ok_or(LanguageError::domain("not gray"))?;
Ok(Some(Color::Rgb(n, n, n)))
},
(Some("rgb"), Some(tail)) => {
let r = try_to_u8(tail.head().map_err(Into::into))?
.ok_or(LanguageError::Domain("not red"))?;
.ok_or(LanguageError::domain("not red"))?;
let g = try_to_u8(tail.tail().head().map_err(Into::into))?
.ok_or(LanguageError::Domain("not green"))?;
.ok_or(LanguageError::domain("not green"))?;
let b = try_to_u8(tail.tail().tail().head().map_err(Into::into))?
.ok_or(LanguageError::Domain("not blue"))?;
.ok_or(LanguageError::domain("not blue"))?;
Ok(Some(Color::Rgb(r, g, b)))
},
(Some(_), _) => return Err(format!("not a color expression: {expr}").into()),
@ -723,7 +732,7 @@ mod app {
}
}
#[allow(unused)]
fn get_opt_u7 (state: &App, src: impl Language) -> Perhaps<Option<u7>> {
fn get_opt_u7 <'a> (state: &App, src: impl Language) -> Perhaps<Option<u7>> {
src.word()?.map(|word|Ok(match word {
":editor/pitch" => Some((
state.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8
@ -731,7 +740,7 @@ mod app {
_ => return Err(format!("unknown midi note: {word}").into())
})).transpose()
}
fn get_opt_usize (state: &App, src: impl Language) -> Perhaps<Option<usize>> {
fn get_opt_usize <'a> (state: &App, src: impl Language) -> Perhaps<Option<usize>> {
src.word()?.map(|word|Ok(match word {
":selected/scene" => state.selection().scene(),
":selected/track" => state.selection().track(),
@ -739,42 +748,42 @@ mod app {
})).transpose()
}
#[allow(unused)]
fn get_clip (state: &App, src: impl Language) -> Perhaps<Option<Arc<RwLock<MidiClip>>>> {
fn get_clip <'a> (state: &App, src: impl Language) -> Perhaps<Option<Arc<RwLock<MidiClip>>>> {
src.word()?.map(|word|Ok(match word {
":selected/clip" if let Selection::TrackClip { track, scene } = state.selection() =>
state.scenes()[*scene].clips[*track].clone(),
_ => return Err(format!("not a clip: {word}").into())
})).transpose()
}
fn get_axis (_: &App, src: impl Language) -> Perhaps<ControlAxis> {
fn get_axis <'a> (_: &App, src: impl Language) -> Perhaps<ControlAxis> {
Ok(src.word()?.map(|word|Ok(match word {
":x" => ControlAxis::X,
":y" => ControlAxis::Y,
_ => return Err(format!("unknown axis {word}"))
})).transpose()?)
}
fn get_opt_u16 (_: &App, _src: impl Language) -> Perhaps<Option<u16>> {
fn get_opt_u16 <'a> (_: &App, _src: impl Language) -> Perhaps<Option<u16>> {
Ok(None)
}
fn get_opt_itemtheme (_: &App, _: impl Language) -> Perhaps<Option<ItemTheme>> {
fn get_opt_itemtheme <'a> (_: &App, _: impl Language) -> Perhaps<Option<ItemTheme>> {
todo!()
}
fn get_opt_vec_opt_arc_rwlock_midiclip (_: &App, _: impl Language) -> Perhaps<Option<Vec<Option<Arc<RwLock<MidiClip>>>>>> {
fn get_opt_vec_opt_arc_rwlock_midiclip <'a> (_: &App, _: impl Language) -> Perhaps<Option<Vec<Option<Arc<RwLock<MidiClip>>>>>> {
todo!()
}
fn get_opt_arc_array_connect (_: &App, _: impl Language) -> Perhaps<Option<Arc<[Connect]>>> {
fn get_opt_arc_array_connect <'a> (_: &App, _: impl Language) -> Perhaps<Option<Arc<[Connect]>>> {
todo!()
}
fn get_arc_array_connect (_: &App, _: impl Language) -> Perhaps<Arc<[Connect]>> {
fn get_arc_array_connect <'a> (_: &App, _: impl Language) -> Perhaps<Arc<[Connect]>> {
todo!()
}
fn get_opt_arc_str (_: &App, _: impl Language) -> Perhaps<Option<Arc<str>>> {
fn get_opt_arc_str <'a> (_: &App, _: impl Language) -> Perhaps<Option<Arc<str>>> {
todo!()
}
fn get_opt_bool (_: &App, _: impl Language) -> Perhaps<Option<bool>> {
fn get_opt_bool <'a> (_: &App, _: impl Language) -> Perhaps<Option<bool>> {
todo!()
}
fn get_path_buf (_: &App, _: impl Language) -> Perhaps<PathBuf> {
fn get_path_buf <'a> (_: &App, _: impl Language) -> Perhaps<PathBuf> {
todo!()
}
}
@ -976,13 +985,15 @@ mod bind {
}
}
pub(crate) fn load_bind (binds: &Binds, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
pub(crate) fn load_bind <'a> (
binds: &Binds, name: &impl AsRef<str>, body: &impl Language
) -> Usually<()> {
binds.write().unwrap().insert(name.as_ref().into(), Bind::load(body)?);
Ok(())
}
impl Bind<TuiEvent, Arc<str>> {
pub fn load (lang: &impl Language) -> Usually<Self> {
pub fn load <'a> (lang: &impl Language) -> Usually<Self> {
let mut map = Self::new();
lang.each(|item|if item.expr().head() == Ok(Some("see")) {
// TODO
@ -1269,7 +1280,7 @@ mod device {
}
}
pub fn view_device (state: &App) -> impl Draw<Tui> {
pub fn view_device (state: &App) -> impl Draw<'_, Tui> {
let selected = state.dialog.device_kind().unwrap();
south(
bold(true, "Add device"),
@ -1301,9 +1312,9 @@ mod draw {
use crate::*;
/// Load custom view definition.
pub(crate) fn load_view (
views: &Views, name: &impl AsRef<str>, body: &impl Language,
) -> Usually<()> {
pub(crate) fn load_view <'a> (
views: &Views, name: &impl AsRef<str>, body: &'a impl Language,
) -> UsuallyRef<'a, ()> {
views.write().unwrap().insert(
name.as_ref().into(),
body.src()?.unwrap_or_default().into()
@ -1311,23 +1322,13 @@ mod draw {
Ok(())
}
impl Keywords<Tui, XYWH<u16>> for App {
fn keywords () -> impl Iterator<Item = fn(&Self, &mut Tui, &str) -> Perhaps<XYWH<u16>>> {
[
kw_when, kw_either, kw_split, kw_align, kw_exact, kw_min, kw_max, kw_push, kw_full,
kw_tui_text, kw_tui_fg, kw_tui_bg
]
.into_iter()
}
}
/// The [Draw] implementation for [App] handles the loaded view,
/// which is defined in terms of [dizzle] DSL.
///
/// If there is an error, the error is displayed. FIXME: overlay it
/// Then, every top-level form of the DSL description is rendered.
impl View<Tui> for App {
fn view (&self) -> impl Draw<Tui> {
fn view (&self) -> impl Draw<'_, Tui> {
//self.perf.cycle(&mut |_|{
draw(|to: &mut Tui|{
self.draw_error(to)?;
@ -1347,15 +1348,8 @@ mod draw {
Ok(())
}
fn draw_mode (&self, to: &mut Tui) -> Usually<()> {
self.mode.as_ref().and_then(|m|self.config.get_mode(m)).map(|mode|{
//south!(
//format!("Mode: {:?}", self.mode.as_ref()),
//format!("Time: {}", self.config.stamp.load(Relaxed)),
//iter_south(||mode.view.iter().enumerate().map(|(index, line)|{
//format!("View #{index} {line}")
//}))
//).align_nw().draw(to)?;
fn draw_mode <'a> (&'a self, to: &mut Tui) -> UsuallyRef<'a, ()> {
if let Some(mode) = self.mode.as_ref().and_then(|m|self.config.get_mode(m)) {
for (index, dsl) in mode.view.iter().enumerate() {
match self.interpret(to, dsl) {
Ok(Some(XYWH(.., w, h))) => {
@ -1364,7 +1358,10 @@ mod draw {
},
Err(e) => {
let src = &dsl.src().unwrap_or(Some("<source error>")).unwrap_or("<no source>");
let message = format!("Mode: {:?}\n\nLayer: #{index}\n\nError: {e}\n\nSource:\n{}", &mode.name, &src);
let message = format!(
"Mode: {:?}\n\nLayer: #{index}\n\nError: {e}\n\nSource:\n{src}",
&mode.name
);
*self.error.write().unwrap() = Some(message.into());
return Err(e);
},
@ -1372,8 +1369,7 @@ mod draw {
}
}
*self.error.write().unwrap() = None;
Ok(())
});
}
Ok(())
}
@ -1385,11 +1381,21 @@ mod draw {
}
}
impl Interpret<Tui, Option<XYWH<u16>>> for App {
fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression) -> Drawn<u16> {
self.keyword(to, lang)
impl<'a> Interpret<'a, Tui, Option<XYWH<u16>>> for App {
fn interpret_expr <L: Language + ?Sized> (&'a self, to: &mut Tui, src: &'a L) -> Drawn<u16> {
if let Some(src) = src.src()? {
interpret_keyword!(self, to, src, [
kw_when, kw_either, kw_split, kw_align, kw_exact, kw_min, kw_max, kw_push, kw_full,
kw_tui_text, kw_tui_fg, kw_tui_bg
]);
Err(format!("interpret_expr: unexpected: {src:?}").into())
} else {
Err(format!("interpret_expr: no keyword: {src:?}").into())
}
}
fn interpret_word <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression) -> Drawn<u16> {
fn interpret_word <L: Language + ?Sized> (&'a self, to: &mut Tui, lang: &'a L) -> Drawn<u16> {
let mut frags = lang.src()?.unwrap().split("/");
match frags.next() {
//Some(":logo") => view_logo().draw(to),
@ -1433,6 +1439,7 @@ mod draw {
_ => unreachable!()
}
}
}
impl_has!(Sizer: |self: App|self.size);
@ -1445,7 +1452,7 @@ mod draw {
fn width_dec (&mut self);
}
pub fn view_templates (_frags: std::str::Split<&str>, state: &App) -> impl Draw<Tui> {
pub fn view_templates <'a> (_frags: std::str::Split<&str>, state: &App) -> impl Draw<'a, Tui> {
let height = (state.config.modes.len() * 2) as u16;
draw(move |to: &mut Tui|{
let mut index = 0;
@ -1466,17 +1473,17 @@ mod draw {
}).min_w(30).exact_h(height)
}
pub fn field_h <T: Screen> (
_theme: ItemTheme, _head: impl Draw<T>, _body: impl Draw<T>
) -> impl Draw<T> {
pub fn field_h <'a, T: Screen> (
_theme: ItemTheme, _head: impl Draw<'a, T>, _body: impl Draw<'a, T>
) -> impl Draw<'a, T> {
}
pub fn field_v <T: Screen> (
_theme: ItemTheme, _head: impl Draw<T>, _body: impl Draw<T>
) -> impl Draw<T> {
pub fn field_v <'a, T: Screen> (
_theme: ItemTheme, _head: impl Draw<'a, T>, _body: impl Draw<'a, T>
) -> impl Draw<'a, T> {
}
pub fn view_sessions () -> impl Draw<Tui> {
pub fn view_sessions <'a> () -> impl Draw<'a, Tui> {
let h = 6;
let w = Some(30);
let f = Rgb(224, 192, 128);
@ -1490,12 +1497,15 @@ mod draw {
Ok(Some(to.area().into()))
}).min_w(w).exact_h(h)
}
/// ```
/// let bg = tengri::ratatui::style::Color::Red;
/// let fg = tengri::ratatui::style::Color::Green;
/// let _ = tek::view_wrap(bg, fg, "and then blue, too!");
/// ```
pub fn view_wrap (bg: Color, fg: Color, content: impl Draw<Tui>) -> impl Draw<Tui> {
pub fn view_wrap <'a> (bg: Color, fg: Color, content: impl Draw<'a, Tui>)
-> impl Draw<'a, Tui>
{
let left = fg_bg(bg, Reset, y_repeat("▐").exact_w(1));
let right = fg_bg(bg, Reset, y_repeat("▌").exact_w(1));
east(left, west(right, fg_bg(fg, bg, content)))
@ -1505,7 +1515,9 @@ mod draw {
/// let _ = tek::view_meter("", 0.0);
/// let _ = tek::view_meters(&[0.0, 0.0]);
/// ```
pub fn view_meter <'a> (label: &'a str, value: f32) -> impl Draw<Tui> + 'a {
pub fn view_meter <'a> (label: &'a str, value: f32)
-> impl Draw<'a, Tui>
{
let f = field_h(ItemTheme::G[128], label, format!("{:>+9.3}", value));
let w = if value >= 0.0 { 13 }
else if value >= -1.0 { 12 }
@ -1527,13 +1539,15 @@ mod draw {
south!(f, bg(c, ()).exact_wh(w, 1))
}
pub fn view_meters (values: &[f32;2]) -> impl Draw<Tui> + use<'_> {
pub fn view_meters (values: &[f32;2]) -> impl Draw<'_, Tui> + use<'_> {
let left = format!("L/{:>+9.3}", values[0]);
let right = format!("R/{:>+9.3}", values[1]);
south(left, right)
}
pub fn view_track_header (theme: ItemTheme, content: impl Draw<Tui>) -> impl Draw<Tui> {
pub fn view_track_header <'a> (theme: ItemTheme, content: impl Draw<'a, Tui>)
-> impl Draw<'a, Tui>
{
bg(theme.darker.term, content.align_e().full_w()).exact_w(12)
}
@ -1541,7 +1555,9 @@ mod draw {
/// let _ = tek::button_2("", "", true);
/// let _ = tek::button_2("", "", false);
/// ```
pub fn button_2 <'a> (key: impl Draw<Tui>, label: impl Draw<Tui>, hide: bool) -> impl Draw<Tui> {
pub fn button_2 <'a> (key: impl Draw<'a, Tui>, label: impl Draw<'a, Tui>, hide: bool)
-> impl Draw<'a, Tui>
{
let c1 = tui_orange();
let c2 = tui_g(0);
let c3 = tui_g(96);
@ -1555,8 +1571,11 @@ mod draw {
/// let _ = tek::button_3("", "", "", false);
/// ```
pub fn button_3 <'a> (
key: impl Draw<Tui>, label: impl Draw<Tui>, value: impl Draw<Tui>, editing: bool,
) -> impl Draw<Tui> {
key: impl Draw<'a, Tui>,
label: impl Draw<'a, Tui>,
value: impl Draw<'a, Tui>,
editing: bool,
) -> impl Draw<'a, Tui> {
let c1 = tui_orange();
let c2 = tui_g(0);
let c3 = tui_g(96);