refactor: Mode, View, Bind

This commit is contained in:
i do not exist 2026-07-02 09:21:47 +03:00
parent 51faa82d74
commit 701a651fbd
21 changed files with 901 additions and 880 deletions

View file

@ -1,5 +1,9 @@
use crate::*;
tui_view!(|self: App| {
""
});
/// The [Draw] implementation for [App] handles the loaded view,
/// which is defined in terms of [dizzle] DSL.
///
@ -137,10 +141,10 @@ pub fn draw_dialog (
pub fn draw_templates (to: &mut Tui, frags: std::str::Split<&str>, state: &App)
-> Usually<XYWH<u16>>
{
let modes = state.config.modes.clone();
let height = (modes.read().unwrap().len() * 2) as u16;
let height = (state.config.modes.len() * 2) as u16;
h_exact(height, w_min(Some(30), thunk(move |to: &mut Tui|{
for (index, (id, profile)) in modes.read().unwrap().iter().enumerate() {
let mut index = 0;
state.config.modes.for_each(|id, profile| {
let b = if index == 0 { Rgb(70,70,70) } else { Rgb(50,50,50) };
let name = profile.name.get(0).map(|x|x.as_ref()).unwrap_or("<no name>");
let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or("<no info>");
@ -149,12 +153,613 @@ pub fn draw_templates (to: &mut Tui, frags: std::str::Split<&str>, state: &App)
let field_name = w_full(origin_w(fg(fg1, name)));
let field_id = w_full(origin_e(fg(fg2, id)));
let field_info = w_full(origin_w(info));
y_push((2 * index) as u16,
let _ = y_push((2 * index) as u16,
h_exact(2, w_full(bg(b, south(
above(field_name, field_id),
field_info
))))).draw(to);
}
index += 1;
});
Ok(to.area().into())
}))).draw(to)
}
/// ```
/// let _ = tek::view_logo();
/// ```
pub fn view_logo () -> impl Draw<Tui> {
wh_exact(Some(32), Some(7), bold(true, fg(Rgb(240, 200, 180), south!{
h_exact(1, ""),
h_exact(1, ""),
h_exact(1, "~~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~~~~~~~~~"),
h_exact(1, east("~~~~ ║ ~ ╟─╌ ~╟─< ~~ ", east(fg(Rgb(230,100,40), "v0.3.0"), " ~~"))),
h_exact(1, "~~~~ ╨ ~ ╙──╜ ╨ ╜ ~~~~~~~~~~~~"),
})))
}
/// ```
/// let x = "";
/// let _ = tek::view_transport(true, x.as_ref(), x.as_ref(), x.as_ref());
/// let _ = tek::view_transport(false, x.as_ref(), x.as_ref(), x.as_ref());
/// ```
pub fn view_transport (play: bool, bpm: &str, beat: &str, time: &str) -> impl Draw<Tui> {
let theme = ItemTheme::G[96];
bg(Black, east!(above(
wh_full(origin_w(button_play_pause(play, false))),
wh_full(origin_e(east!(
field_h(theme, "BPM", bpm),
field_h(theme, "Beat", beat),
field_h(theme, "Time", time),
)))
)))
}
/// ```
/// let x = "";
/// let _ = tek::view_status(None, x.as_ref(), x.as_ref(), x.as_ref());
/// let _ = tek::view_status(Some("".into()), x.as_ref(), x.as_ref(), x.as_ref());
/// ```
pub fn view_status (sel: Option<&str>, sr: &str, buf: &str, lat: &str) -> impl Draw<Tui> {
let theme = ItemTheme::G[96];
let sr = field_h(theme, "SR", sr);
let buf = field_h(theme, "Buf", buf);
let lat = field_h(theme, "Lat", lat);
bg(Black, east!(above(
wh_full(origin_w(sel.map(|sel|field_h(theme, "Selected", sel)))),
wh_full(origin_e(east!(sr, buf, lat))),
)))
}
/// ```
/// let _ = tek::button_play_pause(true, true);
/// let _ = tek::button_play_pause(true, false);
/// let _ = tek::button_play_pause(false, true);
/// let _ = tek::button_play_pause(false, false);
/// ```
pub fn button_play_pause (playing: bool, compact: bool) -> impl Draw<Tui> {
bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) },
either(compact,
thunk(move|to: &mut Tui|w_exact(9, either(playing,
fg(Rgb(0, 255, 0), " PLAYING "),
fg(Rgb(255, 128, 0), " STOPPED "))
).draw(to)),
thunk(move|to: &mut Tui|w_exact(5, either(playing,
fg(Rgb(0, 255, 0), south(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
fg(Rgb(255, 128, 0), south(" ▗▄▖ ", " ▝▀▘ ",)))
).draw(to)),
)
)
}
#[cfg(feature = "track")] pub fn view_track_row_section (
_theme: ItemTheme,
button: impl Draw<Tui>,
button_add: impl Draw<Tui>,
content: impl Draw<Tui>,
) -> impl Draw<Tui> {
west(h_full(w_exact(4, origin_nw(button_add))),
east(w_exact(20, h_full(origin_nw(button))), wh_full(origin_c(content))))
}
/// ```
/// 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> {
let left = fg_bg(bg, Reset, w_exact(1, y_repeat("")));
let right = fg_bg(bg, Reset, w_exact(1, y_repeat("")));
east(left, west(right, fg_bg(fg, bg, content)))
}
/// ```
/// 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 {
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 }
else if value >= -2.0 { 11 }
else if value >= -3.0 { 10 }
else if value >= -4.0 { 9 }
else if value >= -6.0 { 8 }
else if value >= -9.0 { 7 }
else if value >= -12.0 { 6 }
else if value >= -15.0 { 5 }
else if value >= -20.0 { 4 }
else if value >= -25.0 { 3 }
else if value >= -30.0 { 2 }
else if value >= -40.0 { 1 }
else { 0 };
let c = if value >= 0.0 { Red }
else if value >= -3.0 { Yellow }
else { Green };
south!(f, wh_exact(Some(w), Some(1), bg(c, ())))
}
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_sample_info (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> + use<'_> {
when(sample.is_some(), thunk(move|to: &mut Tui|{
let sample = sample.unwrap().read().unwrap();
let theme = sample.color;
east!(
field_h(theme, "Name", format!("{:<10}", sample.name.clone())),
field_h(theme, "Length", format!("{:<8}", sample.channels[0].len())),
field_h(theme, "Start", format!("{:<8}", sample.start)),
field_h(theme, "End", format!("{:<8}", sample.end)),
field_h(theme, "Trans", "0"),
field_h(theme, "Gain", format!("{}", sample.gain)),
).draw(to)
}))
}
pub fn view_sample_info_v (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> + use<'_> {
let a = thunk(move|to: &mut Tui|{
let sample = sample.unwrap().read().unwrap();
let theme = sample.color;
w_exact(20, south!(
w_full(origin_w(field_h(theme, "Name ", format!("{:<10}", sample.name.clone())))),
w_full(origin_w(field_h(theme, "Length", format!("{:<8}", sample.channels[0].len())))),
w_full(origin_w(field_h(theme, "Start ", format!("{:<8}", sample.start)))),
w_full(origin_w(field_h(theme, "End ", format!("{:<8}", sample.end)))),
w_full(origin_w(field_h(theme, "Trans ", "0"))),
w_full(origin_w(field_h(theme, "Gain ", format!("{}", sample.gain)))),
)).draw(to)
});
let b = thunk(|to: &mut Tui|fg(Red, south!(
bold(true, "× No sample."),
"[r] record",
"[Shift-F9] import",
)).draw(to));
either(sample.is_some(), a, b)
}
pub fn view_sample_status (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> {
bold(true, fg(g(224), sample
.map(|sample|{
let sample = sample.read().unwrap();
format!("Sample {}-{}", sample.start, sample.end)
})
.unwrap_or_else(||"No sample".to_string())))
}
pub fn view_track_header (theme: ItemTheme, content: impl Draw<Tui>) -> impl Draw<Tui> {
w_exact(12, bg(theme.darker.term, w_full(origin_e(content))))
}
pub fn view_ports_status <'a, T: JackPort> (theme: ItemTheme, title: &'a str, ports: &'a [T])
-> impl Draw<Tui> + use<'a, T>
{
let ins = ports.len() as u16;
let frame = Outer(true, Style::default().fg(g(96)));
let iter = move||ports.iter();
let names = iter_south(iter, move|port, index|h_full(origin_w(format!(" {index} {}", port.port_name()))));
let field = field_v(theme, title, names);
wh_exact(Some(20), Some(1 + ins), border(true, frame, wh_exact(Some(20), Some(1 + ins), field)))
}
pub fn view_io_ports <'a, T: PortsSizes<'a>> (
fg: Color, bg: Color, items: impl Fn()->T + Send + Sync + 'a
) -> impl Draw<Tui> + 'a {
type Item<'a> = (usize, &'a Arc<str>, &'a [Connect], usize, usize);
iter(items, move|(_index, name, connections, y, y2): Item<'a>, _| {
y_push(y as u16, h_exact((y2-y) as u16,
south(h_full(bold(true, fg_bg(fg, bg, origin_w(east(" 󰣲 ", name))))),
iter(||connections.iter(), move|connect: &'a Connect, index|y_push(
index as u16, h_exact(1, origin_w(bold(false, fg_bg(fg, bg, &connect.info))))
)))))
})
}
pub fn view_scenes_clips <'a, S: ScenesSizes<'a>> (
scenes: impl Fn()->S,
tracks: impl TracksSizes<'a>,
select: &Selection,
editor: Option<&MidiEditor>,
size: &Size,
editing: bool,
) -> impl Draw<Tui> {
let status = wh_full(origin_se(fg(Green, format!("{}x{}", size.w(), size.h()))));
let tracks = iter_once(tracks, move|(track_index, track, _, _), _| {
let scenes = iter_once(scenes(), move|(scene_index, scene, _, _), _| {
let (name, theme): (Arc<str>, ItemTheme) = scene_name_theme(scene, track_index);
let f = theme.lightest.term;
let (b, o) = scene_bg(theme, select, track_index, scene_index);
let w = scene_w(track, select, track_index, editor);
let y = scene_y(select, scene_index, editor);
let is_selected = scene_sel(select, track_index, scene_index, editing);
wh_exact(Some(w), Some(y), below(
wh_full(Outer(true, Style::default().fg(o))),
wh_full(below(
below(
fg_bg(o, b, wh_full("")),
wh_full(origin_nw(fg_bg(f, b, bold(true, name)))),
),
wh_full(when(is_selected, editor.map(|e|e.view())))))))
});
w_exact(track.width as u16, h_full(scenes))
});
return size.of(wh_full(above(status, tracks)));
fn scene_name_theme (scene: &Scene, track_index: usize) -> (Arc<str>, ItemTheme) {
if let Some(Some(clip)) = &scene.clips.get(track_index) {
let clip = clip.read().unwrap();
(format!("{}", &clip.name).into(), clip.color)
} else {
(" ⏹ -- ".into(), ItemTheme::G[32])
}
}
fn scene_bg (
theme: ItemTheme, select: &Selection, track_index: usize, scene_index: usize
) -> (Color, Color) {
let mut outline = theme.base.term;
(if select.track() == Some(track_index) && select.scene() == Some(scene_index) {
outline = theme.lighter.term;
theme.light.term
} else if select.track() == Some(track_index) || select.scene() == Some(scene_index) {
outline = theme.darkest.term;
theme.base.term
} else {
theme.dark.term
}, outline)
}
fn scene_w (
track: &Track, select: &Selection, track_index: usize, editor: Option<&MidiEditor>
) -> u16 {
if select.track() == Some(track_index) && let Some(editor) = editor {
(editor.size.w() as usize).max(24).max(track.width) as u16
} else {
track.width as u16
}
}
fn scene_y (
select: &Selection, scene_index: usize, editor: Option<&MidiEditor>
) -> u16 {
if select.scene() == Some(scene_index) && let Some(editor) = editor {
editor.size.h().max(12)
} else {
H_SCENE as u16
}
}
fn scene_sel (select: &Selection, track_index: usize, scene_index: usize, editing: bool) -> bool {
editing && select.track() == Some(track_index) && select.scene() == Some(scene_index)
}
}
pub fn view_track_names (
theme: ItemTheme,
tracks: impl TracksSizes<'_>,
track_count: usize,
scene_count: usize,
selected: &Selection,
) -> impl Draw<Tui> {
let button = south(
button_3("t", "rack ", format!("{}{track_count}", selected.track()
.map(|track|format!("{track}/")).unwrap_or_default()), false),
button_3("s", "cene ", format!("{}{scene_count}", selected.scene()
.map(|scene|format!("{scene}/")).unwrap_or_default()), false));
let button_2 = south(
button_2("T", "+", false),
button_2("S", "+", false));
view_track_row_section(theme, button, button_2, bg(theme.darker.term,
h_exact(2, thunk(|to: &mut Tui|{
for (index, track, x1, _x2) in tracks {
x_push(x1 as u16, w_exact(track_width(index, track),
bg(if selected.track() == Some(index) {
track.color.light.term
} else {
track.color.base.term
}, south(w_full(origin_nw(east(
format!("·t{index:02} "),
fg(Rgb(255, 255, 255), bold(true, &track.name))
))), ""))) ).draw(to);
}
Ok(XYWH(0, 0, 0, 0))
}))))
}
pub fn view_track_outputs (
theme: ItemTheme, tracks: impl TracksSizes<'_>, midi_outs: impl Iterator<Item = &MidiOutput>,
) -> impl Draw<Tui> {
view_track_row_section(theme,
south(w_full(origin_w(button_2("o", "utput", false))),
thunk(|to: &mut Tui|{
for port in midi_outs {
w_full(origin_w(port.port_name())).draw(to);
}
Ok(XYWH(0, 0, 0, 0))
})),
button_2("O", "+", false),
bg(theme.darker.term, origin_w(thunk(|to: &mut Tui|{
for (index, track, _x1, _x2) in tracks {
let iter = ||track.sequencer.midi_outs.iter();
let draw = |port: &MidiOutput, _|fg(Rgb(255, 255, 255),
h_exact(1, bg(track.color.dark.term, w_full(origin_w(
format!("·o{index:02} {}", port.port_name()))))));
w_exact(track_width(index, track),
origin_nw(h_full(iter_south(iter, draw)))).draw(to);
}
Ok(XYWH(0, 0, 0, 0))
}))))
}
pub fn view_track_inputs (
theme: ItemTheme, tracks: impl TracksSizes<'_>, height: u16,
) -> impl Draw<Tui> {
view_track_row_section(theme, button_2("i", "nput", false), button_2("I", "+", false),
bg(theme.darker.term, origin_w(thunk(move|to: &mut Tui|{
for (index, track, _x1, _x2) in tracks {
wh_exact(Some(track_width(index, track)), Some(height + 1),
origin_nw(south(
bg(track.color.base.term,
w_full(origin_w(east!(
either(track.sequencer.monitoring, fg(Green, "●mon "), "·mon "),
either(track.sequencer.recording, fg(Red, "●rec "), "·rec "),
either(track.sequencer.overdub, fg(Yellow, "●dub "), "·dub "),
)))),
iter_south(||track.sequencer.midi_ins.iter(),
|port, _|fg_bg(Rgb(255, 255, 255), track.color.dark.term,
w_full(origin_w(format!("·i{index:02} {}", port.port_name()))))))))
.draw(to);
}
Ok(XYWH(0, 0, 0, 0))
}))))
}
pub fn view_scenes_names (
scenes: impl ScenesSizes<'_>,
select: &Selection,
editor: Option<&MidiEditor>,
editing: bool,
) -> impl Draw<Tui> {
w_exact(20, thunk(move |to: &mut Tui|{
for (index, scene, ..) in scenes {
view_scene_name(select, editor, index, scene, editing).draw(to);
}
Ok(XYWH(1, 1, 1, 1))
}))
}
pub fn view_scene_name (
select: &Selection,
editor: Option<&MidiEditor>,
index: usize,
scene: &Scene,
editing: bool
) -> impl Draw<Tui> {
let h = if select.scene() == Some(index) && let Some(_editor) = editor {
7
} else {
H_SCENE as u16
};
let a = w_full(origin_w(east(format!("·s{index:02} "),
fg(g(255), bold(true, &scene.name)))));
let b = when(select.scene() == Some(index) && editing,
wh_full(origin_nw(south(
editor.as_ref().map(|e|e.clip_status()),
editor.as_ref().map(|e|e.edit_status())))));
let c = if select.scene() == Some(index) {
scene.color.light.term
} else {
scene.color.base.term
};
wh_exact(Some(20), Some(h), bg(c, origin_nw(south(a, b))))
}
pub fn view_midi_ins_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<Tui> {
track.map(move|track|view_ports_status(theme, "MIDI ins: ", &track.sequencer.midi_ins))
}
pub fn view_midi_outs_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<Tui> {
track.map(move|track|view_ports_status(theme, "MIDI outs: ", &track.sequencer.midi_outs))
}
pub fn view_audio_ins_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<Tui> {
track.map(move|track|view_ports_status(theme, "Audio ins: ", &track.audio_ins()))
}
pub fn view_audio_outs_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<Tui> {
track.map(move|track|view_ports_status(theme, "Audio outs:", &track.audio_outs()))
}
pub fn view_track_per <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
tracks: impl Fn() -> U + Send + Sync + 'a,
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
) -> impl Draw<Tui> {
iter_east(tracks, move|(index, track, x1, x2): (usize, &Track, usize, usize), _|{
w_exact((x2 - x1) as u16, fg_bg(
track.color.lightest.term,
track.color.base.term,
callback(index, track)))
})
}
pub fn view_per_track () -> impl Draw<Tui> {}
pub fn view_per_track_top () -> impl Draw<Tui> {}
pub fn view_inputs (tracks: impl TracksSizes<'_>, midi_ins: &[MidiInput]) -> impl Draw<Tui> {
let title_1 = wh_exact(Some(20), Some(1), origin_w(button_3("i", "nput ", format!("{}", midi_ins.len()), false)));
let title_2 = wh_exact(Some(4), Some(1), w_exact(4, button_2("I", "+", false)));
east(title_1, west(title_2, thunk(move|to: &mut Tui|{
for (_index, track, x1, _x2) in tracks {
south(
x_push(x1 as u16, bg(track.color.dark.term, origin_w(w_exact(track.width as u16, east!(
either(track.sequencer.monitoring, fg(Green, "mon "), "mon "),
either(track.sequencer.recording, fg(Red, "rec "), "rec "),
either(track.sequencer.overdub, fg(Yellow, "dub "), "dub "),
))))),
thunk(move |to: &mut Tui|{
for (index, port) in midi_ins.iter().enumerate() {
x_push(index as u16 * 10, h_exact(1, east(w_exact(20, origin_w(east("", bold(true, fg(Rgb(255,255,255), port.port_name()))))),
west(w_exact(4, ()), thunk(move|to: &mut Tui|{
bg(track.color.darker.term, origin_w(w_exact(track.width as u16, east!(
either(track.sequencer.monitoring, fg(Green, ""), " · "),
either(track.sequencer.recording, fg(Red, ""), " · "),
either(track.sequencer.overdub, fg(Yellow, ""), " · "),
)))).draw(to);
todo!()
}))))).draw(to);
}
todo!()
})
).draw(to);
}
todo!()
})))
}
pub fn view_outputs (
theme: ItemTheme,
tracks: impl TracksSizes<'_>,
midi_outs: &[MidiOutput],
height: u16,
) -> impl Draw<Tui> {
let list = south(
h_exact(1, w_full(origin_w(button_3(
"o", "utput", format!("{}", midi_outs.len()), false
)))),
h_exact(height - 1, wh_full(origin_nw(thunk(|to: &mut Tui|{
for (_index, port) in midi_outs.iter().enumerate() {
h_exact(1,w_full(east(
origin_w(east("", fg(Rgb(255,255,255), bold(true, port.port_name())))),
w_full(origin_e(format!("{}/{} ",
port.port().get_connections().len(),
port.connections.len())))))).draw(to);
for (index, conn) in port.connections.iter().enumerate() {
h_exact(1, w_full(origin_w(format!(" c{index:02}{}", conn.info()))))
.draw(to);
}
}
todo!();
}))))
);
h_exact(height, view_track_row_section(theme, list, button_2("O", "+", false),
bg(theme.darker.term, origin_w(w_full(
thunk(|to: &mut Tui|{
for (index, track, _x1, _x2) in tracks {
w_exact(track_width(index, track),
thunk(|to: &mut Tui|{
h_exact(1, origin_w(east(
either(true, fg(Green, "play "), "play "),
either(false, fg(Yellow, "solo "), "solo "),
))).draw(to);
for (_index, port) in midi_outs.iter().enumerate() {
h_exact(1, origin_w(east(
either(true, fg(Green, ""), " · "),
either(false, fg(Yellow, ""), " · "),
))).draw(to);
for (_index, _conn) in port.connections.iter().enumerate() {
h_exact(1, w_full("")).draw(to);
}
}
todo!()
})
).draw(to);
}
todo!()
})
)))
))
}
pub fn view_track_devices (
theme: ItemTheme,
tracks: impl TracksSizes<'_>,
track: Option<&Track>,
h: u16,
) -> impl Draw<Tui> {
view_track_row_section(theme,
button_3("d", "evice", format!("{}", track.map(|t|t.devices.len()).unwrap_or(0)), false),
button_2("D", "+", false),
iter_once(tracks, move|(_, track, _x1, _x2), index| wh_exact(
Some(track_width(index, track)),
Some(h + 1),
bg(track.color.dark.term, origin_nw(iter_south(move||0..h,
|_, _index|wh_exact(Some(track.width as u16), Some(2),
fg_bg(
ItemTheme::G[32].lightest.term,
ItemTheme::G[32].dark.term,
origin_nw(format!(" · {}", "--"))
)
)))))))
}
pub fn per_track <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
tracks: impl Fn() -> U + Send + Sync + 'a,
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
) -> impl Draw<Tui> + 'a {
per_track_top(tracks, move|index, track|h_full(origin_y(callback(index, track))))
}
pub fn per_track_top <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
tracks: impl Fn() -> U + Send + Sync + 'a,
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
) -> impl Draw<Tui> + 'a {
origin_x(bg(Reset, iter_east(tracks,
move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{
w_exact((x2 - x1) as u16, fg_bg(
track.color.lightest.term,
track.color.base.term,
callback(index, track))) })))
}
pub fn field_h <T: Screen> (theme: ItemTheme, head: impl Draw<T>, body: impl Draw<T>) -> impl Draw<T> {
}
pub fn field_v <T: Screen> (theme: ItemTheme, head: impl Draw<T>, body: impl Draw<T>) -> impl Draw<T> {
}
pub fn view_sessions () -> impl Draw<Tui> {
let h = 6;
let w = Some(30);
let f = Rgb(224, 192, 128);
h_exact(h, w_min(w, thunk(move |to: &mut Tui|{
for (index, name) in ["session1", "session2", "session3"].iter().enumerate() {
let b = if index == 0 { Rgb(50,50,50) } else { Rgb(40,40,40) };
let y = (2 * index) as u16;
let h = 2;
y_push(y, &h_exact(h, w_full(bg(b, origin_w(fg(f, name)))))).draw(to);
}
Ok(to.area().into())
})))
}
pub fn view_browse_title (state: &App) -> impl Draw<Tui> {
w_full(origin_w(field_v(ItemTheme::default(),
match state.dialog.browser_target().unwrap() {
BrowseTarget::SaveProject => "Save project:",
BrowseTarget::LoadProject => "Load project:",
BrowseTarget::ImportSample(_) => "Import sample:",
BrowseTarget::ExportSample(_) => "Export sample:",
BrowseTarget::ImportClip(_) => "Import clip:",
BrowseTarget::ExportClip(_) => "Export clip:",
}, h_exact(1, fg(g(96), x_repeat("🭻")))
)))
}
pub fn view_device (state: &App) -> impl Draw<Tui> {
let selected = state.dialog.device_kind().unwrap();
south(bold(true, "Add device"), iter_south(
move||device_kinds().iter(),
move|_label: &&'static str, i|{
let b = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) };
let l = if i == selected { "[ " } else { " " };
let r = if i == selected { " ]" } else { " " };
w_full(bg(b, east(l, west(r, "FIXME device name")))) }))
}