use gled_model::asset::midi_controller::MidiInputActionKind;
use strum::IntoEnumIterator;
use super::controls::ControlKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ActionGroup {
Global,
Navigation,
Scene,
Animation,
}
impl ActionGroup {
pub(crate) fn label(self) -> &'static str {
match self {
Self::Global => "Global",
Self::Navigation => "Navigation",
Self::Scene => "Scene",
Self::Animation => "Animation",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Fit {
Press,
Level,
Endless,
}
impl Fit {
fn suits(self, kind: ControlKind) -> bool {
match self {
Self::Press => matches!(kind, ControlKind::Button | ControlKind::Pad),
Self::Level => matches!(kind, ControlKind::Fader | ControlKind::Knob),
Self::Endless => kind == ControlKind::Encoder,
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ActionInfo {
pub(crate) kind: MidiInputActionKind,
pub(crate) group: ActionGroup,
pub(crate) label: &'static str,
pub(crate) phrase: &'static str,
pub(crate) description: &'static str,
pub(crate) keywords: &'static str,
fit: Fit,
}
impl ActionInfo {
pub(crate) fn suits(&self, kind: ControlKind) -> bool {
self.fit.suits(kind)
}
pub(crate) fn matches(&self, query: &str) -> bool {
let query = query.trim().to_lowercase();
query.split_whitespace().all(|word| {
self.label.to_lowercase().contains(word)
|| self.keywords.contains(word)
|| self.kind.to_string().to_lowercase().contains(word)
|| self.group.label().to_lowercase().contains(word)
})
}
}
pub(crate) fn action_info(kind: MidiInputActionKind) -> ActionInfo {
use ActionGroup::{Animation, Global, Navigation, Scene};
use Fit::{Endless, Level, Press};
use MidiInputActionKind as K;
let (group, label, phrase, fit, description, keywords) = match kind {
K::Tap => (
Global,
"Tap tempo",
"{@taps the tempo}",
Press,
"Tap a few times to set the BPM",
"bpm beat",
),
K::SetMainDimmer => (
Global,
"Main dimmer",
"{@sets the main dimmer}",
Level,
"Fader position = master brightness",
"master brightness",
),
K::SetMainDimmerRelative => (
Global,
"Main dimmer up / down",
"{@turns the main dimmer up/down} · {encoder}",
Endless,
"An endless encoder nudges the master brightness",
"master brightness",
),
K::SetBlackout => (
Global,
"Blackout while held (flash blackout)",
"{@switches blackout on}",
Press,
"Blackout on press, back to normal on release",
"flash hold momentary kill",
),
K::ToggleBlackout => (
Global,
"Toggle blackout",
"{@toggles blackout}",
Press,
"Each press switches blackout on or off",
"kill",
),
K::SetSpeedAdd => (
Global,
"Speed offset",
"{@sets the speed offset}",
Level,
"Adds to the animation speed",
"tempo",
),
K::SetSpeedMultiply => (
Global,
"Speed multiplier",
"{@sets the speed multiplier}",
Level,
"Multiplies the animation speed",
"tempo",
),
K::SetSpeedRelative => (
Global,
"BPM up / down",
"{@nudges the BPM} · {encoder}",
Endless,
"An endless encoder changes the tempo",
"tempo speed",
),
K::SelectSceneDistributed => (
Navigation,
"Sweep through all scenes (knob)",
"{@sweeps through all scenes}",
Level,
"The knob's position picks a scene: 0 = first … 127 = last",
"select pick",
),
K::SelectSceneRelative => (
Navigation,
"Next / previous scene",
"{@steps through the scenes} · {encoder}",
Endless,
"",
"select",
),
K::SelectSceneRow => (
Navigation,
"Next / previous row",
"{@steps through the rows} · {encoder}",
Endless,
"",
"select",
),
K::SelectSceneColumn => (
Navigation,
"Next / previous column",
"{@steps through the columns} · {encoder}",
Endless,
"",
"select",
),
K::SelectScene => (
Navigation,
"Jump to a scene (button)",
"{@selects} {target}",
Press,
"Makes one particular scene the selected one",
"select pick",
),
K::SelectMode => (
Navigation,
"Switch controller mode",
"{@switches the controller to mode} {mode}",
Press,
"Changes which rules work",
"mode page layer",
),
K::SelectSceneEffect => (
Navigation,
"Jump to an animation (button)",
"{@selects} {animation}",
Press,
"Makes one particular animation the selected one",
"select effect",
),
K::SelectSceneEffectRelative => (
Navigation,
"Next / previous animation",
"{@steps through the animations} · {encoder}",
Endless,
"",
"select effect",
),
K::SelectSceneEffectDistributed => (
Navigation,
"Sweep through all animations (knob)",
"{@sweeps through all animations}",
Level,
"The knob's position picks an animation",
"select effect",
),
K::ToggleSceneActive => (
Scene,
"Toggle scene on / off",
"{@toggles} {target} {@on/off}",
Press,
"Each press switches the scene on or off",
"activate",
),
K::SetSceneActive => (
Scene,
"Scene on while held",
"{@switches on} {target}",
Press,
"On while pressed, off on release",
"hold momentary activate",
),
K::FlashScene => (
Scene,
"Flash scene",
"{@flashes} {target}",
Press,
"The scene shows while the button is held",
"hold momentary",
),
K::SetSceneOpacity => (
Scene,
"Scene opacity",
"{@sets the opacity of} {target}",
Level,
"",
"brightness",
),
K::SetSceneInputDimmer => (
Scene,
"Scene input dimmer",
"{@sets the input dimmer of} {target}",
Level,
"",
"",
),
K::SetSceneBeatOffset => (
Scene,
"Scene beat offset",
"{@sets the beat offset of} {target}",
Level,
"",
"",
),
K::SetSceneIgnoreMainDimmer => (
Scene,
"Scene ignores main dimmer",
"{@sets “ignore main dimmer” of} {target}",
Press,
"",
"",
),
K::SetSceneSetOffsetOnFlash => (
Scene,
"Scene: set offset on flash",
"{@sets “offset on flash” of} {target}",
Press,
"",
"",
),
K::SetSceneEffectSettingF32 => (
Animation,
"Animation setting 1–6",
"{@sets} {setting} of {effect} in {target}",
Level,
"The animation's own sliders",
"effect parameter",
),
K::SetSceneEffectOpacity => (
Animation,
"Animation opacity",
"{@sets the opacity of} {effect} in {target}",
Level,
"",
"effect",
),
K::SetSceneEffectColorShift => (
Animation,
"Animation color shift",
"{@sets the color shift of} {effect} in {target}",
Level,
"",
"effect hue",
),
K::SetSceneEffectBeatProgression => (
Animation,
"Animation beat progression",
"{@sets the beat progression of} {effect} in {target}",
Level,
"",
"effect",
),
K::SetSceneEffectBeatOffset => (
Animation,
"Animation beat offset",
"{@sets the beat offset of} {effect} in {target}",
Level,
"",
"effect",
),
K::SetSceneEffectSpeedExponent => (
Animation,
"Animation speed",
"{@sets the speed of} {effect} in {target}",
Level,
"",
"effect tempo",
),
K::SetSceneEffectLayout => (
Animation,
"Animation layout",
"{@sets the layout of} {effect} in {target}",
Level,
"0 = the tile's layout, 1 = the topology, 2 and up the layouts",
"effect",
),
};
ActionInfo {
kind,
group,
label,
phrase,
description,
keywords,
fit,
}
}
pub(crate) fn actions() -> impl Iterator<Item = ActionInfo> {
MidiInputActionKind::iter().map(action_info)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_search_finds_actions_by_what_people_call_them() {
let found = |query: &str| -> Vec<&'static str> {
actions()
.filter(|info| info.matches(query))
.map(|info| info.label)
.collect()
};
assert!(found("flash").contains(&"Flash scene"));
assert!(found("flash").contains(&"Blackout while held (flash blackout)"));
assert!(found("animation speed").contains(&"Animation speed"));
assert!(found("speed exponent").contains(&"Animation speed"));
}
#[test]
fn every_action_suits_some_control() {
for info in actions() {
assert!(
[
ControlKind::Button,
ControlKind::Fader,
ControlKind::Encoder
]
.iter()
.any(|kind| info.suits(*kind)),
"{:?}",
info.kind
);
}
}
}