diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 9e7780432..8db677abd 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -3,7 +3,7 @@ use crate::gui::{GuiController, MENU_HEIGHT}; use crate::player::{LaunchOptions, PlayerController}; use crate::preferences::GlobalPreferences; use crate::util::{ - get_screen_size, gilrs_button_to_gamepad_button, parse_url, pick_file, plot_stats_in_tracy, + get_screen_size, gilrs_button_to_gamepad_button, parse_url, plot_stats_in_tracy, winit_to_ruffle_key_code, winit_to_ruffle_text_control, }; use anyhow::{Context, Error}; @@ -482,9 +482,10 @@ impl App { winit::event::Event::UserEvent(RuffleEvent::BrowseAndOpen(options)) => { let event_loop = event_loop_proxy.clone(); - let window = self.window.clone(); + let picker = self.gui.borrow().file_picker(); tokio::spawn(async move { - if let Some(url) = pick_file(None, Some(&window)) + if let Some(url) = picker + .pick_file(None) .await .and_then(|p| Url::from_file_path(p).ok()) { diff --git a/desktop/src/gui.rs b/desktop/src/gui.rs index b7e58cbef..09bf6e422 100644 --- a/desktop/src/gui.rs +++ b/desktop/src/gui.rs @@ -3,11 +3,13 @@ mod controller; mod dialogs; mod menu_bar; mod movie; +mod picker; mod theme; mod widgets; pub use controller::GuiController; pub use movie::MovieView; +pub use picker::FilePicker; use std::borrow::Cow; pub use theme::ThemePreference; use url::Url; diff --git a/desktop/src/gui/controller.rs b/desktop/src/gui/controller.rs index 6f3ad2f23..9b5e6a68c 100644 --- a/desktop/src/gui/controller.rs +++ b/desktop/src/gui/controller.rs @@ -23,6 +23,8 @@ use winit::event_loop::EventLoop; use winit::keyboard::{Key, NamedKey}; use winit::window::{Theme, Window}; +use super::FilePicker; + /// Integration layer connecting wgpu+winit to egui. pub struct GuiController { descriptors: Arc, @@ -149,6 +151,10 @@ impl GuiController { &self.descriptors } + pub fn file_picker(&self) -> FilePicker { + self.gui.dialogs.file_picker() + } + pub fn resize(&mut self, size: PhysicalSize) { if size.width > 0 && size.height > 0 { self.size = size; diff --git a/desktop/src/gui/dialogs.rs b/desktop/src/gui/dialogs.rs index 82032ec45..9f674d307 100644 --- a/desktop/src/gui/dialogs.rs +++ b/desktop/src/gui/dialogs.rs @@ -17,10 +17,12 @@ use url::Url; use volume_controls::VolumeControls; use winit::event_loop::EventLoopProxy; +use super::FilePicker; + pub struct Dialogs { - window: Weak, event_loop: EventLoopProxy, + picker: FilePicker, preferences_dialog: Option, bookmarks_dialog: Option, bookmark_add_dialog: Option, @@ -44,6 +46,7 @@ impl Dialogs { window: Weak, event_loop: EventLoopProxy, ) -> Self { + let picker = FilePicker::new(window); Self { preferences_dialog: None, bookmarks_dialog: None, @@ -52,7 +55,7 @@ impl Dialogs { open_dialog: OpenDialog::new( player_options, default_path, - window.clone(), + picker.clone(), event_loop.clone(), ), is_open_dialog_visible: false, @@ -62,12 +65,16 @@ impl Dialogs { is_about_visible: false, - window, event_loop, + picker, preferences, } } + pub fn file_picker(&self) -> FilePicker { + self.picker.clone() + } + pub fn recreate_open_dialog( &mut self, opt: LaunchOptions, @@ -75,7 +82,7 @@ impl Dialogs { event_loop: EventLoopProxy, ) { self.is_open_dialog_visible = false; - self.open_dialog = OpenDialog::new(opt, url, self.window.clone(), event_loop); + self.open_dialog = OpenDialog::new(opt, url, self.picker.clone(), event_loop); } pub fn open_file_advanced(&mut self) { @@ -89,7 +96,7 @@ impl Dialogs { pub fn open_bookmarks(&mut self) { self.bookmarks_dialog = Some(BookmarksDialog::new( self.preferences.clone(), - self.window.clone(), + self.picker.clone(), self.event_loop.clone(), )); } @@ -98,7 +105,7 @@ impl Dialogs { self.bookmark_add_dialog = Some(BookmarkAddDialog::new( self.preferences.clone(), initial_url, - self.window.clone(), + self.picker.clone(), )) } diff --git a/desktop/src/gui/dialogs/bookmarks_dialog.rs b/desktop/src/gui/dialogs/bookmarks_dialog.rs index a1994ac08..a8a3159ed 100644 --- a/desktop/src/gui/dialogs/bookmarks_dialog.rs +++ b/desktop/src/gui/dialogs/bookmarks_dialog.rs @@ -1,11 +1,10 @@ -use crate::gui::text; use crate::gui::widgets::PathOrUrlField; +use crate::gui::{text, FilePicker}; use crate::preferences::GlobalPreferences; use crate::{custom_event::RuffleEvent, player::LaunchOptions}; use egui::{Align2, Button, Grid, Label, Layout, Sense, Ui, Widget, Window}; use egui_extras::{Column, TableBuilder}; use ruffle_frontend_utils::bookmarks::Bookmark; -use std::sync::Weak; use unic_langid::LanguageIdentifier; use url::Url; use winit::event_loop::EventLoopProxy; @@ -20,7 +19,7 @@ impl BookmarkAddDialog { pub fn new( preferences: GlobalPreferences, initial_url: Option, - window: Weak, + picker: FilePicker, ) -> Self { Self { preferences, @@ -29,7 +28,7 @@ impl BookmarkAddDialog { .map(|x| ruffle_frontend_utils::url_to_readable_name(x).into_owned()) .unwrap_or_default(), // TODO: hint. - url: PathOrUrlField::new(initial_url, "", window), + url: PathOrUrlField::new(initial_url, "", picker), } } @@ -100,8 +99,8 @@ struct SelectedBookmark { } pub struct BookmarksDialog { - window: Weak, event_loop: EventLoopProxy, + picker: FilePicker, preferences: GlobalPreferences, selected_bookmark: Option, } @@ -109,11 +108,11 @@ pub struct BookmarksDialog { impl BookmarksDialog { pub fn new( preferences: GlobalPreferences, - window: Weak, + picker: FilePicker, event_loop: EventLoopProxy, ) -> Self { Self { - window, + picker, event_loop, preferences, selected_bookmark: None, @@ -224,7 +223,7 @@ impl BookmarksDialog { url: PathOrUrlField::new( Some(bookmark.url.clone()), "", - self.window.clone(), + self.picker.clone(), ), }); } diff --git a/desktop/src/gui/dialogs/open_dialog.rs b/desktop/src/gui/dialogs/open_dialog.rs index 4510ccb4e..50d896180 100644 --- a/desktop/src/gui/dialogs/open_dialog.rs +++ b/desktop/src/gui/dialogs/open_dialog.rs @@ -1,6 +1,6 @@ use crate::custom_event::RuffleEvent; -use crate::gui::text; use crate::gui::widgets::PathOrUrlField; +use crate::gui::{text, FilePicker}; use crate::player::LaunchOptions; use egui::{ emath, Align2, Button, Checkbox, ComboBox, Grid, Layout, Slider, TextEdit, Ui, Widget, Window, @@ -11,7 +11,6 @@ use ruffle_core::{LoadBehavior, PlayerRuntime, StageAlign, StageScaleMode}; use ruffle_render::quality::StageQuality; use std::borrow::Cow; use std::ops::RangeInclusive; -use std::sync::Weak; use std::time::Duration; use unic_langid::LanguageIdentifier; use url::Url; @@ -50,7 +49,7 @@ impl OpenDialog { pub fn new( defaults: LaunchOptions, default_url: Option, - window: Weak, + picker: FilePicker, event_loop: EventLoopProxy, ) -> Self { let spoof_url = OptionalField::new( @@ -73,7 +72,7 @@ impl OpenDialog { defaults.proxy.as_ref().map(Url::to_string), UrlField::new("socks5://localhost:8080"), ); - let path = PathOrUrlField::new(default_url, "path/to/movie.swf", window); + let path = PathOrUrlField::new(default_url, "path/to/movie.swf", picker); let script_timeout = OptionalField::new( defaults .player diff --git a/desktop/src/gui/picker.rs b/desktop/src/gui/picker.rs new file mode 100644 index 000000000..0f23795ba --- /dev/null +++ b/desktop/src/gui/picker.rs @@ -0,0 +1,40 @@ +use rfd::AsyncFileDialog; +use std::{ + path::PathBuf, + sync::{Arc, Weak}, +}; +use winit::window::Window; + +#[derive(Clone)] +pub struct FilePicker { + data: Arc, +} + +struct FilePickerData { + parent: Weak, +} + +impl FilePicker { + pub fn new(parent: Weak) -> Self { + Self { + data: Arc::new(FilePickerData { parent }), + } + } + + pub async fn pick_file(&self, dir: Option) -> Option { + let mut dialog = AsyncFileDialog::new() + .add_filter("Flash Files", &["swf", "spl", "ruf"]) + .add_filter("All Files", &["*"]) + .set_title("Load a Flash File"); + + if let Some(dir) = dir { + dialog = dialog.set_directory(dir); + } + + if let Some(parent) = self.data.parent.upgrade() { + dialog = dialog.set_parent(&parent); + } + + dialog.pick_file().await.map(|h| h.into()) + } +} diff --git a/desktop/src/gui/widgets.rs b/desktop/src/gui/widgets.rs index ca05e4acd..5363d7902 100644 --- a/desktop/src/gui/widgets.rs +++ b/desktop/src/gui/widgets.rs @@ -1,29 +1,26 @@ use crate::gui::text; -use crate::util::pick_file; use egui::{TextEdit, Ui}; use std::path::Path; -use std::sync::{Arc, Mutex, MutexGuard, Weak}; +use std::sync::{Arc, Mutex, MutexGuard}; use unic_langid::LanguageIdentifier; use url::Url; +use super::FilePicker; + pub struct PathOrUrlField { - window: Weak, + picker: FilePicker, value: Arc>, result: Option, hint: &'static str, } impl PathOrUrlField { - pub fn new( - default: Option, - hint: &'static str, - window: Weak, - ) -> Self { + pub fn new(default: Option, hint: &'static str, picker: FilePicker) -> Self { if let Some(default) = default { if default.scheme() == "file" { if let Ok(path) = default.to_file_path() { return Self { - window, + picker, value: Arc::new(Mutex::new(path.to_string_lossy().to_string())), result: Some(default), hint, @@ -32,7 +29,7 @@ impl PathOrUrlField { } return Self { - window, + picker, value: Arc::new(Mutex::new(default.to_string())), result: Some(default), hint, @@ -40,7 +37,7 @@ impl PathOrUrlField { } Self { - window, + picker, value: Arc::new(Mutex::new("".to_string())), result: None, hint, @@ -65,9 +62,9 @@ impl PathOrUrlField { }); let value = self.value.clone(); - let window = self.window.upgrade(); + let picker = self.picker.clone(); tokio::spawn(async move { - if let Some(path) = pick_file(dir, window.as_ref()).await { + if let Some(path) = picker.pick_file(dir).await { let mut value_lock = Self::lock_value(&value); *value_lock = path.to_string_lossy().to_string(); } diff --git a/desktop/src/util.rs b/desktop/src/util.rs index d60404061..dc6e771bb 100644 --- a/desktop/src/util.rs +++ b/desktop/src/util.rs @@ -1,11 +1,9 @@ use crate::custom_event::RuffleEvent; use anyhow::{anyhow, Error}; use gilrs::Button; -use rfd::AsyncFileDialog; use ruffle_core::events::{GamepadButton, KeyCode, TextControlCode}; -use std::path::{Path, PathBuf}; +use std::path::Path; use url::Url; -use wgpu::rwh::{HasDisplayHandle, HasWindowHandle}; use winit::dpi::PhysicalSize; use winit::event::{KeyEvent, Modifiers}; use winit::event_loop::EventLoop; @@ -245,26 +243,6 @@ pub fn parse_url(path: &Path) -> Result { } } -pub async fn pick_file( - dir: Option, - parent: Option<&W>, -) -> Option { - let mut dialog = AsyncFileDialog::new() - .add_filter("Flash Files", &["swf", "spl", "ruf"]) - .add_filter("All Files", &["*"]) - .set_title("Load a Flash File"); - - if let Some(dir) = dir { - dialog = dialog.set_directory(dir); - } - - if let Some(parent) = parent { - dialog = dialog.set_parent(parent); - } - - dialog.pick_file().await.map(|h| h.into()) -} - #[cfg(not(feature = "tracy"))] pub fn plot_stats_in_tracy(_instance: &wgpu::Instance) {}