use std::collections::HashMap;
use super::app_data::AppData;
use super::surface::SurfaceFocus;
use crate::app::{ App, OverlayId };
use crate::tree::{ find_handlers, find_widget };
use crate::types::{ Point, WidgetId };
use crate::widget::WidgetHandlers;
use crate::widget::scroll::offset_into_view;
use crate::platform::Platform;
impl<A: App, P: Platform> AppData<A, P>
{
pub( crate ) fn dismiss_main_outside_popups( &mut self, pos: Point )
{
if self.overlays.is_empty() { return; }
let specs = self.app.overlays();
for spec in specs
{
let Some( anchor_id ) = spec.anchor_widget_id else { continue; };
let anchor_rect = self.main.frame.widget_rects.iter()
.find( | w | w.id == Some( anchor_id ) )
.map( | w | w.rect );
let on_anchor = anchor_rect
.map( | r | pos.x >= r.x && pos.x < r.x + r.width
&& pos.y >= r.y && pos.y < r.y + r.height )
.unwrap_or( false );
if !on_anchor
{
if let Some( msg ) = spec.on_dismiss
{
self.pending_msgs.push( msg );
}
}
}
}
pub( crate ) fn dismiss_all_popups( &mut self )
{
if self.overlays.is_empty() { return; }
let specs = self.app.overlays();
for spec in specs
{
if spec.anchor_widget_id.is_some()
{
if let Some( msg ) = spec.on_dismiss
{
self.pending_msgs.push( msg );
}
}
}
}
pub( crate ) fn overlay_dismiss_msg( &self, id: OverlayId ) -> Option<A::Message>
{
self.app.overlays().into_iter()
.find( |s| s.id == id )
.and_then( |s| s.on_dismiss )
}
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 focus_pressed( &mut self, focus: SurfaceFocus, hit: Option<usize> )
{
let rects = &self.surface( focus ).frame.widget_rects;
if hit.and_then( |idx| find_widget( rects, idx ) ).is_some_and( |w| !w.focus_on_press ) { return; }
self.show_focus_ring( focus, false );
self.set_focus( focus, hit );
}
pub( crate ) fn show_focus_ring( &mut self, focus: SurfaceFocus, visible: bool )
{
let ss = self.surface_mut( focus );
if ss.focus_visible == visible { return; }
ss.focus_visible = visible;
ss.content_dirty = true;
ss.request_redraw();
}
pub( crate ) fn set_focus( &mut self, focus: SurfaceFocus, idx: Option<usize> )
{
let was_text_input;
let is_text_input;
let secure;
let purpose;
let focus_msg;
{
let ss = self.surface_mut( focus );
focus_msg = idx
.filter( |i| ss.focused_idx != Some( *i ) )
.and_then( |i| find_handlers( &ss.frame.widget_rects, i ) )
.and_then( |h| match h
{
WidgetHandlers::TextEdit { on_focus, .. } => on_focus.clone(),
_ => None,
} );
is_text_input = idx
.and_then( |i| find_handlers( &ss.frame.widget_rects, i ) )
.map( |h| h.is_text_input() )
.unwrap_or( false );
secure = idx
.and_then( |i| find_handlers( &ss.frame.widget_rects, i ) )
.map( |h| matches!( h, WidgetHandlers::TextEdit { secure: true, .. } ) )
.unwrap_or( false );
purpose = idx
.and_then( |i| find_handlers( &ss.frame.widget_rects, i ) )
.map( |h| h.input_purpose() )
.unwrap_or_default();
was_text_input = ss.focused_idx
.and_then( |i| find_handlers( &ss.frame.widget_rects, i ) )
.map( |h| h.is_text_input() )
.unwrap_or( false );
if let Some( prev_idx ) = ss.focused_idx
{
if idx != Some( prev_idx )
{
ss.pending_text_values.remove( &prev_idx );
}
}
ss.focused_idx = idx;
ss.focused_id = idx.and_then( |i|
{
ss.frame.widget_rects.iter()
.find( |w| w.flat_idx == i )
.and_then( |w| w.id )
} );
ss.request_redraw();
if is_text_input
{
if let Some( i ) = idx
{
let handler = find_handlers( &ss.frame.widget_rects, i );
let anchor = match handler
{
Some( WidgetHandlers::TextEdit { select_on_focus: true, .. } ) => 0,
_ => usize::MAX,
};
ss.frame.cursor_state.insert( i, usize::MAX );
ss.frame.selection_anchor.insert( i, anchor );
}
}
}
if let Some( msg ) = focus_msg { self.pending_msgs.push( msg ); }
if was_text_input && !is_text_input
{
self.deactivate_text_input();
self.hide_osk( focus );
self.app.on_text_input_focused( false );
self.dirty_caches();
}
else if is_text_input && !was_text_input
{
self.activate_text_input( secure, purpose );
self.show_osk( focus );
self.app.on_text_input_focused( true );
self.dirty_caches();
}
else if is_text_input
{
self.activate_text_input( secure, purpose );
self.show_osk( focus );
}
}
pub( crate ) fn reveal_clipped_widget( &mut self, id: WidgetId )
{
for ss in std::iter::once( &mut self.main ).chain( self.overlays.values_mut() )
{
let Some( &( _, scroll_idx, target ) ) = ss.frame.scroll_clipped.iter().find( |( w, _, _ )| *w == id ) else { continue };
let Some( &( viewport, _, axis ) ) = ss.frame.scroll_rects.iter().find( |( _, i, _ )| *i == scroll_idx ) else { continue };
let entry = ss.frame.scroll_offsets.entry( scroll_idx ).or_insert( ( 0.0, 0.0 ) );
if axis.allows_x()
{
entry.0 = offset_into_view( entry.0, target.x, target.width, viewport.width );
}
if axis.allows_y()
{
entry.1 = offset_into_view( entry.1, target.y, target.height, viewport.height );
}
ss.request_redraw();
return;
}
}
}
pub( crate ) fn follow_focused_id<Msg: Clone, S>( ss: &mut super::surface::SurfaceState<Msg, S> ) -> bool
{
let ( Some( id ), Some( old ) ) = ( ss.focused_id, ss.focused_idx ) else { return false };
if find_widget( &ss.frame.widget_rects, old ).is_some_and( |w| w.id == Some( id ) ) { return false; }
let Some( new ) = ss.frame.widget_rects.iter().find( |w| w.id == Some( id ) ).map( |w| w.flat_idx ) else
{
if ss.frame.scroll_clipped.iter().any( |( clipped, .. )| *clipped == id ) { return false; }
ss.focused_idx = None;
ss.focused_id = None;
return true;
};
move_entry( &mut ss.pending_text_values, old, new );
move_entry( &mut ss.frame.cursor_state, old, new );
move_entry( &mut ss.frame.selection_anchor, old, new );
move_entry( &mut ss.frame.text_scroll, old, new );
ss.focused_idx = Some( new );
true
}
fn move_entry<V>( map: &mut HashMap<usize, V>, from: usize, to: usize )
{
match map.remove( &from )
{
Some( value ) => { map.insert( to, value ); }
None => { map.remove( &to ); }
}
}
pub( crate ) fn drop_misplaced_focus<Msg: Clone, S>( ss: &mut super::surface::SurfaceState<Msg, S> )
{
let misplaced = ss.focused_idx
.and_then( |idx| find_widget( &ss.frame.widget_rects, idx ) )
.is_some_and( |w| !w.focus_on_press );
if misplaced
{
ss.focused_idx = None;
ss.focused_id = None;
}
}
pub( crate ) fn surface_focused_text_input<Msg: Clone, S>( ss: &super::surface::SurfaceState<Msg, S> ) -> 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() )
}