mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16: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::*;
|
use crate::*;
|
||||||
|
|
||||||
/// A UI component.
|
/// 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].
|
/// 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]
|
/// Marker trait for [Component]s that can [Exit]
|
||||||
pub trait ExitableComponent<E>: Exit + Component<E> where E: Engine {
|
pub trait ExitableComponent<E>: Exit + Component<E> where E: Engine {
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,18 @@ use crate::*;
|
||||||
|
|
||||||
// TODO: Convert to component
|
// TODO: Convert to component
|
||||||
// pub enum Align { Center, NW, N, NE, E, SE, S, SW, W, }
|
// pub enum Align { Center, NW, N, NE, E, SE, S, SW, W, }
|
||||||
pub fn center_box (area: Rect, w: u16, h: u16) -> Rect {
|
pub fn center_box (area: [u16;4], w: u16, h: u16) -> [u16;4] {
|
||||||
let width = w.min(area.width * 3 / 5);
|
let width = w.min(area.w() * 3 / 5);
|
||||||
let height = h.min(area.width * 3 / 5);
|
let height = h.min(area.w() * 3 / 5);
|
||||||
let x = area.x + (area.width - width) / 2;
|
let x = area.x() + (area.w() - width) / 2;
|
||||||
let y = area.y + (area.height - height) / 2;
|
let y = area.y() + (area.h() - height) / 2;
|
||||||
Rect { x, y, width, height }
|
[x, y, width, height]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for structs that compute drawing area before rendering
|
/// Trait for structs that compute drawing area before rendering
|
||||||
pub trait Layout<E: Engine>: Render<E> {
|
pub trait Layout<E: Engine>: Render<E> {
|
||||||
fn layout (&self, area: E::Area) -> Perhaps<E::Area>;
|
fn layout (&self, area: E::Area) -> Perhaps<E::Area>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, T: Layout<E>> Layout<E> for &T {
|
impl<E: Engine, T: Layout<E>> Layout<E> for &T {
|
||||||
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
|
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
|
||||||
(*self).layout(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
|
/// Enforce minimum size of drawing area
|
||||||
pub enum Min<U: Number, L> { W(U, L), H(U, L), WH(U, U, L), }
|
pub enum Min<U: Number, L> { W(U, L), H(U, L), WH(U, U, L), }
|
||||||
/// Enforce maximum size of drawing area
|
/// 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 std::time::Duration;
|
||||||
pub(crate) use atomic_float::*;
|
pub(crate) use atomic_float::*;
|
||||||
use better_panic::{Settings, Verbosity};
|
use better_panic::{Settings, Verbosity};
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub, Div};
|
||||||
use std::cmp::{Ord, Eq, PartialEq};
|
use std::cmp::{Ord, Eq, PartialEq};
|
||||||
use std::fmt::{Debug, Display};
|
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
|
pub trait Number: Send + Sync + Copy
|
||||||
+ Add<Self, Output=Self>
|
+ Add<Self, Output=Self>
|
||||||
+ Sub<Self, Output=Self>
|
+ Sub<Self, Output=Self>
|
||||||
|
+ Div<Self, Output=Self>
|
||||||
+ Ord + PartialEq + Eq
|
+ Ord + PartialEq + Eq
|
||||||
+ Debug + Display {}
|
+ Debug + Display {}
|
||||||
|
|
||||||
|
|
@ -55,6 +56,7 @@ impl<T> Number for T where
|
||||||
T: Send + Sync + Copy
|
T: Send + Sync + Copy
|
||||||
+ Add<Self, Output=Self>
|
+ Add<Self, Output=Self>
|
||||||
+ Sub<Self, Output=Self>
|
+ Sub<Self, Output=Self>
|
||||||
|
+ Div<Self, Output=Self>
|
||||||
+ Ord + PartialEq + Eq
|
+ Ord + PartialEq + Eq
|
||||||
+ Debug + Display
|
+ Debug + Display
|
||||||
{}
|
{}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ impl Engine for Tui {
|
||||||
let better_panic_handler = Settings::auto().verbosity(Verbosity::Full).create_panic_handler();
|
let better_panic_handler = Settings::auto().verbosity(Verbosity::Full).create_panic_handler();
|
||||||
std::panic::set_hook(Box::new(move |info: &std::panic::PanicInfo|{
|
std::panic::set_hook(Box::new(move |info: &std::panic::PanicInfo|{
|
||||||
stdout().execute(LeaveAlternateScreen).unwrap();
|
stdout().execute(LeaveAlternateScreen).unwrap();
|
||||||
|
CrosstermBackend::new(stdout()).show_cursor().unwrap();
|
||||||
disable_raw_mode().unwrap();
|
disable_raw_mode().unwrap();
|
||||||
better_panic_handler(info);
|
better_panic_handler(info);
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -72,3 +72,56 @@ impl<'a> Split<'a, Tui> {
|
||||||
}, areas))
|
}, 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)
|
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();
|
to.make_dim();
|
||||||
let area = center_box(
|
let area = center_box(
|
||||||
area,
|
area,
|
||||||
64.max(area.width.saturating_sub(8)),
|
64.max(area.w().saturating_sub(8)),
|
||||||
20.max(area.width.saturating_sub(8)),
|
20.max(area.w().saturating_sub(8)),
|
||||||
);
|
);
|
||||||
to.fill_fg(area, Color::Reset);
|
to.fill_fg(area, Color::Reset);
|
||||||
to.fill_bg(area, Nord::bg_lo(true, true));
|
to.fill_bg(area, Nord::bg_lo(true, true));
|
||||||
to.fill_char(area, ' ');
|
to.fill_char(area, ' ');
|
||||||
to.blit(&format!("{}", &self.dir.to_string_lossy()), area.x+2, area.y+1, 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()))?;
|
to.blit(&"Select sample:", area.x()+2, area.y()+2, Some(Style::default().bold()))?;
|
||||||
for (i, (is_dir, name)) in self.subdirs.iter()
|
for (i, (is_dir, name)) in self.subdirs.iter()
|
||||||
.map(|path|(true, path))
|
.map(|path|(true, path))
|
||||||
.chain(self.files.iter().map(|path|(false, path)))
|
.chain(self.files.iter().map(|path|(false, path)))
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.skip(self.offset)
|
.skip(self.offset)
|
||||||
{
|
{
|
||||||
if i >= area.height as usize - 4 {
|
if i >= area.h() as usize - 4 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
let t = if is_dir { "" } else { "" };
|
let t = if is_dir { "" } else { "" };
|
||||||
let line = format!("{t} {}", name.to_string_lossy());
|
let line = format!("{t} {}", name.to_string_lossy());
|
||||||
let line = &line[..line.len().min(area.width as usize - 4)];
|
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 {
|
to.blit(&line, area.x() + 2, area.y() + 3 + i as u16, Some(if i == self.cursor {
|
||||||
Style::default().green()
|
Style::default().green()
|
||||||
} else {
|
} else {
|
||||||
Style::default().white()
|
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 crate::*;
|
||||||
use tek_core::Direction;
|
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> {
|
impl Render<Tui> for Track<Tui> {
|
||||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||||
TrackView {
|
TrackView {
|
||||||
|
|
@ -32,30 +37,31 @@ pub struct TrackView<'a, E: Engine> {
|
||||||
}
|
}
|
||||||
impl<'a> Render<Tui> for TrackView<'a, Tui> {
|
impl<'a> Render<Tui> for TrackView<'a, Tui> {
|
||||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||||
let mut area = to.area();
|
todo!();
|
||||||
if let Some(chain) = self.chain {
|
//let mut area = to.area();
|
||||||
match self.direction {
|
//if let Some(chain) = self.chain {
|
||||||
Direction::Down => area.width = area.width.min(40),
|
//match self.direction {
|
||||||
Direction::Right => area.width = area.width.min(10),
|
//Direction::Down => area.width = area.width.min(40),
|
||||||
_ => { unimplemented!() },
|
//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);
|
//to.fill_bg(to.area(), Nord::bg_lo(self.focused, self.entered));
|
||||||
for device in chain.devices.as_slice().iter() {
|
//let mut split = Split::new(self.direction);
|
||||||
split = split.add_ref(device);
|
//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 {
|
//let (area, areas) = split.render_areas(to)?;
|
||||||
Corners(Style::default().green().not_dim()).draw(to.with_rect(areas[0]))?;
|
//if self.focused && self.entered && areas.len() > 0 {
|
||||||
}
|
//Corners(Style::default().green().not_dim()).draw(to.with_rect(areas[0]))?;
|
||||||
Ok(Some(area))
|
//}
|
||||||
} else {
|
//Ok(Some(area))
|
||||||
let [x, y, width, height] = area;
|
//} else {
|
||||||
let label = "No chain selected";
|
//let [x, y, width, height] = area;
|
||||||
let x = x + (width - label.len() as u16) / 2;
|
//let label = "No chain selected";
|
||||||
let y = y + height / 2;
|
//let x = x + (width - label.len() as u16) / 2;
|
||||||
to.blit(&label, x, y, Some(Style::default().dim().bold()))?;
|
//let y = y + height / 2;
|
||||||
Ok(Some(area))
|
//to.blit(&label, x, y, Some(Style::default().dim().bold()))?;
|
||||||
}
|
//Ok(Some(area))
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,3 +99,8 @@ impl Exit for ArrangerRenameModal {
|
||||||
self.done = true
|
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