desktop: Add network access dialog

Add network access dialog written in egui and replace
rfd's socket confirmation with it.
This commit is contained in:
Kamil Jarosz 2024-09-05 12:02:56 +02:00
parent 9fb669a343
commit e179b41ca7
4 changed files with 160 additions and 5 deletions

View File

@ -0,0 +1,6 @@
network-access-dialog-title = Requesting Network Access
network-access-dialog-message = The current movie is attempting to connect to the following host. Are you sure you want to allow it?
network-access-dialog-port = (port { $port })
network-access-dialog-allow = Allow

View File

@ -1,14 +1,18 @@
use rfd::{AsyncMessageDialog, MessageButtons, MessageDialog, MessageDialogResult, MessageLevel};
use rfd::{MessageButtons, MessageDialog, MessageDialogResult, MessageLevel};
use ruffle_frontend_utils::backends::navigator::NavigatorInterface;
use std::fs::File;
use std::io;
use std::io::ErrorKind;
use std::path::Path;
use std::sync::{Arc, Mutex};
use tokio::sync::oneshot;
use url::Url;
use winit::event_loop::EventLoopProxy;
use crate::custom_event::RuffleEvent;
use crate::gui::dialogs::network_access_dialog::{
NetworkAccessDialogConfiguration, NetworkAccessDialogResult,
};
use crate::gui::DialogDescriptor;
use crate::util::open_url;
@ -66,8 +70,15 @@ impl NavigatorInterface for DesktopNavigatorInterface {
}
async fn confirm_socket(&self, host: &str, port: u16) -> bool {
AsyncMessageDialog::new().set_level(MessageLevel::Warning).set_description(format!("The current movie is attempting to connect to {:?} (port {}).\n\nTo allow it to do so, click Yes to grant network access to that host.\n\nOtherwise, click No to deny access.", host, port)).set_buttons(MessageButtons::YesNo)
.show()
.await == MessageDialogResult::Yes
let (notifier, receiver) = oneshot::channel();
let _ = self
.event_loop
.lock()
.expect("Non-poisoned event loop")
.send_event(RuffleEvent::OpenDialog(DialogDescriptor::NetworkAccess(
NetworkAccessDialogConfiguration::new(notifier, host, port),
)));
let result = receiver.await;
result == Ok(NetworkAccessDialogResult::Allow)
}
}

View File

