use std::time::Duration;
use crate::app::{ App, InvalidationScope, SelectionKind, TextCommand };
use crate::platform::Platform;
use crate::tree::find_handlers;
use crate::widget::WidgetHandlers;
use super::{ AppData, SurfaceFocus };
use super::frame::draw_frame;
use super::invalidation::apply_invalidation;
const FOCUS_RETRY_FRAMES: u8 = 4;
impl<A: App, P: Platform> AppData<A, P>
{
pub( crate ) fn next_wakeup( &self ) -> Option<Duration>
{
[ self.next_long_press_wakeup(), self.next_tooltip_wakeup(), self.next_osk_wakeup(), self.next_primary_paste_wakeup() ]
.into_iter()
.flatten()
.min()
}
pub( crate ) fn before_draw( &mut self )
{
self.check_long_press_deadlines();
self.check_tooltip_deadline();
self.check_osk_deadlines();
self.check_primary_paste();
let ext: Vec<_> = self.app.poll_external();
self.pending_msgs.extend( ext );
self.process_messages();
if self.app.needs_repaint()
{
self.main.request_redraw();
for ( _, ss ) in self.secondaries.iter_mut()
{
ss.request_redraw();
}
}
if self.app.requested_exit() { self.exit_requested = true; }
self.sync_fullscreen();
self.sync_external_text_input();
let drag_inits: Vec<_> = self.pending_drag_inits.drain( .. ).collect();
if !drag_inits.is_empty()
{
for origin in drag_inits
{
self.app.on_drag_move( origin.x, origin.y );
}
self.dirty_caches();
self.main.request_redraw();
}
self.main.pending_text_values.clear();
for ss in self.overlays.values_mut()
{
ss.pending_text_values.clear();
}
}
pub( crate ) fn after_draw( &mut self, drawn: bool )
{
if drawn { self.publish_a11y_tree(); }
self.apply_a11y_actions();
if self.app.take_blur_request()
{
let focus = self.keyboard_focus;
self.set_focus( focus, None );
self.focus_retry = None;
}
if let Some( text ) = self.app.take_clipboard_request()
{
self.publish_clipboard( text );
}
self.sync_text_field();
if let Some( text ) = self.app.take_primary_request()
{
self.publish_primary_selection( text );
}
match self.app.take_paste_request()
{
Some( SelectionKind::Clipboard ) =>
{
let text = self.clipboard.clone();
if !text.is_empty() { self.app.on_paste( &text ); }
}
Some( SelectionKind::Primary ) => self.paste_primary( super::clipboard::PrimaryPaste::App ),
None => {}
}
self.apply_focus_request( drawn );
self.apply_text_command();
self.report_text_cursors();
}
fn report_text_cursors( &mut self )
{
let surfaces = std::iter::once( SurfaceFocus::Main )
.chain( self.overlays.keys().map( |id| SurfaceFocus::Overlay( *id ) ) )
.collect::<Vec<_>>();
let mut reports = Vec::new();
for focus in surfaces
{
let frame = &self.surface( focus ).frame;
for w in &frame.widget_rects
{
let WidgetHandlers::TextEdit { value, on_cursor: Some( _ ), .. } = &w.handlers else { continue };
let Some( &at ) = frame.cursor_state.get( &w.flat_idx ) else { continue };
let seen = ( at.min( value.len() ), value.len() );
if self.reported_cursors.get( &( focus, w.flat_idx ) ) == Some( &seen ) { continue; }
if let Some( msg ) = w.handlers.text_cursor_msg( seen.0 )
{
reports.push( ( ( focus, w.flat_idx ), seen, msg ) );
}
}
}
if reports.is_empty() { return; }
for ( key, seen, msg ) in reports
{
self.reported_cursors.insert( key, seen );
self.pending_msgs.push( msg );
}
self.main.request_redraw();
}
fn find_widget( &self, id: crate::types::WidgetId ) -> Option<( SurfaceFocus, usize )>
{
self.main.frame.widget_rects.iter()
.find( |w| w.id == Some( id ) )
.map( |w| ( SurfaceFocus::Main, w.flat_idx ) )
.or_else( || self.overlays.iter().find_map( |( ov_id, surf )|
{
surf.frame.widget_rects.iter()
.find( |w| w.id == Some( id ) )
.map( |w| ( SurfaceFocus::Overlay( *ov_id ), w.flat_idx ) )
} ) )
}
fn apply_text_command( &mut self )
{
let Some( ( id, command ) ) = self.app.take_text_command() else { return };
let Some( ( focus, idx ) ) = self.find_widget( id ) else { return };
if !matches!( command, TextCommand::Reveal { .. } ) && self.surface( focus ).focused_idx != Some( idx )
{
self.set_focus( focus, Some( idx ) );
}
match command
{
TextCommand::Copy => self.handle_copy( focus ),
TextCommand::Cut => self.handle_cut( focus ),
TextCommand::Paste => self.handle_paste( focus ),
TextCommand::SelectAll => self.handle_select_all( focus ),
TextCommand::Select { start, end } | TextCommand::Reveal { start, end } => self.select_text_range( focus, idx, start, end ),
}
if let Some( ss ) = self.try_surface_mut( focus ) { ss.request_redraw(); }
}
fn select_text_range( &mut self, focus: SurfaceFocus, idx: usize, start: usize, end: usize )
{
let ss = self.surface( focus );
let value = ss.pending_text_values.get( &idx ).cloned()
.or_else( || find_handlers( &ss.frame.widget_rects, idx )?.current_value().map( str::to_string ) );
let Some( value ) = value else { return };
let boundary = |at: usize|
{
let mut at = at.min( value.len() );
while !value.is_char_boundary( at ) { at -= 1; }
at
};
let ( start, end ) = ( boundary( start ), boundary( end ) );
let frame = &mut self.surface_mut( focus ).frame;
frame.cursor_state.insert( idx, end );
frame.selection_anchor.insert( idx, start );
}
pub( crate ) fn frame_presented( &mut self, focus: SurfaceFocus, hosts_subsurfaces: bool )
{
let animating = self.app.is_animating();
match focus
{
SurfaceFocus::Main =>
{
self.main.frame_pending = false;
if !animating
{
self.perf.animation_stopped();
return;
}
if hosts_subsurfaces && self.app.subsurface_motion_only()
{
self.main.frame_tick_due = true;
return;
}
let cap = self.app.cap_software_animation();
if self.perf.animated_frame( crate::render::is_software_render(), cap )
{
self.view_dirty = true;
self.main.request_redraw();
} else if self.platform.request_frame( &self.main.platform, SurfaceFocus::Main ) {
self.main.frame_pending = true;
}
}
SurfaceFocus::Overlay( id ) =>
{
let motion_only = hosts_subsurfaces && self.app.subsurface_motion_only();
let Some( ss ) = self.overlays.get_mut( &id ) else { return };
ss.frame_pending = false;
if !animating { return; }
if motion_only
{
ss.frame_tick_due = true;
} else {
ss.request_redraw();
self.overlays_dirty = true;
}
}
}
}
fn apply_focus_request( &mut self, drawn: bool )
{
let retry = self.focus_retry.take();
let Some( ( id, frames_left ) ) = self.app.take_focus_request()
.map( |id| ( id, FOCUS_RETRY_FRAMES ) )
.or( retry )
else { return };
if let Some( ( focus, flat_idx ) ) = self.find_widget( id )
{
self.set_focus( focus, Some( flat_idx ) );
if let Some( ss ) = self.try_surface_mut( focus ) { ss.request_redraw(); }
return;
}
let frames_left = if drawn { frames_left.saturating_sub( 1 ) } else { frames_left };
if frames_left > 0
{
self.reveal_clipped_widget( id );
self.focus_retry = Some( ( id, frames_left ) );
self.view_dirty = true;
self.main.request_redraw();
}
}
pub( crate ) fn process_messages( &mut self )
{
let msgs: Vec<_> = self.pending_msgs.drain( .. ).collect();
if msgs.is_empty() { return; }
let mut scope = InvalidationScope::Only( Vec::new() );
for msg in msgs
{
scope = scope.union( self.app.invalidate_after( &msg ) );
self.app.update( msg );
}
apply_invalidation( self, scope );
let pf = self.pointer_focus;
self.dispatch_cursor_shape( pf );
}
pub( crate ) fn draw_due( &mut self ) -> bool
{
let any_drawable = ( self.main.configured && self.main.needs_redraw && !self.main.frame_pending )
|| self.overlays.values().any( |ss| ss.configured && ss.needs_redraw && !ss.frame_pending )
|| self.secondaries.iter().any( |( _, ss )| ss.configured && ss.needs_redraw && !ss.frame_pending );
if !any_drawable { return false; }
let runtime_slider_motion =
self.main.gesture.dragging_slider.is_some()
|| self.overlays.values().any( |ss| ss.gesture.dragging_slider.is_some() );
if runtime_slider_motion
{
self.view_dirty = true;
self.overlays_dirty = true;
}
if self.view_dirty
{
self.cached_view = Some( self.app.view() );
self.view_dirty = false;
}
if self.overlays_dirty
{
let mut specs = self.app.overlays();
if let Some( ts ) = self.tooltip_overlay() { specs.push( ts ); }
self.cached_overlays = Some( specs );
self.overlays_dirty = false;
}
if draw_frame( self )
{
self.app.on_first_frame_committed();
}
true
}
}