implement sequencer focus; auto update_focus

This commit is contained in:
🪞👃🪞 2024-10-09 22:50:23 +03:00
parent 9f15f8fff9
commit 3bf475d15e
8 changed files with 119 additions and 160 deletions

View file

@ -4,6 +4,7 @@ pub trait FocusGrid<T: Copy + PartialEq> {
fn layout (&self) -> &[&[T]]; fn layout (&self) -> &[&[T]];
fn cursor (&self) -> (usize, usize); fn cursor (&self) -> (usize, usize);
fn cursor_mut (&mut self) -> &mut (usize, usize); fn cursor_mut (&mut self) -> &mut (usize, usize);
fn update_focus (&mut self) {}
fn focused (&self) -> &T { fn focused (&self) -> &T {
let (x, y) = self.cursor(); let (x, y) = self.cursor();
&self.layout()[y][x] &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 ((x as f32 / layout[y].len() as f32) * layout[next_y].len() as f32) as usize
}; };
*self.cursor_mut() = (next_x, next_y); *self.cursor_mut() = (next_x, next_y);
self.update_focus();
} }
fn focus_down (&mut self) { fn focus_down (&mut self) {
let layout = self.layout(); 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 ((x as f32 / layout[y].len() as f32) * layout[next_y].len() as f32) as usize
}; };
*self.cursor_mut() = (next_x, next_y); *self.cursor_mut() = (next_x, next_y);
self.update_focus();
} }
fn focus_left (&mut self) { fn focus_left (&mut self) {
let layout = self.layout(); let layout = self.layout();
let (x, y) = self.cursor(); let (x, y) = self.cursor();
let next_x = if x == 0 { layout[y].len().saturating_sub(1) } else { x - 1 }; let next_x = if x == 0 { layout[y].len().saturating_sub(1) } else { x - 1 };
*self.cursor_mut() = (next_x, y); *self.cursor_mut() = (next_x, y);
self.update_focus();
} }
fn focus_right (&mut self) { fn focus_right (&mut self) {
let layout = self.layout(); let layout = self.layout();
let (x, y) = self.cursor(); let (x, y) = self.cursor();
let next_x = if x >= layout[y].len().saturating_sub(1) { 0 } else { x + 1 }; let next_x = if x >= layout[y].len().saturating_sub(1) { 0 } else { x + 1 };
*self.cursor_mut() = (next_x, y); *self.cursor_mut() = (next_x, y);
self.update_focus();
} }
fn focus_next (&mut self) { fn focus_next (&mut self) {
let current = *self.focused(); let current = *self.focused();
@ -50,6 +55,7 @@ pub trait FocusGrid<T: Copy + PartialEq> {
if *self.focused() == current { // FIXME: prevent infinite loop if *self.focused() == current { // FIXME: prevent infinite loop
self.focus_next() self.focus_next()
} }
self.update_focus();
} }
fn focus_prev (&mut self) { fn focus_prev (&mut self) {
let current = *self.focused(); let current = *self.focused();
@ -65,6 +71,7 @@ pub trait FocusGrid<T: Copy + PartialEq> {
if *self.focused() == current { // FIXME: prevent infinite loop if *self.focused() == current { // FIXME: prevent infinite loop
self.focus_prev() self.focus_prev()
} }
self.update_focus();
} }
} }

View file

