event_loop: retry focus requests that land before the view is rebuilt
`take_focus_request()` looks up the target widget by `WidgetId` in `widget_rects`. Read-only `TextEdit` widgets are not tracked as interactive and therefore absent from `widget_rects`. A swipe-reveal flips the fields from read-only to interactive on `SlideRevealed`, but an intervening input event (e.g. the touch-up at the end of the swipe) can cause `dispatch()` to return before the vblank frame callback clears `frame_pending`, skipping the draw that would have rebuilt the widget tree. When the focus request fires in that same iteration it finds nothing and is consumed without effect. `AppData` gains a `focus_retry` field. When `take_focus_request()` (or a previous retry) returns an id but the widget is not in `widget_rects`, the id is stored in `focus_retry`, `view_dirty` is set and a redraw is requested. The next time around the view has been rebuilt with interactive fields and the widget is present.
This commit is contained in:
@@ -248,6 +248,9 @@ pub struct AppData<A: App>
|
||||
/// Set after the first commit of a rendered buffer on the main surface.
|
||||
/// Used to drive `App::on_first_frame_committed` exactly once.
|
||||
pub first_frame_committed: bool,
|
||||
/// Focus request deferred because the target widget was absent from
|
||||
/// `widget_rects` (e.g. read_only, not yet rebuilt). Retried next draw.
|
||||
pub focus_retry: Option<crate::types::WidgetId>,
|
||||
}
|
||||
|
||||
impl<A: App> AppData<A>
|
||||
|
||||
@@ -329,6 +329,7 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
||||
view_dirty: true,
|
||||
overlays_dirty: true,
|
||||
first_frame_committed: false,
|
||||
focus_retry: None,
|
||||
};
|
||||
|
||||
// Register a calloop channel so the app can send messages from any thread.
|
||||
@@ -809,7 +810,8 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
||||
// Walks main + every overlay because the targeted widget can live
|
||||
// on any of them (a search field on a launcher overlay, a text
|
||||
// edit on a dialog modal, …).
|
||||
if let Some( id ) = data.app.take_focus_request()
|
||||
let focus_id = data.app.take_focus_request().or_else( || data.focus_retry.take() );
|
||||
if let Some( id ) = focus_id
|
||||
{
|
||||
let mut hit: Option<( SurfaceFocus, usize )> = data.main.widget_rects.iter()
|
||||
.find( |w| w.id == Some( id ) )
|
||||
@@ -840,6 +842,10 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data.focus_retry = Some( id );
|
||||
data.view_dirty = true;
|
||||
data.main.request_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user