use crate::{common::ToOpenTimelineType, consts::REMOVE_BUTTON_WIDTH};
use bool_tag_expr::BoolTagExpr;
use eframe::egui::{Context, TextEdit, Ui};
use open_timeline_crud::CrudError;
use open_timeline_gui_core::{
Draw, EmptyConsideredInvalid, ErrorStyle, ShowRemoveButton, Valid, ValidAsynchronous,
ValidSynchronous, ValiditySynchronous, ValitityStatus, body_text_height, widget_x_spacing,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HintText {
None,
Default,
}
#[derive(Debug)]
pub struct BooleanExpressionGui {
expr: String,
validity: ValitityStatus<(), CrudError>,
show_remove_button: ShowRemoveButton,
empty_considered_invalid: EmptyConsideredInvalid,
hint_text: HintText,
changed: bool,
}
impl BooleanExpressionGui {
pub fn new(
show_remove_button: ShowRemoveButton,
empty_considered_invalid: EmptyConsideredInvalid,
hint_text: HintText,
) -> Self {
let mut new = Self {
expr: String::new(),
validity: ValitityStatus::from(ValiditySynchronous::Valid, None),
show_remove_button,
hint_text,
empty_considered_invalid,
changed: false,
};
new.update_validity();
new
}
pub fn changed(&self) -> bool {
self.changed
}
pub fn expr(&self) -> &str {
&self.expr
}
}
impl ErrorStyle for BooleanExpressionGui {}
impl ValidSynchronous for BooleanExpressionGui {
fn is_valid_synchronous(&self) -> bool {
self.validity.synchronous() == ValiditySynchronous::Valid
}
fn update_validity_synchronous(&mut self) {
if self.empty_considered_invalid == EmptyConsideredInvalid::No {
if self.expr.trim().is_empty() {
self.validity.set_synchronous(ValiditySynchronous::Valid);
return;
}
}
if let Err(error) = BoolTagExpr::from(self.expr.clone()) {
self.validity
.set_synchronous(ValiditySynchronous::Invalid(error.to_string()));
return;
}
self.validity.set_synchronous(ValiditySynchronous::Valid);
}
fn validity_synchronous(&self) -> ValiditySynchronous {
self.validity.synchronous().clone()
}
}
impl ValidAsynchronous for BooleanExpressionGui {
type Error = CrudError;
fn check_for_asynchronous_validity_response(&mut self) {
}
fn is_valid_asynchronous(&self) -> Option<Result<(), Self::Error>> {
Some(Ok(()))
}
fn trigger_asynchronous_validity_update(&mut self) {
}
}
impl Valid for BooleanExpressionGui {}
impl ToOpenTimelineType<BoolTagExpr> for BooleanExpressionGui {
fn to_opentimeline_type(&self) -> BoolTagExpr {
BoolTagExpr::from(self.expr.clone()).unwrap()
}
}
impl Draw for BooleanExpressionGui {
fn draw(&mut self, ctx: &Context, ui: &mut Ui) {
ui.scope(|ui| {
self.set_validity_styling(ctx, ui);
let spacing = widget_x_spacing(ui);
let input_height = body_text_height(ui);
let input_width = if self.show_remove_button == ShowRemoveButton::Yes {
ui.available_width() - spacing - REMOVE_BUTTON_WIDTH
} else {
ui.available_width()
};
let hint_text = match self.hint_text {
HintText::None => "",
HintText::Default => {
r#"Tag Boolean Expression (e.g. "british & (scientist | painter)")"#
}
};
let input_box = ui.add_sized(
[input_width, input_height],
TextEdit::singleline(&mut self.expr)
.desired_width(input_width)
.hint_text(hint_text),
);
self.changed = input_box.changed();
{
if input_box.lost_focus() {
self.update_validity();
};
if input_box.changed() {
self.update_validity();
}
}
});
}
}
impl From<Option<BoolTagExpr>> for BooleanExpressionGui {
fn from(original_expr: Option<BoolTagExpr>) -> Self {
let (show_remove_button, expr_as_string) = match &original_expr {
None => (ShowRemoveButton::No, String::new()),
Some(expr) => (ShowRemoveButton::Yes, expr.clone().to_boolean_expression()),
};
Self {
expr: expr_as_string,
validity: ValitityStatus::from(ValiditySynchronous::Valid, None),
show_remove_button,
hint_text: HintText::None,
empty_considered_invalid: EmptyConsideredInvalid::Yes,
changed: false,
}
}
}