mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
implement sequencer focus; auto update_focus
This commit is contained in:
parent
9f15f8fff9
commit
3bf475d15e
8 changed files with 119 additions and 160 deletions
|
|
@ -4,6 +4,7 @@ pub trait FocusGrid<T: Copy + PartialEq> {
|
|||
fn layout (&self) -> &[&[T]];
|
||||
fn cursor (&self) -> (usize, usize);
|
||||
fn cursor_mut (&mut self) -> &mut (usize, usize);
|
||||
fn update_focus (&mut self) {}
|
||||
fn focused (&self) -> &T {
|
||||
let (x, y) = self.cursor();
|
||||
&self.layout()[y][x]
|
||||
|
|
@ -16,6 +17,7 @@ pub trait FocusGrid<T: Copy + PartialEq> {
|
|||
((x as f32 / layout[y].len() as f32) * layout[next_y].len() as f32) as usize
|
||||
};
|
||||
*self.cursor_mut() = (next_x, next_y);
|
||||
self.update_focus();
|
||||
}
|
||||
fn focus_down (&mut self) {
|
||||
let layout = self.layout();
|
||||
|
|
@ -25,18 +27,21 @@ pub trait FocusGrid<T: Copy + PartialEq> {
|
|||
((x as f32 / layout[y].len() as f32) * layout[next_y].len() as f32) as usize
|
||||
};
|
||||
*self.cursor_mut() = (next_x, next_y);
|
||||
self.update_focus();
|
||||
}
|
||||
fn focus_left (&mut self) {
|
||||
let layout = self.layout();
|
||||
let (x, y) = self.cursor();
|
||||
let next_x = if x == 0 { layout[y].len().saturating_sub(1) } else { x - 1 };
|
||||
*self.cursor_mut() = (next_x, y);
|
||||
self.update_focus();
|
||||
}
|
||||
fn focus_right (&mut self) {
|
||||
let layout = self.layout();
|
||||
let (x, y) = self.cursor();
|
||||
let next_x = if x >= layout[y].len().saturating_sub(1) { 0 } else { x + 1 };
|
||||
*self.cursor_mut() = (next_x, y);
|
||||
self.update_focus();
|
||||
}
|
||||
fn focus_next (&mut self) {
|
||||
let current = *self.focused();
|
||||
|
|
@ -50,6 +55,7 @@ pub trait FocusGrid<T: Copy + PartialEq> {
|
|||
if *self.focused() == current { // FIXME: prevent infinite loop
|
||||
self.focus_next()
|
||||
}
|
||||
self.update_focus();
|
||||
}
|
||||
fn focus_prev (&mut self) {
|
||||
let current = *self.focused();
|
||||
|
|
@ -65,6 +71,7 @@ pub trait FocusGrid<T: Copy + PartialEq> {
|
|||
if *self.focused() == current { // FIXME: prevent infinite loop
|
||||
self.focus_prev()
|
||||
}
|
||||
self.update_focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use crate::*;
|
|||
|
||||
/// Root level object for standalone `tek_arranger`
|
||||
pub struct Arranger<E: Engine> {
|
||||
/// Which view is focused
|
||||
pub focus_cursor: (usize, usize),
|
||||
/// Controls the JACK transport.
|
||||
pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
|
||||
/// Contains all the sequencers.
|
||||
|
|
@ -14,24 +16,10 @@ pub struct Arranger<E: Engine> {
|
|||
pub show_sequencer: Option<tek_core::Direction>,
|
||||
/// Slot for modal dialog displayed on top of app.
|
||||
pub modal: Option<Box<dyn ContentComponent<E>>>,
|
||||
/// Focus cursor
|
||||
pub focus_cursor: (usize, usize)
|
||||
}
|
||||
/// Sections in the arranger that may be focused
|
||||
/// Sections in the arranger app that may be focused
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ArrangerFocus { Transport, Arrangement, PhrasePool, PhraseEditor }
|
||||
/// Focus layout of arranger.
|
||||
impl<E: Engine> FocusGrid<ArrangerFocus> for Arranger<E> {
|
||||
fn cursor (&self) -> (usize, usize) { self.focus_cursor }
|
||||
fn cursor_mut (&mut self) -> &mut (usize, usize) { &mut self.focus_cursor }
|
||||
fn layout (&self) -> &[&[ArrangerFocus]] {
|
||||
&[
|
||||
&[ArrangerFocus::Transport],
|
||||
&[ArrangerFocus::Arrangement, ArrangerFocus::Arrangement],
|
||||
&[ArrangerFocus::PhrasePool, ArrangerFocus::PhraseEditor],
|
||||
]
|
||||
}
|
||||
}
|
||||
/// Represents the tracks and scenes of the composition.
|
||||
pub struct Arrangement<E: Engine> {
|
||||
/// Name of arranger
|
||||
|
|
@ -104,6 +92,29 @@ pub struct ArrangerRenameModal<E: Engine> {
|
|||
pub result: Arc<RwLock<String>>,
|
||||
pub cursor: usize
|
||||
}
|
||||
/// Focus layout of arranger app
|
||||
impl<E: Engine> FocusGrid<ArrangerFocus> for Arranger<E> {
|
||||
fn cursor (&self) -> (usize, usize) { self.focus_cursor }
|
||||
fn cursor_mut (&mut self) -> &mut (usize, usize) { &mut self.focus_cursor }
|
||||
fn layout (&self) -> &[&[ArrangerFocus]] { &[
|
||||
&[ArrangerFocus::Transport],
|
||||
&[ArrangerFocus::Arrangement, ArrangerFocus::Arrangement],
|
||||
&[ArrangerFocus::PhrasePool, ArrangerFocus::PhraseEditor],
|
||||
] }
|
||||
fn update_focus (&mut self) {
|
||||
let focused = *self.focused();
|
||||
if let Some(transport) = self.transport.as_ref() {
|
||||
transport.write().unwrap().focused =
|
||||
focused == ArrangerFocus::Transport
|
||||
}
|
||||
self.arrangement.focused =
|
||||
focused == ArrangerFocus::Arrangement;
|
||||
self.phrases.write().unwrap().focused =
|
||||
focused == ArrangerFocus::PhrasePool;
|
||||
self.editor.focused =
|
||||
focused == ArrangerFocus::PhraseEditor;
|
||||
}
|
||||
}
|
||||
/// General methods for arrangement
|
||||
impl<E: Engine> Arrangement<E> {
|
||||
pub fn new (name: &str, phrases: &Arc<RwLock<PhrasePool<E>>>) -> Self {
|
||||
|
|
@ -182,7 +193,7 @@ impl<E: Engine> Arrangement<E> {
|
|||
for track_index in 0..self.tracks.len() {
|
||||
if let Some(phrase) = scene.clip(track_index) {
|
||||
let len = phrase.read().unwrap().name.read().unwrap().len();
|
||||
lens[track_index] = lens[track_index].max(len + 16);
|
||||
lens[track_index] = lens[track_index].max(len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,30 +7,22 @@ pub fn main () -> Usually<()> { ArrangerCli::parse().run() }
|
|||
#[command(version, about, long_about = None)]
|
||||
pub struct ArrangerCli {
|
||||
/// Name of JACK client
|
||||
#[arg(short, long)]
|
||||
name: Option<String>,
|
||||
#[arg(short, long)] name: Option<String>,
|
||||
/// Whether to include a transport toolbar (default: true)
|
||||
#[arg(short, long, default_value_t = true)]
|
||||
transport: bool,
|
||||
#[arg(short, long, default_value_t = true)] transport: bool,
|
||||
/// Number of tracks
|
||||
#[arg(short = 'x', long, default_value_t = 8)]
|
||||
tracks: usize,
|
||||
#[arg(short = 'x', long, default_value_t = 8)] tracks: usize,
|
||||
/// Number of scenes
|
||||
#[arg(short, long, default_value_t = 8)]
|
||||
scenes: usize,
|
||||
#[arg(short, long, default_value_t = 8)] scenes: usize,
|
||||
}
|
||||
|
||||
impl ArrangerCli {
|
||||
/// Run the arranger TUI from CLI arguments.
|
||||
fn run (&self) -> Usually<()> {
|
||||
let jack = JackClient::Inactive(
|
||||
Client::new("tek_arranger", ClientOptions::NO_START_SERVER)?.0
|
||||
);
|
||||
let jack_transport = jack.transport();
|
||||
let mut transport = TransportToolbar::new(Some(jack_transport));
|
||||
transport.set_focused(true);
|
||||
let transport = Arc::new(RwLock::new(transport));
|
||||
let phrases = Arc::new(RwLock::new(PhrasePool::new()));
|
||||
let jack = Client::new("tek_arranger", ClientOptions::NO_START_SERVER)?.0;
|
||||
let jack = JackClient::Inactive(jack);
|
||||
let transport = Arc::new(RwLock::new(TransportToolbar::new(Some(jack.transport()))));
|
||||
let phrases = Arc::new(RwLock::new(PhrasePool::new()));
|
||||
let mut arrangement = Arrangement::new("", &phrases);
|
||||
if let Some(name) = self.name.as_ref() {
|
||||
*arrangement.name.write().unwrap() = name.clone();
|
||||
|
|
@ -57,7 +49,7 @@ impl ArrangerCli {
|
|||
}
|
||||
)?
|
||||
);
|
||||
Tui::run(Arc::new(RwLock::new(Arranger {
|
||||
let mut app = Arranger {
|
||||
focus_cursor: (0, 1),
|
||||
transport: self.transport.then_some(transport),
|
||||
show_sequencer: Some(tek_core::Direction::Down),
|
||||
|
|
@ -65,7 +57,9 @@ impl ArrangerCli {
|
|||
editor: PhraseEditor::new(),
|
||||
arrangement,
|
||||
phrases,
|
||||
})))?;
|
||||
};
|
||||
app.update_focus();
|
||||
Tui::run(Arc::new(RwLock::new(app)))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,15 +28,6 @@ impl Content for Arranger<Tui> {
|
|||
/// Handle top-level events in standalone arranger.
|
||||
impl Handle<Tui> for Arranger<Tui> {
|
||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||
let update_focus = |arranger: &mut Self| {
|
||||
let focused = *arranger.focused();
|
||||
arranger.transport.as_ref().map(|transport|{
|
||||
transport.write().unwrap().focused = focused == ArrangerFocus::Transport
|
||||
});
|
||||
arranger.arrangement.focused = focused == ArrangerFocus::Arrangement;
|
||||
arranger.phrases.write().unwrap().focused = focused == ArrangerFocus::PhrasePool;
|
||||
arranger.editor.focused = focused == ArrangerFocus::PhraseEditor;
|
||||
};
|
||||
if !match self.focused() {
|
||||
ArrangerFocus::Transport => self.transport.handle(from)?,
|
||||
ArrangerFocus::Arrangement => self.arrangement.handle(from)?,
|
||||
|
|
@ -45,15 +36,15 @@ impl Handle<Tui> for Arranger<Tui> {
|
|||
}.unwrap_or(false) {
|
||||
match from.event() {
|
||||
// Tab navigation
|
||||
key!(KeyCode::Tab) => { self.focus_next(); update_focus(self); },
|
||||
key!(Shift-KeyCode::Tab) => { self.focus_prev(); update_focus(self); },
|
||||
key!(KeyCode::BackTab) => { self.focus_prev(); update_focus(self); },
|
||||
key!(Shift-KeyCode::BackTab) => { self.focus_prev(); update_focus(self); },
|
||||
key!(KeyCode::Tab) => { self.focus_next(); },
|
||||
key!(Shift-KeyCode::Tab) => { self.focus_prev(); },
|
||||
key!(KeyCode::BackTab) => { self.focus_prev(); },
|
||||
key!(Shift-KeyCode::BackTab) => { self.focus_prev(); },
|
||||
// Directional navigation
|
||||
key!(KeyCode::Up) => { self.focus_up(); update_focus(self); },
|
||||
key!(KeyCode::Down) => { self.focus_down(); update_focus(self); },
|
||||
key!(KeyCode::Left) => { self.focus_left(); update_focus(self); },
|
||||
key!(KeyCode::Right) => { self.focus_right(); update_focus(self); },
|
||||
key!(KeyCode::Up) => { self.focus_up(); },
|
||||
key!(KeyCode::Down) => { self.focus_down(); },
|
||||
key!(KeyCode::Left) => { self.focus_left(); },
|
||||
key!(KeyCode::Right) => { self.focus_right(); },
|
||||
// Global play/pause binding
|
||||
key!(KeyCode::Char(' ')) => match self.transport {
|
||||
Some(ref mut transport) => { transport.write().unwrap().toggle_play()?; },
|
||||
|
|
@ -86,14 +77,6 @@ impl Arranger<Tui> {
|
|||
};
|
||||
}
|
||||
}
|
||||
impl Focusable<Tui> for Arrangement<Tui> {
|
||||
fn is_focused (&self) -> bool {
|
||||
self.focused
|
||||
}
|
||||
fn set_focused (&mut self, focused: bool) {
|
||||
self.focused = focused
|
||||
}
|
||||
}
|
||||
impl Handle<Tui> for Arrangement<Tui> {
|
||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||
match from.event() {
|
||||
|
|
|
|||
|
|
@ -7,15 +7,18 @@ pub type PhraseMessage = Vec<u8>;
|
|||
pub type PhraseChunk = [Vec<PhraseMessage>];
|
||||
/// Root level object for standalone `tek_sequencer`
|
||||
pub struct Sequencer<E: Engine> {
|
||||
/// Controls the JACK transport.
|
||||
pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
|
||||
/// Pool of all phrases available to the sequencer
|
||||
pub phrases: Arc<RwLock<PhrasePool<E>>>,
|
||||
/// Phrase editor view
|
||||
pub editor: PhraseEditor<E>,
|
||||
/// Which view is focused
|
||||
pub focus: usize
|
||||
pub focus_cursor: (usize, usize),
|
||||
/// Controls the JACK transport.
|
||||
pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
|
||||
/// Pool of all phrases available to the sequencer
|
||||
pub phrases: Arc<RwLock<PhrasePool<E>>>,
|
||||
/// Phrase editor view
|
||||
pub editor: PhraseEditor<E>,
|
||||
}
|
||||
/// Sections in the sequencer app that may be focused
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum SequencerFocus { Transport, PhrasePool, PhraseEditor }
|
||||
/// Contains all phrases in a project
|
||||
pub struct PhrasePool<E: Engine> {
|
||||
_engine: PhantomData<E>,
|
||||
|
|
@ -93,6 +96,26 @@ pub struct PhrasePlayer<E: Engine> {
|
|||
/// Send all notes off
|
||||
pub reset: bool, // TODO?: after Some(nframes)
|
||||
}
|
||||
/// Focus layout of sequencer app
|
||||
impl<E: Engine> FocusGrid<SequencerFocus> for Sequencer<E> {
|
||||
fn cursor (&self) -> (usize, usize) { self.focus_cursor }
|
||||
fn cursor_mut (&mut self) -> &mut (usize, usize) { &mut self.focus_cursor }
|
||||
fn layout (&self) -> &[&[SequencerFocus]] { &[
|
||||
&[SequencerFocus::Transport],
|
||||
&[SequencerFocus::PhrasePool, SequencerFocus::PhraseEditor],
|
||||
] }
|
||||
fn update_focus (&mut self) {
|
||||
let focused = *self.focused();
|
||||
if let Some(transport) = self.transport.as_ref() {
|
||||
transport.write().unwrap().focused =
|
||||
focused == SequencerFocus::Transport
|
||||
}
|
||||
self.phrases.write().unwrap().focused =
|
||||
focused == SequencerFocus::PhrasePool;
|
||||
self.editor.focused =
|
||||
focused == SequencerFocus::PhraseEditor;
|
||||
}
|
||||
}
|
||||
impl<E: Engine> PhrasePool<E> {
|
||||
pub fn new () -> Self {
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -7,24 +7,20 @@ pub fn main () -> Usually<()> { SequencerCli::parse().run() }
|
|||
#[command(version, about, long_about = None)]
|
||||
pub struct SequencerCli {
|
||||
/// Name of JACK client
|
||||
#[arg(short, long)] name: Option<String>,
|
||||
#[arg(short, long)] name: Option<String>,
|
||||
/// Pulses per quarter note (sequencer resolution; default: 96)
|
||||
#[arg(short, long)] ppq: Option<usize>,
|
||||
#[arg(short, long)] ppq: Option<usize>,
|
||||
/// Default phrase duration (in pulses; default: 4 * PPQ = 1 bar)
|
||||
#[arg(short, long)] length: Option<usize>,
|
||||
#[arg(short, long)] length: Option<usize>,
|
||||
/// Whether to include a transport toolbar (default: true)
|
||||
#[arg(short, long)] transport: Option<bool>
|
||||
#[arg(short, long, default_value_t = true)] transport: bool
|
||||
}
|
||||
|
||||
impl SequencerCli {
|
||||
fn run (&self) -> Usually<()> {
|
||||
let jack = JackClient::Inactive(
|
||||
Client::new("tek_arranger", ClientOptions::NO_START_SERVER)?.0
|
||||
);
|
||||
let jack_transport = jack.transport();
|
||||
let mut transport = TransportToolbar::new(Some(jack_transport));
|
||||
transport.set_focused(true);
|
||||
let transport = Arc::new(RwLock::new(transport));
|
||||
let jack = Client::new("tek_arranger", ClientOptions::NO_START_SERVER)?.0;
|
||||
let jack = JackClient::Inactive(jack);
|
||||
let transport = Arc::new(RwLock::new(TransportToolbar::new(Some(jack.transport()))));
|
||||
transport.write().unwrap().jack = Some(
|
||||
jack.activate(
|
||||
&transport.clone(),
|
||||
|
|
@ -34,10 +30,10 @@ impl SequencerCli {
|
|||
)?
|
||||
);
|
||||
let seq = Sequencer {
|
||||
focus: 0,
|
||||
editor: PhraseEditor::new(),
|
||||
phrases: Arc::new(RwLock::new(PhrasePool::new())),
|
||||
transport: Some(transport),
|
||||
focus_cursor: (1, 1),
|
||||
transport: self.transport.then_some(transport),
|
||||
editor: PhraseEditor::new(),
|
||||
phrases: Arc::new(RwLock::new(PhrasePool::new())),
|
||||
};
|
||||
if let Some(name) = self.name.as_ref() {
|
||||
// TODO
|
||||
|
|
|
|||
|
|
@ -10,59 +10,33 @@ impl Content for Sequencer<Tui> {
|
|||
})
|
||||
}
|
||||
}
|
||||
/// Focusable items in standalone arranger.
|
||||
impl Focus<3, Tui> for Sequencer<Tui> {
|
||||
fn focus (&self) -> usize {
|
||||
self.focus
|
||||
}
|
||||
fn focus_mut (&mut self) -> &mut usize {
|
||||
&mut self.focus
|
||||
}
|
||||
fn focusable (&self) -> [&dyn Focusable<Tui>;3] {
|
||||
focusables!(self.transport, self.phrases, self.editor)
|
||||
}
|
||||
fn focusable_mut (&mut self) -> [&mut dyn Focusable<Tui>;3] {
|
||||
focusables_mut!(self.transport, self.phrases, self.editor)
|
||||
}
|
||||
}
|
||||
/// Handle top-level events in standalone arranger.
|
||||
impl Handle<Tui> for Sequencer<Tui> {
|
||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||
let focus = self.focus;
|
||||
let is_first_row = false;
|
||||
let is_last_row = false;
|
||||
match from.event() {
|
||||
key!(KeyCode::Char(' ')) => {
|
||||
if let Some(ref mut transport) = self.transport {
|
||||
transport.write().unwrap().toggle_play()?;
|
||||
} else {
|
||||
return Ok(None)
|
||||
}
|
||||
},
|
||||
key!(KeyCode::Tab) => {
|
||||
self.focus_next();
|
||||
},
|
||||
key!(KeyCode::BackTab) => {
|
||||
self.focus_prev();
|
||||
},
|
||||
key!(KeyCode::Down) => {
|
||||
if focus == 0 {
|
||||
self.focus_next();
|
||||
} else if focus == 1 && is_last_row {
|
||||
self.focus_next();
|
||||
} else {
|
||||
return self.focused_mut().handle(from)
|
||||
}
|
||||
},
|
||||
key!(KeyCode::Up) => {
|
||||
if focus == 1 && is_first_row {
|
||||
self.focus_prev();
|
||||
} else {
|
||||
return self.focused_mut().handle(from)
|
||||
}
|
||||
},
|
||||
_ => return self.focused_mut().handle(from)
|
||||
}
|
||||
if !match self.focused() {
|
||||
SequencerFocus::Transport => self.transport.handle(from)?,
|
||||
SequencerFocus::PhrasePool => self.phrases.handle(from)?,
|
||||
SequencerFocus::PhraseEditor => self.editor.handle(from)?
|
||||
}.unwrap_or(false) {
|
||||
match from.event() {
|
||||
// Tab navigation
|
||||
key!(KeyCode::Tab) => { self.focus_next(); },
|
||||
key!(Shift-KeyCode::Tab) => { self.focus_prev(); },
|
||||
key!(KeyCode::BackTab) => { self.focus_prev(); },
|
||||
key!(Shift-KeyCode::BackTab) => { self.focus_prev(); },
|
||||
// Directional navigation
|
||||
key!(KeyCode::Up) => { self.focus_up(); },
|
||||
key!(KeyCode::Down) => { self.focus_down(); },
|
||||
key!(KeyCode::Left) => { self.focus_left(); },
|
||||
key!(KeyCode::Right) => { self.focus_right(); },
|
||||
// Global play/pause binding
|
||||
key!(KeyCode::Char(' ')) => match self.transport {
|
||||
Some(ref mut transport) => { transport.write().unwrap().toggle_play()?; },
|
||||
None => { return Ok(None) }
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
Ok(Some(true))
|
||||
}
|
||||
}
|
||||
|
|
@ -81,14 +55,6 @@ impl Content for PhrasePool<Tui> {
|
|||
.fg(Color::Rgb(70, 80, 50))))
|
||||
}
|
||||
}
|
||||
impl Focusable<Tui> for PhrasePool<Tui> {
|
||||
fn is_focused (&self) -> bool {
|
||||
self.focused
|
||||
}
|
||||
fn set_focused (&mut self, focused: bool) {
|
||||
self.focused = focused
|
||||
}
|
||||
}
|
||||
impl Handle<Tui> for PhrasePool<Tui> {
|
||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||
match from.event() {
|
||||
|
|
@ -225,14 +191,6 @@ impl Handle<Tui> for PhraseEditor<Tui> {
|
|||
return Ok(Some(true))
|
||||
}
|
||||
}
|
||||
impl Focusable<Tui> for PhraseEditor<Tui> {
|
||||
fn is_focused (&self) -> bool {
|
||||
self.focused
|
||||
}
|
||||
fn set_focused (&mut self, focused: bool) {
|
||||
self.focused = focused
|
||||
}
|
||||
}
|
||||
impl PhraseEditor<Tui> {
|
||||
const H_KEYS_OFFSET: usize = 5;
|
||||
/// Select which pattern to display. This pre-renders it to the buffer at full resolution.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::*;
|
||||
|
||||
impl TransportToolbar<Tui> {
|
||||
fn handle_bpm (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||
match from.event() {
|
||||
|
|
@ -28,7 +27,6 @@ impl TransportToolbar<Tui> {
|
|||
return Ok(Some(true))
|
||||
}
|
||||
}
|
||||
|
||||
impl Handle<Tui> for TransportToolbar<Tui> {
|
||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||
match from.event() {
|
||||
|
|
@ -48,7 +46,6 @@ impl Handle<Tui> for TransportToolbar<Tui> {
|
|||
Ok(Some(true))
|
||||
}
|
||||
}
|
||||
|
||||
impl Content for TransportToolbar<Tui> {
|
||||
type Engine = Tui;
|
||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||
|
|
@ -89,16 +86,6 @@ impl Content for TransportToolbar<Tui> {
|
|||
).fill_x().bg(Color::Rgb(40, 50, 30))
|
||||
}
|
||||
}
|
||||
|
||||
impl Focusable<Tui> for TransportToolbar<Tui> {
|
||||
fn is_focused (&self) -> bool {
|
||||
self.focused
|
||||
}
|
||||
fn set_focused (&mut self, focused: bool) {
|
||||
self.focused = focused
|
||||
}
|
||||
}
|
||||
|
||||
impl TransportToolbarFocus {
|
||||
pub fn wrap <'a, W: Widget<Engine = Tui>> (
|
||||
self, parent_focus: bool, focus: Self, widget: &'a W
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue