use std::rc::Rc;
use crate::base::Rect;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Role {
Generic,
Text,
Heading,
Region,
Button,
Checkbox,
RadioGroup,
Input,
TextArea,
List,
ListItem,
Table,
Cell,
Tabs,
Tab,
Dialog,
Menu,
MenuItem,
ScrollArea,
}
impl Role {
pub fn as_str(self) -> &'static str {
match self {
Role::Generic => "generic",
Role::Text => "text",
Role::Heading => "heading",
Role::Region => "region",
Role::Button => "button",
Role::Checkbox => "checkbox",
Role::RadioGroup => "radiogroup",
Role::Input => "input",
Role::TextArea => "textarea",
Role::List => "list",
Role::ListItem => "listitem",
Role::Table => "table",
Role::Cell => "cell",
Role::Tabs => "tabs",
Role::Tab => "tab",
Role::Dialog => "dialog",
Role::Menu => "menu",
Role::MenuItem => "menuitem",
Role::ScrollArea => "scrollarea",
}
}
}
#[derive(Clone, Default)]
pub(super) struct AccessProps {
pub role: Option<Role>,
pub label: Option<String>,
pub value: Option<Rc<dyn Fn() -> String>>,
}
impl AccessProps {
pub fn is_empty(&self) -> bool {
self.role.is_none() && self.label.is_none() && self.value.is_none()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AccessEntry {
pub role: Role,
pub label: String,
pub value: Option<String>,
pub focused: bool,
pub bounds: Rect,
pub depth: usize,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct AccessSnapshot {
pub entries: Vec<AccessEntry>,
}
impl AccessSnapshot {
pub fn to_text(&self) -> String {
let mut out = String::new();
for e in &self.entries {
for _ in 0..e.depth {
out.push_str(" ");
}
out.push_str(e.role.as_str());
if !e.label.is_empty() {
out.push_str(" \"");
out.push_str(&e.label);
out.push('"');
}
if let Some(v) = &e.value {
out.push_str(" = \"");
out.push_str(v);
out.push('"');
}
if e.focused {
out.push_str(" [focused]");
}
out.push_str(&format!(
" @ {},{} {}x{}",
e.bounds.x, e.bounds.y, e.bounds.w, e.bounds.h
));
out.push('\n');
}
out
}
pub fn find(&self, role: Role) -> Option<&AccessEntry> {
self.entries.iter().find(|e| e.role == role)
}
pub fn focused(&self) -> Option<&AccessEntry> {
self.entries.iter().find(|e| e.focused)
}
}
pub fn focus_affordance_visible(tree: &mut super::UiTree) -> bool {
let Some(focused) = tree.focused() else {
return true;
};
let rect = tree.rect_of(focused);
if rect.is_empty() {
return true; }
let size = tree.viewport_size();
let mut with_focus = super::BufferCanvas::new(size);
tree.draw(&mut with_focus);
tree.set_focus(None);
crate::reactive::flush_effects();
let mut without = super::BufferCanvas::new(size);
tree.draw(&mut without);
tree.set_focus(Some(focused));
crate::reactive::flush_effects();
for y in rect.y..rect.bottom().min(size.h) {
for x in rect.x..rect.right().min(size.w) {
let p = crate::base::Point::new(x, y);
if with_focus.cell(p) != without.cell(p)
|| with_focus.attrs_at(p) != without.attrs_at(p)
{
return true;
}
}
}
false
}