From 5fc7da3aca5540baa55d16d8ec5cb17e8e6f0475 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 7 Sep 2024 20:54:49 +0300 Subject: [PATCH] wip: component playground; Align primitive --- crates/tek_core/src/engine/component.rs | 4 +- crates/tek_core/src/engine/layout.rs | 15 +++--- crates/tek_core/src/lib.rs | 4 +- crates/tek_core/src/tui.rs | 1 + crates/tek_core/src/tui/tui_layout.rs | 53 +++++++++++++++++++ crates/tek_mixer/src/plugin.rs | 6 +++ crates/tek_mixer/src/sample_add.rs | 14 +++--- crates/tek_mixer/src/sampler.rs | 6 +++ crates/tek_mixer/src/track_view.rs | 56 ++++++++++++--------- crates/tek_sequencer/src/arranger_rename.rs | 5 ++ crates/tek_test/Cargo.toml | 9 ++++ crates/tek_test/src/main.rs | 50 ++++++++++++++++++ 12 files changed, 181 insertions(+), 42 deletions(-) create mode 100644 crates/tek_test/Cargo.toml create mode 100644 crates/tek_test/src/main.rs diff --git a/crates/tek_core/src/engine/component.rs b/crates/tek_core/src/engine/component.rs index 71087bf1..cbd99c42 100644 --- a/crates/tek_core/src/engine/component.rs +++ b/crates/tek_core/src/engine/component.rs @@ -1,10 +1,10 @@ use crate::*; /// A UI component. -pub trait Component: Render + Handle {} +pub trait Component: Render + Handle + Layout {} /// Everything that implements [Render] and [Handle] is a [Component]. -impl + Handle> Component for C {} +impl + Handle + Layout> Component for C {} /// Marker trait for [Component]s that can [Exit] pub trait ExitableComponent: Exit + Component where E: Engine { diff --git a/crates/tek_core/src/engine/layout.rs b/crates/tek_core/src/engine/layout.rs index 39f929a9..d647db5d 100644 --- a/crates/tek_core/src/engine/layout.rs +++ b/crates/tek_core/src/engine/layout.rs @@ -2,19 +2,18 @@ use crate::*; // TODO: Convert to component // pub enum Align { Center, NW, N, NE, E, SE, S, SW, W, } -pub fn center_box (area: Rect, w: u16, h: u16) -> Rect { - let width = w.min(area.width * 3 / 5); - let height = h.min(area.width * 3 / 5); - let x = area.x + (area.width - width) / 2; - let y = area.y + (area.height - height) / 2; - Rect { x, y, width, height } +pub fn center_box (area: [u16;4], w: u16, h: u16) -> [u16;4] { + let width = w.min(area.w() * 3 / 5); + let height = h.min(area.w() * 3 / 5); + let x = area.x() + (area.w() - width) / 2; + let y = area.y() + (area.h() - height) / 2; + [x, y, width, height] } /// Trait for structs that compute drawing area before rendering pub trait Layout: Render { fn layout (&self, area: E::Area) -> Perhaps; } - impl> Layout for &T { fn layout (&self, area: E::Area) -> Perhaps { (*self).layout(area) @@ -29,6 +28,8 @@ impl> Layout for Option { } } +/// Override X and Y coordinates, aligning to corner, side, or center of area +pub enum Align { Center(L), NW(L), N(L), NE(L), W(L), E(L), SW(L), S(L), SE(L) } /// Enforce minimum size of drawing area pub enum Min { W(U, L), H(U, L), WH(U, U, L), } /// Enforce maximum size of drawing area diff --git a/crates/tek_core/src/lib.rs b/crates/tek_core/src/lib.rs index 55dcab69..0ba950a9 100644 --- a/crates/tek_core/src/lib.rs +++ b/crates/tek_core/src/lib.rs @@ -15,7 +15,7 @@ pub(crate) use std::thread::{spawn, JoinHandle}; pub(crate) use std::time::Duration; pub(crate) use atomic_float::*; use better_panic::{Settings, Verbosity}; -use std::ops::{Add, Sub}; +use std::ops::{Add, Sub, Div}; use std::cmp::{Ord, Eq, PartialEq}; use std::fmt::{Debug, Display}; @@ -48,6 +48,7 @@ pub type Perhaps = Result, Box>; pub trait Number: Send + Sync + Copy + Add + Sub + + Div + Ord + PartialEq + Eq + Debug + Display {} @@ -55,6 +56,7 @@ impl Number for T where T: Send + Sync + Copy + Add + Sub + + Div + Ord + PartialEq + Eq + Debug + Display {} diff --git a/crates/tek_core/src/tui.rs b/crates/tek_core/src/tui.rs index ea41b212..28ad2ab8 100644 --- a/crates/tek_core/src/tui.rs +++ b/crates/tek_core/src/tui.rs @@ -36,6 +36,7 @@ impl Engine for Tui { let better_panic_handler = Settings::auto().verbosity(Verbosity::Full).create_panic_handler(); std::panic::set_hook(Box::new(move |info: &std::panic::PanicInfo|{ stdout().execute(LeaveAlternateScreen).unwrap(); + CrosstermBackend::new(stdout()).show_cursor().unwrap(); disable_raw_mode().unwrap(); better_panic_handler(info); })); diff --git a/crates/tek_core/src/tui/tui_layout.rs b/crates/tek_core/src/tui/tui_layout.rs index 1612b63a..ba11ee85 100644 --- a/crates/tek_core/src/tui/tui_layout.rs +++ b/crates/tek_core/src/tui/tui_layout.rs @@ -72,3 +72,56 @@ impl<'a> Split<'a, Tui> { }, areas)) } } + +impl> Layout for Align where Self: Render { + fn layout (&self, outer_area: [u16;4]) -> Perhaps<[u16;4]> { + Ok(match self { + Self::Center(inner) => inner, + Self::NW(inner) => inner, + Self::N(inner) => inner, + Self::NE(inner) => inner, + Self::W(inner) => inner, + Self::E(inner) => inner, + Self::SW(inner) => inner, + Self::S(inner) => inner, + Self::SE(inner) => inner, + } + .layout(outer_area)? + .map(|inner_area|match self { + Self::Center(_) => { + let [_, _, w, h] = inner_area.xywh(); + let offset_x = (outer_area.w() - w) / 2; + let offset_y = (outer_area.h() - h) / 2; + [outer_area.x() + offset_x, outer_area.y() + offset_y, w, h] + }, + Self::NW(_) => { todo!() }, + Self::N(_) => { todo!() }, + Self::NE(_) => { todo!() }, + Self::W(_) => { todo!() }, + Self::E(_) => { todo!() }, + Self::SW(_) => { todo!() }, + Self::S(_) => { todo!() }, + Self::SE(_) => { todo!() }, + })) + } +} + +impl + Layout> Render for Align { + fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { + self.layout(to.area())? + .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) + .map(|to|match self { + Self::Center(inner) => inner, + Self::NW(inner) => inner, + Self::N(inner) => inner, + Self::NE(inner) => inner, + Self::W(inner) => inner, + Self::E(inner) => inner, + Self::SW(inner) => inner, + Self::S(inner) => inner, + Self::SE(inner) => inner, + }.render(to)) + .transpose() + .map(|x|x.flatten()) + } +} diff --git a/crates/tek_mixer/src/plugin.rs b/crates/tek_mixer/src/plugin.rs index d0241dbe..f1f15a47 100644 --- a/crates/tek_mixer/src/plugin.rs +++ b/crates/tek_mixer/src/plugin.rs @@ -211,3 +211,9 @@ pub const KEYMAP_PLUGIN: &'static [KeyBinding] = keymap!(Plugin { Ok(true) }], }); + +impl Layout for Plugin { + fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { + todo!() + } +} diff --git a/crates/tek_mixer/src/sample_add.rs b/crates/tek_mixer/src/sample_add.rs index 9da0e766..29657d3f 100644 --- a/crates/tek_mixer/src/sample_add.rs +++ b/crates/tek_mixer/src/sample_add.rs @@ -28,27 +28,27 @@ impl Render for AddSampleModal { to.make_dim(); let area = center_box( area, - 64.max(area.width.saturating_sub(8)), - 20.max(area.width.saturating_sub(8)), + 64.max(area.w().saturating_sub(8)), + 20.max(area.w().saturating_sub(8)), ); to.fill_fg(area, Color::Reset); to.fill_bg(area, Nord::bg_lo(true, true)); to.fill_char(area, ' '); - to.blit(&format!("{}", &self.dir.to_string_lossy()), area.x+2, area.y+1, Some(Style::default().bold()))?; - to.blit(&"Select sample:", area.x+2, area.y+2, Some(Style::default().bold()))?; + to.blit(&format!("{}", &self.dir.to_string_lossy()), area.x()+2, area.y()+1, Some(Style::default().bold()))?; + to.blit(&"Select sample:", area.x()+2, area.y()+2, Some(Style::default().bold()))?; for (i, (is_dir, name)) in self.subdirs.iter() .map(|path|(true, path)) .chain(self.files.iter().map(|path|(false, path))) .enumerate() .skip(self.offset) { - if i >= area.height as usize - 4 { + if i >= area.h() as usize - 4 { break } let t = if is_dir { "" } else { "" }; let line = format!("{t} {}", name.to_string_lossy()); - let line = &line[..line.len().min(area.width as usize - 4)]; - to.blit(&line, area.x + 2, area.y + 3 + i as u16, Some(if i == self.cursor { + let line = &line[..line.len().min(area.w() as usize - 4)]; + to.blit(&line, area.x() + 2, area.y() + 3 + i as u16, Some(if i == self.cursor { Style::default().green() } else { Style::default().white() diff --git a/crates/tek_mixer/src/sampler.rs b/crates/tek_mixer/src/sampler.rs index 675582e7..bda32d27 100644 --- a/crates/tek_mixer/src/sampler.rs +++ b/crates/tek_mixer/src/sampler.rs @@ -143,3 +143,9 @@ impl Sampler { } } } + +impl Layout for Sampler { + fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { + todo!() + } +} diff --git a/crates/tek_mixer/src/track_view.rs b/crates/tek_mixer/src/track_view.rs index 65884e49..eb26cc8b 100644 --- a/crates/tek_mixer/src/track_view.rs +++ b/crates/tek_mixer/src/track_view.rs @@ -1,6 +1,11 @@ use crate::*; use tek_core::Direction; +impl Layout for Track { + fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { + todo!() + } +} impl Render for Track { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { TrackView { @@ -32,30 +37,31 @@ pub struct TrackView<'a, E: Engine> { } impl<'a> Render for TrackView<'a, Tui> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { - let mut area = to.area(); - if let Some(chain) = self.chain { - match self.direction { - Direction::Down => area.width = area.width.min(40), - Direction::Right => area.width = area.width.min(10), - _ => { unimplemented!() }, - } - to.fill_bg(to.area(), Nord::bg_lo(self.focused, self.entered)); - let mut split = Split::new(self.direction); - for device in chain.devices.as_slice().iter() { - split = split.add_ref(device); - } - let (area, areas) = split.render_areas(to)?; - if self.focused && self.entered && areas.len() > 0 { - Corners(Style::default().green().not_dim()).draw(to.with_rect(areas[0]))?; - } - Ok(Some(area)) - } else { - let [x, y, width, height] = area; - let label = "No chain selected"; - let x = x + (width - label.len() as u16) / 2; - let y = y + height / 2; - to.blit(&label, x, y, Some(Style::default().dim().bold()))?; - Ok(Some(area)) - } + todo!(); + //let mut area = to.area(); + //if let Some(chain) = self.chain { + //match self.direction { + //Direction::Down => area.width = area.width.min(40), + //Direction::Right => area.width = area.width.min(10), + //_ => { unimplemented!() }, + //} + //to.fill_bg(to.area(), Nord::bg_lo(self.focused, self.entered)); + //let mut split = Split::new(self.direction); + //for device in chain.devices.as_slice().iter() { + //split = split.add_ref(device); + //} + //let (area, areas) = split.render_areas(to)?; + //if self.focused && self.entered && areas.len() > 0 { + //Corners(Style::default().green().not_dim()).draw(to.with_rect(areas[0]))?; + //} + //Ok(Some(area)) + //} else { + //let [x, y, width, height] = area; + //let label = "No chain selected"; + //let x = x + (width - label.len() as u16) / 2; + //let y = y + height / 2; + //to.blit(&label, x, y, Some(Style::default().dim().bold()))?; + //Ok(Some(area)) + //} } } diff --git a/crates/tek_sequencer/src/arranger_rename.rs b/crates/tek_sequencer/src/arranger_rename.rs index 9df36554..62f82aa6 100644 --- a/crates/tek_sequencer/src/arranger_rename.rs +++ b/crates/tek_sequencer/src/arranger_rename.rs @@ -99,3 +99,8 @@ impl Exit for ArrangerRenameModal { self.done = true } } +impl Layout for ArrangerRenameModal { + fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { + todo!() + } +} diff --git a/crates/tek_test/Cargo.toml b/crates/tek_test/Cargo.toml new file mode 100644 index 00000000..47ccc134 --- /dev/null +++ b/crates/tek_test/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "tek_test" +edition = "2021" +version = "0.1.0" + +[dependencies] +tek_core = { path = "../tek_core" } +tek_mixer = { path = "../tek_mixer" } +tek_sequencer = { path = "../tek_sequencer" } diff --git a/crates/tek_test/src/main.rs b/crates/tek_test/src/main.rs new file mode 100644 index 00000000..cd210778 --- /dev/null +++ b/crates/tek_test/src/main.rs @@ -0,0 +1,50 @@ +use tek_core::*; + +pub fn main () -> Usually<()> { + Tui::run(Arc::new(RwLock::new(Demo::new())))?; + Ok(()) +} + +pub struct Demo { + index: usize, + items: Vec>> +} + +impl Demo { + fn new () -> Self { + let items = vec![]; + Self { index: 0, items } + } +} + +impl Handle for Demo { + fn handle (&mut self, from: &Tui) -> Perhaps { + match from.event() { + key!(KeyCode::PageUp) => { + self.index = (self.index + 1) % self.items.len(); + Ok(Some(true)) + }, + key!(KeyCode::PageDown) => { + self.index = if self.index > 1 { + self.index - 1 + } else { + self.items.len() - 1 + }; + Ok(Some(true)) + }, + _ => Ok(None) + } + } +} + +impl Layout for Demo { + fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { + Align::Center(self.items[self.index]).layout(area) + } +} + +impl Render for Demo { + fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { + Align::Center(self.items[self.index]).render(to) + } +}