From 5bf43f9025b0fed44054fce262cea041510cddd5 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Tue, 28 Mar 2023 02:11:09 -0700 Subject: [PATCH] desktop: Add Open URL option --- desktop/src/custom_event.rs | 3 +++ desktop/src/gui.rs | 53 ++++++++++++++++++++++++++++++++++++- desktop/src/main.rs | 28 ++++++++++++-------- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/desktop/src/custom_event.rs b/desktop/src/custom_event.rs index b70f17cd4..35f267f22 100644 --- a/desktop/src/custom_event.rs +++ b/desktop/src/custom_event.rs @@ -11,6 +11,9 @@ pub enum RuffleEvent { /// The user requested to open a new local SWF. OpenFile, + /// The user requested to open a URL. + OpenURL(url::Url), + /// The user requested to exit Ruffle. ExitRequested, diff --git a/desktop/src/gui.rs b/desktop/src/gui.rs index a950a2ce0..8535d7095 100644 --- a/desktop/src/gui.rs +++ b/desktop/src/gui.rs @@ -147,9 +147,11 @@ impl GuiController { pub struct RuffleGui { event_loop: EventLoopProxy, esc_start_time: Option, + open_url_text: String, is_esc_down: bool, is_ui_visible: bool, is_about_visible: bool, + is_open_url_prompt_visible: bool, context_menu: Vec, } @@ -158,9 +160,11 @@ impl RuffleGui { Self { event_loop, esc_start_time: None, + open_url_text: String::new(), is_esc_down: false, is_ui_visible: false, is_about_visible: false, + is_open_url_prompt_visible: false, context_menu: vec![], } } @@ -202,6 +206,7 @@ impl RuffleGui { if self.is_ui_visible { self.main_menu_bar(egui_ctx); self.about_window(egui_ctx); + self.open_url_prompt(egui_ctx); } if !self.context_menu.is_empty() { @@ -242,13 +247,18 @@ impl RuffleGui { let mut shortcut; shortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::O); - if Button::new("Open...") + if Button::new("Open File...") .shortcut_text(ui.ctx().format_shortcut(&shortcut)) .ui(ui) .clicked() { self.open_file(ui); } + + if Button::new("Open URL...").ui(ui).clicked() { + self.show_open_url_prompt(ui); + } + ui.separator(); shortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Q); @@ -333,6 +343,42 @@ impl RuffleGui { ui.close_menu(); } + fn open_url_prompt(&mut self, egui_ctx: &egui::Context) { + let mut close_prompt = false; + egui::Window::new("Open URL") + .anchor(Align2::CENTER_CENTER, egui::Vec2::ZERO) + .collapsible(false) + .open(&mut self.is_open_url_prompt_visible) + .show(egui_ctx, |ui| { + ui.vertical_centered(|ui| { + let (enter_pressed, esc_pressed) = ui.ctx().input_mut(|input| { + ( + input.consume_key(Modifiers::NONE, Key::Enter), + input.consume_key(Modifiers::NONE, Key::Escape), + ) + }); + ui.text_edit_singleline(&mut self.open_url_text); + ui.horizontal(|ui| { + if ui.button("OK").clicked() || enter_pressed { + if let Ok(url) = url::Url::parse(&self.open_url_text) { + let _ = self.event_loop.send_event(RuffleEvent::OpenURL(url)); + } else { + // TODO: Show error prompt. + tracing::error!("Invalid URL: {}", self.open_url_text); + } + close_prompt = true; + } + if ui.button("Cancel").clicked() || esc_pressed { + close_prompt = true; + } + }); + }); + }); + if close_prompt { + self.is_open_url_prompt_visible = false; + } + } + fn request_exit(&mut self, ui: &mut egui::Ui) { let _ = self.event_loop.send_event(RuffleEvent::ExitRequested); ui.close_menu(); @@ -348,4 +394,9 @@ impl RuffleGui { self.is_about_visible = true; ui.close_menu(); } + + fn show_open_url_prompt(&mut self, ui: &mut egui::Ui) { + self.is_open_url_prompt_visible = true; + ui.close_menu(); + } } diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 274f20363..9360cd403 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -327,7 +327,9 @@ impl App { let (executor, channel) = GlutinAsyncExecutor::new(event_loop.create_proxy()); let navigator = navigator::ExternalNavigatorBackend::new( opt.base.to_owned().unwrap_or( - movie_url.unwrap_or_else(|| Url::parse("file:///empty").expect("Dummy Url")), + movie_url + .clone() + .unwrap_or_else(|| Url::parse("file:///empty").expect("Dummy Url")), ), channel, event_loop.create_proxy(), @@ -403,20 +405,18 @@ impl App { max_window_size, }; - if let Some(path) = &app.opt.input_path { - app.load_swf(&path.clone())?; + if let Some(movie_url) = movie_url { + app.load_swf(movie_url)?; } Ok(app) } - fn load_swf(&mut self, movie_path: &Path) -> Result<(), Error> { - let movie_url = parse_url(movie_path).context("Couldn't load specified path")?; - - let filename = movie_url + fn load_swf(&mut self, url: Url) -> Result<(), Error> { + let filename = url .path_segments() .and_then(|segments| segments.last()) - .unwrap_or_else(|| movie_url.as_str()); + .unwrap_or_else(|| url.as_str()); let title = format!("Ruffle - {filename}"); self.window.set_title(&title); SWF_INFO.with(|i| *i.borrow_mut() = Some(filename.to_string())); @@ -426,10 +426,10 @@ impl App { let _ = event_loop_proxy.send_event(RuffleEvent::OnMetadata(swf_header.clone())); }; - let mut parameters: Vec<(String, String)> = movie_url.query_pairs().into_owned().collect(); + let mut parameters: Vec<(String, String)> = url.query_pairs().into_owned().collect(); parameters.extend(parse_parameters(&self.opt)); self.player.lock().expect("Player lock").fetch_root_movie( - movie_url.to_string(), + url.to_string(), parameters, Box::new(on_metadata), ); @@ -743,11 +743,17 @@ impl App { winit::event::Event::UserEvent(RuffleEvent::OpenFile) => { if let Some(path) = pick_file() { // TODO: Show dialog on error. - let _ = self.load_swf(&path); + let url = parse_url(&path).expect("Couldn't load specified path"); + let _ = self.load_swf(url); self.gui.lock().expect("Gui lock").set_ui_visible(false); } } + winit::event::Event::UserEvent(RuffleEvent::OpenURL(url)) => { + let _ = self.load_swf(url); + self.gui.lock().expect("Gui lock").set_ui_visible(false); + } + winit::event::Event::UserEvent(RuffleEvent::ExitRequested) => { *control_flow = ControlFlow::Exit; return;