@ -2,6 +2,8 @@ use crate::*;
/// Root level object for standalone `tek_arranger` /// Root level object for standalone `tek_arranger`
pub struct Arranger<E: Engine> { pub struct Arranger<E: Engine> {
/// Which view is focused
pub focus_cursor: (usize, usize),
/// Controls the JACK transport. /// Controls the JACK transport.
pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>, pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
/// Contains all the sequencers. /// Contains all the sequencers.
@ -14,24 +16,10 @@ pub struct Arranger<E: Engine> {
pub show_sequencer: Option<tek_core::Direction>, pub show_sequencer: Option<tek_core::Direction>,
/// Slot for modal dialog displayed on top of app. /// Slot for modal dialog displayed on top of app.
pub modal: Option<Box<dyn ContentComponent<E>>>, 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)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum ArrangerFocus { Transport, Arrangement, PhrasePool, PhraseEditor } 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. /// Represents the tracks and scenes of the composition.
pub struct Arrangement<E: Engine> { pub struct Arrangement<E: Engine> {
/// Name of arranger /// Name of arranger
@ -104,6 +92,29 @@ pub struct ArrangerRenameModal<E: Engine> {
pub result: Arc<RwLock<String>>, pub result: Arc<RwLock<String>>,
pub cursor: usize 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 /// General methods for arrangement
impl<E: Engine> Arrangement<E> { impl<E: Engine> Arrangement<E> {
pub fn new (name: &str, phrases: &Arc<RwLock<PhrasePool<E>>>) -> Self { 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() { for track_index in 0..self.tracks.len() {
if let Some(phrase) = scene.clip(track_index) { if let Some(phrase) = scene.clip(track_index) {
let len = phrase.read().unwrap().name.read().unwrap().len(); 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);
} }
} }
} }

View file

@ -7,29 +7,21 @@ pub fn main () -> Usually<()> { ArrangerCli::parse().run() }
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
pub struct ArrangerCli { pub struct ArrangerCli {
/// Name of JACK client /// Name of JACK client
#[arg(short, long)] #[arg(short, long)] name: Option<String>,
name: Option<String>,
/// Whether to include a transport toolbar (default: true) /// Whether to include a transport toolbar (default: true)
#[arg(short, long, default_value_t = true)] #[arg(short, long, default_value_t = true)] transport: bool,
transport: bool,
/// Number of tracks /// Number of tracks
#[arg(short = 'x', long, default_value_t = 8)] #[arg(short = 'x', long, default_value_t = 8)] tracks: usize,
tracks: usize,
/// Number of scenes /// Number of scenes
#[arg(short, long, default_value_t = 8)] #[arg(short, long, default_value_t = 8)] scenes: usize,
scenes: usize,
} }
impl ArrangerCli { impl ArrangerCli {
/// Run the arranger TUI from CLI arguments. /// Run the arranger TUI from CLI arguments.
fn run (&self) -> Usually<()> { fn run (&self) -> Usually<()> {
let jack = JackClient::Inactive( let jack = Client::new("tek_arranger", ClientOptions::NO_START_SERVER)?.0;
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 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 phrases = Arc::new(RwLock::new(PhrasePool::new()));
let mut arrangement = Arrangement::new("", &phrases); let mut arrangement = Arrangement::new("", &phrases);
if let Some(name) = self.name.as_ref() { if let Some(name) = self.name.as_ref() {
@ -57,7 +49,7 @@ impl ArrangerCli {
} }
)? )?
); );
Tui::run(Arc::new(RwLock::new(Arranger { let mut app = Arranger {
focus_cursor: (0, 1), focus_cursor: (0, 1),
transport: self.transport.then_some(transport), transport: self.transport.then_some(transport),
show_sequencer: Some(tek_core::Direction::Down), show_sequencer: Some(tek_core::Direction::Down),
@ -65,7 +57,9 @@ impl ArrangerCli {
editor: PhraseEditor::new(), editor: PhraseEditor::new(),
arrangement, arrangement,
phrases, phrases,
})))?; };
app.update_focus();
Tui::run(Arc::new(RwLock::new(app)))?;
Ok(()) Ok(())
} }
} }

View file

@ -28,15 +28,6 @@ impl Content for Arranger<Tui> {
/// Handle top-level events in standalone arranger. /// Handle top-level events in standalone arranger.
impl Handle<Tui> for Arranger<Tui> { impl Handle<Tui> for Arranger<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> { 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() { if !match self.focused() {
ArrangerFocus::Transport => self.transport.handle(from)?, ArrangerFocus::Transport => self.transport.handle(from)?,
ArrangerFocus::Arrangement => self.arrangement.handle(from)?, ArrangerFocus::Arrangement => self.arrangement.handle(from)?,
@ -45,15 +36,15 @@ impl Handle<Tui> for Arranger<Tui> {
}.unwrap_or(false) { }.unwrap_or(false) {
match from.event() { match from.event() {
// Tab navigation // Tab navigation
key!(KeyCode::Tab) => { self.focus_next(); update_focus(self); }, key!(KeyCode::Tab) => { self.focus_next(); },
key!(Shift-KeyCode::Tab) => { self.focus_prev(); update_focus(self); }, key!(Shift-KeyCode::Tab) => { self.focus_prev(); },
key!(KeyCode::BackTab) => { self.focus_prev(); update_focus(self); }, key!(KeyCode::BackTab) => { self.focus_prev(); },
key!(Shift-KeyCode::BackTab) => { self.focus_prev(); update_focus(self); }, key!(Shift-KeyCode::BackTab) => { self.focus_prev(); },
// Directional navigation // Directional navigation
key!(KeyCode::Up) => { self.focus_up(); update_focus(self); }, key!(KeyCode::Up) => { self.focus_up(); },
key!(KeyCode::Down) => { self.focus_down(); update_focus(self); }, key!(KeyCode::Down) => { self.focus_down(); },
key!(KeyCode::Left) => { self.focus_left(); update_focus(self); }, key!(KeyCode::Left) => { self.focus_left(); },
key!(KeyCode::Right) => { self.focus_right(); update_focus(self); }, key!(KeyCode::Right) => { self.focus_right(); },
// Global play/pause binding // Global play/pause binding
key!(KeyCode::Char(' ')) => match self.transport { key!(KeyCode::Char(' ')) => match self.transport {
Some(ref mut transport) => { transport.write().unwrap().toggle_play()?; }, 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> { impl Handle<Tui> for Arrangement<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> { fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
match from.event() { match from.event() {

View file

@ -7,15 +7,18 @@ pub type PhraseMessage = Vec<u8>;
pub type PhraseChunk = [Vec<PhraseMessage>]; pub type PhraseChunk = [Vec<PhraseMessage>];
/// Root level object for standalone `tek_sequencer` /// Root level object for standalone `tek_sequencer`
pub struct Sequencer<E: Engine> { pub struct Sequencer<E: Engine> {
/// Which view is focused
pub focus_cursor: (usize, usize),
/// Controls the JACK transport. /// Controls the JACK transport.
pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>, pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
/// Pool of all phrases available to the sequencer /// Pool of all phrases available to the sequencer
pub phrases: Arc<RwLock<PhrasePool<E>>>, pub phrases: Arc<RwLock<PhrasePool<E>>>,
/// Phrase editor view /// Phrase editor view
pub editor: PhraseEditor<E>, pub editor: PhraseEditor<E>,
/// Which view is focused
pub focus: usize
} }
/// 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 /// Contains all phrases in a project
pub struct PhrasePool<E: Engine> { pub struct PhrasePool<E: Engine> {
_engine: PhantomData<E>, _engine: PhantomData<E>,
@ -93,6 +96,26 @@ pub struct PhrasePlayer<E: Engine> {
/// Send all notes off /// Send all notes off
pub reset: bool, // TODO?: after Some(nframes) 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> { impl<E: Engine> PhrasePool<E> {
pub fn new () -> Self { pub fn new () -> Self {
Self { Self {

View file

@ -13,18 +13,14 @@ pub struct SequencerCli {
/// Default phrase duration (in pulses; default: 4 * PPQ = 1 bar) /// 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) /// 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 { impl SequencerCli {
fn run (&self) -> Usually<()> { fn run (&self) -> Usually<()> {
let jack = JackClient::Inactive( let jack = Client::new("tek_arranger", ClientOptions::NO_START_SERVER)?.0;
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 jack_transport = jack.transport();
let mut transport = TransportToolbar::new(Some(jack_transport));
transport.set_focused(true);
let transport = Arc::new(RwLock::new(transport));
transport.write().unwrap().jack = Some( transport.write().unwrap().jack = Some(
jack.activate( jack.activate(
&transport.clone(), &transport.clone(),
@ -34,10 +30,10 @@ impl SequencerCli {
)? )?
); );
let seq = Sequencer { let seq = Sequencer {
focus: 0, focus_cursor: (1, 1),
transport: self.transport.then_some(transport),
editor: PhraseEditor::new(), editor: PhraseEditor::new(),
phrases: Arc::new(RwLock::new(PhrasePool::new())), phrases: Arc::new(RwLock::new(PhrasePool::new())),
transport: Some(transport),
}; };
if let Some(name) = self.name.as_ref() { if let Some(name) = self.name.as_ref() {
// TODO // TODO

View file

@ -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. /// Handle top-level events in standalone arranger.
impl Handle<Tui> for Sequencer<Tui> { impl Handle<Tui> for Sequencer<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> { fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
let focus = self.focus; if !match self.focused() {
let is_first_row = false; SequencerFocus::Transport => self.transport.handle(from)?,
let is_last_row = false; SequencerFocus::PhrasePool => self.phrases.handle(from)?,
SequencerFocus::PhraseEditor => self.editor.handle(from)?
}.unwrap_or(false) {
match from.event() { match from.event() {
key!(KeyCode::Char(' ')) => { // Tab navigation
if let Some(ref mut transport) = self.transport { key!(KeyCode::Tab) => { self.focus_next(); },
transport.write().unwrap().toggle_play()?; key!(Shift-KeyCode::Tab) => { self.focus_prev(); },
} else { key!(KeyCode::BackTab) => { self.focus_prev(); },
return Ok(None) key!(Shift-KeyCode::BackTab) => { self.focus_prev(); },
} // Directional navigation
}, key!(KeyCode::Up) => { self.focus_up(); },
key!(KeyCode::Tab) => { key!(KeyCode::Down) => { self.focus_down(); },
self.focus_next(); key!(KeyCode::Left) => { self.focus_left(); },
}, key!(KeyCode::Right) => { self.focus_right(); },
key!(KeyCode::BackTab) => { // Global play/pause binding
self.focus_prev(); key!(KeyCode::Char(' ')) => match self.transport {
}, Some(ref mut transport) => { transport.write().unwrap().toggle_play()?; },
key!(KeyCode::Down) => { None => { return Ok(None) }
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)
} }
};
Ok(Some(true)) Ok(Some(true))
} }
} }
@ -81,14 +55,6 @@ impl Content for PhrasePool<Tui> {
.fg(Color::Rgb(70, 80, 50)))) .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> { impl Handle<Tui> for PhrasePool<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> { fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
match from.event() { match from.event() {
@ -225,14 +191,6 @@ impl Handle<Tui> for PhraseEditor<Tui> {
return Ok(Some(true)) 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> { impl PhraseEditor<Tui> {
const H_KEYS_OFFSET: usize = 5; const H_KEYS_OFFSET: usize = 5;
/// Select which pattern to display. This pre-renders it to the buffer at full resolution. /// Select which pattern to display. This pre-renders it to the buffer at full resolution.

View file

@ -1,5 +1,4 @@
use crate::*; use crate::*;
impl TransportToolbar<Tui> { impl TransportToolbar<Tui> {
fn handle_bpm (&mut self, from: &TuiInput) -> Perhaps<bool> { fn handle_bpm (&mut self, from: &TuiInput) -> Perhaps<bool> {
match from.event() { match from.event() {
@ -28,7 +27,6 @@ impl TransportToolbar<Tui> {
return Ok(Some(true)) return Ok(Some(true))
} }
} }
impl Handle<Tui> for TransportToolbar<Tui> { impl Handle<Tui> for TransportToolbar<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> { fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
match from.event() { match from.event() {
@ -48,7 +46,6 @@ impl Handle<Tui> for TransportToolbar<Tui> {
Ok(Some(true)) Ok(Some(true))
} }
} }
impl Content for TransportToolbar<Tui> { impl Content for TransportToolbar<Tui> {
type Engine = Tui; type Engine = Tui;
fn content (&self) -> impl Widget<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)) ).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 { impl TransportToolbarFocus {
pub fn wrap <'a, W: Widget<Engine = Tui>> ( pub fn wrap <'a, W: Widget<Engine = Tui>> (
self, parent_focus: bool, focus: Self, widget: &'a W self, parent_focus: bool, focus: Self, widget: &'a W