gled-ui 2.31.1

gled's egui interface, shared by the desktop app and the browser
Documentation
use egui::{ComboBox, DragValue, Ui};
use gled_model::asset::midi_controller::{
    MidiController, MidiEffectParameter, MidiMeterOutput, MidiValueOutput, MidiValueSource,
    MidiValueSourceKind,
};
use strum::IntoEnumIterator;

use super::{
    action_converters::{effect_target_editor, scene_target_editor},
    value_source_converters::{value_source_from_kind, value_source_uses_active_value},
};

pub(super) fn value_output_editor(
    ui: &mut Ui,
    value: &mut MidiValueOutput,
    dirty: &mut bool,
    controller_id: gled_model::asset_id::AssetId<MidiController>,
    index: usize,
) -> (bool, bool) {
    let mut preview_active = false;
    let mut blackout_blink_preview_active = false;

    value_source_kind_editor(ui, &mut value.source, dirty, controller_id, index);

    if value_source_uses_active_value(&value.source) {
        let mut r = None;
        ui.horizontal(|ui| {
            ui.label("Value");
            let drag = ui.add(DragValue::new(&mut value.active_value).range(0..=127));
            if drag.changed() {
                *dirty = true;
            }
            r = Some(drag);
        });
        if let Some(r) = r {
            preview_active = preview_active || r.hovered() || r.dragged() || r.changed();
        }
    } else {
        let mut any_hovered = false;
        let mut any_dragged = false;
        ui.horizontal(|ui| {
            ui.label("Min");
            let r = ui.add(DragValue::new(&mut value.min).range(0..=127));
            if r.changed() {
                *dirty = true;
            }
            any_hovered |= r.hovered();
            any_dragged |= r.dragged();
            ui.label("Max");
            let r = ui.add(DragValue::new(&mut value.max).range(0..=127));
            if r.changed() {
                *dirty = true;
            }
            any_hovered |= r.hovered();
            any_dragged |= r.dragged();
        });
        preview_active = preview_active || any_hovered || any_dragged;
    }

    let (settings_preview, blink_preview) =
        value_source_settings_editor(ui, &mut value.source, dirty, controller_id, index);
    preview_active |= settings_preview;
    blackout_blink_preview_active |= blink_preview;

    (preview_active, blackout_blink_preview_active)
}

/// Picks what a value output or meter shows.
fn value_source_kind_editor(
    ui: &mut Ui,
    source: &mut MidiValueSource,
    dirty: &mut bool,
    controller_id: gled_model::asset_id::AssetId<MidiController>,
    index: usize,
) {
    ComboBox::new(format!("{}_value_source_{index}", controller_id), "")
        .selected_text(MidiValueSourceKind::from(&*source).to_string())
        .show_ui(ui, |ui| {
            let mut kind = MidiValueSourceKind::from(&*source);
            let mut changed = false;
            for candidate in MidiValueSourceKind::iter() {
                changed |= ui
                    .selectable_value(&mut kind, candidate, candidate.to_string())
                    .changed();
            }
            if changed {
                *source = value_source_from_kind(kind, source.clone());
                *dirty = true;
            }
        });
}

