core: Implement url handling (+asfunction) in textedits
This commit is contained in:
parent
d32867d992
commit
658ac2dd9f
|
@ -4,7 +4,7 @@ use crate::avm1::function::{Avm1Function, ExecutionReason, FunctionObject};
|
|||
use crate::avm1::object::{Object, TObject};
|
||||
use crate::avm1::property::Attribute;
|
||||
use crate::avm1::runtime::skip_actions;
|
||||
use crate::avm1::scope::Scope;
|
||||
use crate::avm1::scope::{Scope, ScopeClass};
|
||||
use crate::avm1::{fscommand, globals, scope, ArrayObject, ScriptObject, Value};
|
||||
use crate::backend::navigator::{NavigationMethod, Request};
|
||||
use crate::context::UpdateContext;
|
||||
|
@ -2970,6 +2970,17 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
self.scope = scope;
|
||||
}
|
||||
|
||||
pub fn set_scope_to_display_object(&mut self, object: DisplayObject<'gc>) {
|
||||
self.scope = Gc::new(
|
||||
self.context.gc_context,
|
||||
Scope::new(
|
||||
self.scope,
|
||||
ScopeClass::Target,
|
||||
object.object().coerce_to_object(self),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Whether this activation operates in a local scope.
|
||||
pub fn in_local_scope(&self) -> bool {
|
||||
let mut current_scope = Some(self.scope);
|
||||
|
|
|
@ -1600,6 +1600,45 @@ impl<'gc> EditText<'gc> {
|
|||
x: union_bounds.offset_x() + Twips::from_pixels(EditText::INTERNAL_PADDING),
|
||||
})
|
||||
}
|
||||
|
||||
fn execute_avm1_asfunction(
|
||||
self,
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
address: &WStr,
|
||||
) -> Result<(), crate::avm1::Error<'gc>> {
|
||||
let mut activation = Avm1Activation::from_nothing(
|
||||
context.reborrow(),
|
||||
ActivationIdentifier::root("[EditText URL]"),
|
||||
self.avm1_root(),
|
||||
);
|
||||
// [NA]: Should all `from_nothings` be scoped to root? It definitely should here.
|
||||
activation.set_scope_to_display_object(self.avm1_root());
|
||||
let this = self.avm1_root().object().coerce_to_object(&mut activation);
|
||||
|
||||
if let Some((name, args)) = address.split_once(b',') {
|
||||
let name = AvmString::new(activation.context.gc_context, name);
|
||||
let args = AvmString::new(activation.context.gc_context, args);
|
||||
let function = activation.get_variable(name)?;
|
||||
function.call_with_default_this(this, name, &mut activation, &[args.into()])?;
|
||||
} else {
|
||||
let name = AvmString::new(activation.context.gc_context, address);
|
||||
let function = activation.get_variable(name)?;
|
||||
function.call_with_default_this(this, name, &mut activation, &[])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn open_url(self, context: &mut UpdateContext<'_, 'gc>, url: &WStr, target: &WStr) {
|
||||
if let Some(address) = url.strip_prefix(WStr::from_units(b"asfunction:")) {
|
||||
if let Err(e) = self.execute_avm1_asfunction(context, address) {
|
||||
error!("Couldn't execute URL \"{url:?}\": {e:?}");
|
||||
}
|
||||
} else {
|
||||
context
|
||||
.navigator
|
||||
.navigate_to_url(&url.to_utf8_lossy(), &target.to_utf8_lossy(), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
||||
|
@ -1955,11 +1994,19 @@ impl<'gc> TInteractiveObject<'gc> for EditText<'gc> {
|
|||
let tracker = context.focus_tracker;
|
||||
tracker.set(Some(self.into()), context);
|
||||
}
|
||||
if let Some(position) = self
|
||||
.screen_position_to_index(*context.mouse_position)
|
||||
.map(TextSelection::for_position)
|
||||
{
|
||||
self.0.write(context.gc_context).selection = Some(position);
|
||||
if let Some(position) = self.screen_position_to_index(*context.mouse_position) {
|
||||
self.0.write(context.gc_context).selection =
|
||||
Some(TextSelection::for_position(position));
|
||||
|
||||
let format = self.0.read().text_spans.get_text_format(position, position);
|
||||
if let Some(url) = format.url {
|
||||
if !url.is_empty() {
|
||||
// TODO: This fires on mouse DOWN but it should be mouse UP...
|
||||
// but only if it went down in the same span.
|
||||
// Needs more advanced focus handling than we have at time of writing this comment.
|
||||
self.open_url(context, &url, &format.target.unwrap_or_default());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.0.write(context.gc_context).selection =
|
||||
Some(TextSelection::for_position(self.text_length()));
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
[
|
||||
{
|
||||
"type": "MouseDown",
|
||||
"pos": [134, 69],
|
||||
"btn": "Left"
|
||||
},
|
||||
{
|
||||
"type": "MouseUp",
|
||||
"pos": [134, 69],
|
||||
"btn": "Left"
|
||||
},
|
||||
{
|
||||
"type": "MouseDown",
|
||||
"pos": [166, 95],
|
||||
"btn": "Left"
|
||||
},
|
||||
{
|
||||
"type": "MouseUp",
|
||||
"pos": [166, 95],
|
||||
"btn": "Left"
|
||||
},
|
||||
{
|
||||
"type": "MouseDown",
|
||||
"pos": [143, 128],
|
||||
"btn": "Left"
|
||||
},
|
||||
{
|
||||
"type": "MouseUp",
|
||||
"pos": [143, 128],
|
||||
"btn": "Left"
|
||||
},
|
||||
{
|
||||
"type": "MouseDown",
|
||||
"pos": [142, 160],
|
||||
"btn": "Left"
|
||||
},
|
||||
{
|
||||
"type": "MouseUp",
|
||||
"pos": [142, 160],
|
||||
"btn": "Left"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,6 @@
|
|||
alert1 called with input: undefined
|
||||
// this
|
||||
_level0
|
||||
|
||||
_level0.callback with input: Second Test
|
||||
text1.callback with input: a,b,c,d
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
num_ticks = 1
|
|
@ -382,6 +382,12 @@ impl WStr {
|
|||
(&self[..index], &self[index..])
|
||||
}
|
||||
|
||||
/// Analogue of [`str::split_once`].
|
||||
#[inline]
|
||||
pub fn split_once<'a, P: Pattern<'a>>(&'a self, pattern: P) -> Option<(&'a WStr, &'a WStr)> {
|
||||
super::ops::str_split_once(self, pattern)
|
||||
}
|
||||
|
||||
/// Analogue of [`str::rsplit_once`].
|
||||
#[inline]
|
||||
pub fn rsplit_once<'a, P: Pattern<'a>>(&'a self, pattern: P) -> Option<(&'a WStr, &'a WStr)> {
|
||||
|
|
|
@ -327,6 +327,14 @@ pub fn str_split<'a, P: Pattern<'a>>(string: &'a WStr, pattern: P) -> Split<'a,
|
|||
}
|
||||
}
|
||||
|
||||
pub fn str_split_once<'a, P: Pattern<'a>>(
|
||||
string: &'a WStr,
|
||||
pattern: P,
|
||||
) -> Option<(&'a WStr, &'a WStr)> {
|
||||
let (start, end) = pattern.into_searcher(string).next_match()?;
|
||||
Some((&string[..start], &string[end..]))
|
||||
}
|
||||
|
||||
pub fn str_rsplit_once<'a, P: Pattern<'a>>(
|
||||
string: &'a WStr,
|
||||
pattern: P,
|
||||
|
|
Loading…
Reference in New Issue