use std::ops::Range; use egui::{Color32, DragValue, RichText, Ui}; use karlcommon::{Condition, Property, Schedule, Task}; pub struct ShowOrEdit { pub inner: T, pub edit: bool, } impl ShowOrEdit { pub fn new(inner: T, edit: bool) -> ShowOrEdit { Self { inner, edit } } pub fn changed(&mut self, ui: &mut Ui) -> bool { let changed = match self.edit { true => { if ui.button("💾 Save").clicked() { self.edit = false; true } else { false } } false => { if ui.button("✏ Edit").clicked() { self.edit = true; } false } }; self.inner.ui(ui, self.edit); changed } } pub trait EditableWidget { fn ui(&mut self, ui: &mut Ui, edit: bool); } impl EditableWidget for Task { fn ui(&mut self, ui: &mut Ui, edit: bool) { ui.heading(&self.name); ui.indent((), |ui| { if let Some(d) = &mut self.description { if edit { ui.text_edit_singleline(d); } else { ui.label(&*d); } } ui.horizontal(|ui| { ui.label("Tags:"); for t in &self.tags { ui.colored_label(Color32::LIGHT_GREEN, t); } }); self.schedule.ui(ui, edit); }); } } impl EditableWidget for Schedule { fn ui(&mut self, ui: &mut Ui, edit: bool) { match self { Schedule::Never => { ui.label("No schedule"); } Schedule::Dynamic { priority, scheduled, duration, condition, } => { ui.horizontal(|ui| { ui.label("Dynamic with priority"); ui.label(&format!(" {} ", priority)); }); } Schedule::Condition(c) => c.ui(ui, edit), Schedule::Static(t) => t.ui(ui, edit), } } } impl EditableWidget for Condition { fn ui(&mut self, ui: &mut Ui, edit: bool) { ui.group(|ui| match self { Condition::Never => { ui.label("never"); } Condition::From(c) => { ui.horizontal(|ui| { ui.label("Starting from"); c.ui(ui, edit); if edit { ui.menu_button("✏ Change type", |ui| { add_condition(ui, |d| *c = Box::new(d)) }); } }); } Condition::Invert(c) => { ui.horizontal(|ui| { ui.label("not when"); c.ui(ui, edit); if edit { ui.menu_button("✏ Change type", |ui| { add_condition(ui, |d| *c = Box::new(d)) }); } }); } Condition::Or(cs) => combine_condition(ui, edit, "or", cs), Condition::And(cs) => combine_condition(ui, edit, "and", cs), Condition::Equal { prop, value, modulus: _, } => { ui.horizontal(|ui| { ui.label("when"); if edit { egui::ComboBox::from_id_source(ui.id()) .selected_text(prop.to_str()) .show_ui(ui, |ui| { for v in Property::VALUES { ui.selectable_value(prop, *v, v.to_str()); } }); } else { ui.label(prop.to_str()); } ui.label("="); if edit { ui.add(DragValue::new(value)); } else { ui.label(&format!("{}", value)); } }); } Condition::Range { prop, min, max, modulus: _, } => { ui.horizontal(|ui| { ui.label("when "); ui.label(&format!("{}", min)); ui.label("≤"); ui.label(&format!("{:?}", prop)); ui.label("<"); ui.label(&format!("{}", max)) }); } }); } } impl EditableWidget for Range { fn ui(&mut self, ui: &mut Ui, edit: bool) { ui.label("todo"); } } fn combine_condition(ui: &mut Ui, edit: bool, combinator: &str, cs: &mut Vec) { ui.vertical(|ui| { let mut remove = None; for (i, c) in cs.iter_mut().enumerate() { ui.push_id(i, |ui| { ui.horizontal(|ui| { ui.label(if i != 0 { combinator } else { "" }); c.ui(ui, edit); if edit { if ui.button(RichText::from("🗑").color(Color32::RED)).clicked() { remove = Some(i); } } }); }); } if edit { ui.menu_button("➕ Add condition", |ui| { add_condition(ui, |e| { cs.push(e); }) }); } if let Some(remove) = remove { cs.remove(remove); } }); } fn add_condition(ui: &mut Ui, mut add: impl FnMut(Condition) -> ()) { ui.menu_button("Combinators", |ui| { if ui.button("And").clicked() { add(Condition::And(vec![])) } if ui.button("Or").clicked() { add(Condition::Or(vec![])) } }); ui.menu_button("Constraints", |ui| { if ui.button("Equal").clicked() { add(Condition::Equal { modulus: None, prop: Property::Unix, value: 0, }) } if ui.button("Range").clicked() { add(Condition::Range { prop: Property::Unix, min: 0, max: 10, modulus: None, }) } if ui.button("Never").clicked() { add(Condition::Never) } }); ui.menu_button("Modifier", |ui| { if ui.button("Invert").clicked() { add(Condition::Invert(Box::new(Condition::Never))) } if ui.button("Starting from").clicked() { add(Condition::From(Box::new(Condition::Never))) } }); }