use accesskit::{ Action, ActionData, ActionRequest };
use crate::a11y::tree::{ build_tree, parse_id, SurfaceView };
use crate::app::{ App, OverlayId };
use crate::platform::Platform;
use crate::types::Point;
use crate::widget::WidgetHandlers;
use super::{ AppData, SurfaceFocus };
impl<A: App, P: Platform> AppData<A, P>
{
pub( crate ) fn publish_a11y_tree( &mut self )
{
let Some( mut a11y ) = self.a11y.take() else { return };
let main_w = self.main.width as f32;
let main_h = self.main.height as f32;
let mut overlay_meta: Vec<( u8, OverlayId, f32, f32 )> = Vec::new();
let mut next_id: u8 = 1;
for ( id, ss ) in self.overlays.iter()
{
if next_id == 0 { break; }
let ( ox, oy ) = self.surface_offset_for( SurfaceFocus::Overlay( *id ) );
let ( w, h ) = ( ss.width as f32, ss.height as f32 );
if w <= 0.0 || h <= 0.0 || ( ox + w <= 0.0 ) || ( oy + h <= 0.0 ) || ox >= main_w || oy >= main_h { continue; }
overlay_meta.push( ( next_id, *id, ox, oy ) );
next_id = next_id.saturating_add( 1 );
}
let kb_focus_id = match self.keyboard_focus
{
SurfaceFocus::Main => 0u8,
SurfaceFocus::Overlay( id ) =>
overlay_meta.iter().find( |( _, oid, _, _ )| *oid == id ).map( |( i, _, _, _ )| *i ).unwrap_or( 0 ),
};
let mut surfaces: Vec<SurfaceView<A::Message>> = Vec::new();
surfaces.push( SurfaceView
{
focus_id: 0,
is_main: true,
label: None,
widget_rects: &self.main.frame.widget_rects,
extras: &self.main.frame.accessible_extras,
focused_idx: self.main.focused_idx,
pressed_idx: self.main.gesture.pressed_idx,
pending_text_values: &self.main.pending_text_values,
cursor_state: &self.main.frame.cursor_state,
width: main_w,
height: main_h,
offset_x: 0.0,
offset_y: 0.0,
} );
for ( fid, id, ox, oy ) in &overlay_meta
{
let Some( ss ) = self.overlays.get( id ) else { continue };
surfaces.push( SurfaceView
{
focus_id: *fid,
is_main: false,
label: None,
widget_rects: &ss.frame.widget_rects,
extras: &ss.frame.accessible_extras,
focused_idx: ss.focused_idx,
pressed_idx: ss.gesture.pressed_idx,
pending_text_values: &ss.pending_text_values,
cursor_state: &ss.frame.cursor_state,
width: ss.width as f32,
height: ss.height as f32,
offset_x: *ox,
offset_y: *oy,
} );
}
let app_name = self.app.app_id();
a11y.update( || build_tree( &surfaces, kb_focus_id, app_name ) );
self.a11y = Some( a11y );
}
pub( crate ) fn apply_a11y_actions( &mut self )
{
let requests: Vec<ActionRequest> = match self.a11y
{
Some( ref a ) => a.action_rx.try_iter().collect(),
None => return,
};
if requests.is_empty() { return; }
let overlay_keys: Vec<OverlayId> = self.overlays.keys().copied().collect();
for req in requests
{
let Some( r ) = parse_id( req.target ) else { continue };
if r.kind != 0 { continue; }
let idx = r.idx as usize;
let focus_target = if r.focus == 0
{
SurfaceFocus::Main
} else {
let oi = ( r.focus as usize ).saturating_sub( 1 );
let Some( id ) = overlay_keys.get( oi ).copied() else { continue };
SurfaceFocus::Overlay( id )
};
let widget = self.try_surface( focus_target )
.and_then( |ss| ss.frame.widget_rects.iter().find( |w| w.flat_idx == idx ).cloned() );
match req.action
{
Action::Click =>
{
if let Some( msg ) = widget.as_ref().and_then( |w| w.handlers.press_msg() )
{
self.pending_msgs.push( msg );
}
}
Action::Focus => self.set_focus( focus_target, Some( idx ) ),
Action::SetValue =>
{
let Some( w ) = widget else { continue };
let msg = match ( &w.handlers, req.data )
{
( WidgetHandlers::Slider { .. }, Some( ActionData::NumericValue( v ) ) ) => w.handlers.slider_change_msg( v.clamp( 0.0, 1.0 ) as f32 ),
( WidgetHandlers::TextEdit { .. }, Some( ActionData::Value( s ) ) ) => w.handlers.text_change_msg( &s ),
_ => None,
};
self.pending_msgs.extend( msg );
}
Action::Increment | Action::Decrement =>
{
let Some( w ) = widget else { continue };
let WidgetHandlers::Slider { value, .. } = &w.handlers else { continue };
let step = if matches!( req.action, Action::Increment ) { 0.05 } else { -0.05 };
if let Some( msg ) = w.handlers.slider_change_msg( ( *value + step ).clamp( 0.0, 1.0 ) )
{
self.pending_msgs.push( msg );
}
}
Action::ScrollUp | Action::ScrollDown | Action::ScrollLeft | Action::ScrollRight =>
{
const STEP: f32 = 80.0;
let ( dx, dy ) = match req.action
{
Action::ScrollLeft => ( -STEP, 0.0 ),
Action::ScrollRight => ( STEP, 0.0 ),
Action::ScrollUp => ( 0.0, -STEP ),
_ => ( 0.0, STEP ),
};
let Some( ss ) = self.try_surface_mut( focus_target ) else { continue };
let Some( &( _, scroll_idx, _ ) ) = ss.frame.scroll_rects.last() else { continue };
let entry = ss.frame.scroll_offsets.entry( scroll_idx ).or_insert( ( 0.0, 0.0 ) );
entry.0 = ( entry.0 + dx ).max( 0.0 );
entry.1 = ( entry.1 + dy ).max( 0.0 );
ss.request_redraw();
}
Action::ScrollIntoView =>
{
let Some( w ) = widget else { continue };
let Some( ss ) = self.try_surface_mut( focus_target ) else { continue };
let target = w.rect;
let probe = Point { x: target.x + 1.0, y: target.y + 1.0 };
let Some( ( r, idx, _ ) ) = ss.frame.scroll_rects.iter().rev()
.find( |( r, _, _ )| r.contains( probe ) )
.copied()
else { continue };
let entry = ss.frame.scroll_offsets.entry( idx ).or_insert( ( 0.0, 0.0 ) );
if target.y < r.y { entry.1 -= r.y - target.y; }
if target.y + target.height > r.y + r.height
{
entry.1 += ( target.y + target.height ) - ( r.y + r.height );
}
if target.x < r.x { entry.0 -= r.x - target.x; }
if target.x + target.width > r.x + r.width
{
entry.0 += ( target.x + target.width ) - ( r.x + r.width );
}
entry.0 = entry.0.max( 0.0 );
entry.1 = entry.1.max( 0.0 );
ss.request_redraw();
}
_ => {}
}
}
}
}