mod bypass;
pub mod colors;
mod focus;
mod knob;
mod meter;
mod simd;
pub mod state;
mod status_bar;
mod vsep;
pub(crate) mod zones;
pub use colors::{
COL_ACCENT, COL_AMBER, COL_BG, COL_BORDER, COL_BYPASS_OFF, COL_MUTED, COL_PANEL, COL_TEXT,
COL_VU_GREEN, COL_VU_RED, COL_VU_YELLOW,
};
pub use colors::{resolve_accent, resolve_color};
pub use state::{UiState, VuMeterSharedState, VuUniforms};
use self::focus::handle_focus_navigation;
use self::status_bar::draw_zone5_status_bar;
use self::vsep::styled_vsep;
use self::zones::{draw_zone1_identity, draw_zone2_controls, draw_zone3_meters, draw_zone4_bypass};
use crate::clap::extensions::params::bypass_u32_to_bool;
use crate::clap::plugin::NamClapShared;
use clack_plugin::host::HostSharedHandle;
use std::sync::atomic::Ordering;
use std::time::{Duration, Instant};
pub fn draw_ui(
ui: &mut egui::Ui,
shared: &NamClapShared,
host: &HostSharedHandle,
state: &mut UiState,
) {
if shared.cold.ui_load_error.swap(false, Ordering::Relaxed) {
state.error_expiration = Some(Instant::now() + Duration::from_secs(3));
if let Ok(msg_guard) = shared.cold.ui_load_error_msg.lock() {
state.error_msg = msg_guard.clone();
} else {
state.error_msg = "Load failed".to_string();
}
}
if shared.cold.ui_ir_load_error.swap(false, Ordering::Relaxed) {
state.error_expiration = Some(Instant::now() + Duration::from_secs(3));
if let Ok(msg_guard) = shared.cold.ui_ir_load_error_msg.lock() {
state.error_msg = msg_guard.clone();
} else {
state.error_msg = "IR load failed".to_string();
}
}
let current_bypass = bypass_u32_to_bool(shared.ui_to_rt.param_bypass.load(Ordering::Relaxed));
let accent_color = resolve_accent(shared);
let mut load_btn_id = None;
let mut load_ir_btn_id = None;
ui.spacing_mut().item_spacing = egui::vec2(4.0, 4.0);
let available_w = ui.available_width();
ui.horizontal(|ui| {
let total_content_width =
135.0 + 240.0 + 80.0 + 60.0 + 3.0 * 13.0 + 6.0 * ui.spacing().item_spacing.x;
let extra_width = (available_w - total_content_width).max(0.0);
let left_space = (extra_width / 2.0) - ui.spacing().item_spacing.x;
if left_space > 0.0 {
ui.add_space(left_space);
}
let (model_btn, ir_btn) = draw_zone1_identity(ui, shared, host, state, accent_color);
load_btn_id = model_btn;
load_ir_btn_id = ir_btn;
styled_vsep(ui);
draw_zone2_controls(ui, shared, host, current_bypass, accent_color);
styled_vsep(ui);
draw_zone3_meters(ui, shared, state, current_bypass);
styled_vsep(ui);
draw_zone4_bypass(ui, shared, host, accent_color);
});
draw_zone5_status_bar(ui, shared, state, accent_color);
if state.drag_active {
egui::Area::new(egui::Id::new("drop_overlay"))
.anchor(egui::Align2::CENTER_CENTER, egui::vec2(0.0, 0.0))
.order(egui::Order::Foreground)
.interactable(false)
.show(ui.ctx(), |ui| {
let screen_rect = ui.ctx().content_rect();
let painter = ui.painter();
painter.rect_filled(
screen_rect,
0.0,
egui::Color32::from_rgba_unmultiplied(26, 29, 35, 217),
);
ui.vertical_centered(|ui| {
ui.add_space(screen_rect.height() / 2.0 - 20.0);
ui.label(
egui::RichText::new("Drop NAM Model Here ⬇️")
.font(egui::FontId::proportional(20.0))
.strong()
.color(accent_color),
);
});
});
}
handle_focus_navigation(ui, load_btn_id, load_ir_btn_id);
ui.ctx().request_repaint_after(Duration::from_millis(30));
}
#[cfg(test)]
mod ui_test;