Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

desktop: Fix Ruffle not responding when picking a file #17413

Merged
merged 4 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions desktop/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ impl App {

// Poll UI events.
let event_loop = self.event_loop.take().expect("App already running");
let event_loop_proxy = event_loop.create_proxy();
event_loop.run(move |event, elwt| {
let mut check_redraw = false;
match event {
Expand Down Expand Up @@ -514,13 +515,16 @@ impl App {
}

winit::event::Event::UserEvent(RuffleEvent::BrowseAndOpen(options)) => {
if let Some(url) = pick_file(false, None, Some(self.window.clone()))
.and_then(|p| Url::from_file_path(p).ok())
{
self.gui
.borrow_mut()
.create_movie(&mut self.player, *options, url);
}
let event_loop = event_loop_proxy.clone();
let window = self.window.clone();
tokio::spawn(async move {
if let Some(url) = pick_file(None, Some(&window))
.await
.and_then(|p| Url::from_file_path(p).ok())
{
let _ = event_loop.send_event(RuffleEvent::OpenURL(url, options));
}
});
}

winit::event::Event::UserEvent(RuffleEvent::OpenURL(url, options)) => {
Expand Down
8 changes: 4 additions & 4 deletions desktop/src/gui/dialogs/bookmarks_dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl BookmarkAddDialog {
}

fn is_valid(&self) -> bool {
self.url.value().is_some() && !self.name.is_empty()
self.url.result().is_some() && !self.name.is_empty()
}

pub fn show(&mut self, locale: &LanguageIdentifier, egui_ctx: &egui::Context) -> bool {
Expand Down Expand Up @@ -71,7 +71,7 @@ impl BookmarkAddDialog {
name: self.name.clone(),
url: self
.url
.value()
.result()
.cloned()
.expect("is_valid() ensured value exists"),
})
Expand Down Expand Up @@ -248,10 +248,10 @@ impl BookmarksDialog {
}
ui.end_row();

let previous_url = bookmark.url.value().cloned();
let previous_url = bookmark.url.result().cloned();

ui.label(text(locale, "bookmarks-dialog-location"));
let current_url = bookmark.url.ui(locale, ui).value();
let current_url = bookmark.url.ui(locale, ui).result();

// TODO: Change the UrlOrPathField widget to return a response instead, so we can update when we lose the focus, removes the need to clone every redraw.
if previous_url.as_ref() != current_url {
Expand Down
4 changes: 2 additions & 2 deletions desktop/src/gui/dialogs/open_dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ impl OpenDialog {
} else {
self.options.player.frame_rate = None;
}
if let Some(url) = self.path.value() {
if let Some(url) = self.path.result() {
if self
.event_loop
.send_event(RuffleEvent::OpenURL(
Expand Down Expand Up @@ -321,7 +321,7 @@ impl OpenDialog {
.striped(true)
.show(ui, |ui| {
ui.label(text(locale, "open-dialog-path"));
is_valid &= self.path.ui(locale, ui).value().is_some();
is_valid &= self.path.ui(locale, ui).result().is_some();
ui.end_row();
});
});
Expand Down
38 changes: 26 additions & 12 deletions desktop/src/gui/widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use crate::gui::text;
use crate::util::pick_file;
use egui::{TextEdit, Ui};
use std::path::Path;
use std::sync::Weak;
use std::sync::{Arc, Mutex, MutexGuard, Weak};
use unic_langid::LanguageIdentifier;
use url::Url;

pub struct PathOrUrlField {
window: Weak<winit::window::Window>,
value: String,
value: Arc<Mutex<String>>,
result: Option<Url>,
hint: &'static str,
}
Expand All @@ -24,7 +24,7 @@ impl PathOrUrlField {
if let Ok(path) = default.to_file_path() {
return Self {
window,
value: path.to_string_lossy().to_string(),
value: Arc::new(Mutex::new(path.to_string_lossy().to_string())),
result: Some(default),
hint,
};
Expand All @@ -33,20 +33,24 @@ impl PathOrUrlField {

return Self {
window,
value: default.to_string(),
value: Arc::new(Mutex::new(default.to_string())),
result: Some(default),
hint,
};
}

Self {
window,
value: "".to_string(),
value: Arc::new(Mutex::new("".to_string())),
result: None,
hint,
}
}

fn lock_value(value: &Arc<Mutex<String>>) -> MutexGuard<'_, String> {
value.lock().expect("Non-poisoned value")
}

pub fn ui(&mut self, locale: &LanguageIdentifier, ui: &mut Ui) -> &mut Self {
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.button(text(locale, "browse")).clicked() {
Expand All @@ -60,33 +64,43 @@ impl PathOrUrlField {
path
});

if let Some(path) = pick_file(true, dir, self.window.upgrade()) {
self.value = path.to_string_lossy().to_string();
}
let value = self.value.clone();
let window = self.window.upgrade();
tokio::spawn(async move {
if let Some(path) = pick_file(dir, window.as_ref()).await {
let mut value_lock = Self::lock_value(&value);
*value_lock = path.to_string_lossy().to_string();
}
});
}

let mut value_locked = Self::lock_value(&self.value);
let mut value = value_locked.clone();
ui.add_sized(
ui.available_size(),
TextEdit::singleline(&mut self.value)
TextEdit::singleline(&mut value)
.hint_text(self.hint)
.text_color_opt(if self.result.is_none() {
Some(ui.style().visuals.error_fg_color)
} else {
None
}),
);
*value_locked = value;
});

let path = Path::new(&self.value);
let value = Self::lock_value(&self.value).clone();
let path = Path::new(&value);
self.result = if path.is_file() {
Url::from_file_path(path).ok()
} else {
Url::parse(&self.value).ok()
Url::parse(&value).ok()
};

self
}

pub fn value(&self) -> Option<&Url> {
pub fn result(&self) -> Option<&Url> {
self.result.as_ref()
}
}
38 changes: 4 additions & 34 deletions desktop/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
use crate::custom_event::RuffleEvent;
use anyhow::{anyhow, Error};
use gilrs::Button;
use rfd::FileDialog;
use rfd::AsyncFileDialog;
use ruffle_core::events::{GamepadButton, KeyCode, TextControlCode};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use url::Url;
use wgpu::rwh::{HasDisplayHandle, HasWindowHandle};
use winit::dpi::PhysicalSize;
use winit::event::{KeyEvent, Modifiers};
use winit::event_loop::EventLoop;
use winit::keyboard::{Key, KeyLocation, NamedKey};
use winit::window::Window;

/// Converts a winit event to a Ruffle `TextControlCode`.
/// Returns `None` if there is no match.
Expand Down Expand Up @@ -247,11 +245,11 @@ pub fn parse_url(path: &Path) -> Result<Url, Error> {
}
}

fn actually_pick_file<W: HasWindowHandle + HasDisplayHandle>(
pub async fn pick_file<W: HasWindowHandle + HasDisplayHandle>(
dir: Option<PathBuf>,
parent: Option<&W>,
) -> Option<PathBuf> {
let mut dialog = FileDialog::new()
let mut dialog = AsyncFileDialog::new()
.add_filter("Flash Files", &["swf", "spl", "ruf"])
.add_filter("All Files", &["*"])
.set_title("Load a Flash File");
Expand All @@ -264,35 +262,7 @@ fn actually_pick_file<W: HasWindowHandle + HasDisplayHandle>(
dialog = dialog.set_parent(parent);
}

dialog.pick_file()
}

// [NA] Horrible hacky workaround for https://github.com/rust-windowing/winit/issues/2291
// We only need the workaround from within UI code, not when executing custom events
// The workaround causes Ruffle to show as "not responding" on windows, so we don't use it if we don't need to
#[cfg(windows)]
pub fn pick_file(
in_ui: bool,
path: Option<PathBuf>,
parent: Option<Arc<Window>>,
) -> Option<PathBuf> {
if in_ui {
std::thread::spawn(move || actually_pick_file(path, parent.as_ref()))
.join()
.ok()
.flatten()
} else {
actually_pick_file(path, parent.as_ref())
}
}

#[cfg(not(windows))]
pub fn pick_file(
_in_ui: bool,
path: Option<PathBuf>,
parent: Option<Arc<Window>>,
) -> Option<PathBuf> {
actually_pick_file(path, parent.as_ref())
dialog.pick_file().await.map(|h| h.into())
}

#[cfg(not(feature = "tracy"))]
Expand Down