Skip to content

Commit 38bdb8b

Browse files
SamMorrowDrumsHerschel
authored andcommitted
web/desktop: Add basic dialog handling (closes #1978)
1 parent 367404d commit 38bdb8b

File tree

11 files changed

+150
-10
lines changed

11 files changed

+150
-10
lines changed

core/src/backend.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ pub mod log;
55
pub mod navigator;
66
pub mod render;
77
pub mod storage;
8+
pub mod ui;

core/src/backend/ui.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
pub trait UiBackend {
2+
fn message(&self, message: &str);
3+
}
4+
5+
/// UiBackend that does mostly nothing
6+
pub struct NullUiBackend {}
7+
8+
impl NullUiBackend {
9+
pub fn new() -> Self {
10+
Self {}
11+
}
12+
}
13+
14+
impl UiBackend for NullUiBackend {
15+
fn message(&self, _message: &str) {}
16+
}
17+
18+
impl Default for NullUiBackend {
19+
fn default() -> Self {
20+
NullUiBackend::new()
21+
}
22+
}

core/src/player.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::backend::locale::LocaleBackend;
99
use crate::backend::navigator::{NavigatorBackend, RequestOptions};
1010
use crate::backend::storage::StorageBackend;
1111
use crate::backend::{
12-
audio::AudioBackend, log::LogBackend, render::Letterbox, render::RenderBackend,
12+
audio::AudioBackend, log::LogBackend, render::Letterbox, render::RenderBackend, ui::UiBackend,
1313
};
1414
use crate::context::{ActionQueue, ActionType, RenderContext, UpdateContext};
1515
use crate::display_object::{EditText, MorphShape, MovieClip};
@@ -23,7 +23,7 @@ use crate::prelude::*;
2323
use crate::property_map::PropertyMap;
2424
use crate::tag_utils::SwfMovie;
2525
use crate::transform::TransformStack;
26-
use crate::vminterface::Instantiator;
26+
use crate::vminterface::{AvmType, Instantiator};
2727
use enumset::EnumSet;
2828
use gc_arena::{make_arena, ArenaParameters, Collect, GcCell};
2929
use instant::Instant;
@@ -134,6 +134,7 @@ type Input = Box<dyn InputBackend>;
134134
type Storage = Box<dyn StorageBackend>;
135135
type Locale = Box<dyn LocaleBackend>;
136136
type Log = Box<dyn LogBackend>;
137+
type UI = Box<dyn UiBackend>;
137138

138139
pub struct Player {
139140
/// The version of the player we're emulating.
@@ -159,6 +160,7 @@ pub struct Player {
159160
input: Input,
160161
locale: Locale,
161162
log: Log,
163+
pub user_interface: UI,
162164
transform_stack: TransformStack,
163165
view_matrix: Matrix,
164166
inverse_view_matrix: Matrix,
@@ -205,6 +207,7 @@ pub struct Player {
205207
self_reference: Option<Weak<Mutex<Self>>>,
206208
}
207209

210+
#[allow(clippy::too_many_arguments)]
208211
impl Player {
209212
pub fn new(
210213
renderer: Renderer,
@@ -214,6 +217,7 @@ impl Player {
214217
storage: Storage,
215218
locale: Locale,
216219
log: Log,
220+
user_interface: UI,
217221
) -> Result<Arc<Mutex<Self>>, Error> {
218222
let fake_movie = Arc::new(SwfMovie::empty(NEWEST_PLAYER_VERSION));
219223
let movie_width = 550;
@@ -280,6 +284,7 @@ impl Player {
280284
input,
281285
locale,
282286
log,
287+
user_interface,
283288
self_reference: None,
284289
system: SystemProperties::default(),
285290
instance_counter: 0,
@@ -305,10 +310,10 @@ impl Player {
305310

306311
player.build_matrices();
307312
player.audio.set_frame_rate(frame_rate);
308-
309313
let player_box = Arc::new(Mutex::new(player));
310314
let mut player_lock = player_box.lock().unwrap();
311315
player_lock.self_reference = Some(Arc::downgrade(&player_box));
316+
312317
std::mem::drop(player_lock);
313318

314319
Ok(player_box)
@@ -624,7 +629,7 @@ impl Player {
624629
});
625630
}
626631

627-
// Propagte clip events.
632+
// Propagate clip events.
628633
self.mutate_with_update_context(|context| {
629634
let (clip_event, listener) = match event {
630635
PlayerEvent::KeyDown { .. } => {
@@ -799,22 +804,28 @@ impl Player {
799804
/// This should only be called once. Further movie loads should preload the
800805
/// specific `MovieClip` referenced.
801806
fn preload(&mut self) {
807+
let mut is_action_script_3 = false;
802808
self.mutate_with_update_context(|context| {
803809
let mut morph_shapes = fnv::FnvHashMap::default();
804810
let root = *context.levels.get(&0).expect("root level");
805811
root.as_movie_clip()
806812
.unwrap()
807813
.preload(context, &mut morph_shapes);
808814

815+
let lib = context
816+
.library
817+
.library_for_movie_mut(root.as_movie_clip().unwrap().movie().unwrap());
818+
819+
is_action_script_3 = lib.avm_type() == AvmType::Avm2;
809820
// Finalize morph shapes.
810821
for (id, static_data) in morph_shapes {
811822
let morph_shape = MorphShape::new(context.gc_context, static_data);
812-
context
813-
.library
814-
.library_for_movie_mut(root.as_movie_clip().unwrap().movie().unwrap())
815-
.register_character(id, crate::character::Character::MorphShape(morph_shape));
823+
lib.register_character(id, crate::character::Character::MorphShape(morph_shape));
816824
}
817825
});
826+
if is_action_script_3 {
827+
self.user_interface.message("This SWF contains ActionScript 3 which is not yet supported by Ruffle. The movie may not work as intended.");
828+
}
818829
}
819830

820831
pub fn run_frame(&mut self) {

core/tests/regression_tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use ruffle_core::backend::locale::NullLocaleBackend;
77
use ruffle_core::backend::log::LogBackend;
88
use ruffle_core::backend::navigator::{NullExecutor, NullNavigatorBackend};
99
use ruffle_core::backend::storage::MemoryStorageBackend;
10+
use ruffle_core::backend::ui::NullUiBackend;
1011
use ruffle_core::backend::{
1112
audio::NullAudioBackend, input::NullInputBackend, render::NullRenderer,
1213
};
@@ -694,6 +695,7 @@ fn run_swf(
694695
Box::new(MemoryStorageBackend::default()),
695696
Box::new(NullLocaleBackend::new()),
696697
Box::new(TestLogBackend::new(trace_output.clone())),
698+
Box::new(NullUiBackend::new()),
697699
)?;
698700
player.lock().unwrap().set_root_movie(Arc::new(movie));
699701
player

desktop/src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod locale;
88
mod navigator;
99
mod storage;
1010
mod task;
11+
mod ui;
1112

1213
use crate::custom_event::RuffleEvent;
1314
use crate::executor::GlutinAsyncExecutor;
@@ -215,6 +216,7 @@ fn run_player(opt: Opt) -> Result<(), Box<dyn std::error::Error>> {
215216
)); //TODO: actually implement this backend type
216217
let input = Box::new(input::WinitInputBackend::new(window.clone()));
217218
let storage = Box::new(DiskStorageBackend::new());
219+
let user_interface = Box::new(ui::DesktopUiBackend::new());
218220
let locale = Box::new(locale::DesktopLocaleBackend::new());
219221
let player = Player::new(
220222
renderer,
@@ -224,6 +226,7 @@ fn run_player(opt: Opt) -> Result<(), Box<dyn std::error::Error>> {
224226
storage,
225227
locale,
226228
Box::new(NullLogBackend::new()),
229+
user_interface,
227230
)?;
228231
player.lock().unwrap().set_root_movie(Arc::new(movie));
229232
player.lock().unwrap().set_is_playing(true); // Desktop player will auto-play.

desktop/src/ui.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use tinyfiledialogs::{message_box_ok, MessageBoxIcon};
2+
3+
use ruffle_core::backend::ui::UiBackend;
4+
5+
pub struct DesktopUiBackend {}
6+
7+
impl DesktopUiBackend {
8+
pub fn new() -> Self {
9+
Self {}
10+
}
11+
}
12+
13+
impl UiBackend for DesktopUiBackend {
14+
fn message(&self, message: &str) {
15+
message_box_ok("Ruffle", message, MessageBoxIcon::Info)
16+
}
17+
}
18+
19+
impl Default for DesktopUiBackend {
20+
fn default() -> Self {
21+
DesktopUiBackend::new()
22+
}
23+
}

exporter/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use ruffle_core::backend::locale::NullLocaleBackend;
88
use ruffle_core::backend::log::NullLogBackend;
99
use ruffle_core::backend::navigator::NullNavigatorBackend;
1010
use ruffle_core::backend::storage::MemoryStorageBackend;
11+
use ruffle_core::backend::ui::NullUiBackend;
1112
use ruffle_core::tag_utils::SwfMovie;
1213
use ruffle_core::Player;
1314
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
@@ -111,6 +112,7 @@ fn take_screenshot(
111112
Box::new(MemoryStorageBackend::default()),
112113
Box::new(NullLocaleBackend::new()),
113114
Box::new(NullLogBackend::new()),
115+
Box::new(NullUiBackend::new()),
114116
)?;
115117

116118
player

web/packages/core/src/ruffle-player.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,25 @@ export class RufflePlayer extends HTMLElement {
790790
}
791791
}
792792

793+
displayMessage(message: string): void {
794+
// Show a dismissible message in front of the player
795+
const div = document.createElement("div");
796+
div.id = "message_overlay";
797+
div.innerHTML = `<div class="message">
798+
<div>
799+
<p>${message}</p>
800+
</div>
801+
<div>
802+
<button id="continue-btn">continue</button>
803+
</div>`;
804+
this.container.prepend(div);
805+
(<HTMLButtonElement>(
806+
this.container.querySelector("#continue-btn")
807+
)).onclick = () => {
808+
div.remove();
809+
};
810+
}
811+
793812
protected debugPlayerInfo(): string {
794813
return `Allows script access: ${this.allowScriptAccess}\n`;
795814
}

web/packages/core/src/shadow-template.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ ruffleShadowTemplate.innerHTML = `
2525
#play_button,
2626
#unmute_overlay,
2727
#unmute_overlay .background,
28-
#panic {
28+
#panic,
29+
#message_overlay {
2930
width: inherit;
3031
height: inherit;
3132
}
@@ -122,6 +123,39 @@ ruffleShadowTemplate.innerHTML = `
122123
padding: 10px 50px;
123124
}
124125
126+
#message_overlay {
127+
position: absolute;
128+
background-color: #37528C;
129+
color: #FFAD33;
130+
opacity: 1.0;
131+
z-index: 2;
132+
text-align: center;
133+
}
134+
135+
#message_overlay .message {
136+
position: absolute;
137+
top: 50%;
138+
left: 50%;
139+
width: 100%;
140+
padding: 20px;
141+
transform: translate(-50%, -50%);
142+
}
143+
144+
#continue-btn {
145+
cursor: pointer;
146+
background-color: #37528C;
147+
color: #FFAD33;
148+
border: 2px solid #FFAD33;
149+
font-weight: bold;
150+
font-size: 20px;
151+
border-radius: 20px;
152+
padding: 10px;
153+
}
154+
155+
#continue-btn:hover {
156+
background-color: rgba(255, 255, 255, 0.3);
157+
}
158+
125159
#right_click_menu {
126160
color: #FFAD33;
127161
background-color: #37528c;

web/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod locale;
77
mod log_adapter;
88
mod navigator;
99
mod storage;
10+
mod ui;
1011

1112
use crate::log_adapter::WebLogBackend;
1213
use crate::storage::LocalStorageBackend;
@@ -96,6 +97,9 @@ extern "C" {
9697

9798
#[wasm_bindgen(method)]
9899
fn panic(this: &JavascriptPlayer, error: &JsError);
100+
101+
#[wasm_bindgen(method, js_name = "displayMessage")]
102+
fn display_message(this: &JavascriptPlayer, message: &str);
99103
}
100104

101105
struct JavascriptInterface {
@@ -317,7 +321,7 @@ impl Ruffle {
317321

318322
let trace_observer = Arc::new(RefCell::new(JsValue::UNDEFINED));
319323
let log = Box::new(WebLogBackend::new(trace_observer.clone()));
320-
324+
let user_interface = Box::new(ui::WebUiBackend::new(js_player.clone()));
321325
let core = ruffle_core::Player::new(
322326
renderer,
323327
audio,
@@ -326,6 +330,7 @@ impl Ruffle {
326330
local_storage,
327331
locale,
328332
log,
333+
user_interface,
329334
)?;
330335

331336
// Create instance.

0 commit comments

Comments
 (0)