use egui::{Id, Rect, Response, Sense, Ui, WidgetInfo, WidgetType};
#[derive(Clone, Debug, PartialEq)]
pub struct Semantics {
pub typ: WidgetType,
pub label: String,
pub value: Option<f64>,
pub selected: Option<bool>,
pub enabled: bool,
}
impl Semantics {
pub fn new(typ: WidgetType, label: impl Into<String>) -> Self {
Self { typ, label: label.into(), value: None, selected: None, enabled: true }
}
pub fn button(label: impl Into<String>) -> Self {
Self::new(WidgetType::Button, label)
}
pub fn image(label: impl Into<String>) -> Self {
Self::new(WidgetType::Image, label)
}
pub fn list_item(label: impl Into<String>, selected: bool) -> Self {
Self { selected: Some(selected), ..Self::new(WidgetType::SelectableLabel, label) }
}
pub fn value(mut self, v: f64) -> Self {
self.value = Some(v);
self
}
pub fn selected(mut self, sel: bool) -> Self {
self.selected = Some(sel);
self
}
pub fn enabled(mut self, en: bool) -> Self {
self.enabled = en;
self
}
pub fn widget_info(&self) -> WidgetInfo {
let mut info = match self.selected {
Some(sel) => WidgetInfo::selected(self.typ, self.enabled, sel, self.label.clone()),
None => WidgetInfo::labeled(self.typ, self.enabled, self.label.clone()),
};
info.value = self.value;
info
}
}
pub fn stable_id(base: Id, key: impl std::hash::Hash) -> Id {
base.with(key)
}
pub fn node(
ui: &mut Ui,
base: Id,
key: impl std::hash::Hash,
sense: Sense,
rect: Rect,
sem: Semantics,
) -> Response {
let id = stable_id(base, key);
let response = ui.interact(rect, id, sense);
response.widget_info(|| sem.widget_info());
response
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn semantics_button_carries_label_and_value() {
let s = Semantics::button("worker · running").value(42.0);
let info = s.widget_info();
assert_eq!(info.typ, WidgetType::Button);
assert_eq!(info.label.as_deref(), Some("worker · running"));
assert_eq!(info.value, Some(42.0));
assert_eq!(info.selected, None);
}
#[test]
fn semantics_list_item_carries_selection() {
let s = Semantics::list_item("file.txt", true);
let info = s.widget_info();
assert_eq!(info.typ, WidgetType::SelectableLabel);
assert_eq!(info.selected, Some(true));
assert_eq!(info.label.as_deref(), Some("file.txt"));
}
#[test]
fn stable_id_is_key_derived_not_index() {
let base = Id::new("comp");
assert_eq!(stable_id(base, "node-a"), stable_id(base, "node-a"));
assert_ne!(stable_id(base, "node-a"), stable_id(base, "node-b"));
}
}