use accesskit::NodeId;
use oxiui_accessibility::{A11yNode, A11yTree, WidgetRole};
use oxiui_core::{response::WidgetResponse, ButtonResponse, RichTextSpan, UiCtx};
struct NodeIdGen(u64);
impl NodeIdGen {
fn new() -> Self {
NodeIdGen(1)
}
fn next(&mut self) -> NodeId {
let id = NodeId(self.0);
self.0 += 1;
id
}
}
#[derive(Clone, Debug)]
pub struct RecordingEntry {
pub role: WidgetRole,
pub label: String,
pub children: Vec<RecordingEntry>,
}
pub struct RecordingUiCtx<'a> {
delegate: Option<&'a mut dyn UiCtx>,
pub entries: Vec<RecordingEntry>,
}
impl<'a> RecordingUiCtx<'a> {
pub fn new() -> Self {
RecordingUiCtx {
delegate: None,
entries: Vec::new(),
}
}
pub fn with_delegate(delegate: &'a mut dyn UiCtx) -> Self {
RecordingUiCtx {
delegate: Some(delegate),
entries: Vec::new(),
}
}
fn record(&mut self, role: WidgetRole, label: impl Into<String>) {
self.entries.push(RecordingEntry {
role,
label: label.into(),
children: Vec::new(),
});
}
fn record_group(&mut self, role: WidgetRole, label: &str, children: Vec<RecordingEntry>) {
self.entries.push(RecordingEntry {
role,
label: label.to_string(),
children,
});
}
pub fn build_a11y_tree(&self, root_id: oxiui_accessibility::WindowA11yId) -> A11yTree {
let mut gen = NodeIdGen::new();
let root_node_id = NodeId(root_id.0);
let children: Vec<A11yNode> = self
.entries
.iter()
.map(|e| entry_to_node(e, &mut gen))
.collect();
let mut root = A11yNode::simple(root_node_id, WidgetRole::Window, None);
root.children = children;
let mut tree = A11yTree::default();
let _ = tree.build_and_store(&root);
tree
}
}
impl<'a> Default for RecordingUiCtx<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> UiCtx for RecordingUiCtx<'a> {
fn heading(&mut self, text: &str) {
self.record(WidgetRole::Label, text);
if let Some(d) = self.delegate.as_mut() {
d.heading(text);
}
}
fn label(&mut self, text: &str) {
self.record(WidgetRole::Label, text);
if let Some(d) = self.delegate.as_mut() {
d.label(text);
}
}
fn button(&mut self, label: &str) -> ButtonResponse {
self.record(WidgetRole::Button, label);
if let Some(d) = self.delegate.as_mut() {
d.button(label)
} else {
ButtonResponse::default()
}
}
fn horizontal(&mut self, content: &mut dyn FnMut(&mut dyn UiCtx)) -> WidgetResponse {
let mut child_ctx = RecordingUiCtx::new();
content(&mut child_ctx);
let children = child_ctx.entries;
self.record_group(WidgetRole::Group, "horizontal", children);
if let Some(d) = self.delegate.as_mut() {
let mut noop: NullDelegate = NullDelegate;
d.horizontal(&mut |_ui| noop.visit());
}
WidgetResponse::supported()
}
fn vertical(&mut self, content: &mut dyn FnMut(&mut dyn UiCtx)) -> WidgetResponse {
let mut child_ctx = RecordingUiCtx::new();
content(&mut child_ctx);
let children = child_ctx.entries;
self.record_group(WidgetRole::Group, "vertical", children);
if let Some(d) = self.delegate.as_mut() {
let mut noop = NullDelegate;
d.vertical(&mut |_ui| noop.visit());
}
WidgetResponse::supported()
}
fn grid(&mut self, cols: usize, content: &mut dyn FnMut(&mut dyn UiCtx)) -> WidgetResponse {
let mut child_ctx = RecordingUiCtx::new();
content(&mut child_ctx);
let children = child_ctx.entries;
self.record_group(WidgetRole::Group, "grid", children);
if let Some(d) = self.delegate.as_mut() {
let mut noop = NullDelegate;
d.grid(cols, &mut |_ui| noop.visit());
}
WidgetResponse::supported()
}
fn menu_bar(&mut self, content: &mut dyn FnMut(&mut dyn UiCtx)) -> WidgetResponse {
let mut child_ctx = RecordingUiCtx::new();
content(&mut child_ctx);
let children = child_ctx.entries;
self.record_group(WidgetRole::Menu, "menu_bar", children);
if let Some(d) = self.delegate.as_mut() {
let mut noop = NullDelegate;
d.menu_bar(&mut |_ui| noop.visit());
}
WidgetResponse::supported()
}
fn rich_text(&mut self, spans: &[RichTextSpan]) -> WidgetResponse {
let text: String = spans
.iter()
.map(|s| s.text.as_str())
.collect::<Vec<_>>()
.join("");
self.record(WidgetRole::Label, text);
if let Some(d) = self.delegate.as_mut() {
d.rich_text(spans);
}
WidgetResponse::supported()
}
fn drag_source(&mut self, id: u64, content: &mut dyn FnMut(&mut dyn UiCtx)) -> WidgetResponse {
content(self);
if let Some(d) = self.delegate.as_mut() {
let mut noop = NullDelegate;
d.drag_source(id, &mut |_ui| noop.visit());
}
WidgetResponse::unsupported()
}
fn drop_target(
&mut self,
accept_ids: &[u64],
content: &mut dyn FnMut(&mut dyn UiCtx),
) -> WidgetResponse {
content(self);
if let Some(d) = self.delegate.as_mut() {
let mut noop = NullDelegate;
d.drop_target(accept_ids, &mut |_ui| noop.visit());
}
WidgetResponse::unsupported()
}
}
fn entry_to_node(entry: &RecordingEntry, gen: &mut NodeIdGen) -> A11yNode {
let id = gen.next();
let mut node = A11yNode::simple(id, entry.role, Some(entry.label.clone()));
node.children = entry
.children
.iter()
.map(|child| entry_to_node(child, gen))
.collect();
node
}
struct NullDelegate;
impl NullDelegate {
fn visit(&mut self) {}
}