AS3 Inspectable Font Chooser - actionscript-3

I'm building a component and need to be able to select fonts from a font list. I have the font list showing up but I'm unsure of what the proper datatype is or how I should be setting it. I've tried String and Font and I seem to be getting an error.
private var _tfFormat:TextFormat;
_tfFormat = new TextFormat();
This will produce an 1067: Implicit coercion of type String to unrelated flash.text:Font.
private var _font:Font = null;
_tfFormat.font = font.fontName;
[Inspectable(type="Font Name", name="font", defaultValue="Arial")]
public function get font():Font
{
return _font;
}
public function set font(value:Font):void
{
_font = value;
invalidate();
}
This gives me a 1065 Variable is not defined.
private var _font:String = "";
var __cls:Class = getDefinitionByName(font) as Class;
var __fnFont:Font = new __cls() as Font;
_tfFormat.font = __fnFont.fontName;
[Inspectable(type="Font Name", name="font", defaultValue="")]
public function get font():String
{
return _font;
}
public function set font(value:String):void
{
_font = value;
invalidate();
}
I feel I'm pretty close and it's something ridiculously easy that I'm overlooking. Any set of eyes would be appreciated. Thanks.

Ok I solved the issue.
I changed the data type of the font back to String. As it turns out, if using an embedded font, Flash adds an asterisk to the name; eg: ArialBold* But that asterisk isn't included in the linkage identifier for the font. So in my setter I removed the asterisk and then create a class from string as normal. Here's the (shortened) code.
If there's a better way, I'm still all ears. :)
private var _font:String = "";
private var _tfFormat:TextFormat;
_tfFormat = new TextFormat();
var __cls:Class = getDefinitionByName(font) as Class;
var __fnFont:Font = new __cls() as Font;
_tfFormat.font = __fnFont.fontName;
[Inspectable(type="Font Name", name="font", defaultValue="")]
public function get font():String
{
return _font;
}
public function set font(value:String):void
{
_font = value.split("*").join('');
invalidate();
}

Related

Unity Custom Editor Window Input Control for Type Determined by Reflection

