gled-ui 2.34.0

gled's egui interface, shared by the desktop app and the browser
Documentation
//! The edits made through rules. Each writes the mapping's binding lists
//! directly; the window builds the rules again afterwards. An edit of a
//! group changes every member, with the numbers that count up set from
//! each member's `n`.

mod actions;
mod copy;
mod feedback;
mod modes;

pub(crate) use actions::{
    remove_action, remove_rules, set_action, set_mode, set_touch, set_while_held,
};
pub(crate) use copy::{copy_rule, make_mode_switch, relearn_control};
pub(crate) use feedback::{add_feedback, remove_feedback, set_source};
pub(crate) use modes::{add_mode, move_mode, remove_mode, rename_control, rename_mode};

use gled_model::asset::midi_controller::{
    MidiControllerMapping, MidiInputAction, MidiOutputBindingKind, MidiValueSource,
};

use super::build::Rule;
use super::controls::{Control, stored};
use super::groups::{Numbering, RuleGroup};
use super::numbers::{
    action_numbers, output_numbers, set_action_number, set_output_number, set_source_number,
    source_numbers,
};
use super::sentence::Numbers;

/// The rules an edit changes, each with its `n`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Members {
    pub(crate) rules: Vec<Rule>,
    pub(crate) numbers: Vec<i32>,
}

impl Members {
    pub(crate) fn single(rule: Rule) -> Self {
        Self {
            rules: vec![rule],
            numbers: vec![1],
        }
    }

    pub(crate) fn of_group(rules: &[Rule], group: &RuleGroup) -> Self {
        Self {
            rules: group
                .rules
                .iter()
                .map(|&rule| rules[rule].clone())
                .collect(),
            numbers: group.numbers.clone(),
        }
    }

    /// The members' controls and modes, which name their rules.
    pub(crate) fn keys(&self) -> Vec<(usize, Option<u8>)> {
        self.rules
            .iter()
            .map(|rule| (rule.control, rule.mode))
            .collect()
    }

    fn each(&self) -> impl Iterator<Item = (&Rule, i32)> {
        self.rules.iter().zip(self.numbers.iter().copied())
    }
}

/// The value a member with number `n` gets for a number of the template.
fn member_value(template: u8, numbering: Numbering, n: i32) -> u8 {
    match numbering {
        Numbering::Same => template,
        Numbering::Counting { offset } => u8::try_from((n + offset - 1).max(0)).unwrap_or(u8::MAX),
    }
}

/// `template` with the numbers that count set for the member `n`.
fn member_action(template: &MidiInputAction, numbers: &dyn Numbers, n: i32) -> MidiInputAction {
    let mut action = template.clone();
    for (slot, value) in action_numbers(template) {
        set_action_number(
            &mut action,
            slot,
            member_value(value, numbers.numbering(slot), n),
        );
    }
    action
}

fn member_source(template: &MidiValueSource, numbers: &dyn Numbers, n: i32) -> MidiValueSource {
    let mut source = template.clone();
    for (slot, value) in source_numbers(template) {
        set_source_number(
            &mut source,
            slot,
            member_value(value, numbers.numbering(slot), n),
        );
    }
    source
}

/// An output with the numbers that count set for the member `n`.
fn member_output(
    template: &MidiOutputBindingKind,
    numbers: &dyn Numbers,
    n: i32,
) -> MidiOutputBindingKind {
    let mut output = template.clone();
    for (slot, value) in output_numbers(template) {
        set_output_number(
            &mut output,
            slot,
            member_value(value, numbers.numbering(slot), n),
        );
    }
    output
}

/// Removes the input and output bindings at the given indices.
fn remove_bindings(
    mapping: &mut MidiControllerMapping,
    mut inputs: Vec<usize>,
    mut outputs: Vec<usize>,
) {
    inputs.sort_unstable();
    inputs.dedup();
    for index in inputs.into_iter().rev() {
        if index < mapping.input_bindings.len() {
            mapping.input_bindings.remove(index);
        }
    }
    outputs.sort_unstable();
    outputs.dedup();
    for index in outputs.into_iter().rev() {
        if index < mapping.output_bindings.len() {
            mapping.output_bindings.remove(index);
        }
    }
}

/// Stores the controls' names, all but those named after their message.
fn store_controls(mapping: &mut MidiControllerMapping, controls: &[Control]) {
    mapping.controls = controls
        .iter()
        .filter(|control| !control.named_by_message)
        .map(stored)
        .collect();
}