@ -1,6 +1,7 @@
mod about_dialog;
mod bookmarks_dialog;
pub mod message_dialog;
pub mod network_access_dialog;
mod open_dialog;
mod open_url_dialog;
mod preferences_dialog;
@ -11,11 +12,12 @@ use crate::player::LaunchOptions;
use crate::preferences::GlobalPreferences;
use bookmarks_dialog::{BookmarkAddDialog, BookmarksDialog};
use message_dialog::{MessageDialog, MessageDialogConfiguration};
use network_access_dialog::{NetworkAccessDialog, NetworkAccessDialogConfiguration};
use open_dialog::OpenDialog;
use open_url_dialog::OpenUrlDialog;
use preferences_dialog::PreferencesDialog;
use ruffle_core::Player;
use std::sync::Weak;
use std::{collections::VecDeque, sync::Weak};
use unic_langid::LanguageIdentifier;
use url::Url;
use volume_controls::VolumeControls;
@ -33,6 +35,12 @@ pub struct Dialogs {
open_url_dialog: Option<OpenUrlDialog>,
message_dialog: Option<MessageDialog>,
// Use a queue for the following dialogs in order to:
// 1. support handling multiple instances of them,
// 2. prevent new instances popping up over existing ones
// (and possibly stealing a click).
network_access_dialog_queue: VecDeque<NetworkAccessDialog>,
open_dialog: OpenDialog,
is_open_dialog_visible: bool,
@ -47,6 +55,7 @@ pub struct Dialogs {
pub enum DialogDescriptor {
OpenUrl(url::Url),
ShowMessage(MessageDialogConfiguration),
NetworkAccess(NetworkAccessDialogConfiguration),
}
impl Dialogs {
@ -65,6 +74,8 @@ impl Dialogs {
open_url_dialog: None,
message_dialog: None,
network_access_dialog_queue: VecDeque::new(),
open_dialog: OpenDialog::new(
player_options,
default_path,
@ -138,6 +149,9 @@ impl Dialogs {
DialogDescriptor::ShowMessage(config) => {
self.message_dialog = Some(MessageDialog::new(config));
}
DialogDescriptor::NetworkAccess(config) => self
.network_access_dialog_queue
.push_back(NetworkAccessDialog::new(config)),
}
}
@ -155,6 +169,7 @@ impl Dialogs {
self.show_about_dialog(locale, egui_ctx);
self.show_open_url_dialog(locale, egui_ctx);
self.show_message_dialog(locale, egui_ctx);
self.show_network_access_dialog(locale, egui_ctx);
}
fn show_open_dialog(&mut self, locale: &LanguageIdentifier, egui_ctx: &egui::Context) {
@ -239,4 +254,19 @@ impl Dialogs {
self.message_dialog = None;
}
}
fn show_network_access_dialog(
&mut self,
locale: &LanguageIdentifier,
egui_ctx: &egui::Context,
) {
let keep_open = if let Some(dialog) = &mut self.network_access_dialog_queue.front_mut() {
dialog.show(locale, egui_ctx)
} else {
true
};
if !keep_open {
self.network_access_dialog_queue.pop_front();
}
}
}

View File

@ -0,0 +1,108 @@
use crate::gui::{text, text_with_args};
use egui::{Align2, Ui, Window};
use fluent_templates::fluent_bundle::FluentValue;
use std::collections::HashMap;
use tokio::sync::oneshot::Sender;
use unic_langid::LanguageIdentifier;
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum NetworkAccessDialogResult {
Allow,
Cancel,
}
pub struct NetworkAccessDialogConfiguration {
notifier: Option<Sender<NetworkAccessDialogResult>>,
host: String,
port: u16,
}
impl NetworkAccessDialogConfiguration {
pub fn new(
notifier: Sender<NetworkAccessDialogResult>,
host: impl Into<String>,
port: u16,
) -> Self {
Self {
notifier: Some(notifier),
host: host.into(),
port,
}
}
}
pub struct NetworkAccessDialog {
config: NetworkAccessDialogConfiguration,
}
impl Drop for NetworkAccessDialog {
fn drop(&mut self) {
self.respond(NetworkAccessDialogResult::Cancel);
}
}
impl NetworkAccessDialog {
pub fn new(config: NetworkAccessDialogConfiguration) -> Self {
Self { config }
}
fn respond(&mut self, result: NetworkAccessDialogResult) {
if let Some(notifier) = std::mem::take(&mut self.config.notifier) {
let _ = notifier.send(result);
}
}
pub fn show(&mut self, locale: &LanguageIdentifier, egui_ctx: &egui::Context) -> bool {
let mut keep_open = true;
let mut should_close = false;
Window::new(text(locale, "network-access-dialog-title"))
.open(&mut keep_open)
.anchor(Align2::CENTER_CENTER, egui::Vec2::ZERO)
.collapsible(false)
.resizable(false)
.show(egui_ctx, |ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
should_close = self.render_window_contents(locale, ui)
});
});
keep_open && !should_close
}
pub fn render_window_contents(&mut self, locale: &LanguageIdentifier, ui: &mut Ui) -> bool {
let mut should_close = false;
ui.label(text(locale, "network-access-dialog-message"));
ui.label("");
ui.horizontal(|ui| {
ui.monospace(&self.config.host);
ui.label(text_with_args(
locale,
"network-access-dialog-port",
&HashMap::from([(
"port",
FluentValue::String(self.config.port.to_string().into()),
)]),
));
});
ui.label("");
ui.horizontal(|ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui
.button(text(locale, "network-access-dialog-allow"))
.clicked()
{
self.respond(NetworkAccessDialogResult::Allow);
should_close = true;
}
if ui.button(text(locale, "dialog-cancel")).clicked() {
should_close = true;
}
})
});
should_close
}
}