use accesskit::{Node, NodeId, Rect, Role, Toggled};
use elicit_ui::node_roles::*;
use elicit_ui::{RolePreserved, UiNodeBridge, UiRenderBackend};
use elicitation::Established;
use std::collections::BTreeMap;
#[derive(Default)]
pub struct EguiBackend;
impl EguiBackend {
pub fn new() -> Self {
Self
}
}
impl UiRenderBackend for EguiBackend {
fn backend_name(&self) -> &'static str {
"egui"
}
fn supports_role(&self, _role: Role) -> bool {
true
}
}
impl UiNodeBridge for EguiBackend {
type Widget = Box<dyn FnOnce(&mut egui::Ui)>;
fn bridge_unknown(
&self,
node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<UnknownNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
if children.is_empty() {
ui.label(&text);
} else {
ui.group(|ui| {
for c in children {
c(ui);
}
});
}
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_generic_container(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<GenericContainerNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_pane(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<PaneNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_window(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<WindowNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_document(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<DocumentNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_root_web_area(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<RootWebAreaNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_application(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ApplicationNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_terminal(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TerminalNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.monospace(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_button(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ButtonNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let disabled = node.is_disabled();
Box::new(move |ui: &mut egui::Ui| {
ui.add_enabled(!disabled, egui::Button::new(&text));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_link(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<LinkNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let url = node.url().unwrap_or("#").to_string();
Box::new(move |ui: &mut egui::Ui| {
ui.add(egui::Hyperlink::from_label_and_url(&text, &url));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_check_box(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<CheckBoxNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let disabled = node.is_disabled();
let mut checked = matches!(node.toggled(), Some(Toggled::True));
Box::new(move |ui: &mut egui::Ui| {
ui.add_enabled(!disabled, egui::Checkbox::new(&mut checked, &text));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_radio_button(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<RadioButtonNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let disabled = node.is_disabled();
let selected = matches!(node.toggled(), Some(Toggled::True));
Box::new(move |ui: &mut egui::Ui| {
ui.add_enabled(!disabled, egui::RadioButton::new(selected, &text));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_switch(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<SwitchNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let disabled = node.is_disabled();
let mut on = matches!(node.toggled(), Some(Toggled::True));
Box::new(move |ui: &mut egui::Ui| {
ui.horizontal(|ui| {
ui.add_enabled(!disabled, egui::Checkbox::new(&mut on, &text));
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_color_well(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ColorWellNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let disabled = node.is_disabled();
Box::new(move |ui: &mut egui::Ui| {
ui.add_enabled(!disabled, egui::Button::new(format!("🎨 {text}")));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_disclosure_triangle(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<DisclosureTriangleNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let open = matches!(node.toggled(), Some(Toggled::True));
let arrow = if open { "▼" } else { "▶" };
Box::new(move |ui: &mut egui::Ui| {
ui.label(format!("{arrow} {text}"));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_combo_box(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ComboBoxNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let val = node.value().unwrap_or("").to_string();
Box::new(move |ui: &mut egui::Ui| {
egui::ComboBox::from_label(&text)
.selected_text(&val)
.show_ui(ui, |_ui| {});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_list_box(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ListBoxNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_slider(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<SliderNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let disabled = node.is_disabled();
let mut val = node.numeric_value().unwrap_or(0.0);
let min = node.min_numeric_value().unwrap_or(0.0);
let max = node.max_numeric_value().unwrap_or(100.0);
Box::new(move |ui: &mut egui::Ui| {
let mut slider = egui::Slider::new(&mut val, min..=max);
if !text.is_empty() {
slider = slider.text(&text);
}
ui.add_enabled(!disabled, slider);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_spin_button(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<SpinButtonNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let disabled = node.is_disabled();
let mut val = node.numeric_value().unwrap_or(0.0);
let min = node.min_numeric_value().unwrap_or(f64::MIN);
let max = node.max_numeric_value().unwrap_or(f64::MAX);
let step = node.numeric_value_step().unwrap_or(1.0);
Box::new(move |ui: &mut egui::Ui| {
ui.add_enabled(
!disabled,
egui::DragValue::new(&mut val).range(min..=max).speed(step),
);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_progress_indicator(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ProgressIndicatorNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let val = node.numeric_value().unwrap_or(0.0);
let max = node.max_numeric_value().unwrap_or(100.0);
let fraction = if max > 0.0 { (val / max) as f32 } else { 0.0 };
Box::new(move |ui: &mut egui::Ui| {
let mut pb = egui::ProgressBar::new(fraction.clamp(0.0, 1.0));
if !text.is_empty() {
pb = pb.text(&text);
}
ui.add(pb);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_scroll_view(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ScrollViewNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_splitter(
&self,
_node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<SplitterNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
Box::new(|ui: &mut egui::Ui| {
ui.separator();
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_text_input(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TextInputNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let mut buf = node.value().unwrap_or("").to_string();
let hint = node.placeholder().map(|s| s.to_string());
let readonly = node.is_read_only() || node.is_disabled();
Box::new(move |ui: &mut egui::Ui| {
let mut te = egui::TextEdit::singleline(&mut buf);
if let Some(h) = &hint {
te = te.hint_text(h.as_str());
}
if readonly {
te = te.interactive(false);
}
ui.add(te);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_multiline_text_input(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<MultilineTextInputNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let mut buf = node.value().unwrap_or("").to_string();
let hint = node.placeholder().map(|s| s.to_string());
let readonly = node.is_read_only() || node.is_disabled();
Box::new(move |ui: &mut egui::Ui| {
let mut te = egui::TextEdit::multiline(&mut buf);
if let Some(h) = &hint {
te = te.hint_text(h.as_str());
}
if readonly {
te = te.interactive(false);
}
ui.add(te);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_number_input(
&self,
node: &Node,
id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<NumberInputNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
self.bridge_spin_button(
node,
id,
children,
Established::<SpinButtonNodeValid>::prove(&proof),
)
}
fn bridge_text_run(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TextRunNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_paragraph(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ParagraphNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_label(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<LabelNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_heading(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<HeadingNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
let size = heading_size(node);
Box::new(move |ui: &mut egui::Ui| {
ui.add(egui::Label::new(
egui::RichText::new(&text).strong().size(size),
));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_line_break(
&self,
_node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<LineBreakNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
Box::new(|ui: &mut egui::Ui| {
ui.end_row();
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_blockquote(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<BlockquoteNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.add(egui::Label::new(
egui::RichText::new(format!("│ {text}")).italics(),
));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_code(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<CodeNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.add(egui::Label::new(egui::RichText::new(&text).monospace()));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_math(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<MathNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_note(
&self,
node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<NoteNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w: Self::Widget = {
if children.is_empty() {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
} else {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
}
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_term(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TermNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.add(egui::Label::new(egui::RichText::new(&text).strong()));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_definition(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<DefinitionNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_image(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ImageNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let alt = node_label(node);
let text = if alt.is_empty() {
"🖼 [image]".to_string()
} else {
format!("🖼 {alt}")
};
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_figure(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<FigureNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_figure_caption(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<FigureCaptionNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.add(egui::Label::new(egui::RichText::new(&text).italics()));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_canvas(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<CanvasNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(format!("[canvas: {text}]"));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_video(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<VideoNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(format!("[video: {text}]"));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_audio(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<AudioNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(format!("[audio: {text}]"));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_main(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<MainNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_navigation(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<NavigationNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.horizontal(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_banner(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<BannerNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.horizontal(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_content_info(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ContentInfoNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.horizontal(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_complementary(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ComplementaryNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_form(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<FormNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_search(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<SearchNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.horizontal(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_region(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<RegionNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_section(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<SectionNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_section_header(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<SectionHeaderNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.horizontal(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_section_footer(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<SectionFooterNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.horizontal(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_article(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ArticleNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_group(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<GroupNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_dialog(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<DialogNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_details(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<DetailsNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_tooltip(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TooltipNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_alert(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<AlertNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.add(egui::Label::new(
egui::RichText::new(&text).color(egui::Color32::YELLOW),
));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_status(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<StatusNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_timer(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TimerNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_list(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ListNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_list_item(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ListItemNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(format!("• {text}"));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_description_list(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<DescriptionListNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_table(
&self,
node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TableNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
let role_str = format!("{:?}", node.role());
Box::new(move |ui: &mut egui::Ui| {
egui::Grid::new(format!("grid_{role_str}"))
.striped(true)
.show(ui, |ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_row(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<RowNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
for c in children {
c(ui);
}
ui.end_row();
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_cell(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<CellNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.label(&text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_caption(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<CaptionNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
ui.add(egui::Label::new(egui::RichText::new(&text).italics()));
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_row_group(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<RowGroupNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_tree(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TreeNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_tree_item(
&self,
node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TreeItemNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w: Self::Widget = {
if children.is_empty() {
let text = node_label(node);
let selected = node.is_selected().unwrap_or(false);
Box::new(move |ui: &mut egui::Ui| {
if selected {
let hl = ui.visuals().selection.bg_fill;
let fg = ui.visuals().selection.stroke.color;
ui.painter()
.rect_filled(ui.available_rect_before_wrap(), 0.0, hl);
let _ = ui.colored_label(fg, &text);
} else {
ui.label(&text);
}
})
} else {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
}
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_tab(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TabNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
let _ = ui.selectable_label(false, &text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_tab_list(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TabListNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.horizontal(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_tab_panel(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<TabPanelNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.group(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_menu(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<MenuNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_menu_item(
&self,
node: &Node,
_id: NodeId,
_children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<MenuItemNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let __w = {
let text = node_label(node);
Box::new(move |ui: &mut egui::Ui| {
let _ = ui.selectable_label(false, &text);
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_toolbar(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<ToolbarNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.horizontal(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
fn bridge_radio_group(
&self,
_node: &Node,
_id: NodeId,
children: Vec<(Self::Widget, Established<RolePreserved>)>,
proof: Established<RadioGroupNodeValid>,
) -> (Self::Widget, Established<RolePreserved>) {
let children: Vec<Self::Widget> = children.into_iter().map(|(w, _)| w).collect();
let __w = {
Box::new(move |ui: &mut egui::Ui| {
ui.vertical(|ui| {
for c in children {
c(ui);
}
});
})
};
(__w, Established::<RolePreserved>::prove(&proof))
}
}
#[tracing::instrument(skip(ui, nodes), fields(root = ?root))]
pub fn render_tree(
ui: &mut egui::Ui,
nodes: &BTreeMap<NodeId, Node>,
root: NodeId,
) -> (elicit_ui::RenderStats, Vec<NodeId>) {
let mut stats = elicit_ui::RenderStats::default();
let mut clicked_nodes: Vec<NodeId> = Vec::new();
render_node_recursive(ui, nodes, root, &mut stats, &mut clicked_nodes);
tracing::debug!(
visited = stats.nodes_visited,
widgets = stats.widgets_rendered,
containers = stats.containers_rendered,
skipped = stats.nodes_skipped,
"Render pass complete"
);
(stats, clicked_nodes)
}
pub fn bounds_to_size(node: &Node) -> Option<(f32, f32)> {
let Rect { x0, y0, x1, y1 } = node.bounds()?;
let w = (x1 - x0).abs() as f32;
let h = (y1 - y0).abs() as f32;
Some((w, h))
}
fn node_label(node: &Node) -> String {
elicit_accesskit::node_label(node).to_string()
}
fn heading_size(node: &Node) -> f32 {
match node.level() {
Some(1) => 28.0,
Some(2) => 22.0,
Some(3) => 18.0,
Some(4) => 16.0,
Some(5) => 14.0,
_ => 12.0,
}
}
fn render_node_recursive(
ui: &mut egui::Ui,
nodes: &BTreeMap<NodeId, Node>,
node_id: NodeId,
stats: &mut elicit_ui::RenderStats,
clicked_nodes: &mut Vec<NodeId>,
) {
let Some(node) = nodes.get(&node_id) else {
stats.nodes_skipped += 1;
return;
};
stats.nodes_visited += 1;
if node.is_hidden() {
stats.nodes_skipped += 1;
return;
}
let role = node.role();
match role {
Role::Window
| Role::Pane
| Role::Form
| Role::Group
| Role::Section
| Role::Region
| Role::Main
| Role::GenericContainer
| Role::Document => {
render_container(ui, nodes, node, stats, clicked_nodes);
}
Role::Button | Role::DefaultButton => {
let text = node_label(node);
let disabled = node.is_disabled();
let resp = ui.add_enabled(!disabled, egui::Button::new(&text));
if resp.clicked() {
clicked_nodes.push(node_id);
}
stats.widgets_rendered += 1;
}
Role::CheckBox | Role::MenuItemCheckBox => {
let text = node_label(node);
let disabled = node.is_disabled();
let mut checked = matches!(node.toggled(), Some(Toggled::True));
ui.add_enabled(!disabled, egui::Checkbox::new(&mut checked, &text));
stats.widgets_rendered += 1;
}
Role::RadioButton | Role::MenuItemRadio => {
let text = node_label(node);
let disabled = node.is_disabled();
let selected = matches!(node.toggled(), Some(Toggled::True));
ui.add_enabled(!disabled, egui::RadioButton::new(selected, &text));
stats.widgets_rendered += 1;
}
Role::Switch => {
let text = node_label(node);
let disabled = node.is_disabled();
let mut on = matches!(node.toggled(), Some(Toggled::True));
ui.horizontal(|ui| {
ui.add_enabled(!disabled, egui::Checkbox::new(&mut on, &text));
});
stats.widgets_rendered += 1;
}
Role::TextInput
| Role::SearchInput
| Role::EmailInput
| Role::UrlInput
| Role::PhoneNumberInput
| Role::PasswordInput => {
let mut buf = node.value().unwrap_or("").to_string();
let mut te = egui::TextEdit::singleline(&mut buf);
if let Some(hint) = node.placeholder() {
te = te.hint_text(hint);
}
if node.is_read_only() || node.is_disabled() {
te = te.interactive(false);
}
ui.add(te);
stats.widgets_rendered += 1;
}
Role::MultilineTextInput => {
let mut buf = node.value().unwrap_or("").to_string();
let mut te = egui::TextEdit::multiline(&mut buf);
if let Some(hint) = node.placeholder() {
te = te.hint_text(hint);
}
if node.is_read_only() || node.is_disabled() {
te = te.interactive(false);
}
let is_sql = node
.label()
.map(|n: &str| n.eq_ignore_ascii_case("sql editor"))
.unwrap_or(false);
if is_sql {
let mut layouter = |ui: &egui::Ui, s: &dyn egui::TextBuffer, _wrap: f32| {
let job = sql_layout_job(s.as_str(), elicit_ui::palettes::mocha());
ui.painter().layout_job(job)
};
te = te.layouter(&mut layouter);
ui.add(te);
} else {
ui.add(te);
}
stats.widgets_rendered += 1;
}
Role::NumberInput | Role::SpinButton => {
let mut val = node.numeric_value().unwrap_or(0.0);
let min = node.min_numeric_value().unwrap_or(f64::MIN);
let max = node.max_numeric_value().unwrap_or(f64::MAX);
let step = node.numeric_value_step().unwrap_or(1.0);
ui.add_enabled(
!node.is_disabled(),
egui::DragValue::new(&mut val).range(min..=max).speed(step),
);
stats.widgets_rendered += 1;
}
Role::Slider => {
let text = node_label(node);
let disabled = node.is_disabled();
let mut val = node.numeric_value().unwrap_or(0.0);
let min = node.min_numeric_value().unwrap_or(0.0);
let max = node.max_numeric_value().unwrap_or(100.0);
let mut slider = egui::Slider::new(&mut val, min..=max);
if !text.is_empty() {
slider = slider.text(&text);
}
ui.add_enabled(!disabled, slider);
stats.widgets_rendered += 1;
}
Role::ProgressIndicator | Role::Meter => {
let text = node_label(node);
let val = node.numeric_value().unwrap_or(0.0);
let max = node.max_numeric_value().unwrap_or(100.0);
let fraction = if max > 0.0 { (val / max) as f32 } else { 0.0 };
let mut pb = egui::ProgressBar::new(fraction.clamp(0.0, 1.0));
if !text.is_empty() {
pb = pb.text(&text);
}
ui.add(pb);
stats.widgets_rendered += 1;
}
Role::ComboBox | Role::EditableComboBox => {
let text = node_label(node);
let val = node.value().unwrap_or("").to_string();
egui::ComboBox::from_label(&text)
.selected_text(&val)
.show_ui(ui, |_ui| {});
stats.widgets_rendered += 1;
}
Role::Link => {
let text = node_label(node);
let url = node.url().unwrap_or("#");
ui.add(egui::Hyperlink::from_label_and_url(&text, url));
stats.widgets_rendered += 1;
}
Role::ColorWell => {
let text = node_label(node);
let disabled = node.is_disabled();
ui.add_enabled(!disabled, egui::Button::new(format!("🎨 {text}")));
stats.widgets_rendered += 1;
}
Role::Label
| Role::Paragraph
| Role::TextRun
| Role::Heading
| Role::Legend
| Role::Caption
| Role::Blockquote
| Role::Code
| Role::Strong
| Role::Emphasis
| Role::Mark
| Role::Abbr
| Role::Term
| Role::Definition
| Role::Note
| Role::Status
| Role::Alert
| Role::Log
| Role::Time
| Role::Timer => {
render_label(ui, node);
stats.widgets_rendered += 1;
}
Role::Image => {
let alt = node_label(node);
let text = if alt.is_empty() {
"🖼 [image]".to_string()
} else {
format!("🖼 {alt}")
};
ui.label(text);
stats.widgets_rendered += 1;
}
Role::Toolbar => {
stats.containers_rendered += 1;
ui.horizontal(|ui| {
for child_id in node.children() {
render_node_recursive(ui, nodes, *child_id, stats, clicked_nodes);
}
});
}
Role::List | Role::ListBox | Role::Feed | Role::DescriptionList => {
stats.containers_rendered += 1;
ui.vertical(|ui| {
for child_id in node.children() {
render_node_recursive(ui, nodes, *child_id, stats, clicked_nodes);
}
});
}
Role::Table | Role::Grid | Role::TreeGrid | Role::ListGrid => {
stats.containers_rendered += 1;
egui::Grid::new(format!("grid_{:?}", node.role()))
.striped(true)
.show(ui, |ui| {
for child_id in node.children() {
render_node_recursive(ui, nodes, *child_id, stats, clicked_nodes);
}
});
}
Role::TabList => {
stats.containers_rendered += 1;
ui.horizontal(|ui| {
for child_id in node.children() {
render_node_recursive(ui, nodes, *child_id, stats, clicked_nodes);
}
});
}
Role::Tab
| Role::TabPanel
| Role::ListItem
| Role::Row
| Role::Cell
| Role::GridCell
| Role::RowHeader
| Role::ColumnHeader
| Role::RowGroup
| Role::ListBoxOption
| Role::MenuItem
| Role::MenuListOption => {
render_container(ui, nodes, node, stats, clicked_nodes);
}
Role::TreeItem => {
if !node.children().is_empty() {
render_container(ui, nodes, node, stats, clicked_nodes);
} else {
let text = node_label(node);
let selected = node.is_selected().unwrap_or(false);
if selected {
let hl = ui.visuals().selection.bg_fill;
let fg = ui.visuals().selection.stroke.color;
ui.painter()
.rect_filled(ui.available_rect_before_wrap(), 0.0, hl);
let _ = ui.colored_label(fg, &text);
} else {
ui.label(&text);
}
stats.widgets_rendered += 1;
}
}
Role::Dialog | Role::AlertDialog => {
render_container(ui, nodes, node, stats, clicked_nodes);
}
Role::Menu | Role::MenuBar | Role::MenuListPopup => {
render_container(ui, nodes, node, stats, clicked_nodes);
}
Role::Navigation
| Role::Banner
| Role::Complementary
| Role::ContentInfo
| Role::Header
| Role::Footer
| Role::SectionHeader
| Role::SectionFooter
| Role::Search
| Role::Article => {
render_container(ui, nodes, node, stats, clicked_nodes);
}
Role::ScrollView | Role::ScrollBar => {
render_container(ui, nodes, node, stats, clicked_nodes);
}
Role::Splitter => {
ui.separator();
stats.widgets_rendered += 1;
}
Role::LineBreak => {
ui.end_row();
stats.widgets_rendered += 1;
}
Role::Figure => {
let desc = node.description().unwrap_or("");
let children = node.children();
if desc.contains("w=") && desc.contains("h=") && !children.is_empty() {
let coords = parse_kv_coords_egui(desc);
let cw = coords.get("w").copied().unwrap_or(800.0);
let ch = coords.get("h").copied().unwrap_or(600.0);
struct ErdBox {
x: f32,
y: f32,
w: f32,
h: f32,
name: String,
cols: Vec<String>,
}
struct ErdEdge {
x1: f32,
y1: f32,
x2: f32,
y2: f32,
label: String,
}
let mut boxes: Vec<ErdBox> = Vec::new();
let mut edges: Vec<ErdEdge> = Vec::new();
for child_id in children.iter() {
let Some(child) = nodes.get(child_id) else {
continue;
};
let child_desc = child.description().unwrap_or("");
let c = parse_kv_coords_egui(child_desc);
if child_desc.contains("x1=") {
edges.push(ErdEdge {
x1: c.get("x1").copied().unwrap_or(0.0),
y1: c.get("y1").copied().unwrap_or(0.0),
x2: c.get("x2").copied().unwrap_or(0.0),
y2: c.get("y2").copied().unwrap_or(0.0),
label: child.label().unwrap_or("").to_string(),
});
} else if child_desc.contains("x=") {
let cols: Vec<String> = child
.children()
.iter()
.filter_map(|&col_id| nodes.get(&col_id))
.map(|n| n.label().unwrap_or("").to_string())
.collect();
boxes.push(ErdBox {
x: c.get("x").copied().unwrap_or(0.0),
y: c.get("y").copied().unwrap_or(0.0),
w: c.get("w").copied().unwrap_or(200.0),
h: c.get("h").copied().unwrap_or(80.0),
name: child.label().unwrap_or("").to_string(),
cols,
});
}
}
let desired = egui::Vec2::new(cw.min(2000.0), ch.min(1200.0));
let (resp, painter) = ui.allocate_painter(desired, egui::Sense::hover());
let origin = resp.rect.min;
let scale = (resp.rect.width() / cw).min(1.0);
let col_fg = egui::Color32::from_rgb(205, 214, 244);
let title_fg = egui::Color32::from_rgb(137, 180, 250);
let box_bg = egui::Color32::from_rgb(49, 50, 68);
let header_bg = egui::Color32::from_rgb(30, 30, 46);
let border = egui::Color32::from_rgb(69, 71, 90);
let edge_col = egui::Color32::from_rgb(108, 112, 134);
for e in &edges {
let p1 = origin + egui::Vec2::new(e.x1 * scale, e.y1 * scale);
let p2 = origin + egui::Vec2::new(e.x2 * scale, e.y2 * scale);
painter.line_segment([p1, p2], egui::Stroke::new(1.5_f32, edge_col));
let _ = &e.label; }
let font_id = egui::FontId::monospace(10.0 * scale);
let title_font = egui::FontId::monospace(11.0 * scale);
for b in &boxes {
let tl = origin + egui::Vec2::new(b.x * scale, b.y * scale);
let br = tl + egui::Vec2::new(b.w * scale, b.h * scale);
let rect = egui::Rect::from_min_max(tl, br);
painter.rect_filled(rect, 3.0, box_bg);
painter.rect_stroke(
rect,
3.0,
egui::Stroke::new(1.0_f32, border),
egui::StrokeKind::Outside,
);
let header_br = tl + egui::Vec2::new(b.w * scale, 24.0 * scale);
let header_rect = egui::Rect::from_min_max(tl, header_br);
painter.rect_filled(header_rect, 3.0, header_bg);
painter.rect_stroke(
header_rect,
3.0,
egui::Stroke::new(1.0_f32, border),
egui::StrokeKind::Outside,
);
painter.text(
header_rect.center(),
egui::Align2::CENTER_CENTER,
&b.name,
title_font.clone(),
title_fg,
);
for (i, col) in b.cols.iter().enumerate() {
let ty = tl.y + (24.0 + (i as f32 + 0.5) * 20.0 + 4.0) * scale;
let tx = tl.x + 6.0 * scale;
painter.text(
egui::Pos2::new(tx, ty),
egui::Align2::LEFT_CENTER,
col,
font_id.clone(),
col_fg,
);
}
}
stats.containers_rendered += 1;
} else {
render_container(ui, nodes, node, stats, clicked_nodes);
}
}
_ => {
if node.children().is_empty() {
stats.nodes_skipped += 1;
} else {
render_container(ui, nodes, node, stats, clicked_nodes);
}
}
}
}
fn render_container(
ui: &mut egui::Ui,
nodes: &BTreeMap<NodeId, Node>,
node: &Node,
stats: &mut elicit_ui::RenderStats,
clicked_nodes: &mut Vec<NodeId>,
) {
stats.containers_rendered += 1;
let children = node.children();
if children.is_empty() {
return;
}
ui.group(|ui| {
for child_id in children {
render_node_recursive(ui, nodes, *child_id, stats, clicked_nodes);
}
});
}
fn render_label(ui: &mut egui::Ui, node: &Node) {
let text = node.value().or(node.label()).unwrap_or("");
let rt = match node.role() {
Role::Heading => egui::RichText::new(text).strong().size(heading_size(node)),
Role::Strong => egui::RichText::new(text).strong(),
Role::Emphasis | Role::Mark => egui::RichText::new(text).italics(),
Role::Code => egui::RichText::new(text).monospace(),
Role::Alert | Role::Status => egui::RichText::new(text).color(egui::Color32::YELLOW),
_ => egui::RichText::new(text),
};
ui.add(egui::Label::new(rt));
}
fn parse_kv_coords_egui(desc: &str) -> std::collections::HashMap<&str, f32> {
desc.split(',')
.filter_map(|part| {
let (k, v) = part.split_once('=')?;
let v = v.trim().parse::<f32>().ok()?;
Some((k.trim(), v))
})
.collect()
}
fn sql_layout_job(sql: &str, palette: &elicit_ui::Palette) -> egui::text::LayoutJob {
use elicit_accesskit::sql::{SqlTokenKind, sql_tokens};
use elicit_ui::SemanticRole;
let font = egui::FontId::monospace(14.0);
let mut job = egui::text::LayoutJob::default();
for token in sql_tokens(sql) {
let role = match token.kind {
SqlTokenKind::Keyword => SemanticRole::Keyword,
SqlTokenKind::StringLiteral => SemanticRole::StringLit,
SqlTokenKind::Comment => SemanticRole::Comment,
SqlTokenKind::Number => SemanticRole::Number,
SqlTokenKind::Plain => SemanticRole::Text,
};
let c = palette.color(role);
job.append(
token.text,
0.0,
egui::text::TextFormat {
font_id: font.clone(),
color: egui::Color32::from_rgb(
(c.r * 255.0).round() as u8,
(c.g * 255.0).round() as u8,
(c.b * 255.0).round() as u8,
),
..Default::default()
},
);
}
job
}