event_loop: drop the text-input focus when the focused field vanishes from a rebuilt view
A view rebuild that no longer contains the focused text field left `focused_idx` pointing at a stale index and the `zwp_text_input_v3` object enabled. The compositor kept seeing an active text-input, so the on-screen keyboard stayed up over a screen with nothing to type into. The lockscreen's inactivity return from the password form to the idle screen is the visible case: the form disappears, the keyboard does not. `focus.rs` gains `surface_focused_text_input`, which resolves the focused widget by id when it has one and by index otherwise and reports whether it is a text input, and `reconcile_text_focus`, which clears the focus, deactivates the text-input and calls `on_text_input_focused( false )` when it no longer resolves to one. `draw_frame` notes, before drawing the main surface and each overlay, whether its focus was a text input, and reconciles those surfaces once every draw is done and no view borrow is live. Focus that lands on the field again through `take_focus_request` re-enables the text-input as before.
This commit is contained in:
@@ -80,6 +80,23 @@ impl<A: App> AppData<A>
|
||||
.and_then( |s| s.on_dismiss )
|
||||
}
|
||||
|
||||
/// After a rebuild: a focused text input that vanished from the frame
|
||||
/// would otherwise keep the text-input protocol (and the OSK) active.
|
||||
pub( crate ) fn reconcile_text_focus( &mut self, focus: SurfaceFocus )
|
||||
{
|
||||
if surface_focused_text_input( self.surface( focus ) ) { return; }
|
||||
let ss = self.surface_mut( focus );
|
||||
if let Some( idx ) = ss.focused_idx.take()
|
||||
{
|
||||
ss.pending_text_values.remove( &idx );
|
||||
}
|
||||
ss.focused_id = None;
|
||||
ss.request_redraw();
|
||||
self.deactivate_text_input();
|
||||
self.app.on_text_input_focused( false );
|
||||
self.dirty_caches();
|
||||
}
|
||||
|
||||
pub( crate ) fn set_focus( &mut self, focus: SurfaceFocus, idx: Option<usize>, qh: &QueueHandle<Self> )
|
||||
{
|
||||
let was_text_input;
|
||||
@@ -168,3 +185,15 @@ impl<A: App> AppData<A>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the surface's focused widget is a text input in its current frame.
|
||||
pub( crate ) fn surface_focused_text_input<Msg: Clone>( ss: &super::surface::SurfaceState<Msg> ) -> bool
|
||||
{
|
||||
let by_id = ss.focused_id.and_then( |id|
|
||||
{
|
||||
ss.frame.widget_rects.iter().find( |w| w.id == Some( id ) ).map( |w| &w.handlers )
|
||||
} );
|
||||
by_id
|
||||
.or_else( || ss.focused_idx.and_then( |i| find_handlers( &ss.frame.widget_rects, i ) ) )
|
||||
.is_some_and( |h| h.is_text_input() )
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::types::{ Color, Rect };
|
||||
use crate::widget::Element;
|
||||
|
||||
use super::AppData;
|
||||
use super::focus::surface_focused_text_input;
|
||||
use super::surface::{ SurfaceFocus, SurfaceState };
|
||||
use crate::draw::gles::{ draw_surface_full_gpu, draw_surface_partial_gpu };
|
||||
use crate::draw::software::{ draw_surface_full, draw_surface_partial };
|
||||
@@ -47,8 +48,13 @@ pub( crate ) fn draw_frame<A: App>( data: &mut AppData<A> ) -> bool
|
||||
let qh = &data.qh;
|
||||
|
||||
let mut first_commit_now = false;
|
||||
let mut text_focus_rebuilt: Vec<SurfaceFocus> = Vec::new();
|
||||
if data.main.configured && data.main.needs_redraw && !data.main.frame_pending
|
||||
{
|
||||
if surface_focused_text_input( &data.main )
|
||||
{
|
||||
text_focus_rebuilt.push( SurfaceFocus::Main );
|
||||
}
|
||||
let req_frame = | wl: &WlSurface | { let _ = wl.frame( qh, SurfaceFocus::Main ); };
|
||||
draw_surface::<A::Message>(
|
||||
&mut data.main,
|
||||
@@ -104,6 +110,10 @@ pub( crate ) fn draw_frame<A: App>( data: &mut AppData<A> ) -> bool
|
||||
{
|
||||
if !ss.configured || !ss.needs_redraw || ss.frame_pending { continue; }
|
||||
let focus = SurfaceFocus::Overlay( spec.id );
|
||||
if surface_focused_text_input( ss )
|
||||
{
|
||||
text_focus_rebuilt.push( focus );
|
||||
}
|
||||
let req_frame = | wl: &WlSurface | { let _ = wl.frame( qh, focus ); };
|
||||
let bg = Color::rgba( 0.0, 0.0, 0.0, 0.0 );
|
||||
draw_surface::<A::Message>(
|
||||
@@ -123,6 +133,11 @@ pub( crate ) fn draw_frame<A: App>( data: &mut AppData<A> ) -> bool
|
||||
}
|
||||
}
|
||||
|
||||
for focus in text_focus_rebuilt
|
||||
{
|
||||
data.reconcile_text_focus( focus );
|
||||
}
|
||||
|
||||
first_commit_now
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user