use super::*;
#[unsafe(no_mangle)]
pub extern "C" fn kui_access_tree(ptr: *mut KuiCtx, out: *mut KuiAccessNode, cap: usize) -> usize {
guard(0, || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return 0;
};
c.last_access = c.core().access_tree().clone();
let total = c.last_access.nodes.len();
if out.is_null() || cap == 0 {
return total;
}
let s = |s: &Option<String>| match s {
Some(s) => KuiStr {
ptr: s.as_ptr(),
len: s.len(),
},
None => KuiStr {
ptr: std::ptr::null(),
len: 0,
},
};
for (i, n) in c.last_access.nodes.iter().take(cap).enumerate() {
let mut flags = 0;
if n.value.is_some() {
flags |= KUI_ACCESS_HAS_VALUE;
}
if n.selection.is_some() {
flags |= KUI_ACCESS_HAS_SELECTION;
}
if n.focused {
flags |= KUI_ACCESS_FOCUSED;
}
if let Some(checked) = n.checked {
flags |= KUI_ACCESS_CHECKED_SET;
if checked {
flags |= KUI_ACCESS_CHECKED;
}
if n.mixed {
flags |= KUI_ACCESS_MIXED;
}
}
if n.number.is_some() {
flags |= KUI_ACCESS_HAS_NUMBER;
}
if n.min.is_some() {
flags |= KUI_ACCESS_HAS_MIN;
}
if n.max.is_some() {
flags |= KUI_ACCESS_HAS_MAX;
}
if n.scroll.is_some() {
flags |= KUI_ACCESS_HAS_SCROLL;
}
if n.anchor.is_some() && n.focus.is_some() {
flags |= KUI_ACCESS_HAS_TEXT_SELECTION;
}
if n.disabled {
flags |= KUI_ACCESS_DISABLED;
}
if let Some(selected) = n.selected {
flags |= KUI_ACCESS_SELECTED_SET;
if selected {
flags |= KUI_ACCESS_SELECTED;
}
}
if let Some(expanded) = n.expanded {
flags |= KUI_ACCESS_EXPANDED_SET;
if expanded {
flags |= KUI_ACCESS_EXPANDED;
}
}
if n.pos_in_set.is_some() {
flags |= KUI_ACCESS_HAS_POS_IN_SET;
}
if n.set_size.is_some() {
flags |= KUI_ACCESS_HAS_SET_SIZE;
}
if n.modal {
flags |= KUI_ACCESS_MODAL;
}
match n.live {
kui_core::Live::Off => {}
kui_core::Live::Polite => flags |= KUI_ACCESS_LIVE_POLITE,
kui_core::Live::Assertive => flags |= KUI_ACCESS_LIVE_ASSERTIVE,
}
let scroll = n.scroll.unwrap_or_default();
let (sel_start, sel_end) = n.selection.unwrap_or((0, 0));
unsafe {
out.add(i).write(KuiAccessNode {
key: n.key.0,
parent: n.parent.map_or(0, |k| k.0),
origin: n.origin.0 as u32,
role: role_code(n.role),
flags,
actions: n.actions,
name: s(&n.name),
description: s(&n.description),
value: s(&n.value),
x: n.rect.x,
y: n.rect.y,
w: n.rect.w,
h: n.rect.h,
caret: n.caret.unwrap_or(0) as u32,
selection_start: sel_start as u32,
selection_end: sel_end as u32,
value_now: n.number.unwrap_or(0.0),
value_min: n.min.unwrap_or(0.0),
value_max: n.max.unwrap_or(0.0),
scroll_x: scroll.x,
scroll_y: scroll.y,
scroll_max_x: scroll.max_x,
scroll_max_y: scroll.max_y,
anchor_run: n.anchor.map_or(0, |p| p.run.0),
anchor_char: n.anchor.map_or(0, |p| p.character as u32),
focus_run: n.focus.map_or(0, |p| p.run.0),
focus_char: n.focus.map_or(0, |p| p.character as u32),
run_count: n.runs.len() as u32,
pos_in_set: n.pos_in_set.unwrap_or(0) as u32,
set_size: n.set_size.unwrap_or(0) as u32,
orientation: orientation_code(n.orientation),
})
};
}
total
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_input_access(ptr: *mut KuiCtx, key: u64, action: u32, value: KuiStr) {
guard((), || {
let Some(action) = kui_core::AccessAction::ALL
.iter()
.copied()
.find(|a| a.bit() == action)
else {
return;
};
let value = (!value.ptr.is_null() && value.len > 0).then(|| kstr(value).into_owned());
push_input(
ptr,
InputEvent::Access(kui_core::AccessRequest {
key: Key(key),
action,
value,
anchor: None,
focus: None,
}),
);
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_access_runs(
ptr: *mut KuiCtx,
key: u64,
out: *mut KuiAccessRun,
cap: usize,
) -> usize {
guard(0, || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return 0;
};
c.last_runs = match c.core().access_tree().get(Key(key)) {
Some(node) => node.runs.clone(),
None => Vec::new(),
};
let total = c.last_runs.len();
if out.is_null() || cap == 0 {
return total;
}
for (i, r) in c.last_runs.iter().take(cap).enumerate() {
unsafe {
out.add(i).write(KuiAccessRun {
key: r.key.0,
line: r.line as u32,
start: r.start as u32,
end: r.end as u32,
text: KuiStr {
ptr: r.text.as_ptr(),
len: r.text.len(),
},
x: r.rect.x,
y: r.rect.y,
w: r.rect.w,
h: r.rect.h,
char_count: r.char_lengths.len() as u32,
char_lengths: r.char_lengths.as_ptr(),
char_positions: r.char_positions.as_ptr(),
char_widths: r.char_widths.as_ptr(),
word_start_count: r.word_starts.len() as u32,
word_starts: r.word_starts.as_ptr(),
rtl: r.rtl as u32,
})
};
}
total
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_input_access_text(
ptr: *mut KuiCtx,
key: u64,
action: u32,
anchor_run: u64,
anchor_char: u32,
focus_run: u64,
focus_char: u32,
value: KuiStr,
) {
guard((), || {
let Some(action) = kui_core::AccessAction::ALL
.iter()
.copied()
.find(|a| a.bit() == action)
else {
return;
};
let mut req = kui_core::AccessRequest::new(Key(key), action);
if !value.ptr.is_null() && value.len > 0 {
req.value = Some(kstr(value).into_owned());
}
if action == kui_core::AccessAction::SetTextSelection {
req.anchor = Some(kui_core::TextPos {
run: Key(anchor_run),
character: anchor_char as usize,
});
req.focus = Some(kui_core::TextPos {
run: Key(focus_run),
character: focus_char as usize,
});
}
push_input(ptr, InputEvent::Access(req));
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_announce(ptr: *mut KuiCtx, text: KuiStr, live: u32) {
guard((), || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return;
};
let text = kstr(text);
c.core()
.announce(&text, kui_core::Live::from_index(live as usize));
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_take_announcements(
ptr: *mut KuiCtx,
out: *mut KuiAnnouncement,
cap: usize,
) -> usize {
guard(0, || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return 0;
};
if out.is_null() || cap == 0 {
return 0;
}
c.last_announcements = c.core().take_announcements();
let n = c.last_announcements.len().min(cap);
for (i, a) in c.last_announcements.iter().take(n).enumerate() {
unsafe {
out.add(i).write(KuiAnnouncement {
text: KuiStr {
ptr: a.text.as_ptr(),
len: a.text.len(),
},
live: match a.live {
kui_core::Live::Off => KUI_LIVE_OFF,
kui_core::Live::Polite => KUI_LIVE_POLITE,
kui_core::Live::Assertive => KUI_LIVE_ASSERTIVE,
},
})
};
}
n
})
}