Skip to content

Commit e9532fe

Browse files
committed
BUGFIX: tab index from defered command
1 parent 5f4ca86 commit e9532fe

File tree

17 files changed

+419
-116
lines changed

17 files changed

+419
-116
lines changed

anathema-backend/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ impl<'rt, 'bp, T: Backend> WidgetCycle<'rt, 'bp, T> {
6060
// - Layout -
6161
// -----------------------------------------------------------------------------
6262
if needs_layout {
63-
let filter = LayoutFilter::all();
64-
self.layout(ctx, filter)?;
63+
self.layout(ctx, LayoutFilter)?;
6564
}
6665

6766
// -----------------------------------------------------------------------------
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use std::collections::VecDeque;
2+
3+
use anathema_widgets::components::events::{ComponentEvent, KeyEvent};
4+
5+
pub struct EventsMut<'a> {
6+
event_queue: &'a mut VecDeque<Option<ComponentEvent>>,
7+
}
8+
9+
impl EventsMut<'_> {
10+
pub fn next(self) -> Self {
11+
self.event_queue.push_back(None);
12+
self
13+
}
14+
15+
pub fn stop(self) -> Self {
16+
self.event_queue.push_back(Some(ComponentEvent::Stop));
17+
self
18+
}
19+
20+
pub fn press(self, event: KeyEvent) -> Self {
21+
self.event_queue.push_back(Some(ComponentEvent::Key(event)));
22+
self
23+
}
24+
}
25+
26+
#[derive(Debug)]
27+
pub struct Events {
28+
event_queue: VecDeque<Option<ComponentEvent>>,
29+
}
30+
31+
impl Events {
32+
pub(super) fn new() -> Self {
33+
Self {
34+
event_queue: VecDeque::new(),
35+
}
36+
}
37+
38+
pub(super) fn mut_ref(&mut self) -> EventsMut<'_> {
39+
EventsMut {
40+
event_queue: &mut self.event_queue,
41+
}
42+
}
43+
44+
pub(super) fn pop(&mut self) -> Option<ComponentEvent> {
45+
self.event_queue.pop_front().flatten()
46+
}
47+
}

anathema-backend/src/testing/mod.rs

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::collections::VecDeque;
21
use std::time::Duration;
32

43
use anathema_geometry::Size;
@@ -10,13 +9,16 @@ use surface::TestSurface;
109

1110
use crate::Backend;
1211

12+
mod events;
1313
mod surface;
1414

15-
pub struct GlyphSomething<'a> {
15+
#[derive(Debug)]
16+
/// This is used for testing
17+
pub struct GlyphRef<'a> {
1618
inner: Option<&'a Glyph>,
1719
}
1820

