use egui::{ComboBox, Ui};
use gled_model::asset::midi_controller::MidiController;
use gled_protocol::midi::TestCommand;
use std::collections::BTreeSet;
use super::MidiControllerTestState;
use crate::midi_learn::MidiPortDiagnostics;
fn is_selectable_output_port(name: &str) -> bool {
!name.trim().is_empty() && !gled_midi::is_own_port(name)
}
fn available_output_ports() -> Vec<String> {
crate::ui::frame_requests::devices()
.midi_outputs
.into_iter()
.filter(|name| is_selectable_output_port(name))
.collect()
}
fn test_device_port_options(diagnostics: &[MidiPortDiagnostics]) -> Vec<String> {
let mut ports = BTreeSet::new();
for diag in diagnostics {
if diag.output_connected && is_selectable_output_port(&diag.port_name) {
ports.insert(diag.port_name.clone());
}
}
for port in available_output_ports() {
ports.insert(port);
}
ports.into_iter().collect()
}
pub(super) fn short_port_name(port: &str) -> String {
let key = gled_model::asset::midi_controller::normalize_controller_key(port);
match key.split_once(':') {
Some((_, name)) if !name.trim().is_empty() => name.trim().to_owned(),
_ => key,
}
}
pub(super) fn render_test_device_box(
ui: &mut Ui,
test_state: &mut MidiControllerTestState,
diagnostics: &[MidiPortDiagnostics],
controller_id: gled_model::asset_id::AssetId<MidiController>,
test_command_sender: &kanal::Sender<TestCommand>,
) -> Option<String> {
let mut picked = None;
let selected = test_state
.selected_output_port
.as_deref()
.map_or_else(|| "nothing".to_owned(), short_port_name);
ComboBox::new(format!("{controller_id}_test_output_port"), "")
.width(260.0)
.selected_text(selected)
.show_ui(ui, |ui| {
if ui
.selectable_label(test_state.selected_output_port.is_none(), "nothing")
.clicked()
{
test_state.selected_output_port = None;
let _ = test_command_sender.send(TestCommand::UnsetTestDevice);
}
for port_name in test_device_port_options(diagnostics) {
let current =
test_state.selected_output_port.as_deref() == Some(port_name.as_str());
if ui
.selectable_label(current, short_port_name(&port_name))
.on_hover_text(&port_name)
.clicked()
&& !current
{
test_state.selected_output_port = Some(port_name.clone());
let _ = test_command_sender.send(TestCommand::SetTestDevice {
port_name: port_name.clone(),
});
picked = Some(port_name);
}
}
});
picked
}
#[cfg(test)]
mod tests {
use super::short_port_name;
#[test]
fn ports_show_without_client_and_address() {
assert_eq!(
short_port_name("iCON P1-X1:iCON P1-X1 MIDI 1 129:0"),
"iCON P1-X1 MIDI 1"
);
assert_eq!(short_port_name("APC40 [2]"), "APC40");
}
}