I am creating a GUI for selecting a function in a Unity custom EditorWindow inspired by the UnityEvent GUI. I can't get UnityEvent itself to work; using a EditorGUILayout.PropertyField and referencing the UnityEvent member as a serialized property produces an empty foldout.
I have choosing a function working, but I can't figure out how to allow the user to specify the function parameter arguments.
using System.Reflection;
int functionIndex = 0;
MethodInfo[] methods = typeof(LvlGenFunctions).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
string methodNames = new string[methods.Length];
for(int i = 0; i < methods.Length; ++i)
{
methodNames[i] = methods[i].Name;
}
functionIndex = EditorGUILayout.Popup(functionIndex, methodNames);
methods[functionIndex].Invoke(null, null);
I can get the ParameterInfo and thus the ParameterType, but I don't know how to create an input GUI field to specify an appropriate parameter argument.
How can I create a Unity IMGUI input field for a parameter whose type is determined by reflection?
To be clear
EditorWindow : ScriptableObject and ScriptableObject : Object
SerializedObject work with UnityEngine.Object so you can use this constructor:
new SerializedObject(Object target);
EditorWindow
public UnityEvent OnSomeEvent;
private SerializedObject serializedObject;
private SerializedProperty property;
private void OnEnable()
{
serializedObject = new SerializedObject(this);
property = serializedObject.FindProperty(nameof(OnSomeEvent));
}
private void OnGUI()
{
EditorGUILayout.PropertyField(property, true);
}
Now you have the inspector but it's not something that you want.
Problem is it`s too ugly.
Update your code like below:
public UnityEvent OnSomeEvent;
private SerializedObject serializedObject;
private SerializedProperty property;
// new fields
private UnityEventDrawer EventDrawer;
private GUIContent Content;
private void OnEnable()
{
serializedObject = new SerializedObject(this);
property = serializedObject.FindProperty(nameof(OnSomeEvent));
// new initialization
EventDrawer = new UnityEventDrawer();
Content = new GUIContent(property.displayName, property.tooltip);
}
private void OnGUI()
{
// new drawer
var eventHeigth = EventDrawer.GetPropertyHeight(property, label);
var rect = EditorGUILayout.GetControlRect(true, eventHeigth);
EventDrawer.OnGUI(rect, property, label);
}

Get AS3 instance method reference from Class object

class Foo {
public function bar():void { ... }
}
var clazz:Class = Foo;
// ...enter the function (no Foo literal here)
var fun:Function = clazz["bar"]; // PROBLEM: returns null
// later
fun.call(new Foo(), ...);
What is the correct way to do the above? The Java equivalent of what I want to do is:
Method m = Foo.class.getMethod("bar", ...);
m.invoke(new Foo(), ...);
Actual code (with workaround):
class SerClass {
public var className:String;
public var name:String;
private var ser:String = null;
private var unser:Function = null;
public function SerClass(clazz:Class):void {
var type:XML = describeType(clazz);
className = type.#name;
// determine name
name = type.factory.metadata.(#name=="CompactType").arg.(#key=="name").#value;
// find unserializer
var mdesc:XML = XML(type.method.metadata.(#name=="Unserialize")).parent();
if (mdesc is XML) {
unser = clazz[mdesc.#name];
}
// find serializer
var sdesc:XML = XML(type.factory.method.metadata.(#name=="Serialize")).parent();
if (sdesc is XML) {
ser = sdesc.#name;
}
}
public function serialize(obj:Object, ous:ByteArray):void {
if (ser == null) throw new Error(name + " is not serializable");
obj[ser](ous);
}
public function unserialize(ins:ByteArray):Object {
if (unser == null) throw new Error(name + " is not unserializable");
return unser.call(null, ins);
}
}
Here the function bar only exist when your class is instanciated :
var foo:Foo = new Foo()
var fun:Function = foo.bar // <-- here you can get the function from the new instance
if you want to access it directlty you have to make it static:
class Foo {
public static function bar():void{ ... }
}
now you can access your function from the class Foo:
var fun:Function = Foo.bar
or
var clazz:Class = Foo
var fun:Function = clazz["bar"]
I am not sure about what you are intending to do.
However AS3Commons, especially the reflect package have API's that let you work with methods, instances and properties of a class.
There are also API methods to create instances of certain class types on the fly and call their respective methods.
Cheers
It's not
fun.call(new Foo(), ...);
Use instead since no parameters are required for the function
fun.call(clazz);
The first parameter as specified by adobe docs.
An object that specifies the value of thisObject within the function body.
[EDIT]
Forgot to point out you have to instantiate a non-static class with the "new" keyword.
var clazz:Class = new Foo();
[EDIT2]
Ok I played around and think I got what you want.
base.as
package{
public class Base {
public function Base() {
trace('Base constructor')
}
public function someFunc( ){
trace('worked');
}
}
}
//called with
var b:Base = new Base( );// note I am not type casting to Class
var func:Function = b.someFunc;
func.call( );
My workaround is to store the function name instead of the Function object.
var fun:String = "bar";
// later...
new Foo()[fun](...);

Getting autocomplete of object-keys in flashbuilder

Is it possible to get autocomplete-functionality on objects keys?
var obj:Object = new Object();
obj.name = "AName";
obj.weight = "100";
When I type obj. -> i would like to see the keys(name,weight);
Thanks
Flash builder autocompletes only those properties/methods that are defined in the class (Defined might be a wrong word here, but I guess it is clear what I meant.) It does not autocomplete properties added in this way. As far as I know, this is not possible with flash builder.
create a class :
package {
public class NameWeightVO {
public var name : String;
public var weight : Number;
public function NameWeightVO(pName : String = "", pWeight : Number = 0) {
}
}
}
then you can create a new NameWeightVO Object with autocomplete functionality:
var nameWeightVO : NameWeightVO = new NameWeightVO();
nameWeightVO.name = "name";
nameWeightVO.weight = 10;

ActionScript - Determine If Value is Class Constant

i'd like to throw an argument error if a particular function doesn't work without a passed value that also happens to be a public constant of the class containing the function.
is there anyway to determine if a class owns a public constant instead of having to iterate thru all of them?
something like this:
public static const HALIFAX:String = "halifax";
public static const MONTREAL:String = "montreal";
public static const TORONTO:String = "toronto";
private var cityProperty:String;
public function set city(value:String):void
{
if (!this.hasConstant(value))
throw new ArgumentError("set city value is not applicable.");
cityProperty = value;
}
public function get city():Strig
{
return cityProperty;
}
currently, for this functionality i have to write the city setter function like this:
public function set city(value:String):void
{
if (value != HALIFAX && value != MONTREAL && value != TORONTO)
throw new ArgumentError("set city value is not applicable.");
cityProperty = value;
}
is this the only way to accomplish this task?
Yes, if you use reflections:
private var type:Class;
private var description:XML;
private function hasConstant (str : String ) : Boolean
{
if (description == null)
{
type = getDefinitionByName (getQualifiedClassName (this)) as Class;
description = describeType (type);
}
for each ( var constant:XML in description.constant)
{
if (type[constant.#name] == str) return true;
}
return false;
}
Note that for this to work, all constants must always be String objects declared public static const.
I was looking for an answer to this question myself and found it annoying that hasOwnProperty() did not work for static properties. Turns out though, that if you cast your class to a Class object, it does work.
Here's an example:
public final class DisplayMode
{
public static const one: String = "one";
public static const two: String = "two";
public static const three: String = "three";
public static function isValid(aDisplayMode: String): Boolean {
return Class(DisplayMode).hasOwnProperty(aDisplayMode);
}
}
I owe this solution to jimmy5804 from this discussion, so hats off to him.
You should be able to use bracket notation to do this. For example:
var foo:Sprite = new Sprite();
foo.rotation = 20;
trace( foo["x"], foo["rotation"]); // traces "0 20"
or more specific to your case:
var bar:String = "rotation";
trace( foo[bar] ); // traces "20"
The only thing you have to look out for here, is that the bracket accessor will throw a ReferenceError if you ask for an object property that isn't there, such as:
trace ( foo["cat"] ); // throws ReferenceError
But it will not throw if you are asking for a static property:
trace ( Sprite["cat"] ); // traces "undefined"
So in your case you might try:
if ( this[value] == undefined ) {
throw new ArgumentError("set city value is not applicable.");
}
EDIT:
Sorry, I was confusing the const's names with their values.
For this to work on your problem you would have to make the String value the same as the const's name, so for example:
public static const HALIFAX:String = "HALIFAX";
then you could use the query as described above and it would give you the desired result.

AS3 Custom Object to ByteArray then to Custom Object

Having problem reading bytearray of custom objects. Any help is appreciated
public class CustomObject extends Object {
public function CustomObject() {
public var _x:Number = 100
public var _y:Number = 10
public var _z:Number = 60
}
}
var cObj:CustomObject = new CustomObject()
var bytes:ByteArray = new ByteArray()
bytes.writeObject(cObj)
bytes.compress()
//read
try { bytes.uncompress() } catch (e:Error) { }
var obj:CustomObject = bytes.readObject() as CustomObject
trace(obj) // null why?!
trace(obj._z) // Obviously - TypeError: Error #1009: Cannot access a property or method of a null object reference.
What you want to do is use the registerClassAlias method to register type information along with the data. That way Flash will know how to serialize/deserialize your object. Here's some sample code from Adobe's documentation:
registerClassAlias("com.example.eg", ExampleClass);
var eg1:ExampleClass = new ExampleClass();
var ba:ByteArray = new ByteArray();
ba.writeObject(eg1);
ba.position = 0;
var eg2:* = ba.readObject();
trace(eg2 is ExampleClass); // true
It should be noted that all types that should be serialized must be registered for the type information to be saved. So if you have another type that is referenced by your type, it too must be registered.
Your CustomObject class is wrong , it should throw an error actually , it should be this instead
public class CustomObject
{
public var _x:Number = 100
public var _y:Number = 10
public var _z:Number = 60
public function CustomObject()
{
}
}
Edit:
Sounds like macke has a point, because this works...
//read
try { bytes.uncompress() } catch (e:Error) { }
var obj:Object = bytes.readObject();
trace(obj) // [object Object]
trace(obj._z) // 60
Look at object that ByteArray.readObject() returns. You'll probably see that all properties are there, but type information is lost. So, you can solve this by creating some
public static function fromObject(value:Object):CustomObject {
var result:CustomObject = new CustomObject();
result._x = value._x;
//and so on...
return result;
}
To serialize custom classes to the ByteArray, you must put registerClassAlias in the constructor of the class calling the byteArray.writeObject() function.
If you don't, your custom class will be serialized as Object type. I was calling registerClassAlias in the serialize function below and my custom class keeps getting serialized as Object until I moved the registerClassAlias to the constructor.
public class MyClass{
public function MyClass(){
registerClassAlias("com.myclass", MyClass); // Ok, serializes as MyClass
serialize( new MyClass() );
}
private function serialize( _c:MyClass ){
var byteArray:ByteArray = new ByteArray();
byteArray.writeObject( _c );
//registerClassAlias("com.myclass", MyClass); Not ok, serialized as Object
EncryptedLocalStorage.setItem('key', byteArray);
}
}