core: Button work
This commit is contained in:
parent
91a0272773
commit
0143d9716e
|
@ -162,6 +162,9 @@ impl Avm1 {
|
|||
root: DisplayNode<'gc>,
|
||||
mut path: &str,
|
||||
) -> Option<DisplayNode<'gc>> {
|
||||
if path == "/" {
|
||||
log::warn!("ROOT");
|
||||
}
|
||||
let mut cur_clip = if path.bytes().nth(0).unwrap_or(0) == b'/' {
|
||||
path = &path[1..];
|
||||
root
|
||||
|
@ -771,8 +774,11 @@ impl Avm1 {
|
|||
|
||||
fn play(&mut self, context: &mut ActionContext) -> Result<(), Error> {
|
||||
let mut display_object = context.active_clip.write(context.gc_context);
|
||||
let clip = display_object.as_movie_clip_mut().unwrap();
|
||||
clip.play();
|
||||
if let Some(clip) = display_object.as_movie_clip_mut() {
|
||||
clip.play()
|
||||
} else {
|
||||
log::warn!("Play failed: Not a MovieClip");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -909,6 +915,7 @@ impl Avm1 {
|
|||
} else if let Some(clip) =
|
||||
Avm1::resolve_slash_path(context.start_clip, context.root, target)
|
||||
{
|
||||
log::warn!("Path: {}", target);
|
||||
context.active_clip = clip;
|
||||
} else {
|
||||
log::warn!("SetTarget failed: {} not found", target);
|
||||
|
@ -931,8 +938,15 @@ impl Avm1 {
|
|||
|
||||
fn action_stop(&mut self, context: &mut ActionContext) -> Result<(), Error> {
|
||||
let mut display_object = context.active_clip.write(context.gc_context);
|
||||
let clip = display_object.as_movie_clip_mut().unwrap();
|
||||
if let None = display_object.as_movie_clip_mut() {
|
||||
log::warn!("NO");
|
||||
}
|
||||
|
||||
if let Some(clip) = display_object.as_movie_clip_mut() {
|
||||
clip.stop();
|
||||
} else {
|
||||
log::warn!("Stop failed: Not a MovieClip");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::display_object::{DisplayObject, DisplayObjectBase};
|
||||
use crate::event::ClipEvent;
|
||||
use crate::events::ButtonEvent;
|
||||
use crate::player::{RenderContext, UpdateContext};
|
||||
use crate::prelude::*;
|
||||
use std::collections::BTreeMap;
|
||||
|
@ -113,18 +113,53 @@ impl<'gc> Button<'gc> {
|
|||
self.children[i].values_mut()
|
||||
}
|
||||
|
||||
pub fn handle_button_event(
|
||||
&mut self,
|
||||
context: &mut crate::player::UpdateContext<'_, 'gc, '_>,
|
||||
event: ButtonEvent,
|
||||
) {
|
||||
let new_state = match event {
|
||||
ButtonEvent::RollOut => ButtonState::Up,
|
||||
ButtonEvent::RollOver => ButtonState::Over,
|
||||
ButtonEvent::Press => ButtonState::Down,
|
||||
ButtonEvent::Release => ButtonState::Over,
|
||||
ButtonEvent::KeyPress(key) => {
|
||||
self.run_actions(context, swf::ButtonActionCondition::KeyPress, Some(key));
|
||||
self.state
|
||||
}
|
||||
};
|
||||
|
||||
match (self.state, new_state) {
|
||||
(ButtonState::Up, ButtonState::Over) => {
|
||||
self.run_actions(context, swf::ButtonActionCondition::IdleToOverUp, None);
|
||||
}
|
||||
(ButtonState::Over, ButtonState::Up) => {
|
||||
self.run_actions(context, swf::ButtonActionCondition::OverUpToIdle, None);
|
||||
}
|
||||
(ButtonState::Over, ButtonState::Down) => {
|
||||
self.run_actions(context, swf::ButtonActionCondition::OverUpToOverDown, None);
|
||||
}
|
||||
(ButtonState::Down, ButtonState::Over) => {
|
||||
self.run_actions(context, swf::ButtonActionCondition::OverDownToOverUp, None);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.state = new_state;
|
||||
}
|
||||
|
||||
fn run_actions(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
condition: swf::ButtonActionCondition,
|
||||
key_code: Option<u8>,
|
||||
) {
|
||||
if let Some(parent) = self.parent() {
|
||||
for action in &self.static_data.actions {
|
||||
if action.condition == condition && action.key_code == key_code {
|
||||
// Note that AVM1 buttons run actions relative to their parent, not themselves.
|
||||
context
|
||||
.actions
|
||||
.push((self.parent().unwrap(), action.action_data.clone()));
|
||||
context.actions.push((parent, action.action_data.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,45 +215,29 @@ impl<'gc> DisplayObject<'gc> for Button<'gc> {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
context: &mut crate::player::UpdateContext<'_, 'gc, '_>,
|
||||
event: ClipEvent,
|
||||
) {
|
||||
let new_state = match event {
|
||||
ClipEvent::RollOut => ButtonState::Up,
|
||||
ClipEvent::RollOver => ButtonState::Over,
|
||||
ClipEvent::Press => ButtonState::Down,
|
||||
ClipEvent::Release => ButtonState::Over,
|
||||
ClipEvent::KeyPress(key) => {
|
||||
self.run_actions(context, swf::ButtonActionCondition::KeyPress, Some(key));
|
||||
self.state
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
self_node: DisplayNode<'gc>,
|
||||
point: (Twips, Twips),
|
||||
) -> Option<DisplayNode<'gc>> {
|
||||
// The button is hovered if the mouse is over any child nodes.
|
||||
if self.hit_test(point) {
|
||||
Some(self_node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
_ => self.state,
|
||||
};
|
||||
|
||||
match (self.state, new_state) {
|
||||
(ButtonState::Up, ButtonState::Over) => {
|
||||
self.run_actions(context, swf::ButtonActionCondition::IdleToOverUp, None);
|
||||
}
|
||||
(ButtonState::Over, ButtonState::Up) => {
|
||||
self.run_actions(context, swf::ButtonActionCondition::OverUpToIdle, None);
|
||||
}
|
||||
(ButtonState::Over, ButtonState::Down) => {
|
||||
self.run_actions(context, swf::ButtonActionCondition::OverUpToOverDown, None);
|
||||
}
|
||||
(ButtonState::Down, ButtonState::Over) => {
|
||||
self.run_actions(context, swf::ButtonActionCondition::OverDownToOverUp, None);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.state = new_state;
|
||||
fn as_button(&self) -> Option<&Self> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn as_button_mut(&mut self) -> Option<&mut Self> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,12 @@ pub trait DisplayObject<'gc>: 'gc + Collect {
|
|||
fn run_post_frame(&mut self, _context: &mut UpdateContext<'_, 'gc, '_>) {}
|
||||
fn render(&self, _context: &mut RenderContext<'_, 'gc>) {}
|
||||
|
||||
fn handle_click(&mut self, _pos: (f32, f32)) {}
|
||||
fn as_button(&self) -> Option<&crate::button::Button<'gc>> {
|
||||
None
|
||||
}
|
||||
fn as_button_mut(&mut self) -> Option<&mut crate::button::Button<'gc>> {
|
||||
None
|
||||
}
|
||||
fn as_movie_clip(&self) -> Option<&crate::movie_clip::MovieClip<'gc>> {
|
||||
None
|
||||
}
|
||||
|
@ -110,19 +115,16 @@ pub trait DisplayObject<'gc>: 'gc + Collect {
|
|||
}
|
||||
fn box_clone(&self) -> Box<dyn DisplayObject<'gc>>;
|
||||
|
||||
fn pick(&self, _: (Twips, Twips)) -> Option<DisplayNode<'gc>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn hit_test(&self, _: (Twips, Twips)) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
_context: &mut crate::player::UpdateContext<'_, 'gc, '_>,
|
||||
_event: crate::event::ClipEvent,
|
||||
) {
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
_self_node: DisplayNode<'gc>,
|
||||
_: (Twips, Twips),
|
||||
) -> Option<DisplayNode<'gc>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
use swf::Twips;
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
MouseMove { x: Twips, y: Twips },
|
||||
MouseUp { x: Twips, y: Twips },
|
||||
MouseDown { x: Twips, y: Twips },
|
||||
MouseLeft,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ClipEvent {
|
||||
Press,
|
||||
Release,
|
||||
RollOut,
|
||||
RollOver,
|
||||
KeyPress(KeyCode),
|
||||
}
|
||||
|
||||
type KeyCode = u8;
|
|
@ -0,0 +1,31 @@
|
|||
use swf::Twips;
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Debug)]
|
||||
pub enum PlayerEvent {
|
||||
MouseMove { x: Twips, y: Twips },
|
||||
MouseUp { x: Twips, y: Twips },
|
||||
MouseDown { x: Twips, y: Twips },
|
||||
MouseLeft,
|
||||
}
|
||||
|
||||
/// The events that an AVM1 button can fire.
|
||||
///
|
||||
/// In Flash, these are created using `on` code on the button instance:
|
||||
/// ```ignore
|
||||
/// on(release) {
|
||||
/// trace("Button clicked");
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub enum ButtonEvent {
|
||||
Press,
|
||||
Release,
|
||||
RollOut,
|
||||
RollOver,
|
||||
KeyPress(KeyCode),
|
||||
}
|
||||
|
||||
/// Flash virtual keycode.
|
||||
/// TODO: This will eventually move to a separate module.
|
||||
pub type KeyCode = u8;
|
|
@ -6,7 +6,7 @@ mod bounding_box;
|
|||
mod button;
|
||||
mod character;
|
||||
mod color_transform;
|
||||
mod event;
|
||||
mod events;
|
||||
mod font;
|
||||
mod graphic;
|
||||
mod library;
|
||||
|
@ -22,7 +22,7 @@ mod transform;
|
|||
|
||||
pub mod backend;
|
||||
|
||||
pub use event::Event;
|
||||
pub use events::PlayerEvent;
|
||||
pub use player::Player;
|
||||
pub use swf;
|
||||
pub use swf::Color;
|
||||
|
|
|
@ -55,14 +55,17 @@ impl<'gc> MovieClip<'gc> {
|
|||
) -> Self {
|
||||
Self {
|
||||
base: Default::default(),
|
||||
static_data: Gc::allocate(gc_context, MovieClipStatic {
|
||||
static_data: Gc::allocate(
|
||||
gc_context,
|
||||
MovieClipStatic {
|
||||
id,
|
||||
tag_stream_start,
|
||||
tag_stream_len,
|
||||
total_frames: num_frames,
|
||||
audio_stream_info: None,
|
||||
frame_labels: HashMap::new(),
|
||||
}),
|
||||
},
|
||||
),
|
||||
tag_stream_pos: 0,
|
||||
is_playing: true,
|
||||
goto_queue: Vec::new(),
|
||||
|
@ -186,10 +189,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
.find(|child| child.read().name() == name)
|
||||
}
|
||||
|
||||
pub fn frame_label_to_number(
|
||||
&self,
|
||||
frame_label: &str,
|
||||
) -> Option<FrameNumber> {
|
||||
pub fn frame_label_to_number(&self, frame_label: &str) -> Option<FrameNumber> {
|
||||
self.static_data.frame_labels.get(frame_label).copied()
|
||||
}
|
||||
|
||||
|
@ -224,7 +224,10 @@ impl<'gc> MovieClip<'gc> {
|
|||
|
||||
pub fn get_variable(&self, var_name: &str) -> avm1::Value {
|
||||
// TODO: Value should be Copy (and contain a Cow/GcCell for big objects)
|
||||
self.variables.get(var_name).unwrap_or(&avm1::Value::Undefined).clone()
|
||||
self.variables
|
||||
.get(var_name)
|
||||
.unwrap_or(&avm1::Value::Undefined)
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn set_variable(&mut self, var_name: &str, value: avm1::Value) {
|
||||
|
@ -338,17 +341,31 @@ impl<'gc> DisplayObject<'gc> for MovieClip<'gc> {
|
|||
TagCode::DefineSound => self.define_sound(context, reader, tag_len),
|
||||
TagCode::DefineSprite => self.define_sprite(context, reader, tag_len),
|
||||
TagCode::DefineText => self.define_text(context, reader),
|
||||
TagCode::FrameLabel => self.frame_label(context, reader, tag_len, cur_frame, &mut static_data),
|
||||
TagCode::FrameLabel => {
|
||||
self.frame_label(context, reader, tag_len, cur_frame, &mut static_data)
|
||||
}
|
||||
TagCode::JpegTables => self.jpeg_tables(context, reader, tag_len),
|
||||
TagCode::PlaceObject => self.preload_place_object(context, reader, tag_len, &mut ids, 1),
|
||||
TagCode::PlaceObject2 => self.preload_place_object(context, reader, tag_len, &mut ids, 2),
|
||||
TagCode::PlaceObject3 => self.preload_place_object(context, reader, tag_len, &mut ids, 3),
|
||||
TagCode::PlaceObject4 => self.preload_place_object(context, reader, tag_len, &mut ids, 4),
|
||||
TagCode::PlaceObject => {
|
||||
self.preload_place_object(context, reader, tag_len, &mut ids, 1)
|
||||
}
|
||||
TagCode::PlaceObject2 => {
|
||||
self.preload_place_object(context, reader, tag_len, &mut ids, 2)
|
||||
}
|
||||
TagCode::PlaceObject3 => {
|
||||
self.preload_place_object(context, reader, tag_len, &mut ids, 3)
|
||||
}
|
||||
TagCode::PlaceObject4 => {
|
||||
self.preload_place_object(context, reader, tag_len, &mut ids, 4)
|
||||
}
|
||||
TagCode::RemoveObject => self.preload_remove_object(context, reader, &mut ids, 1),
|
||||
TagCode::RemoveObject2 => self.preload_remove_object(context, reader, &mut ids, 2),
|
||||
TagCode::ShowFrame => self.preload_show_frame(context, reader, &mut cur_frame),
|
||||
TagCode::SoundStreamHead => self.preload_sound_stream_head(context, reader, &mut static_data, 1),
|
||||
TagCode::SoundStreamHead2 => self.preload_sound_stream_head(context, reader, &mut static_data, 2),
|
||||
TagCode::SoundStreamHead => {
|
||||
self.preload_sound_stream_head(context, reader, &mut static_data, 1)
|
||||
}
|
||||
TagCode::SoundStreamHead2 => {
|
||||
self.preload_sound_stream_head(context, reader, &mut static_data, 2)
|
||||
}
|
||||
TagCode::SoundStreamBlock => self.preload_sound_stream_block(context, reader, tag_len),
|
||||
_ => Ok(()),
|
||||
};
|
||||
|
@ -391,19 +408,17 @@ impl<'gc> DisplayObject<'gc> for MovieClip<'gc> {
|
|||
context.transform_stack.pop();
|
||||
}
|
||||
|
||||
fn pick(&self, point: (Twips, Twips)) -> Option<DisplayNode<'gc>> {
|
||||
//if self.world_bounds().contains(point) {
|
||||
for child in self.children.values().rev() {
|
||||
if child.read().hit_test(point) {
|
||||
return Some(*child);
|
||||
}
|
||||
|
||||
let button = child.read().pick(point);
|
||||
if button.is_some() {
|
||||
return button;
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
_self_node: DisplayNode<'gc>,
|
||||
point: (Twips, Twips),
|
||||
) -> Option<DisplayNode<'gc>> {
|
||||
for child in self.children.values() {
|
||||
let result = child.read().mouse_pick(*child, point);
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -794,8 +809,13 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
) -> DecodeResult {
|
||||
let id = reader.read_character_id()?;
|
||||
let num_frames = reader.read_u16()?;
|
||||
let mut movie_clip =
|
||||
MovieClip::new_with_data(context.gc_context, id, reader.get_ref().position(), tag_len - 4, num_frames);
|
||||
let mut movie_clip = MovieClip::new_with_data(
|
||||
context.gc_context,
|
||||
id,
|
||||
reader.get_ref().position(),
|
||||
tag_len - 4,
|
||||
num_frames,
|
||||
);
|
||||
|
||||
movie_clip.preload(context);
|
||||
|
||||
|
@ -830,7 +850,11 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
static_data: &mut MovieClipStatic,
|
||||
) -> DecodeResult {
|
||||
let frame_label = reader.read_frame_label(tag_len)?;
|
||||
if static_data.frame_labels.insert(frame_label.label, cur_frame).is_some() {
|
||||
if static_data
|
||||
.frame_labels
|
||||
.insert(frame_label.label, cur_frame)
|
||||
.is_some()
|
||||
{
|
||||
log::warn!("Movie clip {}: Duplicated frame label", self.id());
|
||||
}
|
||||
Ok(())
|
||||
|
@ -1007,7 +1031,7 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
#[inline]
|
||||
fn remove_object(
|
||||
&mut self,
|
||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
reader: &mut SwfStream<&'a [u8]>,
|
||||
version: u8,
|
||||
) -> DecodeResult {
|
||||
|
@ -1016,7 +1040,9 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
} else {
|
||||
reader.read_remove_object_2()
|
||||
}?;
|
||||
self.children.remove(&remove_object.depth);
|
||||
if let Some(child) = self.children.remove(&remove_object.depth) {
|
||||
child.write(context.gc_context).set_parent(None);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1036,7 +1062,8 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_reader: &mut SwfStream<&'a [u8]>,
|
||||
) -> DecodeResult {
|
||||
if let (Some(stream_info), None) = (&self.static_data.audio_stream_info, &self.audio_stream) {
|
||||
if let (Some(stream_info), None) = (&self.static_data.audio_stream_info, &self.audio_stream)
|
||||
{
|
||||
let slice = crate::tag_utils::SwfSlice {
|
||||
data: std::sync::Arc::clone(context.swf_data),
|
||||
start: self.tag_stream_start() as usize,
|
||||
|
@ -1063,7 +1090,6 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Static data shared between all instances of a movie clip.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::avm1::Avm1;
|
||||
use crate::backend::{audio::AudioBackend, render::RenderBackend};
|
||||
use crate::event::{ClipEvent, Event};
|
||||
use crate::events::{ButtonEvent, PlayerEvent};
|
||||
use crate::library::Library;
|
||||
use crate::movie_clip::MovieClip;
|
||||
use crate::prelude::*;
|
||||
|
@ -155,9 +155,20 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
|||
self.movie_height
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, event: Event) {
|
||||
pub fn handle_event(&mut self, event: PlayerEvent) {
|
||||
let mut needs_render = false;
|
||||
|
||||
// Update mouse position from mouse events.
|
||||
if let PlayerEvent::MouseMove { x, y }
|
||||
| PlayerEvent::MouseDown { x, y }
|
||||
| PlayerEvent::MouseUp { x, y } = event
|
||||
{
|
||||
self.mouse_pos = (x, y);
|
||||
if self.update_roll_over() {
|
||||
needs_render = true;
|
||||
}
|
||||
}
|
||||
|
||||
let (
|
||||
global_time,
|
||||
swf_data,
|
||||
|
@ -166,7 +177,6 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
|||
renderer,
|
||||
audio,
|
||||
avm,
|
||||
mouse_pos,
|
||||
is_mouse_down,
|
||||
) = (
|
||||
self.global_time,
|
||||
|
@ -176,13 +186,10 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
|||
&mut self.renderer,
|
||||
&mut self.audio,
|
||||
&mut self.avm,
|
||||
&mut self.mouse_pos,
|
||||
&mut self.is_mouse_down,
|
||||
);
|
||||
|
||||
let mut needs_mouse_update = false;
|
||||
self.gc_arena.mutate(|gc_context, gc_root| {
|
||||
let actions = {
|
||||
let mut update_context = UpdateContext {
|
||||
global_time,
|
||||
swf_data,
|
||||
|
@ -197,80 +204,44 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
|||
active_clip: gc_root.root,
|
||||
};
|
||||
|
||||
if let Some(node) = &*gc_root.mouse_hover_node.read() {
|
||||
if let Some(button) = node.write(gc_context).as_button_mut() {
|
||||
match event {
|
||||
Event::MouseMove { x, y } => {
|
||||
*mouse_pos = (x, y);
|
||||
needs_mouse_update = true;
|
||||
}
|
||||
Event::MouseLeft => {
|
||||
// TODO: Just setting to a bogus value for now.
|
||||
*mouse_pos = (Twips::new(std::i32::MAX), Twips::new(std::i32::MAX));
|
||||
needs_mouse_update = true;
|
||||
}
|
||||
Event::MouseDown { x, y } => {
|
||||
*mouse_pos = (x, y);
|
||||
PlayerEvent::MouseDown { .. } => {
|
||||
*is_mouse_down = true;
|
||||
needs_render = true;
|
||||
if let Some(node) = &*gc_root.mouse_hover_node.read() {
|
||||
update_context.active_clip = *node;
|
||||
node.write(gc_context)
|
||||
.handle_event(&mut update_context, ClipEvent::Press);
|
||||
button.handle_button_event(&mut update_context, ButtonEvent::Press);
|
||||
}
|
||||
}
|
||||
Event::MouseUp { x, y } => {
|
||||
*mouse_pos = (x, y);
|
||||
|
||||
PlayerEvent::MouseUp { .. } => {
|
||||
*is_mouse_down = false;
|
||||
needs_render = true;
|
||||
if let Some(node) = &*gc_root.mouse_hover_node.read() {
|
||||
if gc_root.root.read().pick(*mouse_pos).map(GcCell::as_ptr)
|
||||
== Some(node.as_ptr())
|
||||
{
|
||||
update_context.active_clip = *node;
|
||||
node.write(gc_context)
|
||||
.handle_event(&mut update_context, ClipEvent::Release);
|
||||
}
|
||||
}
|
||||
needs_mouse_update = true;
|
||||
button.handle_button_event(&mut update_context, ButtonEvent::Release);
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
update_context.actions
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if !actions.is_empty() {
|
||||
let mut action_context = crate::avm1::ActionContext {
|
||||
gc_context,
|
||||
global_time,
|
||||
root: gc_root.root,
|
||||
start_clip: gc_root.root,
|
||||
active_clip: gc_root.root,
|
||||
audio,
|
||||
};
|
||||
for (active_clip, action) in actions {
|
||||
action_context.start_clip = active_clip;
|
||||
action_context.active_clip = active_clip;
|
||||
let _ = avm.do_action(&mut action_context, action.as_ref());
|
||||
}
|
||||
}
|
||||
Self::run_actions(&mut update_context, gc_root.root);
|
||||
});
|
||||
|
||||
if needs_mouse_update && self.update_mouse() {
|
||||
needs_render = true;
|
||||
}
|
||||
|
||||
if needs_render {
|
||||
// Update display after mouse events.
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_mouse(&mut self) -> bool {
|
||||
// Don't update roll-over state when mouse is down.
|
||||
// TODO: This depends on button tracking.
|
||||
fn update_roll_over(&mut self) -> bool {
|
||||
// TODO: While the mouse is down, maintain the hovered node.
|
||||
if self.is_mouse_down {
|
||||
return false;
|
||||
}
|
||||
|
||||
let (global_time, swf_data, swf_version, background_color, renderer, audio, avm, mouse_pos) = (
|
||||
let (global_time, swf_data, swf_version, background_color, renderer, audio, avm) = (
|
||||
self.global_time,
|
||||
&mut self.swf_data,
|
||||
self.swf_version,
|
||||
|
@ -278,17 +249,17 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
|||
&mut self.renderer,
|
||||
&mut self.audio,
|
||||
&mut self.avm,
|
||||
self.mouse_pos,
|
||||
);
|
||||
|
||||
let hover_changed = self.gc_arena.mutate(|gc_context, gc_root| {
|
||||
// Updare hovered display object.
|
||||
let new_hover_node = gc_root.root.read().pick(mouse_pos);
|
||||
// Check if the hovered object has changed.
|
||||
// TODO: Yuck... what's the best way to determine if the pointers are the same?
|
||||
if gc_root.mouse_hover_node.read().map(GcCell::as_ptr)
|
||||
!= new_hover_node.map(GcCell::as_ptr)
|
||||
{
|
||||
let mouse_pos = &self.mouse_pos;
|
||||
// Check hovered object.
|
||||
self.gc_arena.mutate(|gc_context, gc_root| {
|
||||
let new_hover_node = gc_root
|
||||
.root
|
||||
.read()
|
||||
.mouse_pick(gc_root.root, (mouse_pos.0, mouse_pos.1));
|
||||
let mut cur_hover_node = gc_root.mouse_hover_node.write(gc_context);
|
||||
if cur_hover_node.map(GcCell::as_ptr) != new_hover_node.map(GcCell::as_ptr) {
|
||||
let mut update_context = UpdateContext {
|
||||
global_time,
|
||||
swf_data,
|
||||
|
@ -303,30 +274,30 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
|||
active_clip: gc_root.root,
|
||||
};
|
||||
|
||||
// Fire rollOut event for previous object.
|
||||
if let Some(node) = &*gc_root.mouse_hover_node.write(gc_context) {
|
||||
// RollOut of previous node.
|
||||
if let Some(node) = &*cur_hover_node {
|
||||
if let Some(button) = node.write(gc_context).as_button_mut() {
|
||||
update_context.active_clip = *node;
|
||||
node.write(gc_context)
|
||||
.handle_event(&mut update_context, ClipEvent::RollOut);
|
||||
button.handle_button_event(&mut update_context, ButtonEvent::RollOut);
|
||||
}
|
||||
}
|
||||
|
||||
*gc_root.mouse_hover_node.write(gc_context) = new_hover_node;
|
||||
|
||||
// Fire rollOver event for new object.
|
||||
// RollOver on new node.
|
||||
if let Some(node) = new_hover_node {
|
||||
if let Some(button) = node.write(gc_context).as_button_mut() {
|
||||
update_context.active_clip = node;
|
||||
node.write(gc_context)
|
||||
.handle_event(&mut update_context, ClipEvent::RollOver);
|
||||
button.handle_button_event(&mut update_context, ButtonEvent::RollOver);
|
||||
}
|
||||
}
|
||||
|
||||
*cur_hover_node = new_hover_node;
|
||||
|
||||
Self::run_actions(&mut update_context, gc_root.root);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: render
|
||||
hover_changed
|
||||
})
|
||||
}
|
||||
|
||||
fn preload(&mut self) {
|
||||
|
@ -371,7 +342,6 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
|||
);
|
||||
|
||||
self.gc_arena.mutate(|gc_context, gc_root| {
|
||||
let mut actions = {
|
||||
let mut update_context = UpdateContext {
|
||||
global_time,
|
||||
swf_data,
|
||||
|
@ -390,60 +360,12 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
|||
.root
|
||||
.write(gc_context)
|
||||
.run_frame(&mut update_context);
|
||||
update_context.actions
|
||||
};
|
||||
|
||||
// TODO: Loop here because goto-ing a frame can queue up for actions.
|
||||
// Need to figure out the proper order of operations between ticking a clip
|
||||
// and running the actions.
|
||||
loop {
|
||||
{
|
||||
let mut action_context = crate::avm1::ActionContext {
|
||||
gc_context,
|
||||
global_time,
|
||||
root: gc_root.root,
|
||||
start_clip: gc_root.root,
|
||||
active_clip: gc_root.root,
|
||||
audio,
|
||||
};
|
||||
for (active_clip, action) in actions {
|
||||
action_context.start_clip = active_clip;
|
||||
action_context.active_clip = active_clip;
|
||||
let _ = avm.do_action(&mut action_context, action.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
actions = {
|
||||
let mut update_context = UpdateContext {
|
||||
global_time,
|
||||
swf_data,
|
||||
swf_version,
|
||||
library: gc_root.library.write(gc_context),
|
||||
background_color,
|
||||
avm,
|
||||
renderer,
|
||||
audio,
|
||||
actions: vec![],
|
||||
gc_context,
|
||||
active_clip: gc_root.root,
|
||||
};
|
||||
|
||||
gc_root
|
||||
.root
|
||||
.write(gc_context)
|
||||
.run_post_frame(&mut update_context);
|
||||
|
||||
update_context.actions
|
||||
};
|
||||
|
||||
if actions.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Self::run_actions(&mut update_context, gc_root.root);
|
||||
});
|
||||
|
||||
// Update mouse state (check for new hovered button, etc.)
|
||||
self.update_mouse();
|
||||
self.update_roll_over();
|
||||
}
|
||||
|
||||
pub fn render(&mut self) {
|
||||
|
@ -484,6 +406,40 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
|||
pub fn renderer_mut(&mut self) -> &mut Renderer {
|
||||
&mut self.renderer
|
||||
}
|
||||
|
||||
fn run_actions<'gc>(update_context: &mut UpdateContext<'_, 'gc, '_>, root: DisplayNode<'gc>) {
|
||||
// TODO: Loop here because goto-ing a frame can queue up for actions.
|
||||
// I think this will eventually be cleaned up;
|
||||
// Need to figure out the proper order of operations between ticking a clip
|
||||
// and running the actions.
|
||||
let mut actions = std::mem::replace(&mut update_context.actions, vec![]);
|
||||
while !actions.is_empty() {
|
||||
{
|
||||
let mut action_context = crate::avm1::ActionContext {
|
||||
gc_context: update_context.gc_context,
|
||||
global_time: update_context.global_time,
|
||||
root,
|
||||
start_clip: root,
|
||||
active_clip: root,
|
||||
audio: update_context.audio,
|
||||
};
|
||||
for (active_clip, action) in actions {
|
||||
action_context.start_clip = active_clip;
|
||||
action_context.active_clip = active_clip;
|
||||
let _ = update_context
|
||||
.avm
|
||||
.do_action(&mut action_context, action.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
// Run goto queues.
|
||||
update_context.active_clip = root;
|
||||
root.write(update_context.gc_context)
|
||||
.run_post_frame(update_context);
|
||||
|
||||
actions = std::mem::replace(&mut update_context.actions, vec![]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UpdateContext<'a, 'gc, 'gc_context> {
|
||||
|
|
|
@ -75,7 +75,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
mouse_pos = position;
|
||||
let event = ruffle_core::Event::MouseMove {
|
||||
let event = ruffle_core::PlayerEvent::MouseMove {
|
||||
x: Twips::from_pixels(position.x),
|
||||
y: Twips::from_pixels(position.y),
|
||||
};
|
||||
|
@ -87,12 +87,12 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
|||
..
|
||||
} => {
|
||||
let event = if pressed == ElementState::Pressed {
|
||||
ruffle_core::Event::MouseDown {
|
||||
ruffle_core::PlayerEvent::MouseDown {
|
||||
x: Twips::from_pixels(mouse_pos.x),
|
||||
y: Twips::from_pixels(mouse_pos.y),
|
||||
}
|
||||
} else {
|
||||
ruffle_core::Event::MouseUp {
|
||||
ruffle_core::PlayerEvent::MouseUp {
|
||||
x: Twips::from_pixels(mouse_pos.x),
|
||||
y: Twips::from_pixels(mouse_pos.y),
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
|||
player.handle_event(event);
|
||||
}
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
player.handle_event(ruffle_core::Event::MouseLeft)
|
||||
player.handle_event(ruffle_core::PlayerEvent::MouseLeft)
|
||||
}
|
||||
WindowEvent::CloseRequested => request_close = true,
|
||||
_ => (),
|
||||
|
|
Loading…
Reference in New Issue