use gled_model::asset::midi_controller::{
MidiControllerMapping, MidiInputAction, MidiMode, MidiValueSource,
};
use super::store_controls;
use crate::ui::windows::midi_controllers::rules::{
controls::{Control, control_index},
naming::name_rules_of,
};
pub(crate) fn add_mode(mapping: &mut MidiControllerMapping, name: &str) -> u8 {
mapping.modes.push(MidiMode {
name: name.to_owned(),
highlight: None,
});
u8::try_from(mapping.modes.len() - 1).unwrap_or(u8::MAX)
}
pub(crate) fn rename_mode(
mapping: &mut MidiControllerMapping,
controls: &[Control],
mode: u8,
name: &str,
) {
let Some(entry) = mapping.modes.get_mut(usize::from(mode)) else {
return;
};
entry.name = name.to_owned();
let switches: Vec<(usize, Option<u8>)> = mapping
.input_bindings
.iter()
.filter(|binding| matches!(binding.action, MidiInputAction::SelectMode { mode: switched } if switched == mode))
.filter_map(|binding| control_index(controls, &binding.trigger).map(|control| (control, binding.mode)))
.collect();
name_rules_of(mapping, controls, &switches);
}
pub(crate) fn move_mode(mapping: &mut MidiControllerMapping, from: u8, to: u8) {
let count = mapping.modes.len();
if usize::from(from) >= count || usize::from(to) >= count || from == to {
return;
}
let mode = mapping.modes.remove(usize::from(from));
mapping.modes.insert(usize::from(to), mode);
let moved = |index: u8| {
if index == from {
to
} else if from < to && index > from && index <= to {
index - 1
} else if to < from && index >= to && index < from {
index + 1
} else {
index
}
};
for binding in &mut mapping.input_bindings {
binding.mode = binding.mode.map(moved);
if let MidiInputAction::SelectMode { mode } = &mut binding.action {
*mode = moved(*mode);
}
}
for binding in &mut mapping.output_bindings {
binding.mode = binding.mode.map(moved);
if let Some(MidiValueSource::ModeActive { mode }) = binding.kind.source_mut() {
*mode = moved(*mode);
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ModeInUse {
pub(crate) bindings: usize,
}
pub(crate) fn remove_mode(mapping: &mut MidiControllerMapping, mode: u8) -> Result<(), ModeInUse> {
if mapping.remove_unused_mode(mode) {
Ok(())
} else {
Err(ModeInUse {
bindings: mapping.mode_uses(mode),
})
}
}
pub(crate) fn rename_control(
mapping: &mut MidiControllerMapping,
controls: &mut [Control],
control: usize,
name: &str,
) {
let Some(entry) = controls.get_mut(control) else {
return;
};
entry.name = name.trim().to_owned();
entry.named_by_message = false;
store_controls(mapping, controls);
let modes: Vec<(usize, Option<u8>)> = std::iter::once(None)
.chain((0..mapping.modes.len()).map(|mode| u8::try_from(mode).ok()))
.map(|mode| (control, mode))
.collect();
name_rules_of(mapping, controls, &modes);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ui::windows::midi_controllers::rules::{
build::build_rules, controls::controls_of, test_mappings::p1x,
};
#[test]
fn a_used_mode_stays() {
let mut mapping = p1x();
assert!(remove_mode(&mut mapping, 0).is_err());
assert_eq!(mapping.modes.len(), 2);
let grid = add_mode(&mut mapping, "Grid");
assert_eq!(remove_mode(&mut mapping, grid), Ok(()));
}
#[test]
fn moving_a_mode_keeps_what_works_in_it() {
let mut mapping = p1x();
let controls = controls_of(&mapping);
let before = build_rules(&mapping, &controls)
.iter()
.filter(|rule| rule.mode == Some(1))
.count();
move_mode(&mut mapping, 1, 0);
assert_eq!(mapping.modes[0].name, "Animations");
let after = build_rules(&mapping, &controls)
.iter()
.filter(|rule| rule.mode == Some(0))
.count();
assert_eq!(before, after);
assert!(
mapping
.input_bindings
.iter()
.any(|binding| binding.name.starts_with("Encoder 2 push")
&& binding.action == MidiInputAction::SelectMode { mode: 0 })
);
}
#[test]
fn a_renamed_control_keeps_its_name_and_renames_its_bindings() {
let mut mapping = p1x();
let mut controls = controls_of(&mapping);
let fader = controls
.iter()
.position(|control| control.name == "Fader 3")
.unwrap_or_else(|| panic!("no Fader 3"));
rename_control(&mut mapping, &mut controls, fader, "Strip 3");
assert!(
mapping
.controls
.iter()
.any(|control| control.name == "Strip 3")
);
assert!(
mapping
.input_bindings
.iter()
.any(|binding| binding.name.starts_with("Strip 3:"))
);
assert!(
controls_of(&mapping)
.iter()
.any(|control| control.name == "Strip 3")
);
}
}