Skip to content

Commit

Permalink
Allow opening multiple files at once
Browse files Browse the repository at this point in the history
  • Loading branch information
maddymakesgames committed Mar 28, 2024
1 parent 73ad640 commit dee553a
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 65 deletions.
29 changes: 25 additions & 4 deletions gui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,13 @@ impl App for SaveEditor {
.map(ScreenState::name)
.map(str::to_owned)
.collect::<Vec<_>>(),
|selected, ui| self.screens[selected].update(ui, &self.runtime, &self.popups),
|selected, ui| {
if let Some(iter) =
self.screens[selected].update(ui, &self.runtime, &self.popups)
{
self.screens.extend(iter)
}
},
);
});

Expand Down Expand Up @@ -170,21 +176,36 @@ enum ScreenState {
}

impl ScreenState {
fn update(&mut self, ui: &mut Ui, rt: &Runtime, popups: &Arc<Mutex<Vec<PopupWindow>>>) {
fn update(
&mut self,
ui: &mut Ui,
rt: &Runtime,
popups: &Arc<Mutex<Vec<PopupWindow>>>,
) -> Option<impl Iterator<Item = ScreenState>> {
match self {
// Startup just immediately transitions to the main menu
ScreenState::Startup => *self = ScreenState::Menu(MainMenu::default()),
ScreenState::Menu(m) =>
// The main menu displays until a file has been opened
// In which case we transition to the editor
if let Some((file_name, save)) = m.display(ui, rt, popups) {
*self = ScreenState::Editor(EditorScreen::new(file_name, save))
if let Some(saves) = m.display(ui, rt, popups) {
let mut saves_iter = saves.into_iter();
if let Some((file_name, save)) = saves_iter.next() {
*self = ScreenState::Editor(EditorScreen::new(file_name, save))
}

return Some(
saves_iter
.map(|(a, b)| EditorScreen::new(a, b))
.map(ScreenState::Editor),
);
},
// The editor (current) doesn't ever transition out
ScreenState::Editor(e) => {
e.display(ui, rt, popups);
}
}
None
}

fn name(&self) -> &str {
Expand Down
102 changes: 41 additions & 61 deletions gui/src/main_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rfd::AsyncFileDialog;
use tokio::{
runtime::Runtime,
sync::{
oneshot::{error::TryRecvError, Receiver, Sender},
mpsc::{channel, error::TryRecvError, Receiver, Sender},
Mutex,
},
};
Expand All @@ -17,6 +17,7 @@ use crate::{spawn, ErrorSeverity, PopupWindow};
pub struct MainMenu {
#[allow(clippy::type_complexity)]
file_listener: Option<Receiver<Option<(String, SaveData)>>>,
output: Vec<(String, SaveData)>,
}

impl MainMenu {
Expand All @@ -25,10 +26,10 @@ impl MainMenu {
ui: &mut Ui,
rt: &Runtime,
popups: &Arc<Mutex<Vec<PopupWindow>>>,
) -> Option<(String, SaveData)> {
) -> Option<Vec<(String, SaveData)>> {
// Update the listener and return if we've recieved some data
if let Some(inner) = self.update_listener(ui, popups) {
return inner;
if let Some(inner) = self.update_listener(ui) {
return Some(inner);
}

// On wasm make a note of the native version being the preferred way of using the app
Expand All @@ -53,13 +54,13 @@ impl MainMenu {
// disable the ui when we're already trying to read a file
ui.set_enabled(self.file_listener.is_none());

if ui.button("Open File").clicked() {
if ui.button("Open Files").clicked() {
// Create a file dialogue filtered for .celeste files
let file_dialogue =
AsyncFileDialog::new().add_filter("Celeste Save File", &["celeste"]);

// Create a channel to send the parsed file back through
let (send, recv) = tokio::sync::oneshot::channel();
let (send, recv) = channel(5);

// Spawn a task to read and parse the file
spawn(rt, handle_file_picker(file_dialogue, send, popups.clone()));
Expand All @@ -71,31 +72,21 @@ impl MainMenu {
None
}

fn update_listener(
&mut self,
ui: &mut Ui,
popups: &Arc<Mutex<Vec<PopupWindow>>>,
) -> Option<Option<(String, SaveData)>> {
fn update_listener(&mut self, ui: &mut Ui) -> Option<Vec<(String, SaveData)>> {
if let Some(recv) = &mut self.file_listener {
// Try to recieve file data from the channel
// We use try_recv because it will give Err(Empty) if it can't immediately read data
match recv.try_recv() {
Ok(file) => {
self.file_listener = None;
return Some(file);
Ok(Some(file)) => {
self.output.push(file);
}
Err(TryRecvError::Empty) => {}
Err(TryRecvError::Closed) => {
Err(TryRecvError::Disconnected) => {
self.file_listener = None;
// If the sender has already close we have a bug and its better to force quit the app
// so that users will immediately make a bug report
popups.blocking_lock().push(PopupWindow::new(
ErrorSeverity::Severe,
"file_listner dropped before it sent any signals.\nThis is a bug and a \
critical issue. Please make a bug report on github.\nThe program will \
now close.",
));
if !self.output.is_empty() {
return Some(std::mem::take(&mut self.output));
}
}
_ => {}
}

// Display a little spinner to show we're working <3
Expand All @@ -111,48 +102,37 @@ async fn handle_file_picker(
popups: Arc<Mutex<Vec<PopupWindow>>>,
) {
// Wait for the user to pick a file
let file = file_dialogue.pick_file().await;

if let Some(file) = file {
// Read the contents of the file
let name = file.file_name();
let contents = file.read().await;
drop(file);

// Attempt to parse the save file showing an error popup if we fail
match SaveData::from_reader(Cursor::new(contents)) {
Ok(save) =>
if send.send(Some((name, save))).is_err() {
popups.lock().await.push(PopupWindow::new(
ErrorSeverity::Error,
"Error sending data back to main thread.\nThis is a bug, please make a \
bug report on github.",
))
},
Err(e) => {
popups.lock().await.push(PopupWindow::new(
ErrorSeverity::Error,
format!(
"Errors found when parsing save file: {e}.\nMake sure the file you \
selected is actually a save file.\nIf this continues please report it as \
a bug on github."
),
));

if send.send(None).is_err() {
let files = file_dialogue.pick_files().await;

if let Some(files) = files {
for file in files {
// Read the contents of the file
let name = file.file_name();
println!("{name}");
let contents = file.read().await;
drop(file);

// Attempt to parse the save file showing an error popup if we fail
match SaveData::from_reader(Cursor::new(contents)) {
Ok(save) =>
if send.send(Some((name, save))).await.is_err() {
popups.lock().await.push(PopupWindow::new(
ErrorSeverity::Error,
"Error sending data back to main thread.\nThis is a bug, please make \
a bug report on github.",
))
},
Err(e) => {
popups.lock().await.push(PopupWindow::new(
ErrorSeverity::Error,
"Error sending data back to main thread.\nThis is a bug, please make a \
bug report on github.",
format!(
"Errors found when parsing save file: {e}.\nMake sure the file you \
selected is actually a save file.\nIf this continues please report \
it as a bug on github."
),
));
}
}
}
} else if send.send(None).is_err() {
popups.lock().await.push(PopupWindow::new(
ErrorSeverity::Error,
"Error sending data back to main thread.\nThis is a bug, please make a bug report on \
github.",
));
}
}

0 comments on commit dee553a

Please sign in to comment.