plottery_editor 0.7.0

Graphical Editor of Plottery, a creative coding framework for generative vector graphics and pen plotting.
use std::sync::Arc;

use dioxus::prelude::*;
use plottery_project::{
    project_param::ProjectParam, project_param_value::ProjectParamValue,
    project_params_list_wrapper::ProjectParamsListWrapper,
};
use tokio::sync::Mutex;

use crate::router::editor::{
    console_messages::ConsoleMessages, project_runner::ProjectRunner, running_state::RunningState,
};

#[derive(PartialEq, Props, Clone)]
pub struct EditorSliderProps {
    param: ProjectParam,
    project_params: SyncSignal<ProjectParamsListWrapper>,
    project_runner: SyncSignal<Arc<Mutex<ProjectRunner>>>,
    running_state: SyncSignal<RunningState>,
    console: SyncSignal<ConsoleMessages>,
    release: bool,
}

#[component]
pub fn Slider(mut props: EditorSliderProps) -> Element {
    let slider_step = match props.param.value {
        ProjectParamValue::FloatRanged { val: _, min, max } => {
            ((max - min) / 100_000_f32).to_string()
        }
        ProjectParamValue::IntRanged {
            val: _,
            min: _,
            max: _,
        } => "1".to_string(),
        _ => panic!("Unexpected Error"),
    };

    let mut slider_value = use_signal(|| match props.param.value {
        ProjectParamValue::FloatRanged {
            val,
            min: _,
            max: _,
        } => val,
        ProjectParamValue::IntRanged {
            val,
            min: _,
            max: _,
        } => val as f32,
        _ => panic!("Unexpected Error"),
    });
    let slider_value_string = use_memo(move || {
        format!("{:.5}", slider_value.read())
            .trim_end_matches('0')
            .trim_end_matches('.')
            .to_string()
    });

    rsx! {
        style { { include_str!("slider.css") } }
        div { class: "Slider",
            p { class: "slider_value",
                "{slider_value_string}"
            }
            input {
                class: "slider",
                name: "{props.param.name.clone()}",
                required: true,
                r#type: "range",
                step: slider_step,
                min: match props.param.value {
                    ProjectParamValue::FloatRanged { val: _, min, max: _ } => min.to_string(),
                    ProjectParamValue::IntRanged { val: _, min, max: _ } => min.to_string(),
                    _ => panic!("Unexpected Error"),
                },
                max: match props.param.value {
                    ProjectParamValue::FloatRanged { val: _, min: _, max } => max.to_string(),
                    ProjectParamValue::IntRanged { val: _, min: _, max } => max.to_string(),
                    _ => panic!("Unexpected Error"),
                },
                value: slider_value.to_string(),
                oninput: move |event| {
                    let new_value = event.value().parse::<f32>().unwrap();
                    slider_value.set(new_value);
                },
                onchange: move |event| {
                    let mut new_params = props.project_params.read().clone();
                    for param_field in new_params.list.iter_mut() {
                        if param_field.name == props.param.name.clone() {
                            let new_val = event.value().parse().expect("Failed to parse slider value");
                            match param_field.value {
                                ProjectParamValue::FloatRanged { .. } => param_field.value.set_f32(new_val),
                                ProjectParamValue::IntRanged { .. } => param_field.value.set_i32(new_val.round() as i32),
                                _ => panic!("Unexpected parameter value type in slider"),
                            }
                        }
                    }
                    props.project_params.set(new_params);
                    match props.project_runner.read().try_lock() {
                        Ok(mut runner) => runner.trigger_run_project(props.release, props.running_state, props.console),
                        Err(e) => {
                            log::error!("Error preparing to run: {:?}", e);
                            props.running_state.set(RunningState::RunFailed { msg: format!("Error preparing to run: {}", e) });
                        },
                    }
                }
            }
        }
    }
}