/// The settings of the picked source. Returns whether the value and the
/// blackout blink are being previewed.
fn value_source_settings_editor(
    ui: &mut Ui,
    source: &mut MidiValueSource,
    dirty: &mut bool,
    controller_id: gled_model::asset_id::AssetId<MidiController>,
    index: usize,
) -> (bool, bool) {
    let mut preview_active = false;
    let mut blackout_blink_preview_active = false;

    match source {
        MidiValueSource::BeatFlankPulse {
            start_beat,
            end_beat,
            blackout_blink_value,
        } => {
            let mut any_hovered = false;
            let mut any_dragged = false;
            ui.horizontal(|ui| {
                ui.label("Start Beat");
                let r = ui.add(DragValue::new(start_beat).speed(0.01).range(0.0..=4.0));
                if r.changed() {
                    *dirty = true;
                }
                any_hovered |= r.hovered();
                any_dragged |= r.dragged();
                ui.label("End Beat");
                let r = ui.add(DragValue::new(end_beat).speed(0.01).range(0.0..=4.0));
                if r.changed() {
                    *dirty = true;
                }
                any_hovered |= r.hovered();
                any_dragged |= r.dragged();
            });
            preview_active = preview_active || any_hovered || any_dragged;
            ui.small("Inclusive beat range on 0.0..4.0, wrap supported");

            let mut blink_enabled = blackout_blink_value.is_some();
            if ui
                .checkbox(&mut blink_enabled, "Blink at 4 Hz on blackout")
                .changed()
            {
                *blackout_blink_value = if blink_enabled { Some(127) } else { None };
                *dirty = true;
            }
            if let Some(v) = blackout_blink_value {
                let mut blink_hovered = false;
                let mut blink_dragged = false;
                ui.horizontal(|ui| {
                    ui.label("Blackout blink value");
                    let r = ui.add(DragValue::new(v).range(1..=127));
                    if r.changed() {
                        *dirty = true;
                    }
                    blink_hovered |= r.hovered();
                    blink_dragged |= r.dragged();
                });
                blackout_blink_preview_active = blink_hovered || blink_dragged;
            }
        }
        MidiValueSource::Blackout { inverted, blink } => {
            let invert_response = ui.checkbox(inverted, "Invert (On when blackout is off)");
            if invert_response.changed() {
                *dirty = true;
            }
            let blink_response = ui.checkbox(blink, "Blink at 2 Hz when active");
            if blink_response.changed() {
                *dirty = true;
            }
            preview_active = preview_active
                || invert_response.hovered()
                || invert_response.changed()
                || blink_response.hovered()
                || blink_response.changed();
        }
        MidiValueSource::SceneOpacity { target }
        | MidiValueSource::SceneInputDimmer { target }
        | MidiValueSource::SceneBeatOffset { target }
        | MidiValueSource::SceneIgnoreMainDimmer { target }
        | MidiValueSource::SceneSetOffsetOnFlash { target }
        | MidiValueSource::SceneActive { target }
        | MidiValueSource::SceneFlashed { target } => {
            scene_target_editor(
                ui,
                target,
                dirty,
                format!("{}_value_target_{index}", controller_id),
            );
        }
        MidiValueSource::SceneEffectSettingF32 {
            target,
            effect,
            setting_index,
        } => {
            scene_target_editor(
                ui,
                target,
                dirty,
                format!("{}_value_target_{index}", controller_id),
            );
            let r = effect_target_editor(
                ui,
                effect,
                dirty,
                format!("{controller_id}_value_effect_{index}"),
            );
            preview_active = preview_active || r.hovered();
            ui.horizontal(|ui| {
                ui.label("Setting Index");
                let r = ui.add(DragValue::new(setting_index).range(0..=5));
                if r.changed() {
                    *dirty = true;
                }
                preview_active = preview_active || r.hovered() || r.dragged();
            });
        }
        MidiValueSource::SceneEffectOpacity { target, effect }
        | MidiValueSource::SceneEffectColorShift { target, effect }
        | MidiValueSource::SceneEffectBeatProgression { target, effect }
        | MidiValueSource::SceneEffectBeatOffset { target, effect }
        | MidiValueSource::SceneEffectSpeedExponent { target, effect }
        | MidiValueSource::SceneEffectLayout { target, effect } => {
            scene_target_editor(
                ui,
                target,
                dirty,
                format!("{}_value_target_{index}", controller_id),
            );
            let r = effect_target_editor(
                ui,
                effect,
                dirty,
                format!("{controller_id}_value_effect_{index}"),
            );
            preview_active = preview_active || r.hovered();
        }
        MidiValueSource::EffectSelected {
            index: effect_index,
        } => {
            ui.horizontal(|ui| {
                ui.label("Effect Index");
                let r = ui.add(DragValue::new(effect_index).range(0..=255));
                if r.changed() {
                    *dirty = true;
                }
                preview_active = preview_active || r.hovered() || r.dragged();
            });
        }
        MidiValueSource::SelectedSceneEffectLive { effect, parameter } => {
            let r = effect_target_editor(
                ui,
                effect,
                dirty,
                format!("{controller_id}_value_effect_{index}"),
            );
            preview_active = preview_active || r.hovered();
            ui.horizontal(|ui| {
                ui.label("Value");
                ComboBox::new(format!("{controller_id}_value_parameter_{index}"), "")
                    .selected_text(parameter.to_string())
                    .show_ui(ui, |ui| {
                        for candidate in MidiEffectParameter::iter() {
                            if ui
                                .selectable_value(parameter, candidate, candidate.to_string())
                                .changed()
                            {
                                *dirty = true;
                            }
                        }
                    });
            });
        }
        _ => {}
    }

    (preview_active, blackout_blink_preview_active)
}

/// Edits a Mackie meter: its strip and what it shows.
pub(super) fn meter_output_editor(
    ui: &mut Ui,
    meter: &mut MidiMeterOutput,
    dirty: &mut bool,
    controller_id: gled_model::asset_id::AssetId<MidiController>,
    index: usize,
) {
    ui.horizontal(|ui| {
        ui.label("Strip");
        let mut strip = meter.strip + 1;
        if ui
            .add(DragValue::new(&mut strip).range(1..=8))
            .on_hover_text("The channel strip whose meter shows the value")
            .changed()
        {
            meter.strip = strip - 1;
            *dirty = true;
        }
    });
    value_source_kind_editor(ui, &mut meter.source, dirty, controller_id, index);
    let _ = value_source_settings_editor(ui, &mut meter.source, dirty, controller_id, index);
}