Scripts: a Scripts folder read asked for during another starts when that one ends

A File menu opening or a failed ui.runInstalledScript while a read of the
Scripts folder was under way was dropped, so a script added after that
read passed the folder stayed unlisted until the next window activation
or File menu opening. The request is now kept and the next frame starts
the read once the running one is taken in.
This commit is contained in:
dotCooCoo
2026-11-10 18:23:33 -08:00
parent 5bc51a7c35
commit f78d715fee
2 changed files with 54 additions and 3 deletions
+31 -3
View File
@@ -38,6 +38,8 @@ struct State {
revision: u64,
/// The listing being read on another thread.
reading: Option<Receiver<Listing>>,
/// Another read was asked for while one was under way; [`poll`] starts it when that one ends.
pending: bool,
/// A first read was asked for.
started: bool,
/// The window was the active one at the last [`poll`].
@@ -94,12 +96,14 @@ pub fn revision() -> u64 {
state().revision
}
/// Read the Scripts folder again on another thread, unless a read is under way or the session has
/// no folder (the web, and a run without preferences). `ctx` is asked to draw when the read ends.
/// Read the Scripts folder again on another thread, unless the session has no folder (the web, and
/// a run without preferences). While a read is under way, another read starts when it ends
/// ([`poll`]). `ctx` is asked to draw when the read ends.
pub fn refresh(app: &VectorcraftApp, ctx: Option<&egui::Context>) {
let Some(root) = app.session.scripts_dir().map(Path::to_path_buf) else { return };
let mut state = state();
if state.reading.is_some() {
state.pending = true;
return;
}
let (tx, rx) = std::sync::mpsc::channel();
@@ -119,7 +123,7 @@ pub fn refresh(app: &VectorcraftApp, ctx: Option<&egui::Context>) {
}
/// Once per iteration: start a read on the first one and when the window becomes the active one,
/// and take in a read that ended.
/// take in a read that ended, and then start a read asked for while it was under way.
pub fn poll(app: &VectorcraftApp, ctx: &egui::Context) {
let focused = ctx.input(|i| i.focused);
let start = {
@@ -132,6 +136,17 @@ pub fn poll(app: &VectorcraftApp, ctx: &egui::Context) {
refresh(app, Some(ctx));
}
take(false);
let again = {
let mut state = state();
let again = state.pending && state.reading.is_none();
if again {
state.pending = false;
}
again
};
if again {
refresh(app, Some(ctx));
}
}
/// Take in the read under way: when it has ended, or with `wait` once it ends.
@@ -158,6 +173,19 @@ pub(crate) fn wait() {
take(true);
}
/// Wait for the read under way to end and leave its listing to be taken in, so the read is under
/// way until the next [`poll`].
#[cfg(test)]
pub(crate) fn end_untaken() {
let mut state = state();
let Some(reading) = &state.reading else { return };
let (tx, rx) = std::sync::mpsc::channel();
if let Ok(listing) = reading.recv() {
let _ = tx.send(listing);
}
state.reading = Some(rx);
}
/// File › Scripts' items: the listed scripts and subfolders, a note when some may be missing or
/// the folder cannot be read, then Check Script… and Other Script….
pub fn items() -> Vec<Item> {
+23
View File
@@ -317,6 +317,29 @@ fn the_folder_is_read_at_the_first_frame_and_when_the_window_becomes_active() {
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn a_read_asked_for_during_another_runs_when_that_one_ends() {
let dir = folder("pending");
let app = app(&dir);
write(&dir, "first.js", "");
let ctx = egui::Context::default();
let frame = || {
let mut out = ctx.run_ui(egui::RawInput { focused: true, ..Default::default() }, |ui| scripts_menu::poll(&app, ui.ctx()));
out.textures_delta.clear();
};
frame();
scripts_menu::wait();
// A read that has passed the folder when a script is added, and the File menu opening then.
scripts_menu::refresh(&app, None);
scripts_menu::end_untaken();
write(&dir, "added.js", "");
scripts_menu::refresh(&app, None);
frame();
scripts_menu::wait();
assert!(shape(&scripts()).starts_with("added, first, -"), "{}", shape(&scripts()));
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn an_app_without_a_scripts_folder_keeps_the_two_fixed_items() {
let mut app = VectorcraftApp::new(Session::new(), crate::Services::default());