mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56: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
17
src/core.rs
17
src/core.rs
|
|
@ -148,17 +148,12 @@ pub fn input_thread (
|
|||
// Listen for events and send them to the main thread
|
||||
if ::crossterm::event::poll(poll).is_ok() {
|
||||
let event = ::crossterm::event::read().unwrap();
|
||||
match event {
|
||||
Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('c'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
}) => {
|
||||
exited.store(true, Ordering::Relaxed);
|
||||
},
|
||||
_ => if device.write().unwrap().handle(&AppEvent::Input(event)).is_err() {
|
||||
break
|
||||
}
|
||||
if let Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, ..
|
||||
}) = event {
|
||||
exited.store(true, Ordering::Relaxed);
|
||||
} else if let Err(e) = device.write().unwrap().handle(&AppEvent::Input(event)) {
|
||||
panic!("{e}")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use crate::core::*;
|
||||
pub(crate) use ratatui::prelude::*;
|
||||
pub(crate) use ratatui::buffer::Cell;
|
||||
pub(crate) use ratatui::prelude::CrosstermBackend;
|
||||
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;
|
||||
|
||||
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)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
@ -190,6 +126,5 @@ mod test {
|
|||
assert_eq!(buffer.cells.len(), 12);
|
||||
buffer.put("FOO", 0, 1);
|
||||
assert_eq!(buffer.cells.len(), 24);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{core::*, model::App};
|
||||
use crate::{core::*, model::*};
|
||||
|
||||
impl App {
|
||||
pub fn next_scene (&mut self) {
|
||||
|
|
@ -11,8 +11,11 @@ impl App {
|
|||
format!("Scene {}", self.scenes.len() + 1)
|
||||
}
|
||||
pub fn add_scene (&mut self, name: Option<&str>) -> Usually<&mut Scene> {
|
||||
let name = name.ok_or_else(||self.new_scene_name())?;
|
||||
self.scenes.push(Scene::new(&name, vec![]));
|
||||
let clips = vec![None;self.tracks.len()];
|
||||
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();
|
||||
Ok(&mut self.scenes[self.scene_cursor - 1])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::core::*;
|
||||
use crate::model::*;
|
||||
use crate::{core::*, model::*};
|
||||
|
||||
impl App {
|
||||
pub fn next_track (&mut self) {
|
||||
|
|
@ -8,32 +7,29 @@ impl App {
|
|||
pub fn prev_track (&mut self) {
|
||||
self.track_cursor = self.track_cursor.saturating_sub(1);
|
||||
}
|
||||
|
||||
pub fn new_track_name (&self) -> String {
|
||||
format!("Track {}", self.tracks.len() + 1)
|
||||
}
|
||||
|
||||
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(Track::new(&name, None, None)?);
|
||||
self.tracks.push(match name {
|
||||
Some(name) => Track::new(name, None, None)?,
|
||||
None => Track::new(&self.new_track_name(), None, None)?
|
||||
});
|
||||
self.track_cursor = self.tracks.len();
|
||||
Ok(&mut self.tracks[self.track_cursor - 1])
|
||||
}
|
||||
|
||||
pub fn track (&self) -> Option<(usize, &Track)> {
|
||||
match self.track_cursor { 0 => None, _ => {
|
||||
let id = self.track_cursor as usize - 1;
|
||||
self.tracks.get(id).map(|t|(id, t))
|
||||
} }
|
||||
}
|
||||
|
||||
pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> {
|
||||
match self.track_cursor { 0 => None, _ => {
|
||||
let id = self.track_cursor as usize - 1;
|
||||
self.tracks.get_mut(id).map(|t|(id, t))
|
||||
} }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub struct Track {
|
||||
|
|
|
|||
69
src/view.rs
69
src/view.rs
|
|
@ -5,7 +5,9 @@ pub mod transport;
|
|||
pub mod plugin;
|
||||
pub mod border;
|
||||
pub mod theme;
|
||||
pub mod split;
|
||||
|
||||
pub use self::split::*;
|
||||
pub use self::border::*;
|
||||
pub use self::theme::*;
|
||||
pub use self::arranger::*;
|
||||
|
|
@ -28,70 +30,3 @@ render!(App |self, buf, 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 {
|
||||
break
|
||||
}
|
||||
let label = if let Some(Some(clip)) = scene.clips.get(track_index) {
|
||||
if let Some(phrase) = track.phrases.get(*clip) {
|
||||
let icon = match track.sequence { Some(_) => "", None => "┊" };
|
||||
let label = match scene.clips.get(track_index) {
|
||||
Some(Some(clip)) => if let Some(phrase) = track.phrases.get(*clip) {
|
||||
let icon = if track.sequence == Some(*clip) { "" } else { "┊" };
|
||||
format!("{icon} {}", phrase.name)
|
||||
} else {
|
||||
format!(" ??? ")
|
||||
}
|
||||
} else {
|
||||
format!("┊ ········")
|
||||
},
|
||||
_ => format!("┊ ········")
|
||||
};
|
||||
let hi = (track_index + 1 == self.cursor.0) && (scene_index + 1 == self.cursor.1);
|
||||
let style = Some(Nord::style_hi(self.focused, hi));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use crate::core::*;
|
||||
use crate::model::*;
|
||||
use crate::view::{*, Direction};
|
||||
use crate::{core::*, model::*, view::*};
|
||||
|
||||
pub struct ChainView<'a> {
|
||||
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