mpc_valet/components/
tuning_form.rs

1use crate::components::Icon;
2use crate::model::LayerVelocityMode;
3use wasm_bindgen::JsCast;
4use web_sys::{HtmlInputElement, InputEvent, MouseEvent};
5use yew::{html, Callback, Component, Context, Html, Properties};
6use yew_utils::components::drop_down::DropDown;
7
8#[derive(Properties, PartialEq)]
9pub struct TuningFormProps {
10    #[prop_or_default]
11    pub pitch_preference: f32,
12
13    #[prop_or_default]
14    pub layer_velocity_mode: LayerVelocityMode,
15
16    #[prop_or_default]
17    pub program_name: String,
18
19    #[prop_or_default]
20    pub on_pitch_preference_change: Callback<f32>,
21
22    #[prop_or_default]
23    pub on_layer_velocity_mode_change: Callback<LayerVelocityMode>,
24
25    #[prop_or_default]
26    pub on_program_name_change: Callback<String>,
27
28    #[prop_or_default]
29    pub on_save: Callback<()>,
30}
31
32pub enum TuningFormMessages {
33    PitchPreferenceChange(f32),
34    LayerVelocityModeChange(LayerVelocityMode),
35    ProgramNameChanged(String),
36    Save,
37}
38
39/// Root note selector for a list of sample files.
40#[derive(Default)]
41pub struct TuningForm {}
42
43impl Component for TuningForm {
44    type Message = TuningFormMessages;
45    type Properties = TuningFormProps;
46
47    fn create(_ctx: &Context<Self>) -> Self {
48        Self {}
49    }
50
51    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
52        match msg {
53            TuningFormMessages::PitchPreferenceChange(pitch_preference) => {
54                ctx.props()
55                    .on_pitch_preference_change
56                    .emit(pitch_preference);
57                false
58            }
59            TuningFormMessages::LayerVelocityModeChange(mode) => {
60                ctx.props().on_layer_velocity_mode_change.emit(mode);
61                false
62            }
63            TuningFormMessages::ProgramNameChanged(name) => {
64                ctx.props().on_program_name_change.emit(name);
65                false
66            }
67            TuningFormMessages::Save => {
68                ctx.props().on_save.emit(());
69                false
70            }
71        }
72    }
73
74    fn view(&self, ctx: &Context<Self>) -> Html {
75        let layer_help_text = match ctx.props().layer_velocity_mode {
76            LayerVelocityMode::Automatic => {
77                "Each layer will only be used for a range of the velocity."
78            }
79            LayerVelocityMode::Unison => "All the layers will play at the same time.",
80        };
81        html! {
82            <div class="box">
83                <div class="block">
84                    <div class="field">
85                        <label class="label">{"Pitch Preference"}</label>
86                        <div class="control">
87                            <input
88                                id="pitch_preference"
89                                type="range"
90                                min=0
91                                max=1
92                                step=0.01
93                                value={ctx.props().pitch_preference.to_string()}
94                                oninput={TuningForm::on_pitch_preference_change(ctx)}
95                            />
96                        </div>
97                    </div>
98                    <div class="field">
99                        <label class="label">{"Layer Velocity Mode"}</label>
100                        <div class="control">
101                            <div class="select">
102                                <DropDown<LayerVelocityMode>
103                                    initial={ctx.props().layer_velocity_mode}
104                                    options={vec![LayerVelocityMode::Unison, LayerVelocityMode::Automatic]}
105                                    selection_changed={ctx.link().callback(TuningFormMessages::LayerVelocityModeChange)}
106                                />
107                            </div>
108                        </div>
109                        <p class="help">{layer_help_text}</p>
110                    </div>
111                </div>
112                <div class="block">
113                    <div class="field has-addons">
114                        <div class="control">
115                            <input
116                                class="input"
117                                type="text"
118                                placeholder="Program Name"
119                                value={ctx.props().program_name.clone()}
120                                oninput={TuningForm::on_program_name_change(ctx)}
121                            />
122                        </div>
123                        <div class="control">
124                            <button class="button is-link" onclick={ctx.link().callback(|_: MouseEvent| TuningFormMessages::Save)}>
125                                <Icon icon="save" text="Save" />
126                            </button>
127                        </div>
128                    </div>
129                </div>
130            </div>
131        }
132    }
133}
134
135impl TuningForm {
136    fn on_pitch_preference_change(ctx: &Context<TuningForm>) -> Callback<InputEvent> {
137        ctx.link().batch_callback(|e: InputEvent| {
138            let input: HtmlInputElement = e
139                .target()
140                .and_then(|t| t.dyn_into::<HtmlInputElement>().ok())?;
141            Some(TuningFormMessages::PitchPreferenceChange(
142                input.value_as_number() as f32,
143            ))
144        })
145    }
146
147    fn on_program_name_change(ctx: &Context<TuningForm>) -> Callback<InputEvent> {
148        ctx.link().batch_callback(|e: InputEvent| {
149            let input: HtmlInputElement = e
150                .target()
151                .and_then(|t| t.dyn_into::<HtmlInputElement>().ok())?;
152            Some(TuningFormMessages::ProgramNameChanged(input.value()))
153        })
154    }
155}