This yields nodes as `Step`s. This allows keeping track of the structure of the tree as you walk through descendents, as each element will be yielded twice: both as a `Step::In` *and* as a `Step::Out`. Non-element nodes will be yielded once as a `Step::Around`.
I'm adding `walk` iteration specifically to avoid having to write certain methods recursively. Existing recursive callers of `children` should probably be updated to `walk` the tree and maintain a separate `Vec` stack.
This also necessitated the addition of code to:
* Ensure span breaks exist at both sides of the text boundary, without creating degenerate (length-0) spans
* Consolidate spans with matching text formats
* Shorten or lengthen the total list of text spans to match the backing string
* Ensure at least one text span exists at all times
This still has some minor to-dos: for example, it relies on the default `TextSpan` formatting, which probably should be replaced with actually accepting or storing a default format to be used when constructing new text spans.
Despite having HTML and CSS rendering capabilities, the Flash text field actually does not use HTML as it's internal representation. Instead, the text-span format implied by `getTextFormat` and `setTextFormat` is used to drive layout. You can see this by watching what happens to `htmlText`, *especially* when you add and remove stylesheets.
The `LayoutBox` machinery will be adapted to consume text spans in a future commit. This would make the entire rendering pipeline: HTML/CSS -> Text Spans -> Layout Boxes -> Render commands.
This makes the implementation of rectangle union (`Add`/`AddAssign`) far easier as we just compute the min and max of the offset and extent coordinates. It also makes the conversion into and from `swf::Rectangle` easier as it's now effectively a generic version of that datatype.
On the other hand, `width`, `height`, and `size` now have to be calculated, and require `T` to be self-`Sub`. I'm not sure if this is that much of a problem or not.
We're reusing the XML machinery to handle HTML - this is probably not 100% correct, but writing a new HTML parser to cover just `EditText` will be rather complicated.
If a class is registered to a clip that is placed on the timeline
during a goto, that constructor should run after the frame is
completely constructed. In order to tell whether to run the
constructor immediately, add a parameter to `post_instantiation`
to indicate if the clip is instantiated from the AVM or via a
standard timeline seek.
There was a one-frame-off flicker when a button changed states.
Now children will tick a frame so that they are properly created
immediately when the parent button changes state.
Previously a MovieClip's clip action would have a set of events
that would trigger it. Now we flatten these out into a single
event per action, because this is by far the common case. If an
action does happen to have >1 event, it will be duplicated for each.
Don't call `render` from `Player::tick`; instead, require the
frontends to explicitly call `render` when they wish to redraw.
The frontend can query `Player::needs_render` to see if the stage
is dirty and needs a redraw. Update desktop and web to use this
new method.
This fits better with the newer winit event loop model, which
requires explicitly calling `request_redraw`, and should avoid
spurious renders.
A previous version of this PR (whose history has been scrubbed, but go check 918d88abe68b7467a4194738b95e5bf3e9b5bb72 if you're curious) implemented a new `TObject` property which basically every line of code that dealt with object construction had to populate. It was terrible.
This is accomplished via two new `TObject` methods: `has_own_virtual` and `call_setter`. If an object does not contain it's own version of a property, it will first crawl the prototype chain to see if there is an overwritable virtual. If so, it will invoke that prototype's setter.
A bit of borrow finangling was required to do this; `super` now no longer caches it's proto and constr values and instead dynamically constructs them. This also means it can't be downcasted to `Executable` anymore.
With this commit, virtual setters and super-setters now work correctly.
A base prototype is only applicable in cases where a method is being called as a property on an object. Bare function calls, `apply`/`call` calls, and so on do not generate a base prototype.
We also add a convenience method, `call_method`, to all objects. This method automatically calculates the correct base prototype for a method call on an object, which is the only operation that should generate base prototypes.
Revert to the old action queue method of popping off actions in a
loop, as new actions can be queued while iterating. Store proto
changes in a separate queue to maintain the high priority behavior.
Change `ActionGetTime` (`getTimer`) to use a new backend method.
This allows it to return updated times if it is called multiple
times in a single frame. This fixes hangs caused by games that use
busy-loop "frame limiter" code.
`PropertyMap` wraps over `IndexMap` to handle object properties in
AVM1. All insert/remove/get methods require and `swf_version`
parameter, and the `PropertyMap` will take care of handling case
senstivity and maintaing iteration order based on the SWF version.
First implementation of Button object. Hook up to the button
display object and run onRelease etc. methods as appropriate.
Pull out common display object methods into globals::display_object.
This conversion allows negative octal values, but not negative
hex values, and ignores only leading ASCII whitespace. A test
for this behavior is included.
Specifically fall back to the device font when the UseOutlines
flag is not set in DefineEditText (SWF19 p.172). Fixes#451.
Note that since we only use a single font for "device" rendering,
this may sometimes be a different font than is specified in the
Flash IDE.
It doesn't feel like Flash without having the hand cursor display
when hovering over buttons. First pass at implementing this;
core communicates which mouse cursor to use via
`InputBackend::set_mouse_cursor`.
TODO: Hand cursor only displayed for Button display objects
currently. Movie clips should also display this when they are in
"button mode" (when a button mouse event is set on them in AVM1,
or `buttonMode` property in AVM2).
Implements MovieClip.getBounds, and also reorganized the
DisplayObject AABB methods:
* `self_bounds` calculates the inherent untransfomed bounds of
the object without children. All `DisplayObject`s must implement
this method. For example, `Bitmap` returns the size of bitmap.
Composite objects like `MovieClip` return a null AABB because they
are made up of only children.
* `bounds` calculates the untransformed bounds including children.
* `local_bounds` calculates the bounds relative to the object's
parent.
* `world_bounds` calculates the bounds in global stage space.
* `bounds_with_transform` calculates a tight AABB for the object
with a given transform, and is used to implement the above.
Try to keep style more consistent by using functions for all MC
methods. Previous was a mix of closures and functions (we're still
a little bad with this elsewhere)
This is caused by the fact that `avm.root_object` references the *current* stack frame, not the one we are about to introduce. Ergo, we need to pull the base clip of the *new* stack frame and find it's root.
This particular behavior only crops up in situations where there can be multiple root objects, at least until we implement `_lockroot`.
`_root` is calculated dynamically based on the clip the currently executing function was called in.
Other things that used `context.root` have been changed to either update all layers or just update layer 0, which is the former `context.root`.
Interestingly enough, very little actually has to be done inside the async process for XML. The async process basically just fetches data and fires an event handler when it's done. Everything else is handled via a system builtin, `XML.onData`.
This implementation just returns the size of the current loaded movie. The test is also deliberately written to be loose on timings so that it likely won't see a partially loaded movie. (I don't want it to be a test of load events, so I just wait a few frames, rather than the correct way of waiting for `onLoadComplete`.)
Until we support streaming file loads, we can't faithfully support these properties. Still, it's better to have them, just in case.
This is technically better, but it may make more sense to trigger `ClipEvent::Load` at the start of the next frame instead. Furthermore, I don't know if other forms of load events should trigger on the next frame (or end of the current one) like this.
This also adjusts `MovieClip.unloadMovie` to do just that, instead of removing the clip from the display list. We also have to unload clips when loading new movies into them, since `unloadMovie` desugars to loading `""` as the URL.
Interestingly, this constitutes an implementation of `AsBroadcaster`. It appears Macromedia decided to implement event handling on `MovieClipLoader` in a very similar fashion to `AsBroadcaster`, down to invoking `broadcastMessage` and searching a `_listeners` property for listeners.
*De*implement the free function versions of the above, as well as their `Num` variants, since they don't actually exist as callables. Instead, the ActionScript compiler treats them as preprocessor functions that represent various forms of `ActionGetURL`/`ActionGetURL2`.
This function is part of `Avm1`, rather than a hypothetical `LayerManager`, because we're going to need to eventually construct layers differently for AVM2.
This has some subtle problems: we cannot hold references to garbage-collected data in Futures, so we have to arrange for the AVM itself to forcibly root them for us. Then we get them back when our async code is ready to do something to the AVM.
This allows the formation of `'static` futures that can still interact with a player. Async code will need to upgrade the weak reference in order to be able to interact with the player.
Due to some strangeness with the way Rust implemented unsafe-to-move behavior, boxed futures are implicitly `Unpin`. Which is useless to us.
The reason for this is a little counter-intuitive. Actually, the fact that Rust supports memory pinning at all is a little odd, because the core language explicitly requires all types be movable. To get around this, Pin requires that all !Unpin types be *born pinned*. This is because you can't re-pin an already unpinned value in memory.
Anyway, this necessitates this silly API change.
When `_parent` is preloaded in a `DefineFunction2` action,
we previously resolve it on the scope chain. This could cause
double borrow panics as the parent object could already be
borrowed, and also this matches the behavior of the official Flash
player.
Addresses #398.
This is the smallest positive number, not the most negative value.
This is actually the smallest positive subnormal f64, which Rust
does not provide a constant for. This is ~5e-324.
Match Flash's more closely when converting number to string:
* NAN -> NaN
* inf -> Infinity
* -inf -> -Infinity
* Use exponential notation for very large/very small
This is a little bit of a cheat by using Rust's number-to-string
formatting for exponentials, and shoving a sign in front of the
exponent.
Setting a property such as _x to undefined or null should have no
effect. This was working for v7+ SWFs because it would coerce to
NaN and we toss out NaNs. But on v6 and below, these coerce to 0
and would end up setting the property to 0.
Explicitly check for undefined/null and bail out. Fixes#380.
Also adjust the _visible setter, since this actually coerces to a
number (because of its legacy from SWFv4). For example,
_visible = "" should have no effect.
If a property is not set on the object passed to Color.setTransform,
then that channel is left unmodified. This fixes invisible objects
in some games (fixes#369, addresses #380).
Also improve handling of wrapping/invalid values to better match the
behavior in the Flash Player (some work pending on #193).
When a movie clip or button is used as a mask, the masking will be
disabled if that object has no children; the maskee will be
completely visible. An empty movie clip inside an empty movie clip
successfully masks.
An EditText can also not be used as a masker (although it can be
wrapped inside a movie clip, and then the text successfully masks).
Add a `TDisplayObject::allow_mask` trait method that will
return whether the object can be used as a mask.
This fixes characters not being visible in Dad 'n' Me.
If you goto past the final loaded frame of a timeline, for example,
with gotoAndStop(9999), this seeks to the final frame on the
timeline, but it doesn't run the actions on this frame.
MovieClip::goto_frame now will not run the final frame actions if
the target frame was not reached.
Use the same code path for the global GotoFrame2 action and
MovieClip.gotoAndX, which properly handles out-of-range and invalid
values like NaN.
Fixes Disorderly hanging on game start
(https://www.newgrounds.com/portal/view/121896)
The list of goto commands is now a Vec that will already be in order
of creation. This ensures that subsequent ActionScript in these clips
runs in the correct order.
Ops and functions that take a movie clip path in String form have
a very forgiving syntax. These include:
* `SetTarget`
* `CloneSprite`
* `RemoveSprite`
* `swapDepths`
This change adds `Avm1::resolve_target_display_object` to parse
these paths correctly, along with `target_paths` test to test a
wide variety of formats.
This also applies to `GetVariable`/`SetVariable`, which accept
target paths to variables and is used by some SWF4/5 content.
(fixes#324, #337).
Previously we set the name of the root clip to `_level0`. Top-level
clips should actually have no name (`_root._name` returns `""`).
However, when constructing a dot path, `_level0` still gets inserted
by `DisplayObject::path` for the top-level, so that `trace(_root)`
still correctly prints `_level0`.
TODO: When `loadMovieNum` gets merged in, the proper level # needs
to be returned by `.DisplayObject::path`.
Add these methods that will explicilty coerce a value to an int,
following the wrapping behavior in the ECMAScript specs (ToInt32,
ToUInt32, ToUInt16).
This also fixed an off-by-one error for negative numbers in the
previous implementation.
These will call `valueOf` if necessary. AVM code that requires an
integer will probably use one of these (`coerce_to_i32` usually).
Properties of a display object would not reset when rewinding if
it existed in both the initial and final frames of the goto.
This fixes the weapons toggles in UFA.
When setting a variable in a function-local scope, if that variable
has not been defined in the function scope, it should be defined in
the executing movieclip's scope. Previously it would get defined
in the function's scope. Changed Scope::overwrite to Scope::set,
and modified the behavior to stop traversing and define the value
when it hits a movie clip Target scope.
Also, modified With scopes to properly add onto the end of the scope
chain.