mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
don't crash on add track/scene
This commit is contained in:
parent
5a9ec0a63d
commit
6a738375e2
9 changed files with 205 additions and 207 deletions
15
src/core.rs
15
src/core.rs
|
|
@ -148,17 +148,12 @@ pub fn input_thread (
|
||||||
// Listen for events and send them to the main thread
|
// Listen for events and send them to the main thread
|
||||||
if ::crossterm::event::poll(poll).is_ok() {
|
if ::crossterm::event::poll(poll).is_ok() {
|
||||||
let event = ::crossterm::event::read().unwrap();
|
let event = ::crossterm::event::read().unwrap();
|
||||||
match event {
|
if let Event::Key(KeyEvent {
|
||||||
Event::Key(KeyEvent {
|
code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, ..
|
||||||
code: KeyCode::Char('c'),
|
}) = event {
|
||||||
modifiers: KeyModifiers::CONTROL,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
exited.store(true, Ordering::Relaxed);
|
exited.store(true, Ordering::Relaxed);
|
||||||
},
|
} else if let Err(e) = device.write().unwrap().handle(&AppEvent::Input(event)) {
|
||||||
_ => if device.write().unwrap().handle(&AppEvent::Input(event)).is_err() {
|
panic!("{e}")
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
pub(crate) use ratatui::prelude::*;
|
pub(crate) use ratatui::prelude::CrosstermBackend;
|
||||||
pub(crate) use ratatui::buffer::Cell;
|
pub(crate) use ratatui::style::{Stylize, Style, Color};
|
||||||
|
pub(crate) use ratatui::layout::Rect;
|
||||||
|
pub(crate) use ratatui::buffer::{Buffer, Cell};
|
||||||
use ratatui::widgets::WidgetRef;
|
use ratatui::widgets::WidgetRef;
|
||||||
|
|
||||||
pub fn fill_bg (buf: &mut Buffer, area: Rect, color: Color) {
|
pub fn fill_bg (buf: &mut Buffer, area: Rect, color: Color) {
|
||||||
|
|
@ -105,72 +107,6 @@ impl WidgetRef for dyn Render {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LineBuffer {
|
|
||||||
width: usize,
|
|
||||||
cells: Vec<Cell>,
|
|
||||||
style: Option<Style>,
|
|
||||||
bg: Cell,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LineBuffer {
|
|
||||||
pub fn new (bg: Cell, width: usize, height: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
style: None,
|
|
||||||
width: width,
|
|
||||||
cells: vec![bg.clone();width*height],
|
|
||||||
bg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn height (&self) -> usize {
|
|
||||||
self.cells.len() / self.width
|
|
||||||
}
|
|
||||||
pub fn style (&mut self, style: Style) -> &mut Self {
|
|
||||||
self.style = Some(style);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn no_style (&mut self) -> &mut Self {
|
|
||||||
self.style = None;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn put (&mut self, data: &str, x: usize, y: usize) -> &mut Self {
|
|
||||||
if x < self.width {
|
|
||||||
for (i, c) in data.chars().enumerate() {
|
|
||||||
if x + i >= self.width {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let index = y * self.width + x + i;
|
|
||||||
while index >= self.cells.len() {
|
|
||||||
self.cells.extend_from_slice(&vec![self.bg.clone();self.width]);
|
|
||||||
}
|
|
||||||
self.cells[index].set_char(c);
|
|
||||||
if let Some(s) = self.style {
|
|
||||||
self.cells[index].set_style(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn show (&self, buf: &mut Buffer, area: Rect, offset: isize) -> Usually<Rect> {
|
|
||||||
let Rect { x, mut y, width, height } = area;
|
|
||||||
for row in offset..self.height() as isize {
|
|
||||||
let length = self.cells.len();
|
|
||||||
let start = ((row.max(0) as usize)*self.width).min(length);
|
|
||||||
let end = (((row + 1).max(0) as usize)*self.width).min(length);
|
|
||||||
for (column, cell) in self.cells[start..end].iter().enumerate() {
|
|
||||||
if column >= width as usize {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
*buf.get_mut(x + column as u16, y + row as u16) = cell.clone();
|
|
||||||
}
|
|
||||||
y = y + 1;
|
|
||||||
if y > height {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(area)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -190,6 +126,5 @@ mod test {
|
||||||
assert_eq!(buffer.cells.len(), 12);
|
assert_eq!(buffer.cells.len(), 12);
|
||||||
buffer.put("FOO", 0, 1);
|
buffer.put("FOO", 0, 1);
|
||||||
assert_eq!(buffer.cells.len(), 24);
|
assert_eq!(buffer.cells.len(), 24);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{core::*, model::App};
|
use crate::{core::*, model::*};
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn next_scene (&mut self) {
|
pub fn next_scene (&mut self) {
|
||||||
|
|
@ -11,8 +11,11 @@ impl App {
|
||||||
format!("Scene {}", self.scenes.len() + 1)
|
format!("Scene {}", self.scenes.len() + 1)
|
||||||
}
|
}
|
||||||
pub fn add_scene (&mut self, name: Option<&str>) -> Usually<&mut Scene> {
|
pub fn add_scene (&mut self, name: Option<&str>) -> Usually<&mut Scene> {
|
||||||
let name = name.ok_or_else(||self.new_scene_name())?;
|
let clips = vec![None;self.tracks.len()];
|
||||||
self.scenes.push(Scene::new(&name, vec![]));
|
self.scenes.push(match name {
|
||||||
|
Some(name) => Scene::new(name, clips),
|
||||||
|
None => Scene::new(&self.new_scene_name(), clips)
|
||||||
|
});
|
||||||
self.scene_cursor = self.scenes.len();
|
self.scene_cursor = self.scenes.len();
|
||||||
Ok(&mut self.scenes[self.scene_cursor - 1])
|
Ok(&mut self.scenes[self.scene_cursor - 1])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::core::*;
|
use crate::{core::*, model::*};
|
||||||
use crate::model::*;
|
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn next_track (&mut self) {
|
pub fn next_track (&mut self) {
|
||||||
|
|
@ -8,32 +7,29 @@ impl App {
|
||||||
pub fn prev_track (&mut self) {
|
pub fn prev_track (&mut self) {
|
||||||
self.track_cursor = self.track_cursor.saturating_sub(1);
|
self.track_cursor = self.track_cursor.saturating_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_track_name (&self) -> String {
|
pub fn new_track_name (&self) -> String {
|
||||||
format!("Track {}", self.tracks.len() + 1)
|
format!("Track {}", self.tracks.len() + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_track (&mut self, name: Option<&str>) -> Usually<&mut Track> {
|
pub fn add_track (&mut self, name: Option<&str>) -> Usually<&mut Track> {
|
||||||
let name = name.ok_or_else(||self.new_track_name())?;
|
self.tracks.push(match name {
|
||||||
self.tracks.push(Track::new(&name, None, None)?);
|
Some(name) => Track::new(name, None, None)?,
|
||||||
|
None => Track::new(&self.new_track_name(), None, None)?
|
||||||
|
});
|
||||||
self.track_cursor = self.tracks.len();
|
self.track_cursor = self.tracks.len();
|
||||||
Ok(&mut self.tracks[self.track_cursor - 1])
|
Ok(&mut self.tracks[self.track_cursor - 1])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn track (&self) -> Option<(usize, &Track)> {
|
pub fn track (&self) -> Option<(usize, &Track)> {
|
||||||
match self.track_cursor { 0 => None, _ => {
|
match self.track_cursor { 0 => None, _ => {
|
||||||
let id = self.track_cursor as usize - 1;
|
let id = self.track_cursor as usize - 1;
|
||||||
self.tracks.get(id).map(|t|(id, t))
|
self.tracks.get(id).map(|t|(id, t))
|
||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> {
|
pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> {
|
||||||
match self.track_cursor { 0 => None, _ => {
|
match self.track_cursor { 0 => None, _ => {
|
||||||
let id = self.track_cursor as usize - 1;
|
let id = self.track_cursor as usize - 1;
|
||||||
self.tracks.get_mut(id).map(|t|(id, t))
|
self.tracks.get_mut(id).map(|t|(id, t))
|
||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Track {
|
pub struct Track {
|
||||||
|
|
|
||||||
69
src/view.rs
69
src/view.rs
|
|
@ -5,7 +5,9 @@ pub mod transport;
|
||||||
pub mod plugin;
|
pub mod plugin;
|
||||||
pub mod border;
|
pub mod border;
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
|
pub mod split;
|
||||||
|
|
||||||
|
pub use self::split::*;
|
||||||
pub use self::border::*;
|
pub use self::border::*;
|
||||||
pub use self::theme::*;
|
pub use self::theme::*;
|
||||||
pub use self::arranger::*;
|
pub use self::arranger::*;
|
||||||
|
|
@ -28,70 +30,3 @@ render!(App |self, buf, area| {
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(area)
|
||||||
});
|
});
|
||||||
|
|
||||||
pub struct If<'a>(pub bool, pub &'a (dyn Render + Sync));
|
|
||||||
|
|
||||||
impl<'a> Render for If<'a> {
|
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
|
||||||
if self.0 {
|
|
||||||
self.1.render(buf, area)
|
|
||||||
} else {
|
|
||||||
().render(buf, area)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Modal<T>: Device {
|
|
||||||
fn handle_with_state (&self, state: &mut T, event: &AppEvent) -> Usually<bool>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum Direction { Down, Right }
|
|
||||||
|
|
||||||
impl Direction {
|
|
||||||
pub fn split <'a, const N: usize> (&self, items: [&'a (dyn Render + Sync);N]) -> Split<'a, N> {
|
|
||||||
Split(*self, items)
|
|
||||||
}
|
|
||||||
pub fn is_down (&self) -> bool {
|
|
||||||
match self { Self::Down => true, _ => false }
|
|
||||||
}
|
|
||||||
pub fn is_right (&self) -> bool {
|
|
||||||
match self { Self::Right => true, _ => false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Split<'a, const N: usize>(
|
|
||||||
pub Direction, pub [&'a (dyn Render + Sync);N]
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<'a, const N: usize> Split<'a, N> {
|
|
||||||
pub fn down (items: [&'a (dyn Render + Sync);N]) -> Self {
|
|
||||||
Self(Direction::Down, items)
|
|
||||||
}
|
|
||||||
pub fn right (items: [&'a (dyn Render + Sync);N]) -> Self {
|
|
||||||
Self(Direction::Right, items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, const N: usize> Render for Split<'a, N> {
|
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
|
||||||
let Rect { mut x, mut y, mut width, mut height } = area;
|
|
||||||
for item in self.1 {
|
|
||||||
if width == 0 || height == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
let result = item.render(buf, Rect { x, y, width, height })?;
|
|
||||||
match self.0 {
|
|
||||||
Direction::Down => {
|
|
||||||
y = y + result.height;
|
|
||||||
height = height.saturating_sub(result.height);
|
|
||||||
},
|
|
||||||
Direction::Right => {
|
|
||||||
x = x + result.width;
|
|
||||||
width = width.saturating_sub(result.width);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(area)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -83,15 +83,14 @@ impl<'a> ArrangerView<'a> {
|
||||||
if y + 2 * scene_index as u16 >= height {
|
if y + 2 * scene_index as u16 >= height {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
let label = if let Some(Some(clip)) = scene.clips.get(track_index) {
|
let label = match scene.clips.get(track_index) {
|
||||||
if let Some(phrase) = track.phrases.get(*clip) {
|
Some(Some(clip)) => if let Some(phrase) = track.phrases.get(*clip) {
|
||||||
let icon = match track.sequence { Some(_) => "", None => "┊" };
|
let icon = if track.sequence == Some(*clip) { "" } else { "┊" };
|
||||||
format!("{icon} {}", phrase.name)
|
format!("{icon} {}", phrase.name)
|
||||||
} else {
|
} else {
|
||||||
format!(" ??? ")
|
format!(" ??? ")
|
||||||
}
|
},
|
||||||
} else {
|
_ => format!("┊ ········")
|
||||||
format!("┊ ········")
|
|
||||||
};
|
};
|
||||||
let hi = (track_index + 1 == self.cursor.0) && (scene_index + 1 == self.cursor.1);
|
let hi = (track_index + 1 == self.cursor.0) && (scene_index + 1 == self.cursor.1);
|
||||||
let style = Some(Nord::style_hi(self.focused, hi));
|
let style = Some(Nord::style_hi(self.focused, hi));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
use crate::core::*;
|
use crate::{core::*, model::*, view::*};
|
||||||
use crate::model::*;
|
|
||||||
use crate::view::{*, Direction};
|
|
||||||
|
|
||||||
pub struct ChainView<'a> {
|
pub struct ChainView<'a> {
|
||||||
pub track: Option<&'a Track>,
|
pub track: Option<&'a Track>,
|
||||||
|
|
@ -58,42 +56,3 @@ impl<'a> Render for ChainView<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Renderables<'a> = &'a [JackDevice];
|
|
||||||
|
|
||||||
impl<'a> Direction {
|
|
||||||
fn split_focus (&self, index: usize, items: Renderables<'a>, style: Style) -> SplitFocus<'a> {
|
|
||||||
SplitFocus(*self, index, items, style)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SplitFocus<'a>(pub Direction, pub usize, pub Renderables<'a>, pub Style);
|
|
||||||
|
|
||||||
impl<'a> Render for SplitFocus<'a> {
|
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
|
||||||
let Rect { mut x, mut y, mut width, mut height } = area;
|
|
||||||
let mut results = vec![];
|
|
||||||
for item in self.2.iter() {
|
|
||||||
if width == 0 || height == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
let result = item.render(buf, Rect { x, y, width, height })?;
|
|
||||||
results.push(result);
|
|
||||||
match self.0 {
|
|
||||||
Direction::Down => {
|
|
||||||
y = y + result.height;
|
|
||||||
height = height.saturating_sub(result.height);
|
|
||||||
},
|
|
||||||
Direction::Right => {
|
|
||||||
x = x + result.width;
|
|
||||||
width = width.saturating_sub(result.width);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
results
|
|
||||||
.get(self.1)
|
|
||||||
.map(|focused|Lozenge(self.3).draw(buf, Rect { width, ..*focused }))
|
|
||||||
.transpose()?;
|
|
||||||
Ok(area)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
66
src/view/linebuf.rs
Normal file
66
src/view/linebuf.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
pub struct LineBuffer {
|
||||||
|
width: usize,
|
||||||
|
cells: Vec<Cell>,
|
||||||
|
style: Option<Style>,
|
||||||
|
bg: Cell,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineBuffer {
|
||||||
|
pub fn new (bg: Cell, width: usize, height: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
style: None,
|
||||||
|
width: width,
|
||||||
|
cells: vec![bg.clone();width*height],
|
||||||
|
bg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn height (&self) -> usize {
|
||||||
|
self.cells.len() / self.width
|
||||||
|
}
|
||||||
|
pub fn style (&mut self, style: Style) -> &mut Self {
|
||||||
|
self.style = Some(style);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn no_style (&mut self) -> &mut Self {
|
||||||
|
self.style = None;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn put (&mut self, data: &str, x: usize, y: usize) -> &mut Self {
|
||||||
|
if x < self.width {
|
||||||
|
for (i, c) in data.chars().enumerate() {
|
||||||
|
if x + i >= self.width {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let index = y * self.width + x + i;
|
||||||
|
while index >= self.cells.len() {
|
||||||
|
self.cells.extend_from_slice(&vec![self.bg.clone();self.width]);
|
||||||
|
}
|
||||||
|
self.cells[index].set_char(c);
|
||||||
|
if let Some(s) = self.style {
|
||||||
|
self.cells[index].set_style(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn show (&self, buf: &mut Buffer, area: Rect, offset: isize) -> Usually<Rect> {
|
||||||
|
let Rect { x, mut y, width, height } = area;
|
||||||
|
for row in offset..self.height() as isize {
|
||||||
|
let length = self.cells.len();
|
||||||
|
let start = ((row.max(0) as usize)*self.width).min(length);
|
||||||
|
let end = (((row + 1).max(0) as usize)*self.width).min(length);
|
||||||
|
for (column, cell) in self.cells[start..end].iter().enumerate() {
|
||||||
|
if column >= width as usize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
*buf.get_mut(x + column as u16, y + row as u16) = cell.clone();
|
||||||
|
}
|
||||||
|
y = y + 1;
|
||||||
|
if y > height {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(area)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
use crate::{core::*, view::*};
|
||||||
|
|
||||||
|
pub struct If<'a>(pub bool, pub &'a (dyn Render + Sync));
|
||||||
|
|
||||||
|
impl<'a> Render for If<'a> {
|
||||||
|
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
|
match self.0 {
|
||||||
|
true => self.1 as &dyn Render,
|
||||||
|
false => &() as &dyn Render
|
||||||
|
}.render(buf, area)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IfElse<'a>(pub bool, pub &'a (dyn Render + Sync), pub &'a (dyn Render + Sync));
|
||||||
|
|
||||||
|
impl<'a> Render for IfElse<'a> {
|
||||||
|
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
|
match self.0 {
|
||||||
|
true => self.1 as &dyn Render,
|
||||||
|
false => &() as &dyn Render
|
||||||
|
}.render(buf, area)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Direction { Down, Right }
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
pub fn split <'a, const N: usize> (&self, items: [&'a (dyn Render + Sync);N]) -> Split<'a, N> {
|
||||||
|
Split(*self, items)
|
||||||
|
}
|
||||||
|
pub fn split_focus <'a> (&self, index: usize, items: Renderables<'a>, style: Style) -> SplitFocus<'a> {
|
||||||
|
SplitFocus(*self, index, items, style)
|
||||||
|
}
|
||||||
|
pub fn is_down (&self) -> bool {
|
||||||
|
match self { Self::Down => true, _ => false }
|
||||||
|
}
|
||||||
|
pub fn is_right (&self) -> bool {
|
||||||
|
match self { Self::Right => true, _ => false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Split<'a, const N: usize>(
|
||||||
|
pub Direction, pub [&'a (dyn Render + Sync);N]
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<'a, const N: usize> Split<'a, N> {
|
||||||
|
pub fn down (items: [&'a (dyn Render + Sync);N]) -> Self {
|
||||||
|
Self(Direction::Down, items)
|
||||||
|
}
|
||||||
|
pub fn right (items: [&'a (dyn Render + Sync);N]) -> Self {
|
||||||
|
Self(Direction::Right, items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, const N: usize> Render for Split<'a, N> {
|
||||||
|
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
|
let Rect { mut x, mut y, mut width, mut height } = area;
|
||||||
|
for item in self.1 {
|
||||||
|
if width == 0 || height == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let result = item.render(buf, Rect { x, y, width, height })?;
|
||||||
|
match self.0 {
|
||||||
|
Direction::Down => {
|
||||||
|
y = y + result.height;
|
||||||
|
height = height.saturating_sub(result.height);
|
||||||
|
},
|
||||||
|
Direction::Right => {
|
||||||
|
x = x + result.width;
|
||||||
|
width = width.saturating_sub(result.width);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(area)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Renderables<'a> = &'a [JackDevice];
|
||||||
|
|
||||||
|
pub struct SplitFocus<'a>(pub Direction, pub usize, pub Renderables<'a>, pub Style);
|
||||||
|
|
||||||
|
impl<'a> Render for SplitFocus<'a> {
|
||||||
|
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
|
let Rect { mut x, mut y, mut width, mut height } = area;
|
||||||
|
let mut results = vec![];
|
||||||
|
for item in self.2.iter() {
|
||||||
|
if width == 0 || height == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let result = item.render(buf, Rect { x, y, width, height })?;
|
||||||
|
results.push(result);
|
||||||
|
match self.0 {
|
||||||
|
Direction::Down => {
|
||||||
|
y = y + result.height;
|
||||||
|
height = height.saturating_sub(result.height);
|
||||||
|
},
|
||||||
|
Direction::Right => {
|
||||||
|
x = x + result.width;
|
||||||
|
width = width.saturating_sub(result.width);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results
|
||||||
|
.get(self.1)
|
||||||
|
.map(|focused|Lozenge(self.3).draw(buf, Rect { width, ..*focused }))
|
||||||
|
.transpose()?;
|
||||||
|
Ok(area)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue