use egui::{Button, Color32, Key, KeyboardShortcut, Modifiers, RichText, Ui};
use egui_phosphor_icons::icons;
use gled_model::asset::midi_controller::MidiController;
use super::MidiControllerTestState;
use super::list_state::{LearnRequest, ListState};
use super::side_tabs::SideTab;
use crate::canvas::edit::Edit;
use crate::midi_learn::LearnState;
const UNDO: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Z);
const REDO: KeyboardShortcut =
KeyboardShortcut::new(Modifiers::COMMAND.plus(Modifiers::SHIFT), Key::Z);
const REDO_Y: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Y);
const LISTENING: Color32 = Color32::from_rgb(200, 60, 60);
pub(super) struct Bar<'a, 'e> {
pub(super) list: &'a mut ListState,
pub(super) learning: &'a mut bool,
pub(super) tab: &'a mut SideTab,
pub(super) edit: &'a mut Edit<'e, MidiController>,
pub(super) learn_state: &'a mut LearnState,
pub(super) test_state: &'a MidiControllerTestState,
}
pub(super) fn top_bar(ui: &mut Ui, bar: Bar) {
ui.horizontal_wrapped(|ui| {
let listening = *bar.learning || bar.learn_state.active_request().is_some();
let learn = if listening {
ui.add(
Button::new(
RichText::new("Listening — move a control (Esc stops)").color(Color32::WHITE),
)
.fill(LISTENING),
)
} else {
ui.button(super::iconized(ui, icons::RECORD, " Learn (L)"))
.on_hover_text("Then move or press controls on your device; each one gets a rule")
};
if learn.clicked() {
if listening {
stop_learning(bar.learning, bar.learn_state);
} else {
bar.list.learn_request = Some(LearnRequest::NewControls);
}
}
ui.separator();
if ui
.add_enabled(
bar.edit.can_undo(),
Button::new(icons::ARROW_COUNTER_CLOCKWISE),
)
.on_hover_text(format!("Undo ({})", ui.ctx().format_shortcut(&UNDO)))
.clicked()
{
bar.edit.undo();
}
if ui
.add_enabled(bar.edit.can_redo(), Button::new(icons::ARROW_CLOCKWISE))
.on_hover_text(format!("Redo ({})", ui.ctx().format_shortcut(&REDO)))
.clicked()
{
bar.edit.redo();
}
ui.separator();
let ports = &bar.edit.value.default_for_ports;
let used = if ports.is_empty() {
RichText::new("Not used automatically for any device — choose…")
.color(ui.visuals().warn_fg_color)
} else {
let names: Vec<String> = ports
.iter()
.map(|port| super::test_panel::short_port_name(port))
.collect();
RichText::new(format!("Used automatically for {}", names.join(", ")))
};
if ui
.link(used)
.on_hover_text("Choose devices in the Devices tab")
.clicked()
{
*bar.tab = SideTab::Devices;
}
ui.separator();
let lights = bar
.test_state
.selected_output_port
.as_deref()
.map_or_else(|| "nothing".to_owned(), super::test_panel::short_port_name);
if ui
.link(format!("Test lights on: {lights}"))
.on_hover_text("Point at a light in the rules to light it on this device")
.clicked()
{
*bar.tab = SideTab::Devices;
}
});
}
fn stop_learning(learning: &mut bool, learn_state: &mut LearnState) {
*learning = false;
learn_state.disarm();
}
pub(super) fn keys(
ui: &mut Ui,
list: &mut ListState,
learning: &mut bool,
edit: &mut Edit<MidiController>,
learn_state: &mut LearnState,
) {
let typing = ui.ctx().text_edit_focused();
let (undo, redo) = ui.input_mut(|input| {
(
input.consume_shortcut(&UNDO),
input.consume_shortcut(&REDO) || input.consume_shortcut(&REDO_Y),
)
});
if !typing && undo {
edit.undo();
}
if !typing && redo {
edit.redo();
}
if typing {
return;
}
let (learn, find, escape) = ui.input(|input| {
(
input.key_pressed(Key::L) && input.modifiers.is_none(),
input.key_pressed(Key::Slash),
input.key_pressed(Key::Escape),
)
});
if learn {
if *learning || learn_state.active_request().is_some() {
stop_learning(learning, learn_state);
} else {
list.learn_request = Some(LearnRequest::NewControls);
}
}
if find {
list.focus_find = true;
}
if escape && !egui::Popup::is_any_open(ui.ctx()) {
stop_learning(learning, learn_state);
}
}