mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: component playground; Align primitive
This commit is contained in:
parent
4cca03352a
commit
5fc7da3aca
12 changed files with 181 additions and 42 deletions
|
|
@ -1,10 +1,10 @@
|
|||
use crate::*;
|
||||
|
||||
/// A UI component.
|
||||
pub trait Component<E: Engine>: Render<E> + Handle<E> {}
|
||||
pub trait Component<E: Engine>: Render<E> + Handle<E> + Layout<E> {}
|
||||
|
||||
/// Everything that implements [Render] and [Handle] is a [Component].
|
||||
impl<E: Engine, C: Render<E> + Handle<E>> Component<E> for C {}
|
||||
impl<E: Engine, C: Render<E> + Handle<E> + Layout<E>> Component<E> for C {}
|
||||
|
||||
/// Marker trait for [Component]s that can [Exit]
|
||||
pub trait ExitableComponent<E>: Exit + Component<E> where E: Engine {
|
||||
|
|
|
|||
|
|
@ -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<E: Engine>: Render<E> {
|
||||
fn layout (&self, area: E::Area) -> Perhaps<E::Area>;
|
||||
}
|
||||
|
||||
impl<E: Engine, T: Layout<E>> Layout<E> for &T {
|
||||
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
|
||||
(*self).layout(area)
|
||||
|
|
@ -29,6 +28,8 @@ impl<E: Engine, T: Layout<E>> Layout<E> for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Override X and Y coordinates, aligning to corner, side, or center of area
|
||||
pub enum Align<L> { 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<U: Number, L> { W(U, L), H(U, L), WH(U, U, L), }
|
||||
/// Enforce maximum size of drawing area
|
||||
|
|
|
|||
|
|
@ -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<T> = Result<Option<T>, Box<dyn Error>>;
|
|||
pub trait Number: Send + Sync + Copy
|
||||
+ Add<Self, Output=Self>
|
||||
+ Sub<Self, Output=Self>
|
||||
+ Div<Self, Output=Self>
|
||||
+ Ord + PartialEq + Eq
|
||||
+ Debug + Display {}
|
||||
|
||||
|
|
@ -55,6 +56,7 @@ impl<T> Number for T where
|
|||
T: Send + Sync + Copy
|
||||
+ Add<Self, Output=Self>
|
||||
+ Sub<Self, Output=Self>
|
||||
+ Div<Self, Output=Self>
|
||||
+ Ord + PartialEq + Eq
|
||||
+ Debug + Display
|
||||
{}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -72,3 +72,56 @@ impl<'a> Split<'a, Tui> {
|
|||
}, areas))
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Layout<Tui>> Layout<Tui> for Align<L> where Self: Render<Tui> {
|
||||
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<R: Render<Tui> + Layout<Tui>> Render<Tui> for Align<R> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,3 +211,9 @@ pub const KEYMAP_PLUGIN: &'static [KeyBinding<Plugin>] = keymap!(Plugin {
|
|||
Ok(true)
|
||||
}],
|
||||
});
|
||||
|
||||
impl Layout<Tui> for Plugin {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,27 +28,27 @@ impl Render<Tui> 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()
|
||||
|
|
|
|||
|
|
@ -143,3 +143,9 @@ impl Sampler {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout<Tui> for Sampler {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
use crate::*;
|
||||
use tek_core::Direction;
|
||||
|
||||
impl Layout<Tui> for Track<Tui> {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
impl Render<Tui> for Track<Tui> {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||
TrackView {
|
||||
|
|
@ -32,30 +37,31 @@ pub struct TrackView<'a, E: Engine> {
|
|||
}
|
||||
impl<'a> Render<Tui> 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))
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,3 +99,8 @@ impl Exit for ArrangerRenameModal {
|
|||
self.done = true
|
||||
}
|
||||
}
|
||||
impl Layout<Tui> for ArrangerRenameModal {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
crates/tek_test/Cargo.toml
Normal file
9
crates/tek_test/Cargo.toml
Normal file
|
|
@ -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" }
|
||||
50
crates/tek_test/src/main.rs
Normal file
50
crates/tek_test/src/main.rs
Normal file
|
|
@ -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<Box<dyn Component<Tui>>>
|
||||
}
|
||||
|
||||
impl Demo {
|
||||
fn new () -> Self {
|
||||
let items = vec![];
|
||||
Self { index: 0, items }
|
||||
}
|
||||
}
|
||||
|
||||
impl Handle<Tui> for Demo {
|
||||
fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
|
||||
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<Tui> for Demo {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
Align::Center(self.items[self.index]).layout(area)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render<Tui> for Demo {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||
Align::Center(self.items[self.index]).render(to)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue