use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use ek_station::default_kinds;
use crate::launcher::{POLL, RunShared, launch_worker};
use crate::plugin::UI_NAME;
pub const BTN_LAUNCH: &str = "启动";
pub const BTN_CANCEL: &str = "取消";
const PICKED_NONE: &str = "已选: (无)";
#[must_use]
pub fn pick_kinds() -> Vec<(String, String)> {
let mut list = default_kinds()
.into_iter()
.map(|k| (k.name.to_string(), k.doc.to_string()))
.collect::<Vec<_>>();
list.push((UI_NAME.to_string(), "常驻观测界面(加载即开窗)".to_string()));
list
}
pub fn render_pick(
ui: &mut egui::Ui,
selected: &mut [bool],
kinds: &[(String, String)],
on_launch: &mut dyn FnMut(),
on_cancel: &mut dyn FnMut(),
) {
ui.heading("ek-station 启动选择");
ui.label("勾选要装载的插件;启动写入清单并拉起 ek-ui(ui 自动补上)。");
ui.separator();
egui::ScrollArea::vertical()
.max_height(240.0)
.show(ui, |ui| {
for (i, (name, doc)) in kinds.iter().enumerate() {
ui.horizontal(|ui| {
ui.checkbox(&mut selected[i], name);
let _ = ui.label(doc.clone());
});
}
});
ui.separator();
let picked: Vec<String> = kinds
.iter()
.zip(selected)
.filter(|(_, ok)| **ok)
.map(|(k, _)| k.0.clone())
.collect();
if picked.is_empty() {
ui.label(PICKED_NONE);
} else {
ui.label(format!("已选: {}", picked.join(", ")));
}
ui.horizontal(|ui| {
if ui
.add_enabled(!picked.is_empty(), egui::Button::new(BTN_LAUNCH))
.clicked()
{
on_launch();
}
if ui.button(BTN_CANCEL).clicked() {
on_cancel();
}
});
}
pub struct PickApp {
kinds: Vec<(String, String)>,
selected: Vec<bool>,
program: PathBuf,
shared: Arc<RunShared>,
spawned: bool,
cancel: bool,
}
impl PickApp {
pub fn new(program: PathBuf, shared: Arc<RunShared>) -> Self {
let kinds = pick_kinds();
let selected = vec![false; kinds.len()];
Self {
kinds,
selected,
program,
shared,
spawned: false,
cancel: false,
}
}
}
impl eframe::App for PickApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
crate::render::apply_ui_style(ctx); let kinds = self.kinds.clone();
let mut req_launch = false;
let mut req_cancel = false;
egui::CentralPanel::default().show(ctx, |ui| {
crate::launcher_ui::render_pick(
ui,
&mut self.selected,
&kinds,
&mut || req_launch = true,
&mut || req_cancel = true,
);
});
if req_cancel {
self.cancel = true;
}
if req_launch && !self.spawned {
let picked: Vec<String> = self
.kinds
.iter()
.zip(&self.selected)
.filter(|(_, ok)| **ok)
.map(|(k, _)| k.0.clone())
.collect();
if !picked.is_empty() {
self.spawned = true;
let shared = Arc::clone(&self.shared);
let program = self.program.clone();
let worker_names = picked;
let h = std::thread::spawn(move || {
launch_worker(&shared, &worker_names, program);
});
*self.shared.handle.lock().unwrap() = Some(h);
}
}
if self.spawned {
let done = self.shared.outcome.lock().unwrap().is_some();
if done {
ctx.send_viewport_cmd(egui::ViewportCommand::Close); } else {
ctx.request_repaint_after(POLL);
}
}
if self.cancel {
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
}
if !self.spawned {
ctx.request_repaint_after(Duration::from_millis(500));
}
}
}