use std::time::Duration;
use super::app_data::AppData;
use super::surface::{ Insets, SurfaceFocus };
use crate::app::App;
use crate::input::event::KeyInput;
use crate::osk::{ Event, Modifiers, Panel };
use crate::platform::Platform;
use crate::Keysym;
use crate::time::Instant;
use crate::tree::find_handlers;
use crate::types::{ Point, Rect };
pub( crate ) fn place_osk( osk: &mut Option<Panel>, pw: u32, ph: u32, sf: f32, insets: Insets ) -> f32
{
let Some( panel ) = osk.as_mut() else { return 0.0 };
let width = ( pw as f32 - ( insets.left + insets.right ) * sf ).max( 0.0 );
let bottom = ph as f32 - insets.bottom * sf;
let h = panel.height_within( width, sf, ph as f32 );
panel.set_rect( Rect { x: insets.left * sf, y: bottom - h, width, height: h }, sf );
h
}
impl<A: App, P: Platform> AppData<A, P>
{
fn builtin_keyboard( &self ) -> bool
{
self.builtin_osk || self.app.builtin_keyboard()
}
pub( crate ) fn show_osk( &mut self, focus: SurfaceFocus )
{
let ss = self.surface( focus );
let purpose = ss.focused_idx
.and_then( |i| find_handlers( &ss.frame.widget_rects, i ) )
.map( |h| h.input_purpose() )
.unwrap_or_default();
self.show_osk_for( focus, purpose );
}
pub( crate ) fn show_osk_for( &mut self, focus: SurfaceFocus, purpose: crate::osk::Purpose )
{
if !self.builtin_keyboard() { return; }
let ss = self.surface_mut( focus );
if ss.osk.is_none()
{
ss.osk = Some( Panel::for_language( &crate::platform::language() ) );
}
if let Some( panel ) = ss.osk.as_mut() { panel.set_purpose( purpose ); }
let sf = ss.scale_factor;
let ( pw, ph ) = ( ss.physical_width(), ss.physical_height() );
place_osk( &mut ss.osk, pw, ph, sf, ss.insets );
ss.request_redraw();
}
pub( crate ) fn hide_osk( &mut self, focus: SurfaceFocus )
{
let ss = self.surface_mut( focus );
if ss.osk.take().is_some() { ss.request_redraw(); }
}
pub( crate ) fn osk_after_press( &mut self, focus: SurfaceFocus, hit: Option<usize> )
{
let Some( idx ) = hit else { return };
let ss = self.surface( focus );
let is_text = find_handlers( &ss.frame.widget_rects, idx ).map( |h| h.is_text_input() ).unwrap_or( false );
if is_text && ss.osk.is_none() && self.press_on_focused { self.show_osk( focus ); }
}
pub( crate ) fn osk_press( &mut self, focus: SurfaceFocus, pos: Point ) -> bool
{
let now = Instant::now();
let Some( event ) = self.surface_mut( focus ).osk.as_mut().and_then( |p| p.press_down( pos, now ) ) else { return false };
self.apply_osk_event( focus, event );
true
}
pub( crate ) fn osk_move( &mut self, focus: SurfaceFocus, pos: Point ) -> bool
{
let ss = self.surface_mut( focus );
let Some( panel ) = ss.osk.as_mut().filter( |p| p.pressed().is_some() ) else { return false };
if panel.press_move( pos ) { ss.request_redraw(); }
true
}
pub( crate ) fn osk_release( &mut self, focus: SurfaceFocus, pos: Point ) -> bool
{
let now = Instant::now();
let Some( event ) = self.surface_mut( focus ).osk.as_mut().and_then( |p| p.press_up( pos, now ) ) else { return false };
self.apply_osk_event( focus, event );
true
}
pub( crate ) fn cancel_osk_presses( &mut self )
{
for ss in std::iter::once( &mut self.main ).chain( self.overlays.values_mut() )
{
if let Some( panel ) = ss.osk.as_mut() { panel.cancel_press(); }
}
}
pub( crate ) fn next_osk_wakeup( &self ) -> Option<Duration>
{
let now = Instant::now();
std::iter::once( &self.main ).chain( self.overlays.values() )
.filter_map( |ss| ss.osk.as_ref().and_then( Panel::next_deadline ) )
.map( |deadline| deadline.saturating_duration_since( now ) )
.min()
}
pub( crate ) fn check_osk_deadlines( &mut self )
{
let now = Instant::now();
let mut due: Vec<( SurfaceFocus, Event )> = Vec::new();
if let Some( event ) = self.main.osk.as_mut().and_then( |p| p.tick( now ) ) { due.push( ( SurfaceFocus::Main, event ) ); }
for ( id, ss ) in self.overlays.iter_mut()
{
if let Some( event ) = ss.osk.as_mut().and_then( |p| p.tick( now ) ) { due.push( ( SurfaceFocus::Overlay( *id ), event ) ); }
}
for ( focus, event ) in due { self.apply_osk_event( focus, event ); }
}
fn apply_osk_event( &mut self, focus: SurfaceFocus, event: Event )
{
if self.external_text_input.is_some() && !self.focused_text_input( focus )
{
self.apply_osk_event_to_app( focus, event );
return;
}
match event
{
Event::Text( text ) => self.handle_text_insert( focus, &text ),
Event::Backspace => self.handle_backspace( focus ),
Event::Enter => self.osk_enter( focus ),
Event::Key( keysym, mods ) => self.osk_key( focus, keysym, mods ),
Event::Hide => self.hide_osk( focus ),
Event::Changed => {}
}
self.surface_mut( focus ).request_redraw();
}
fn apply_osk_event_to_app( &mut self, focus: SurfaceFocus, event: Event )
{
match event
{
Event::Text( text ) =>
{
let keysym = text.chars().next().map_or( Keysym::NoSymbol, Keysym::from_char );
self.osk_app_key( keysym, Some( text ), Modifiers::default() );
}
Event::Backspace => self.osk_app_key( Keysym::BackSpace, None, Modifiers::default() ),
Event::Enter => self.osk_app_key( Keysym::Return, None, Modifiers::default() ),
Event::Key( keysym, mods ) => self.osk_app_key( keysym, None, mods ),
Event::Hide => self.hide_osk( focus ),
Event::Changed => {}
}
self.surface_mut( focus ).request_redraw();
}
fn osk_app_key( &mut self, keysym: Keysym, text: Option<String>, mods: Modifiers )
{
let text = text.or_else( || ( !mods.any() ).then( || keysym.key_char() ).flatten().map( String::from ) );
let held = ( self.ctrl_pressed, self.shift_pressed, self.alt_pressed );
self.modifiers_changed( mods.ctrl, held.1, mods.alt );
let key = KeyInput::synthetic( keysym, text );
self.key_press( key.clone() );
self.key_release( &key );
self.modifiers_changed( held.0, held.1, held.2 );
}
fn osk_key( &mut self, focus: SurfaceFocus, keysym: Keysym, mods: Modifiers )
{
let utf8 = ( !mods.any() ).then( || keysym.key_char() ).flatten().map( String::from );
let event = KeyInput::synthetic( keysym, utf8 );
let ctrl = std::mem::replace( &mut self.ctrl_pressed, mods.ctrl );
self.dispatch_key( focus, event );
self.ctrl_pressed = ctrl;
}
fn osk_enter( &mut self, focus: SurfaceFocus )
{
let ss = self.surface( focus );
let Some( idx ) = ss.focused_idx else { return };
let Some( handlers ) = find_handlers( &ss.frame.widget_rects, idx ) else { return };
if handlers.is_multiline_text_input()
{
self.handle_text_insert( focus, "\n" );
} else if let Some( msg ) = handlers.submit_msg().or_else( || handlers.press_msg() ) {
self.pending_msgs.push( msg );
}
}
}