gled-ui 2.32.1

gled's egui interface, shared by the desktop app and the browser
Documentation
//! The two-step layout/mode picker used by tiles and effects.

use egui::Ui;
use egui::containers::menu::{MenuButton, SubMenuButton};
use gled_model::patching::Patch;
use gled_model::patching::layout::LayoutSelection;

/// How a selection reads: the layout, plus its mode when that is not the
/// layout's default.
pub(crate) fn selection_label(patch: &Patch, selection: LayoutSelection) -> String {
    let Some(layout) = patch.layout(selection.layout) else {
        return "missing layout".to_string();
    };
    if selection.mode == 0 {
        layout.name.clone()
    } else {
        format!(
            "{} \u{203a} {}",
            layout.name,
            layout.mode(selection.mode).name
        )
    }
}

/// Lists every layout. Clicking a layout picks it with its default mode; a
/// layout with more than the default mode also opens a submenu of its modes on
/// hover, marked with a right-pointing arrow.
fn layout_entries(ui: &mut Ui, patch: &Patch, picked: &mut Option<LayoutSelection>) {
    for layout_ref in patch.layout_refs() {
        let Some(layout) = patch.layout(layout_ref) else {
            continue;
        };
        let name = layout.name.clone();

        if layout.modes.len() > 1 {
            let modes: Vec<String> = layout.modes.iter().map(|mode| mode.name.clone()).collect();
            let (response, _) = SubMenuButton::new(name).ui(ui, |ui| {
                for (index, mode) in modes.iter().enumerate() {
                    if ui.button(mode).clicked() {
                        *picked = Some(LayoutSelection::new(layout_ref, index));
                        ui.close();
                    }
                }
            });
            // the parent is selectable in its own right, taking the default mode
            if response.clicked() {
                *picked = Some(LayoutSelection::new(layout_ref, 0));
                ui.close();
            }
        } else if ui.button(name).clicked() {
            *picked = Some(LayoutSelection::new(layout_ref, 0));
            ui.close();
        }
    }
}

/// Picker for a tile, which always drives some layout.
pub(crate) fn selection_button(ui: &mut Ui, patch: &Patch, current: &mut LayoutSelection) -> bool {
    let mut picked = None;
    MenuButton::new(selection_label(patch, *current)).ui(ui, |ui| {
        layout_entries(ui, patch, &mut picked);
    });
    match picked {
        Some(picked) if picked != *current => {
            *current = picked;
            true
        }
        _ => false,
    }
}

/// Picker for an effect, which inherits its tile's selection unless overridden.
pub(crate) fn inherited_selection_button(
    ui: &mut Ui,
    patch: &Patch,
    current: &mut Option<LayoutSelection>,
) -> bool {
    let label = match current {
        None => "inherit".to_string(),
        Some(selection) => selection_label(patch, *selection),
    };

    let mut picked = None;
    let mut inherit = false;
    MenuButton::new(label).ui(ui, |ui| {
        if ui.button("inherit").clicked() {
            inherit = true;
            ui.close();
        }
        ui.separator();
        layout_entries(ui, patch, &mut picked);
    });

    if inherit {
        let changed = current.is_some();
        *current = None;
        return changed;
    }
    match picked {
        Some(picked) if *current != Some(picked) => {
            *current = Some(picked);
            true
        }
        _ => false,
    }
}