mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-07-17 07:56:56 +02:00
drawing and singing fixes
This commit is contained in:
parent
e0781571c4
commit
0273d2ac75
7 changed files with 244 additions and 195 deletions
189
src/sing.rs
189
src/sing.rs
|
|
@ -1,6 +1,9 @@
|
||||||
pub use ::jack::{*, contrib::{*, ClosureProcessHandler}};
|
pub use ::jack::{*, contrib::{*, ClosureProcessHandler}};
|
||||||
use crate::{*, time::PerfModel};
|
use crate::{*, time::PerfModel};
|
||||||
use JackState::*;
|
|
||||||
|
mod jack; pub use self::jack::*;
|
||||||
|
mod jack_event; pub use self::jack_event::*;
|
||||||
|
mod jack_perf; pub use self::jack_perf::*;
|
||||||
|
|
||||||
/// Trait for thing that has a JACK process callback.
|
/// Trait for thing that has a JACK process callback.
|
||||||
pub trait Audio {
|
pub trait Audio {
|
||||||
|
|
@ -22,58 +25,6 @@ pub trait Audio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps [JackState], and through it [jack::Client] when connected.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let jack = tengri::sing::Jack::default();
|
|
||||||
/// ```
|
|
||||||
#[derive(Clone, Debug, Default)] pub struct Jack<'j> (
|
|
||||||
pub(crate) Arc<RwLock<JackState<'j>>>
|
|
||||||
);
|
|
||||||
|
|
||||||
/// This is a connection which may be [Inactive], [Activating], or [Active].
|
|
||||||
/// In the [Active] and [Inactive] states, [JackState::client] returns a
|
|
||||||
/// [jack::Client], which you can use to talk to the JACK API.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let state = tengri::sing::JackState::default();
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug, Default)] pub enum JackState<'j> {
|
|
||||||
/// Unused
|
|
||||||
#[default] Inert,
|
|
||||||
/// Before activation.
|
|
||||||
Inactive(Client),
|
|
||||||
/// During activation.
|
|
||||||
Activating,
|
|
||||||
/// After activation. Must not be dropped for JACK thread to persist.
|
|
||||||
Active(DynamicAsyncClient<'j>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Event enum for JACK events.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let event = tengri::sing::JackEvent::XRun; // kerpop
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug, Clone, PartialEq)] pub enum JackEvent {
|
|
||||||
ThreadInit,
|
|
||||||
Shutdown(ClientStatus, Arc<str>),
|
|
||||||
Freewheel(bool),
|
|
||||||
SampleRate(Frames),
|
|
||||||
ClientRegistration(Arc<str>, bool),
|
|
||||||
PortRegistration(PortId, bool),
|
|
||||||
PortRename(PortId, Arc<str>, Arc<str>),
|
|
||||||
PortsConnected(PortId, PortId, bool),
|
|
||||||
GraphReorder,
|
|
||||||
XRun,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generic notification handler that emits [JackEvent]
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let notify = tengri::sing::JackNotify(|_|{});
|
|
||||||
/// ```
|
|
||||||
pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);
|
|
||||||
|
|
||||||
/// Running JACK [AsyncClient] with maximum type erasure.
|
/// Running JACK [AsyncClient] with maximum type erasure.
|
||||||
///
|
///
|
||||||
/// One [Box] contains function that handles [JackEvent]s.
|
/// One [Box] contains function that handles [JackEvent]s.
|
||||||
|
|
@ -92,134 +43,6 @@ pub type DynamicAudioHandler<'j> =
|
||||||
pub type BoxedAudioHandler<'j> =
|
pub type BoxedAudioHandler<'j> =
|
||||||
Box<dyn FnMut(&Client, &ProcessScope) -> Control + Send + Sync + 'j>;
|
Box<dyn FnMut(&Client, &ProcessScope) -> Control + Send + Sync + 'j>;
|
||||||
|
|
||||||
/// Notification handler wrapper for [BoxedJackEventHandler].
|
|
||||||
pub type DynamicNotifications<'j> =
|
|
||||||
JackNotify<BoxedJackEventHandler<'j>>;
|
|
||||||
|
|
||||||
/// Boxed [JackEvent] callback.
|
|
||||||
pub type BoxedJackEventHandler<'j> =
|
|
||||||
Box<dyn Fn(JackEvent) + Send + Sync + 'j>;
|
|
||||||
|
|
||||||
impl<'j> HasJack<'j> for Jack<'j> { fn jack (&self) -> &Jack<'j> { self } }
|
|
||||||
|
|
||||||
impl<'j> HasJack<'j> for &Jack<'j> { fn jack (&self) -> &Jack<'j> { self } }
|
|
||||||
|
|
||||||
/// Implement [Jack] constructor and methods
|
|
||||||
impl<'j> Jack<'j> {
|
|
||||||
/// Register new [Client] and wrap it for shared use.
|
|
||||||
pub fn new_run <T: HasJack<'j> + Audio + Send + Sync + 'static> (
|
|
||||||
name: &impl AsRef<str>,
|
|
||||||
init: impl FnOnce(Jack<'j>)->Usually<T>
|
|
||||||
) -> Usually<Arc<RwLock<T>>> {
|
|
||||||
Jack::new(name)?.run(init)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new (name: &impl AsRef<str>) -> Usually<Self> {
|
|
||||||
let client = Client::new(name.as_ref(), ClientOptions::NO_START_SERVER)?.0;
|
|
||||||
Ok(Jack(Arc::new(RwLock::new(JackState::Inactive(client)))))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run <T: HasJack<'j> + Audio + Send + Sync + 'static>
|
|
||||||
(self, init: impl FnOnce(Self)->Usually<T>) -> Usually<Arc<RwLock<T>>>
|
|
||||||
{
|
|
||||||
let client_state = self.0.clone();
|
|
||||||
let app: Arc<RwLock<T>> = Arc::new(RwLock::new(init(self)?));
|
|
||||||
let mut state = Activating;
|
|
||||||
std::mem::swap(&mut*client_state.write().unwrap(), &mut state);
|
|
||||||
if let Inactive(client) = state {
|
|
||||||
// This is the misc notifications handler. It's a struct that wraps a [Box]
|
|
||||||
// which performs type erasure on a callback that takes [JackEvent], which is
|
|
||||||
// one of the available misc notifications.
|
|
||||||
let notify = JackNotify(Box::new({
|
|
||||||
let app = app.clone();
|
|
||||||
move|event|(&mut*app.write().unwrap()).handle(event)
|
|
||||||
}) as BoxedJackEventHandler);
|
|
||||||
// This is the main processing handler. It's a struct that wraps a [Box]
|
|
||||||
// which performs type erasure on a callback that takes [Client] and [ProcessScope]
|
|
||||||
// and passes them down to the `app`'s `process` callback, which in turn
|
|
||||||
// implements audio and MIDI input and output on a realtime basis.
|
|
||||||
let process = ::jack::contrib::ClosureProcessHandler::new(Box::new({
|
|
||||||
let app = app.clone();
|
|
||||||
move|c: &_, s: &_|if let Ok(mut app) = app.write() {
|
|
||||||
app.process(c, s)
|
|
||||||
} else {
|
|
||||||
Control::Quit
|
|
||||||
}
|
|
||||||
}) as BoxedAudioHandler);
|
|
||||||
// Launch a client with the two handlers.
|
|
||||||
*client_state.write().unwrap() = Active(
|
|
||||||
client.activate_async(notify, process)?
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
Ok(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run something with the client.
|
|
||||||
pub fn with_client <T> (&self, op: impl FnOnce(&Client)->T) -> T {
|
|
||||||
match &*self.0.read().unwrap() {
|
|
||||||
Inert => panic!("jack client not activated"),
|
|
||||||
Inactive(client) => op(client),
|
|
||||||
Activating => panic!("jack client has not finished activation"),
|
|
||||||
Active(client) => op(client.as_client()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Fn(JackEvent) + Send> NotificationHandler for JackNotify<T> {
|
|
||||||
fn thread_init(&self, _: &Client) {
|
|
||||||
self.0(JackEvent::ThreadInit);
|
|
||||||
}
|
|
||||||
unsafe fn shutdown(&mut self, status: ClientStatus, reason: &str) {
|
|
||||||
self.0(JackEvent::Shutdown(status, reason.into()));
|
|
||||||
}
|
|
||||||
fn freewheel(&mut self, _: &Client, enabled: bool) {
|
|
||||||
self.0(JackEvent::Freewheel(enabled));
|
|
||||||
}
|
|
||||||
fn sample_rate(&mut self, _: &Client, frames: Frames) -> Control {
|
|
||||||
self.0(JackEvent::SampleRate(frames));
|
|
||||||
Control::Quit
|
|
||||||
}
|
|
||||||
fn client_registration(&mut self, _: &Client, name: &str, reg: bool) {
|
|
||||||
self.0(JackEvent::ClientRegistration(name.into(), reg));
|
|
||||||
}
|
|
||||||
fn port_registration(&mut self, _: &Client, id: PortId, reg: bool) {
|
|
||||||
self.0(JackEvent::PortRegistration(id, reg));
|
|
||||||
}
|
|
||||||
fn port_rename(&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control {
|
|
||||||
self.0(JackEvent::PortRename(id, old.into(), new.into()));
|
|
||||||
Control::Continue
|
|
||||||
}
|
|
||||||
fn ports_connected(&mut self, _: &Client, a: PortId, b: PortId, are: bool) {
|
|
||||||
self.0(JackEvent::PortsConnected(a, b, are));
|
|
||||||
}
|
|
||||||
fn graph_reorder(&mut self, _: &Client) -> Control {
|
|
||||||
self.0(JackEvent::GraphReorder);
|
|
||||||
Control::Continue
|
|
||||||
}
|
|
||||||
fn xrun(&mut self, _: &Client) -> Control {
|
|
||||||
self.0(JackEvent::XRun);
|
|
||||||
Control::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JackPerfModel for PerfModel {
|
|
||||||
fn update_from_jack_scope (&self, t0: Option<u64>, scope: &ProcessScope) {
|
|
||||||
if let Some(t0) = t0 {
|
|
||||||
let t1 = self.clock.raw();
|
|
||||||
self.used.store(
|
|
||||||
self.clock.delta_as_nanos(t0, t1) as f64,
|
|
||||||
Relaxed,
|
|
||||||
);
|
|
||||||
self.window.store(
|
|
||||||
scope.cycle_times().unwrap().period_usecs as f64,
|
|
||||||
Relaxed,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Things that can provide a [jack::Client] reference.
|
/// Things that can provide a [jack::Client] reference.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -266,10 +89,6 @@ pub trait HasJack<'j>: Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait JackPerfModel {
|
|
||||||
fn update_from_jack_scope (&self, t0: Option<u64>, scope: &ProcessScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implement [Audio]: provide JACK callbacks.
|
/// Implement [Audio]: provide JACK callbacks.
|
||||||
#[macro_export] macro_rules! impl_audio {
|
#[macro_export] macro_rules! impl_audio {
|
||||||
|
|
||||||
|
|
|
||||||
98
src/sing/jack.rs
Normal file
98
src/sing/jack.rs
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
use crate::{*, sing::*, time::PerfModel};
|
||||||
|
pub use ::jack::{*, contrib::{*, ClosureProcessHandler}};
|
||||||
|
|
||||||
|
use JackState::*;
|
||||||
|
|
||||||
|
/// Wraps [JackState], and through it [jack::Client] when connected.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let jack = tengri::sing::Jack::default();
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug, Default)] pub struct Jack<'j> (
|
||||||
|
pub(crate) Arc<RwLock<JackState<'j>>>
|
||||||
|
);
|
||||||
|
|
||||||
|
/// This is a connection which may be [Inactive], [Activating], or [Active].
|
||||||
|
/// In the [Active] and [Inactive] states, [JackState::client] returns a
|
||||||
|
/// [jack::Client], which you can use to talk to the JACK API.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let state = tengri::sing::JackState::default();
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Default)] pub enum JackState<'j> {
|
||||||
|
/// Unused
|
||||||
|
#[default] Inert,
|
||||||
|
/// Before activation.
|
||||||
|
Inactive(Client),
|
||||||
|
/// During activation.
|
||||||
|
Activating,
|
||||||
|
/// After activation. Must not be dropped for JACK thread to persist.
|
||||||
|
Active(DynamicAsyncClient<'j>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement [Jack] constructor and methods
|
||||||
|
impl<'j> Jack<'j> {
|
||||||
|
/// Register new [Client] and wrap it for shared use.
|
||||||
|
pub fn new_run <T: HasJack<'j> + Audio + Send + Sync + 'static> (
|
||||||
|
name: impl AsRef<str>,
|
||||||
|
init: impl FnOnce(Jack<'j>)->Usually<T>
|
||||||
|
) -> Usually<Arc<RwLock<T>>> {
|
||||||
|
Jack::new(name)?.run(init)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new (name: impl AsRef<str>) -> Usually<Self> {
|
||||||
|
let client = Client::new(name.as_ref(), ClientOptions::NO_START_SERVER)?.0;
|
||||||
|
Ok(Jack(Arc::new(RwLock::new(JackState::Inactive(client)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run <T: HasJack<'j> + Audio + Send + Sync + 'static>
|
||||||
|
(self, init: impl FnOnce(Self)->Usually<T>) -> Usually<Arc<RwLock<T>>>
|
||||||
|
{
|
||||||
|
let client_state = self.0.clone();
|
||||||
|
let app: Arc<RwLock<T>> = Arc::new(RwLock::new(init(self)?));
|
||||||
|
let mut state = Activating;
|
||||||
|
std::mem::swap(&mut*client_state.write().unwrap(), &mut state);
|
||||||
|
if let Inactive(client) = state {
|
||||||
|
// This is the misc notifications handler. It's a struct that wraps a [Box]
|
||||||
|
// which performs type erasure on a callback that takes [JackEvent], which is
|
||||||
|
// one of the available misc notifications.
|
||||||
|
let notify = JackNotify(Box::new({
|
||||||
|
let app = app.clone();
|
||||||
|
move|event|(&mut*app.write().unwrap()).handle(event)
|
||||||
|
}) as BoxedJackEventHandler);
|
||||||
|
// This is the main processing handler. It's a struct that wraps a [Box]
|
||||||
|
// which performs type erasure on a callback that takes [Client] and [ProcessScope]
|
||||||
|
// and passes them down to the `app`'s `process` callback, which in turn
|
||||||
|
// implements audio and MIDI input and output on a realtime basis.
|
||||||
|
let process = ::jack::contrib::ClosureProcessHandler::new(Box::new({
|
||||||
|
let app = app.clone();
|
||||||
|
move|c: &_, s: &_|if let Ok(mut app) = app.write() {
|
||||||
|
app.process(c, s)
|
||||||
|
} else {
|
||||||
|
Control::Quit
|
||||||
|
}
|
||||||
|
}) as BoxedAudioHandler);
|
||||||
|
// Launch a client with the two handlers.
|
||||||
|
*client_state.write().unwrap() = Active(
|
||||||
|
client.activate_async(notify, process)?
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
Ok(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run something with the client.
|
||||||
|
pub fn with_client <T> (&self, op: impl FnOnce(&Client)->T) -> T {
|
||||||
|
match &*self.0.read().unwrap() {
|
||||||
|
Inert => panic!("jack client not activated"),
|
||||||
|
Inactive(client) => op(client),
|
||||||
|
Activating => panic!("jack client has not finished activation"),
|
||||||
|
Active(client) => op(client.as_client()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'j> HasJack<'j> for Jack<'j> { fn jack (&self) -> &Jack<'j> { self } }
|
||||||
|
|
||||||
|
impl<'j> HasJack<'j> for &Jack<'j> { fn jack (&self) -> &Jack<'j> { self } }
|
||||||
72
src/sing/jack_event.rs
Normal file
72
src/sing/jack_event.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
use crate::{*, time::PerfModel};
|
||||||
|
pub use ::jack::{*, contrib::{*, ClosureProcessHandler}};
|
||||||
|
|
||||||
|
/// Event enum for JACK events.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let event = tengri::sing::JackEvent::XRun; // kerpop
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq)] pub enum JackEvent {
|
||||||
|
ThreadInit,
|
||||||
|
Shutdown(ClientStatus, Arc<str>),
|
||||||
|
Freewheel(bool),
|
||||||
|
SampleRate(Frames),
|
||||||
|
ClientRegistration(Arc<str>, bool),
|
||||||
|
PortRegistration(PortId, bool),
|
||||||
|
PortRename(PortId, Arc<str>, Arc<str>),
|
||||||
|
PortsConnected(PortId, PortId, bool),
|
||||||
|
GraphReorder,
|
||||||
|
XRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic notification handler that emits [JackEvent]
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let notify = tengri::sing::JackNotify(|_|{});
|
||||||
|
/// ```
|
||||||
|
pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);
|
||||||
|
|
||||||
|
/// Notification handler wrapper for [BoxedJackEventHandler].
|
||||||
|
pub type DynamicNotifications<'j> =
|
||||||
|
JackNotify<BoxedJackEventHandler<'j>>;
|
||||||
|
|
||||||
|
/// Boxed [JackEvent] callback.
|
||||||
|
pub type BoxedJackEventHandler<'j> =
|
||||||
|
Box<dyn Fn(JackEvent) + Send + Sync + 'j>;
|
||||||
|
|
||||||
|
impl<T: Fn(JackEvent) + Send> NotificationHandler for JackNotify<T> {
|
||||||
|
fn thread_init(&self, _: &Client) {
|
||||||
|
self.0(JackEvent::ThreadInit);
|
||||||
|
}
|
||||||
|
unsafe fn shutdown(&mut self, status: ClientStatus, reason: &str) {
|
||||||
|
self.0(JackEvent::Shutdown(status, reason.into()));
|
||||||
|
}
|
||||||
|
fn freewheel(&mut self, _: &Client, enabled: bool) {
|
||||||
|
self.0(JackEvent::Freewheel(enabled));
|
||||||
|
}
|
||||||
|
fn sample_rate(&mut self, _: &Client, frames: Frames) -> Control {
|
||||||
|
self.0(JackEvent::SampleRate(frames));
|
||||||
|
Control::Quit
|
||||||
|
}
|
||||||
|
fn client_registration(&mut self, _: &Client, name: &str, reg: bool) {
|
||||||
|
self.0(JackEvent::ClientRegistration(name.into(), reg));
|
||||||
|
}
|
||||||
|
fn port_registration(&mut self, _: &Client, id: PortId, reg: bool) {
|
||||||
|
self.0(JackEvent::PortRegistration(id, reg));
|
||||||
|
}
|
||||||
|
fn port_rename(&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control {
|
||||||
|
self.0(JackEvent::PortRename(id, old.into(), new.into()));
|
||||||
|
Control::Continue
|
||||||
|
}
|
||||||
|
fn ports_connected(&mut self, _: &Client, a: PortId, b: PortId, are: bool) {
|
||||||
|
self.0(JackEvent::PortsConnected(a, b, are));
|
||||||
|
}
|
||||||
|
fn graph_reorder(&mut self, _: &Client) -> Control {
|
||||||
|
self.0(JackEvent::GraphReorder);
|
||||||
|
Control::Continue
|
||||||
|
}
|
||||||
|
fn xrun(&mut self, _: &Client) -> Control {
|
||||||
|
self.0(JackEvent::XRun);
|
||||||
|
Control::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/sing/jack_perf.rs
Normal file
22
src/sing/jack_perf.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
use crate::{*, time::PerfModel};
|
||||||
|
pub use ::jack::{*, contrib::{*, ClosureProcessHandler}};
|
||||||
|
|
||||||
|
pub trait JackPerfModel {
|
||||||
|
fn update_from_jack_scope (&self, t0: Option<u64>, scope: &ProcessScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JackPerfModel for PerfModel {
|
||||||
|
fn update_from_jack_scope (&self, t0: Option<u64>, scope: &ProcessScope) {
|
||||||
|
if let Some(t0) = t0 {
|
||||||
|
let t1 = self.clock.raw();
|
||||||
|
self.used.store(
|
||||||
|
self.clock.delta_as_nanos(t0, t1) as f64,
|
||||||
|
Relaxed,
|
||||||
|
);
|
||||||
|
self.window.store(
|
||||||
|
scope.cycle_times().unwrap().period_usecs as f64,
|
||||||
|
Relaxed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,20 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Iterate over a collection of renderables:
|
/// Iterate over a collection of the same kind of [Draw]able:
|
||||||
|
pub fn iter <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
||||||
|
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||||
|
) -> impl Draw<S> {
|
||||||
|
thunk(move|_to: &mut S|{ todo!() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over a collection of the same kind of [Draw]able:
|
||||||
|
pub fn iter_once <'a, S: Screen, D: 'a, U: Draw<S>> (
|
||||||
|
_iter: impl Iterator<Item = D>, _draw: impl Fn(D, usize)->U,
|
||||||
|
) -> impl Draw<S> {
|
||||||
|
thunk(move|_to: &mut S|{ todo!() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over a collection of various [Draw]ables:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use tengri::draw::{Origin::*, Split::*};
|
/// use tengri::draw::{Origin::*, Split::*};
|
||||||
|
|
@ -20,12 +34,6 @@ pub fn iter_dyn <
|
||||||
thunk(move|_to: &mut S|{ todo!() })
|
thunk(move|_to: &mut S|{ todo!() })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
|
||||||
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
|
||||||
) -> impl Draw<S> {
|
|
||||||
thunk(move|_to: &mut S|{ todo!() })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
||||||
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||||
) -> impl Draw<S> {
|
) -> impl Draw<S> {
|
||||||
|
|
@ -61,3 +69,27 @@ pub fn iter_west <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
||||||
) -> impl Draw<S> {
|
) -> impl Draw<S> {
|
||||||
thunk(move|_to: &mut S|{ todo!() })
|
thunk(move|_to: &mut S|{ todo!() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn row_south <S: Screen> (south: S::Unit, height: S::Unit, content: impl Draw<S>)
|
||||||
|
-> impl Draw<S>
|
||||||
|
{
|
||||||
|
y_push(south, h_exact(height, content))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn row_north <S: Screen> (north: S::Unit, height: S::Unit, content: impl Draw<S>)
|
||||||
|
-> impl Draw<S>
|
||||||
|
{
|
||||||
|
y_pull(north, h_exact(height, content))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn col_east <S: Screen> (east: S::Unit, width: S::Unit, content: impl Draw<S>)
|
||||||
|
-> impl Draw<S>
|
||||||
|
{
|
||||||
|
x_push(east, h_exact(width, content))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn col_west <S: Screen> (west: S::Unit, width: S::Unit, content: impl Draw<S>)
|
||||||
|
-> impl Draw<S>
|
||||||
|
{
|
||||||
|
x_pull(west, h_exact(width, content))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,14 @@ use super::*;
|
||||||
|
|
||||||
/// Uses [AtomicUsize] to measure size during\
|
/// Uses [AtomicUsize] to measure size during\
|
||||||
/// rendering (which is normally read-only).
|
/// rendering (which is normally read-only).
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct Size(AtomicUsize, AtomicUsize);
|
pub struct Size(Arc<AtomicUsize>, Arc<AtomicUsize>);
|
||||||
|
|
||||||
|
impl PartialEq for Size {
|
||||||
|
fn eq (&self, _: &Self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl X<u16> for Size {
|
impl X<u16> for Size {
|
||||||
fn x (&self) -> u16 { 0 }
|
fn x (&self) -> u16 { 0 }
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ pub(crate) use ::unicode_width::*;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Draw<Tui> for &std::sync::Arc<str> {
|
impl Draw<Tui> for std::sync::Arc<str> {
|
||||||
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||||
self.as_ref().draw(to)
|
self.as_ref().draw(to)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue