use std::sync::{Arc, mpsc::Sender};
use crate::{
model::{
AppEvent, CropData, CustomSelectData, InputConstraints, TrimData, ValidationCallback,
ValueFormatter,
},
visitors::VisitorContext,
};
#[derive(Debug, Clone)]
pub(crate) struct SelectOption {
pub(crate) name: String,
pub(crate) value: String,
pub(crate) available: bool,
}
impl From<&str> for SelectOption {
fn from(value: &str) -> Self {
SelectOption {
name: value.to_owned(),
value: value.to_owned(),
available: true,
}
}
}
impl From<(&str, &str)> for SelectOption {
fn from((name, value): (&str, &str)) -> Self {
SelectOption {
name: name.to_owned(),
value: value.to_owned(),
available: true,
}
}
}
impl SelectOption {
pub(crate) fn from_slice(values: &[&str]) -> Vec<Self> {
values.iter().map(|&v| v.into()).collect()
}
pub(crate) fn from_pairs(names: &[(&str, &str)]) -> Vec<Self> {
names.iter().map(|&v| v.into()).collect()
}
}
pub(crate) enum ParameterData {
Select {
options: Vec<SelectOption>,
selected_index: usize,
},
CustomSelect {
options: Vec<SelectOption>,
selected_index: usize,
value: String,
constraints: InputConstraints,
validator: ValidationCallback,
formatter: Option<ValueFormatter>,
},
Toggle {
value: bool,
},
Trim(TrimData),
Crop(CropData),
}
pub(crate) struct Parameter {
pub(crate) id: &'static str,
pub(crate) name: String,
pub(crate) enabled: bool,
pub(crate) order: usize,
pub(crate) data: ParameterData,
}
impl Parameter {
pub(crate) fn new(id: &'static str, name: &str, data: ParameterData) -> Self {
Parameter {
id,
name: name.to_owned(),
enabled: true,
order: 1000,
data,
}
}
pub(crate) fn with_order(mut self, order: usize) -> Self {
self.order = order;
self
}
pub(crate) fn toggle_prev(&mut self, event_sender: &Sender<AppEvent>) {
if !self.enabled {
return;
}
match &mut self.data {
ParameterData::Toggle { value } => {
*value = !*value;
}
ParameterData::Select {
options,
selected_index,
} => {
if !options.is_empty() {
let last = options.len() - 1;
let mut next = if *selected_index == 0 {
last
} else {
*selected_index - 1
};
for _ in 0..=last {
if options[next].available {
break;
}
next = if next == 0 { last } else { next - 1 };
}
*selected_index = next;
}
}
ParameterData::CustomSelect {
options,
selected_index,
value,
..
} => {
if !options.is_empty() {
*selected_index = if *selected_index == 0 {
options.len() - 1
} else {
*selected_index - 1
};
value.clone_from(&options[*selected_index].value);
}
}
ParameterData::Trim(_) | ParameterData::Crop(_) => self.open_modal(event_sender),
}
}
pub(crate) fn toggle_next(&mut self, event_sender: &Sender<AppEvent>) {
if !self.enabled {
return;
}
match &mut self.data {
ParameterData::Toggle { value } => {
*value = !*value;
}
ParameterData::Select {
options,
selected_index,
} => {
if !options.is_empty() {
let last = options.len() - 1;
let mut next = if *selected_index >= last {
0
} else {
*selected_index + 1
};
for _ in 0..=last {
if options[next].available {
break;
}
next = if next >= last { 0 } else { next + 1 };
}
*selected_index = next;
}
}
ParameterData::CustomSelect {
options,
selected_index,
value,
..
} => {
if !options.is_empty() {
*selected_index = if *selected_index >= options.len() - 1 {
0
} else {
*selected_index + 1
};
value.clone_from(&options[*selected_index].value);
}
}
ParameterData::Trim(_) | ParameterData::Crop(_) => self.open_modal(event_sender),
}
}
pub(crate) fn describe_value(&self) -> String {
match &self.data {
ParameterData::Toggle { value } => if *value { "on" } else { "off" }.to_owned(),
ParameterData::Select {
options,
selected_index,
..
} => options
.get(*selected_index)
.map_or_else(String::new, |option| option.name.clone()),
ParameterData::CustomSelect {
value, formatter, ..
} => {
if let Some(fmt) = formatter {
fmt(value)
} else {
value.clone()
}
}
ParameterData::Trim(data) => data.to_string(),
ParameterData::Crop(data) => data.to_string(),
}
}
pub(crate) fn describe(&self) -> String {
format!("{}: {}", self.name, self.describe_value())
}
pub(crate) fn open_modal(&self, event_sender: &Sender<AppEvent>) {
if !self.enabled {
return;
}
match &self.data {
ParameterData::CustomSelect {
value,
constraints,
validator,
..
} => {
let _ = event_sender.send(AppEvent::OpenCustomSelectModal(CustomSelectData {
name: Arc::from(self.name.clone()), value: value.clone(),
constraints: *constraints,
validator: Arc::clone(validator),
}));
}
ParameterData::Trim(data) => {
let _ = event_sender.send(AppEvent::OpenTrimModal(data.clone()));
}
ParameterData::Crop(data) => {
let _ = event_sender.send(AppEvent::OpenCropModal(data.clone()));
}
_ => {}
}
}
}
impl Default for Parameter {
fn default() -> Self {
Parameter {
id: "",
name: String::new(),
enabled: false,
order: 0,
data: ParameterData::Toggle { value: false },
}
}
}
impl ParameterData {
pub(crate) fn is_editable(&self) -> bool {
matches!(
self,
ParameterData::CustomSelect { .. } | ParameterData::Trim { .. }
)
}
}
pub trait PresetParameter<'a> {
fn apply_preset(ctx: &VisitorContext, data: &mut ParameterData, preset_value: &str);
fn save_preset(ctx: &VisitorContext, data: &'a ParameterData) -> Option<String>;
fn set_parameter_value(data: &mut ParameterData, new_value: &str) {
match data {
ParameterData::Toggle { value } => {
*value = new_value == "true" || new_value == "1";
}
ParameterData::CustomSelect { value, .. } => {
new_value.clone_into(value);
}
ParameterData::Select {
options,
selected_index,
} => {
for (i, option) in options.iter().enumerate() {
if option.value == new_value && option.available {
*selected_index = i;
break;
}
}
}
_ => {}
}
}
}