gled-ui 2.34.0

gled's egui interface, shared by the desktop app and the browser
Documentation
//! Changing what rules show on the controller: adding a light, motor
//! fader, meter, ring or pad color, changing what it shows, removing it.

use gled_model::asset::midi_controller::{
    MidiControllerMapping, MidiOutputBinding, MidiValueSource,
};

use super::actions::rule_action;
use super::{Members, member_source, remove_bindings};
use crate::ui::windows::midi_controllers::rules::{
    controls::Control,
    feedback::{FeedbackRole, new_feedback, pad_target, source_for},
    naming::name_rules_of,
    sentence::Numbers,
};

/// Makes the feedback at `position` of each member show `template`, each
/// with its numbers.
pub(crate) fn set_source(
    mapping: &mut MidiControllerMapping,
    controls: &[Control],
    members: &Members,
    position: usize,
    template: &MidiValueSource,
    numbers: &dyn Numbers,
) {
    for (rule, n) in members.each() {
        let Some(&output) = rule.feedback.get(position) else {
            continue;
        };
        if let Some(source) = mapping.output_bindings[output].kind.source_mut() {
            *source = member_source(template, numbers, n);
        }
    }
    name_rules_of(mapping, controls, &members.keys());
}

/// Gives each member feedback of `role` showing what its action changes;
/// a pad color shows through the color mapping `mapping_name`. Returns
/// whether every member got one.
pub(crate) fn add_feedback(
    mapping: &mut MidiControllerMapping,
    controls: &[Control],
    members: &Members,
    role: FeedbackRole,
    mapping_name: &str,
) -> bool {
    let mut all = true;
    let mut added = Vec::new();
    for rule in &members.rules {
        let action = rule_action(mapping, rule);
        let source = source_for(role, action).unwrap_or(MidiValueSource::SelectedSceneOpacity);
        let target = pad_target(action);
        let kind = new_feedback(&controls[rule.control], role, source, target, mapping_name);
        if role != FeedbackRole::PadColor && source_for(role, action).is_none() {
            all = false;
        }
        added.push(MidiOutputBinding {
            name: String::new(),
            kind,
            mode: rule.mode,
        });
    }
    mapping.output_bindings.extend(added);
    name_rules_of(mapping, controls, &members.keys());
    all
}

/// Removes the feedback at `position` of each member.
pub(crate) fn remove_feedback(
    mapping: &mut MidiControllerMapping,
    members: &Members,
    position: usize,
) {
    let outputs = members
        .rules
        .iter()
        .filter_map(|rule| rule.feedback.get(position).copied())
        .collect();
    remove_bindings(mapping, Vec::new(), outputs);
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ui::windows::midi_controllers::rules::{
        build::build_rules, controls::controls_of, sentence::Plain, test_mappings::p1x,
    };
    use gled_model::asset::midi_controller::MidiOutputBindingKind;

    /// Sheet C, task 4: strip 8's meter shows the main dimmer in Rows.
    #[test]
    fn a_meter_can_show_the_main_dimmer() {
        let mut mapping = p1x();
        let controls = controls_of(&mapping);
        let rules = build_rules(&mapping, &controls);
        let fader = rules
            .iter()
            .find(|rule| controls[rule.control].name == "Fader 8" && rule.mode == Some(0))
            .cloned()
            .unwrap_or_else(|| panic!("no Fader 8"));
        let meter = fader
            .feedback
            .iter()
            .position(|&output| {
                matches!(
                    mapping.output_bindings[output].kind,
                    MidiOutputBindingKind::Meter(_)
                )
            })
            .unwrap_or_else(|| panic!("no meter"));
        set_source(
            &mut mapping,
            &controls,
            &Members::single(fader.clone()),
            meter,
            &MidiValueSource::MainDimmer,
            &Plain,
        );
        let output = &mapping.output_bindings[fader.feedback[meter]];
        assert_eq!(output.kind.source(), Some(&MidiValueSource::MainDimmer));
        assert_eq!(output.name, "Fader 8 meter: the main dimmer");
    }
}