19-
impl<'a> GlyphSomething<'a> {
21+
impl<'a> GlyphRef<'a> {
2022
pub fn is_char(&self, rhs: char) -> bool {
2123
let Some(glyph) = self.inner else { return false };
2224
match glyph {
@@ -29,32 +31,40 @@ impl<'a> GlyphSomething<'a> {
2931
#[derive(Debug)]
3032
pub struct TestBackend {
3133
surface: TestSurface,
32-
event_queue: VecDeque<Option<ComponentEvent>>,
34+
events: events::Events,
3335
}
3436

3537
impl TestBackend {
3638
pub fn new(size: impl Into<Size>) -> Self {
3739
let size = size.into();
3840
Self {
3941
surface: TestSurface::new(size),
40-
// NOTE:
41-
// we have to start by return None for the first event,
42-
// as the first frame tick is there to populate the widget tree.
43-
//
44-
// This is required for tab indexing
45-
event_queue: VecDeque::from([None]),
42+
events: events::Events::new(),
4643
}
4744
}
4845

49-
pub fn add_event(&mut self, event: Option<ComponentEvent>) {
50-
self.event_queue.push_back(event);
51-
}
52-
53-
pub fn at(&self, x: usize, y: usize) -> GlyphSomething<'_> {
54-
GlyphSomething {
46+
pub fn at(&self, x: usize, y: usize) -> GlyphRef<'_> {
47+
GlyphRef {
5548
inner: self.surface.get(x, y),
5649
}
5750
}
51+
52+
pub fn line(&self, index: usize) -> String {
53+
let glyphs = self.surface.line(index);
54+
glyphs
55+
.iter()
56+
.filter_map(|g| match g {
57+
Glyph::Single(c, _) => Some(c),
58+
Glyph::Cluster(_, _) => None,
59+
})
60+
.collect::<String>()
61+
.trim()
62+
.to_string()
63+
}
64+
65+
pub fn events(&mut self) -> events::EventsMut<'_> {
66+
self.events.mut_ref()
67+
}
5868
}
5969

6070
impl Backend for TestBackend {
@@ -63,7 +73,7 @@ impl Backend for TestBackend {
6373
}
6474

6575
fn next_event(&mut self, _timeout: Duration) -> Option<ComponentEvent> {
66-
self.event_queue.pop_front()?
76+
self.events.pop()
6777
}
6878

6979
fn resize(&mut self, new_size: Size, glyph_map: &mut GlyphMap) {
@@ -76,6 +86,7 @@ impl Backend for TestBackend {
7686
widgets: PaintChildren<'_, 'bp>,
7787
attribute_storage: &AttributeStorage<'bp>,
7888
) {
89+
self.surface.clear();
7990
paint(&mut self.surface, glyph_map, widgets, attribute_storage);
8091
}
8192

anathema-backend/src/testing/surface.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,51 @@ use anathema_widgets::{GlyphMap, Style, WidgetRenderer};
77
pub struct TestSurface {
88
pub(super) size: Size,
99
lines: Vec<Glyph>,
10+
prev_lines: Vec<Glyph>,
1011
}
1112

1213
impl TestSurface {
1314
pub fn new(size: Size) -> Self {
1415
let len = size.width * size.height;
1516
let lines = vec![Glyph::space(); len as usize];
16-
Self { size, lines }
17+
Self {
18+
size,
19+
lines,
20+
prev_lines: vec![],
21+
}
1722
}
1823

1924
pub(crate) fn resize(&mut self, new_size: Size, _glyph_map: &mut GlyphMap) {
2025
self.size = new_size;
2126
todo!("truncate and pop lines outside of the new size")
2227
}
2328

29+
// Get from previous lines as the clear function has most likely
30+
// been called by the time this is relevant
2431
pub(crate) fn get(&self, x: usize, y: usize) -> Option<&Glyph> {
2532
let index = y * self.size.width as usize + x;
26-
self.lines.get(index)
33+
self.prev_lines.get(index)
34+
}
35+
36+
// Look at the old lines as the surface has been cleared
37+
// by the time we can inspect the lines
38+
pub(crate) fn line(&self, index: usize) -> &[Glyph] {
39+
let from = index * self.size.width as usize;
40+
let to = from + self.size.width as usize;
41+
&self.prev_lines[from..to]
42+
}
43+
44+
pub(crate) fn clear(&mut self) {
45+
let len = self.size.width * self.size.height;
46+
let mut lines = vec![Glyph::space(); len as usize];
47+
std::mem::swap(&mut lines, &mut self.lines);
48+
std::mem::swap(&mut lines, &mut self.prev_lines);
2749
}
2850
}
2951

3052
impl WidgetRenderer for TestSurface {
3153
fn draw_glyph(&mut self, mut glyph: Glyph, local_pos: Pos) {
32-
let index = self.size.height as i32 * local_pos.y + local_pos.x;
54+
let index = self.size.width as i32 * local_pos.y + local_pos.x;
3355
let index = index as usize;
3456
std::mem::swap(&mut glyph, &mut self.lines[index]);
3557
}

anathema-backend/src/tui/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ impl Backend for TuiBackend {
143143
attribute_storage: &AttributeStorage<'bp>,
144144
) {
145145
anathema_widgets::paint::paint(&mut self.screen, glyph_map, widgets, attribute_storage);
146-
// TODO: decide if we need `paint` to return a Result or not
147146
}
148147

149148
fn render(&mut self, glyph_map: &mut GlyphMap) {

anathema-runtime/src/runtime/mod.rs

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,7 @@ impl<G: GlobalEventHandler> Runtime<G> {
125125
// yet have any widgets or components.
126126
frame.tick(backend)?;
127127

128-
let mut changed = false;
129-
let mut tabindex = TabIndex::new(&mut frame.tabindex, frame.tree.view_mut(), &mut changed);
128+
let mut tabindex = TabIndex::new(&mut frame.tabindex, frame.tree.view_mut());
130129
tabindex.next();
131130

132131
if let Some(current) = frame.tabindex.as_ref() {
@@ -231,21 +230,19 @@ pub struct Frame<'rt, 'bp, G> {
231230
needs_layout: bool,
232231
stop: bool,
233232
global_event_handler: &'rt G,
234-
tabindex: Option<Index>,
233+
pub tabindex: Option<Index>,
235234
}
236235

237236
impl<'rt, 'bp, G: GlobalEventHandler> Frame<'rt, 'bp, G> {
238237
pub fn handle_global_event(&mut self, event: ComponentEvent) -> Option<ComponentEvent> {
239-
let mut changed = false;
240-
let mut tabindex = TabIndex::new(&mut self.tabindex, self.tree.view_mut(), &mut changed);
238+
let mut tabindex = TabIndex::new(&mut self.tabindex, self.tree.view_mut());
241239

242240
let event = self
243241
.global_event_handler
244242
.handle(event, &mut tabindex, self.deferred_components);
245243

246-
let prev = tabindex.consume();
247-
248-
if changed {
244+
if tabindex.changed {
245+
let prev = tabindex.consume();
249246
if let Some(prev) = prev {
250247
self.with_component(prev.widget_id, prev.state_id, |comp, children, ctx| {
251248
comp.dyn_component.any_blur(children, ctx)
@@ -315,6 +312,7 @@ impl<'rt, 'bp, G: GlobalEventHandler> Frame<'rt, 'bp, G> {
315312
puffin::GlobalProfiler::lock().new_frame();
316313

317314
let now = Instant::now();
315+
self.cycle(backend)?;
318316
self.tick_components(self.dt.elapsed());
319317
let elapsed = self.handle_messages(now);
320318
self.poll_events(elapsed, now, backend);
@@ -324,7 +322,6 @@ impl<'rt, 'bp, G: GlobalEventHandler> Frame<'rt, 'bp, G> {
324322
// TODO: this secondary call is here to deal with changes causing changes
325323
// which happens when values are removed or inserted and indices needs updating
326324
self.apply_changes()?;
327-
self.cycle(backend)?;
328325

329326
*self.dt = Instant::now();
330327
Ok(now.elapsed())
@@ -407,15 +404,6 @@ impl<'rt, 'bp, G: GlobalEventHandler> Frame<'rt, 'bp, G> {
407404
// Nb: Add drain_into to DeferredComponents
408405
let commands = self.deferred_components.drain().collect::<Vec<_>>();
409406
for mut cmd in commands {
410-
// Blur the current component if the message is a `Focus` message
411-
if let CommandKind::Focus = cmd.kind {
412-
if let Some(current) = self.tabindex.take() {
413-
self.with_component(current.widget_id, current.state_id, |comp, children, ctx| {
414-
comp.dyn_component.any_blur(children, ctx)
415-
});
416-
}
417-
}
418-
419407
for index in 0..self.layout_ctx.components.len() {
420408
let Some((widget_id, state_id)) = self.layout_ctx.components.get(index) else { continue };
421409
let Some(comp) = self.tree.get_ref(widget_id) else { continue };
@@ -431,18 +419,59 @@ impl<'rt, 'bp, G: GlobalEventHandler> Frame<'rt, 'bp, G> {
431419
continue;
432420
}
433421

434-
self.with_component(widget_id, state_id, |comp, children, ctx| match cmd.kind {
435-
CommandKind::Focus => {
436-
// Some(Index {
437-
// path: panic!(),//children.offset.into(),
438-
// index: comp.tabindex,
439-
// widget_id,
440-
// state_id,
441-
// });
442-
comp.dyn_component.any_focus(children, ctx);
422+
// -----------------------------------------------------------------------------
423+
// - Set focus -
424+
// TODO: here is another candidate for refactoring to make it
425+
// less cludgy and verbose.
426+
// -----------------------------------------------------------------------------
427+
// Blur the current component if the message is a `Focus` message
428+
if let CommandKind::Focus = cmd.kind {
429+
// If this component current has focus ignore this command
430+
if let Some(index) = self.tabindex.as_ref() {
431+
if index.widget_id == widget_id {
432+
continue;
433+
}
443434
}
444-
CommandKind::SendMessage(msg) => comp.dyn_component.any_message(children, ctx, msg),
445-
});
435+
436+
// here we can find the component that should receive focus
437+
let new_index = self
438+
.with_component(widget_id, state_id, |comp, children, ctx| {
439+
if comp.dyn_component.any_accept_focus() {
440+
let index = Index {
441+
path: children.parent_path().into(),
442+
index: comp.tabindex,
443+
widget_id,
444+
state_id,
445+
};
446+
447+
comp.dyn_component.any_focus(children, ctx);
448+
449+
Some(index)
450+
} else {
451+
None
452+
}
453+
})
454+
.flatten();
455+
456+
if let Some(index) = new_index {
457+
// If there is currently a component with focus that component
458+
// should only lose focus if the selected component accepts focus.
459+
if let Some(old) = self.tabindex.replace(index) {
460+
self.with_component(old.widget_id, old.state_id, |comp, children, ctx| {
461+
comp.dyn_component.any_blur(children, ctx)
462+
});
463+
}
464+
}
465+
}
466+
467+
// -----------------------------------------------------------------------------
468+
// - Send message -
469+
// -----------------------------------------------------------------------------
470+
if let CommandKind::SendMessage(msg) = cmd.kind {
471+
self.with_component(widget_id, state_id, |comp, children, ctx| {
472+
comp.dyn_component.any_message(children, ctx, msg);
473+
});
474+
}
446475
break;
447476
}
448477
}

0 commit comments

Comments
 (0)