desktop: Set main window as parent of file pickers

This patch makes sure that all file pickers are children of Ruffle.
This way, file pickers will hover over the main window and will have
to be dismissed before the user may interact with it.
This commit is contained in:
Kamil Jarosz 2024-08-07 14:38:15 +02:00
parent 5b3c9722d5
commit 87bf7b5b19
11 changed files with 101 additions and 22 deletions

30
Cargo.lock generated
View File

@ -251,9 +251,13 @@ dependencies = [
"futures-channel",
"futures-util",
"rand",
"raw-window-handle",
"serde",
"serde_repr",
"url",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"zbus",
]
@ -3501,6 +3505,7 @@ checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
"bitflags 2.6.0",
"block2 0.5.1",
"dispatch",
"libc",
"objc2 0.5.2",
]
@ -4199,6 +4204,27 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "rfd"
version = "0.14.1"
source = "git+https://github.com/PolyMeilex/rfd.git?rev=42dcc7d61fc5e278b4ed76bb9720ba4d89266f01#42dcc7d61fc5e278b4ed76bb9720ba4d89266f01"
dependencies = [
"ashpd",
"block2 0.5.1",
"js-sys",
"log",
"objc2 0.5.2",
"objc2-app-kit",
"objc2-foundation",
"pollster",
"raw-window-handle",
"urlencoding",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows-sys 0.48.0",
]
[[package]]
name = "ring"
version = "0.17.8"
@ -4308,7 +4334,7 @@ dependencies = [
"image",
"os_info",
"rand",
"rfd",
"rfd 0.14.1 (git+https://github.com/PolyMeilex/rfd.git?rev=42dcc7d61fc5e278b4ed76bb9720ba4d89266f01)",
"ruffle_core",
"ruffle_frontend_utils",
"ruffle_render",
@ -4570,7 +4596,7 @@ dependencies = [
"getrandom",
"gloo-net",
"js-sys",
"rfd",
"rfd 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ruffle_core",
"ruffle_render",
"ruffle_render_canvas",

View File

@ -79,6 +79,8 @@ github = [
"ruffle-rs",
# TODO: Remove once a release with https://github.com/emilk/egui/pull/4847 in it is out.
"emilk",
# TODO: Remove once a release with https://github.com/PolyMeilex/rfd/pull/209 in it is out.
"PolyMeilex",
]
[advisories]

View File

@ -32,7 +32,7 @@ winit = "0.29.15"
webbrowser = "1.0.1"
url = { workspace = true }
dirs = "5.0"
rfd = "0.14.1"
rfd = { git = "https://github.com/PolyMeilex/rfd.git", rev = "42dcc7d61fc5e278b4ed76bb9720ba4d89266f01" }
anyhow = { workspace = true }
bytemuck = { workspace = true }
os_info = { version = "3", default-features = false }

View File

@ -514,8 +514,8 @@ impl App {
}
winit::event::Event::UserEvent(RuffleEvent::BrowseAndOpen(options)) => {
if let Some(url) =
pick_file(false, None).and_then(|p| Url::from_file_path(p).ok())
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()

View File

@ -25,7 +25,7 @@ use rfd::FileDialog;
use ruffle_core::debug_ui::Message as DebugMessage;
use ruffle_core::{Player, PlayerEvent};
use std::collections::HashMap;
use std::sync::MutexGuard;
use std::sync::{MutexGuard, Weak};
use std::{fs, mem};
use unic_langid::LanguageIdentifier;
use winit::event_loop::EventLoopProxy;
@ -93,6 +93,7 @@ pub struct RuffleGui {
impl RuffleGui {
fn new(
window: Weak<winit::window::Window>,
event_loop: EventLoopProxy<RuffleEvent>,
default_path: Option<Url>,
default_launch_options: LaunchOptions,
@ -106,6 +107,7 @@ impl RuffleGui {
preferences.clone(),
default_launch_options.clone(),
default_path,
window.clone(),
event_loop.clone(),
),
menu_bar: MenuBar::new(

View File

@ -112,6 +112,7 @@ impl GuiController {
egui_wgpu::Renderer::new(&descriptors.device, surface_format, None, 1, true);
let descriptors = Arc::new(descriptors);
let gui = RuffleGui::new(
Arc::downgrade(&window),
event_loop,
initial_movie_url.clone(),
LaunchOptions::from(&preferences),

View File

@ -11,12 +11,14 @@ use bookmarks_dialog::{BookmarkAddDialog, BookmarksDialog};
use open_dialog::OpenDialog;
use preferences_dialog::PreferencesDialog;
use ruffle_core::Player;
use std::sync::Weak;
use unic_langid::LanguageIdentifier;
use url::Url;
use volume_controls::VolumeControls;
use winit::event_loop::EventLoopProxy;
pub struct Dialogs {
window: Weak<winit::window::Window>,
preferences_dialog: Option<PreferencesDialog>,
bookmarks_dialog: Option<BookmarksDialog>,
bookmark_add_dialog: Option<BookmarkAddDialog>,
@ -37,6 +39,7 @@ impl Dialogs {
preferences: GlobalPreferences,
player_options: LaunchOptions,
default_path: Option<Url>,
window: Weak<winit::window::Window>,
event_loop: EventLoopProxy<RuffleEvent>,
) -> Self {
Self {
@ -44,7 +47,7 @@ impl Dialogs {
bookmarks_dialog: None,
bookmark_add_dialog: None,
open_dialog: OpenDialog::new(player_options, default_path, event_loop),
open_dialog: OpenDialog::new(player_options, default_path, window.clone(), event_loop),
is_open_dialog_visible: false,
volume_controls: VolumeControls::new(&preferences),
@ -52,6 +55,7 @@ impl Dialogs {
is_about_visible: false,
window,
preferences,
}
}
@ -63,7 +67,7 @@ impl Dialogs {
event_loop: EventLoopProxy<RuffleEvent>,
) {
self.is_open_dialog_visible = false;
self.open_dialog = OpenDialog::new(opt, url, event_loop);
self.open_dialog = OpenDialog::new(opt, url, self.window.clone(), event_loop);
}
pub fn open_file_advanced(&mut self) {
@ -75,13 +79,17 @@ impl Dialogs {
}
pub fn open_bookmarks(&mut self) {
self.bookmarks_dialog = Some(BookmarksDialog::new(self.preferences.clone()));
self.bookmarks_dialog = Some(BookmarksDialog::new(
self.preferences.clone(),
self.window.clone(),
));
}
pub fn open_add_bookmark(&mut self, initial_url: Option<url::Url>) {
self.bookmark_add_dialog = Some(BookmarkAddDialog::new(
self.preferences.clone(),
initial_url,
self.window.clone(),
))
}

View File

@ -4,6 +4,7 @@ use crate::preferences::GlobalPreferences;
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;
@ -14,7 +15,11 @@ pub struct BookmarkAddDialog {
}
impl BookmarkAddDialog {
pub fn new(preferences: GlobalPreferences, initial_url: Option<Url>) -> Self {
pub fn new(
preferences: GlobalPreferences,
initial_url: Option<Url>,
window: Weak<winit::window::Window>,
) -> Self {
Self {
preferences,
name: initial_url
@ -22,7 +27,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, ""),
url: PathOrUrlField::new(initial_url, "", window),
}
}
@ -93,13 +98,15 @@ struct SelectedBookmark {
}
pub struct BookmarksDialog {
window: Weak<winit::window::Window>,
preferences: GlobalPreferences,
selected_bookmark: Option<SelectedBookmark>,
}
impl BookmarksDialog {
pub fn new(preferences: GlobalPreferences) -> Self {
pub fn new(preferences: GlobalPreferences, window: Weak<winit::window::Window>) -> Self {
Self {
window,
preferences,
selected_bookmark: None,
}
@ -201,7 +208,11 @@ impl BookmarksDialog {
index,
// TODO: set hint
name: bookmark.name.clone(),
url: PathOrUrlField::new(Some(bookmark.url.clone()), ""),
url: PathOrUrlField::new(
Some(bookmark.url.clone()),
"",
self.window.clone(),
),
});
}
});

View File

@ -11,6 +11,7 @@ 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;
@ -49,6 +50,7 @@ impl OpenDialog {
pub fn new(
defaults: LaunchOptions,
default_url: Option<Url>,
window: Weak<winit::window::Window>,
event_loop: EventLoopProxy<RuffleEvent>,
) -> Self {
let spoof_url = OptionalField::new(
@ -71,7 +73,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");
let path = PathOrUrlField::new(default_url, "path/to/movie.swf", window);
let script_timeout = OptionalField::new(
defaults
.player

View File

@ -2,21 +2,28 @@ use crate::gui::text;
use crate::util::pick_file;
use egui::{TextEdit, Ui};
use std::path::Path;
use std::sync::Weak;
use unic_langid::LanguageIdentifier;
use url::Url;
pub struct PathOrUrlField {
window: Weak<winit::window::Window>,
value: String,
result: Option<Url>,
hint: &'static str,
}
impl PathOrUrlField {
pub fn new(default: Option<Url>, hint: &'static str) -> Self {
pub fn new(
default: Option<Url>,
hint: &'static str,
window: Weak<winit::window::Window>,
) -> Self {
if let Some(default) = default {
if default.scheme() == "file" {
if let Ok(path) = default.to_file_path() {
return Self {
window,
value: path.to_string_lossy().to_string(),
result: Some(default),
hint,
@ -25,6 +32,7 @@ impl PathOrUrlField {
}
return Self {
window,
value: default.to_string(),
result: Some(default),
hint,
@ -32,6 +40,7 @@ impl PathOrUrlField {
}
Self {
window,
value: "".to_string(),
result: None,
hint,
@ -51,7 +60,7 @@ impl PathOrUrlField {
path
});
if let Some(path) = pick_file(true, dir) {
if let Some(path) = pick_file(true, dir, self.window.upgrade()) {
self.value = path.to_string_lossy().to_string();
}
}

View File

@ -4,11 +4,14 @@ use gilrs::Button;
use rfd::FileDialog;
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.
@ -244,7 +247,10 @@ pub fn parse_url(path: &Path) -> Result<Url, Error> {
}
}
fn actually_pick_file(dir: Option<PathBuf>) -> Option<PathBuf> {
fn actually_pick_file<W: HasWindowHandle + HasDisplayHandle>(
dir: Option<PathBuf>,
parent: Option<&W>,
) -> Option<PathBuf> {
let mut dialog = FileDialog::new()
.add_filter("Flash Files", &["swf", "spl", "ruf"])
.add_filter("All Files", &["*"])
@ -254,6 +260,10 @@ fn actually_pick_file(dir: Option<PathBuf>) -> Option<PathBuf> {
dialog = dialog.set_directory(dir);
}
if let Some(parent) = parent {
dialog = dialog.set_parent(parent);
}
dialog.pick_file()
}
@ -261,20 +271,28 @@ fn actually_pick_file(dir: Option<PathBuf>) -> Option<PathBuf> {
// 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>) -> Option<PathBuf> {
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))
std::thread::spawn(move || actually_pick_file(path, parent.as_ref()))
.join()
.ok()
.flatten()
} else {
actually_pick_file(path)
actually_pick_file(path, parent.as_ref())
}
}
#[cfg(not(windows))]
pub fn pick_file(_in_ui: bool, path: Option<PathBuf>) -> Option<PathBuf> {
actually_pick_file(path)
pub fn pick_file(
_in_ui: bool,
path: Option<PathBuf>,
parent: Option<Arc<Window>>,
) -> Option<PathBuf> {
actually_pick_file(path, parent.as_ref())
}
#[cfg(not(feature = "tracy"))]