blob: 7fac6a24aad8573741d9fdd901c8c3d763f3826e [file] [log] [blame]
<?xml version="1.0" encoding="utf-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:c="com.flexcapacitor.controls.*"
xmlns:handlers="com.flexcapacitor.handlers.*"
xmlns:collections="com.flexcapacitor.effects.collections.*"
xmlns:components="com.flexcapacitor.components.*"
minWidth="100"
minHeight="100"
creationComplete="creationCompleteHandler(event)"
>
<!--
This is a standard properties grid that is showing property and value
except that the editor (color picker, text input, etc) is
in the footer rather than next to the view.
There is a second view that attempts to put the editor next to the
property. This sort of works but is CPU intensive and causes slow
down. This may be because it has hundreds of properties or
another cause. The idea then would be to specify the properties
in the spark or inspectors manifest file and only show those.
Or it would show only the properties defined on it's self.
For example, if it's a Button it will show only the Button
class properties but not ButtonBase, UIComponent and so on.
This has its limitations though. What about size and position?
In that case, the common properties from the inherited classes
would have to be specified. So Button would show all of it's
properties, then any properties on ButtonBase that were specified
as useful would be shown and then add any properties on UIComponent
that were specified as useful would be shown.
For Button this would look like this:
-
Label - Part of Button base
-
Width - Part of UIComponent
Height
X
Y
Unfortunately there is the order and grouping to keep in mind.
The third view would be custom property inspectors based on type or class.
This is defined in the inspectors manifest file. It specifies the views to
display by class name or URL.
One of the current problems is that the component or class and it's
description is in XML still in some locations and not stored anywhere else.
We use describeType to get the properties, styles, events and inheritence.
Using XML is relatively heavy, slow and is not typed. We use this
information to dynamically build the inspectors for color, numbers, boolean
and enumerated values.
It should be refactored. For example, class information should be globally accessable,
and return a strong data type. It should have look up for getting the
inspector type (color, boolean, text inspector etc) and for setting it's value.
TODO:
- do not update if not visible or is not active tab
- add refresh button
- display read only property
- filter to show only the properties that we specify
- show custom views when defined
- add better way to switch between views
-->
<fx:Script>
<![CDATA[
import com.flexcapacitor.controller.Radiate;
import com.flexcapacitor.events.RadiateEvent;
import com.flexcapacitor.model.AccessorMetaData;
import com.flexcapacitor.model.MetaData;
import com.flexcapacitor.model.StyleMetaData;
import com.flexcapacitor.utils.ClassUtils;
import com.flexcapacitor.utils.DisplayObjectUtils;
import com.flexcapacitor.utils.InspectorUtils;
import com.flexcapacitor.utils.StringUtils;
import com.flexcapacitor.views.renderers.BooleanRenderer;
import com.flexcapacitor.views.renderers.ColorPickerRenderer;
import com.flexcapacitor.views.renderers.ComboBoxRenderer;
import com.flexcapacitor.views.renderers.NumberRenderer;
import com.flexcapacitor.views.renderers.TextRenderer;
import flash.utils.getTimer;
import mx.collections.XMLListCollection;
import mx.core.IFlexModule;
import mx.core.IFlexModuleFactory;
import mx.core.UIComponent;
import mx.effects.effectClasses.PropertyChanges;
import mx.events.FlexEvent;
import mx.utils.DescribeTypeCacheRecord;
import spark.events.GridSelectionEvent;
import spark.events.IndexChangeEvent;
import spark.events.TextOperationEvent;
import flashx.textLayout.operations.SplitParagraphOperation;
public var CUSTOM_ITEM_SORT_CHARACTER:String = "~";
public var PROPERTIES_STATE:String = "properties";
public var NONE_STATE:String = "none";
public var BOOLEAN_STATE:String = "boolean";
public var COLOR_STATE:String = "color";
public var FORMATS_STATE:String = "formats";
public var TEXT_STATE:String = "text";
public var NUMBER_STATE:String = "number";
public var INT_STATE:String = "int";
public var UINT_STATE:String = "uint";
// LETS GET RID OF SOME OF THESE!
private var fontInfoCollection:XMLListCollection = new XMLListCollection();
private var allItems:XMLListCollection = new XMLListCollection();
private var inheritingStylesList:XMLListCollection = new XMLListCollection();
private var nonInheritingStylesList:XMLListCollection = new XMLListCollection();
private var objectPropertiesList:XMLListCollection = new XMLListCollection();
private var _dataProviderProperties:XMLListCollection;
public var describedType:XML;
[Bindable]
public var showSearchBox:Boolean = true;
[Bindable]
public var showValueBox:Boolean = true;
[Bindable]
public var showHeader:Boolean = false;
/**
* Reference to Radiate
* */
public var radiate:Radiate;
/**
* Height of the header when not show (to allow resizing of columns)
* */
public var hiddenHeaderHeight:uint = 0;
private var _target:Object;
public function get target():Object {
return _target;
}
[Bindable]
public function set target(value:Object):void {
_target = value;
if (_target) {
clear();
currentState = NONE_STATE;
populatePropertiesGrid(_target);
}
else {
clear();
}
}
protected function creationCompleteHandler(event:FlexEvent):void {
radiate = Radiate.getInstance();
if (!showHeader) {
//propertiesGrid.columnHeaderGroup.visible = showHeader;
propertiesGrid.columnHeaderGroup.height = hiddenHeaderHeight;
}
radiate.addEventListener(RadiateEvent.TARGET_CHANGE, handleTargetChange, false, 0, true);
radiate.addEventListener(RadiateEvent.PROPERTY_CHANGED, propertyChangeHandler, false, 0, true);
if (radiate.target) {
target = radiate.target;
}
}
protected function handleTargetChange(event:RadiateEvent):void {
target = event.selectedItem;
}
public function get dataProviderProperties():XMLListCollection {
return _dataProviderProperties;
}
[Bindable]
public function set dataProviderProperties(value:XMLListCollection):void {
if (_dataProviderProperties) {
_dataProviderProperties = new XMLListCollection(new XMLList());
}
/*value.sort = new Sort();
value.sort.fields = [new SortField("@name", false)];
value.refresh();*/
_dataProviderProperties = value;
}
/**
* Get Name or ID of target object
* */
public function getName(element:Object):String {
var id:String;
if (element is UIComponent && element.id != null) {
id = UIComponent(element).id;
}
else if (element.hasOwnProperty("id") && element.id != null) {
id = element.id;
}
else if (element.hasOwnProperty("name") && element.name != null) {
id = element.name;
}
else {
id = "";
}
return id;
}
/**
* Populates the datagrid with all the properties that describe type returns for the given object
* */
public function populatePropertiesGrid(target:Object):void {
var describedTypeRecord:mx.utils.DescribeTypeCacheRecord;
var stylesList:XMLListCollection;
var accessorList:XMLListCollection;
var propertyName:String;
var fontLookup:String;
var fontFamily:String;
var renderingMode:String;
var properties:String;
var inheritingStyles:Object;
var nonInheritingStyles:Object;
var property:String;
var item:XML;
var styleNotDefined:Boolean;
var styleIsColor:Boolean;
// TODO: not use xml - use json version
if (target != null) {
//describedType = describeType(target);
var time:int = getTimer();
/*Radiate.log.info("Get properties on target " + getName(target));
describedType = ClassUtils.getDescribeType(target);
Radiate.log.info("2 Get properties on target : " + String(getTimer()-time));*/
describedType = ClassUtils.getDescribeType(target);
//Radiate.log.info("3 Get properties on target : " + String(getTimer()-time));
accessorList = new XMLListCollection(describedType.accessor);
// DISABLE UPDATE UNTIL COMPLETE
//allItems.removeAll();
//allItems.disableAutoUpdate();
// ADD ACCESSORS LIST
allItems.addAll(accessorList);
// if item is an object enumerate
if (describedType.@name=="Object") {
for (property in target) {
item = createXMLItem(property, target[property], null, true, true);
objectPropertiesList.addItem(item); // we could use allItems.addItem();
}
}
allItems.addAll(objectPropertiesList);
/* var blah:Object = getMemberNames(this, true);
blah = getMemberNames(this);
blah = nonInheritingStyles;
blah = styleDeclaration;
blah = styleName;
blah = styleManager.inheritingStyles;
blah = styleManager.qualifiedTypeSelectors;
blah = styleManager.selectors;
blah = styleManager.stylesRoot;
blah = styleManager.typeHierarchyCache;
blah = styleManager.typeSelectorCache;
blah = styleManager.hasAdvancedSelectors(); */
if (filterInput) filterInput.text = "";
// we check for the text flow property so we can see if fonts are embedded
if (target.hasOwnProperty("textFlow")) {
/* if (target.textFlow.computedFormat) {
fontLookup = target.textFlow.computedFormat.fontLookup;
fontFamily = target.textFlow.computedFormat.fontFamily;
renderingMode = target.textFlow.computedFormat.renderingMode;
fontLookup = "<accessor name='fontLookup' value='" + fontLookup + "'/>";
fontFamily = "<accessor name='fontFamily' value='" + fontFamily + "'/>";
renderingMode = "<accessor name='renderingMode' value='" + renderingMode + "'/>";
properties = fontLookup + fontFamily + renderingMode + propertiesList.toString();
propertiesList = new XMLList(properties);
} */
}
//allItems.addAll(propertiesList);
var styles:XMLList = ClassUtils.concatenateMetaDataXMLItems(target, "Style", new XMLList());
//describedTypeXML = describeType(target);
stylesList = new XMLListCollection(styles);
allItems.addAll(stylesList);
var moduleFactory:IFlexModuleFactory = null;
if (target is IFlexModule)
moduleFactory = target.moduleFactory;
// attempts to get the values of the properties on the current target
// and add them to the node in string representation
for each (var node:XML in allItems) {
setValueOnNode(node);
}
// Add in inheriting styles
/*if ("inheritingStyles" in target) {
inheritingStyles = target.inheritingStyles as Object;
for (property in inheritingStyles) {
item = createXMLItem(property, inheritingStyles[property], null, true, true);
inheritingStylesList.addItem(item);
}
//trace("Inheriting style count", inheritingStylesList.length);
//allItems.addAll(inheritingStylesList);
}*/
// Add in non inheriting styles -
/*if (target.hasOwnProperty("nonInheritingStyles")) {
nonInheritingStyles = target.nonInheritingStyles as Object;
for (property in nonInheritingStyles) {
item = createXMLItem(property, nonInheritingStyles[property], null, true, false);
nonInheritingStylesList.addItem(item);
}
//trace("Non-Inheriting style count", nonInheritingStylesList.length);
allItems.addAll(nonInheritingStylesList);
}*/
// not using
// create an item for unnamed style
// this floats at the top so we can get the value of a style not listed by describe type
var customItem:XML = createXMLItem(CUSTOM_ITEM_SORT_CHARACTER, "", "String");
customItem.@search = true;
//allItems.addItem(customItem);
dataProviderProperties = allItems;
}
else {
dataProviderProperties = new XMLListCollection();
}
}
/**
* Set the value on the node
* */
public function setValueOnNode(node:XML):void {
var propertyName:String;
var styleNotDefined:Boolean;
var styleIsColor:Boolean;
propertyName = node.@name;
if (propertyName=="backgroundColor") {
//trace("HELLO");
}
if (node.@access != "writeonly") {
if (propertyName in target) {
try {
node.@value = target[propertyName];
//node.@valueIsObject = (target[propertyName] is Object);
}
catch (error:Error) {
node.@value = error.message;
node.@valueError = true;
}
}
// styles
else if (target is IStyleClient) {
styleNotDefined = false;
styleIsColor = false;
var currentVal:Object = target.getStyle(propertyName);
// Handle situation of turning strings into Boolean values
//if (currentVal is Boolean)
//{
//if (val is String) val = (value.toLowerCase() == "true");
//}
// Handle turning standard string representations of colors
// into numberic values
if (propertyName.toLowerCase().indexOf("color") != -1 &&
propertyName.toLowerCase().indexOf("colors") == -1)
{
styleIsColor = true;
}
if (currentVal is Number && styleIsColor)
{
currentVal = StyleManager.getStyleManager(moduleFactory).getColorName(currentVal);
}
try {
if (target.getStyle(propertyName)===undefined) {
styleNotDefined = true;
node.@styleNotDefined = true;
node.@value = "";
}
else {
if (styleIsColor) {
node.@value = DisplayObjectUtils.getColorInHex(currentVal as uint, true);
}
else {
node.@value = currentVal;
}
//node.@valueIsObject = (currentVal is Object);
}
}
catch (error:Error) {
node.@value = error.message;
node.@valueError = true;
}
}
}
}
/**
* From SetAction
* */
public function getValue(propName:String):*
{
if (propName in target)
return target[propName];
else
return target.getStyle(propName);
}
public function createXMLItem(name:String, value:*, type:String, style:Boolean=false, inheriting:Boolean=false):XML {
var xml:XML = <accessor />;
xml.@name = name;
xml.@style = style;
xml.@inheriting = inheriting;
xml.@access = "readwrite";
if (type) {
xml.@type = type;
}
else {
xml.@type = InspectorUtils.getValueType(value);
}
xml.@value = value!=null || undefined ? String(value) : "";
return xml;
}
/*
protected function filterDisplayObjectChangeHandler(item:XML):Boolean {
var itemName:String = item.attribute("name") ? item.attribute("name") : "";
var value:String = searchPropertyInput.text;
if (itemName.toLowerCase().indexOf(value) != -1) {
return true;
}
return false;
}*/
/**
* Filters the property list
* if we type a period or a space at the end of the word then
* the value and the name have to match exactly (case-insensitive)
* */
/*protected function filterPropertyChangeHandler(item:XML):Boolean {
var itemName:String = item.attribute("name") ? item.attribute("name") : "";
var value:String = searchPropertyInput.text;
var valueLength:int = value.length;
var itemNameLength:int = itemName.length;
var valueLowerCase:String = value.toLowerCase();
var itemNameLowerCase:String = itemName.toLowerCase();
// show all items if search is empty
if (valueLength==0) {
return true;
}
// show custom item in case of style
if (item.@search=="true") {
// undefined
// at Function/<anonymous>()[E:\dev\4.y\frameworks\projects\framework\src\mx\utils\XMLNotifier.as:93]
// at com.flexcapacitor.views::Properties/filterPropertyChangeHandler()[/Users/monkeypunch/Documents/ProjectsGithub/Radii8/Radii8Library/src/com/flexcapacitor/views/Properties.mxml:331]
// at mx.collections::ListCollectionView/addItemsToView()[E:\dev\4.y\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:1089]
// at mx.collections::ListCollectionView/moveItemInView()[E:\dev\4.y\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:1594]
// at mx.collections::ListCollectionView/handlePropertyChangeEvents()[E:\dev\4.y\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:1421]
// at mx.collections::ListCollectionView/listChangeHandler()[E:\dev\4.y\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:1316]
// at flash.events::EventDispatcher/dispatchEventFunction()
// at flash.events::EventDispatcher/dispatchEvent()
// at mx.collections::ListCollectionView/dispatchEvent()[E:\dev\4.y\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:1024]
// at mx.collections::ListCollectionView/handlePropertyChangeEvents()[E:\dev\4.y\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:1449]
// at mx.collections::ListCollectionView/listChangeHandler()[E:\dev\4.y\frameworks\projects\framework\src\mx\collections\ListCollectionView.as:1316]
// at flash.events::EventDispatcher/dispatchEventFunction()
// at flash.events::EventDispatcher/dispatchEvent()
// at mx.collections::XMLListAdapter/itemUpdateHandler()[E:\dev\4.y\frameworks\projects\framework\src\mx\collections\XMLListAdapter.as:623]
// at mx.collections::XMLListAdapter/itemUpdated()[E:\dev\4.y\frameworks\projects\framework\src\mx\collections\XMLListAdapter.as:366]
// at mx.collections::XMLListAdapter/xmlNotification()[E:\dev\4.y\frameworks\projects\framework\src\mx\collections\XMLListAdapter.as:725]
// Seems like it's caused by a change > update > change > update loop
// we have a custom item at the top of the
//filteredPropertiesCollection.disableAutoUpdate();
item.@name = CUSTOM_ITEM_SORT_CHARACTER + value;
//filteredPropertiesCollection.enableAutoUpdate();
return true;
}
else {
//filteredPropertiesCollection.disableAutoUpdate();
}
// if we type a period or a space at the end of the word then
// the value and the name have to match exactly (case-insensitive)
if (value.lastIndexOf(".")==valueLength-1 || value.lastIndexOf(" ")==valueLength-1) {
if (itemNameLowerCase+"."==valueLowerCase || itemNameLowerCase+" "==valueLowerCase) {
return true;
}
else {
return false;
}
}
// we filter from any index
if (itemNameLowerCase.indexOf(valueLowerCase) != -1) {
return true;
}
return false;
}*/
/*protected function findPropertyChangeHandler(event:TextOperationEvent):void {
filteredPropertiesCollection.refresh();
}*/
/**
* Attempts to drill into the selected property
* */
/*protected function gridDoubleClickHandler(event:MouseEvent):void {
// if the user switches applications and the editor is not closed
// we get the text control of the editor. we don't want that so return
if (!(event.target is IDataRenderer)) return;
var instance:Object = event.currentTarget;
var itemRenderer:IDataRenderer = IDataRenderer(event.target);
var data:XML = itemRenderer.data as XML;
// this is a check for double click on dataGridColumn
if (data==null) return;
var currentValue:String = String(data.@value);
var propertyName:String = data.@name;
var type:String = String(data.@type);
var something:*;
if (propertyName in target) {
something = target[propertyName];
// if object set new target
if (!ObjectUtil.isSimple(something)) {
target = something;
radiate.dispatchTargetChangeEvent(something);
//InspectorUtils.dispatchTargetChangeEvent(something, this);
}
}
}*/
/*
protected function propertiesGrid_itemEditEndHandler(oldValue:Object, newValue:Object, editor:DefaultGridItemEditor):void {
throw new Error("Is this used?");
var instance:Object = editor.column.itemEditor;
if (instance==null) return;
var data:XML = editor.column.grid.selectedItem as XML; //event.itemRenderer.data as XML;
var currentValue:String = String(data[editor.column.dataField]);
var propertyName:String = data.@name;
var searchField:String = data.@search;
var isSearchField:Boolean = searchField=="" || searchField==null ? false : searchField as Boolean;;
var type:String = String(data.@type);
var styleAttribute:String = data.@style;
var isStyle:Boolean = styleAttribute=="" || styleAttribute==null ? false : styleAttribute as Boolean;
var newAssignedValue:*;
var isChanged:Boolean;
if (isSearchField) {
propertyName = propertyName.replace(CUSTOM_ITEM_SORT_CHARACTER, "");
isStyle = true;
}
if (target) {
newAssignedValue = TypeUtils.getTypedValue(newValue, type)
InspectorUtils.setTargetProperty(target, propertyName, newAssignedValue, type, isStyle);
}
callLater(maintainFocus);
}*/
/*
private function maintainFocus():void {
//propertiesGrid.editedItemPosition = null;
}*/
/**
* Pressing enter would retrieve the style value.
*
* This is no longer necessary since values are updated on property change events.
* */
protected function searchPropertyInput_enterHandler(event:FlexEvent):void {
var searchText:String = filterInput.text;
var item:XML;
return; // DISABLE FOR NOW
if (filteredPropertiesCollection.length==1) {
item = filteredPropertiesCollection.getItemAt(0) as XML;
if (item && item.nodeName()=="metadata") {
item.@value = target is UIComponent ? UIComponent(target).getStyle(searchText) : "";
filteredPropertiesCollection.itemUpdated(item, "@value");
filteredPropertiesCollection.refresh();
}
}
}
/**
*
* */
public function clear():void {
// on application getting this error:
//RangeError: Index '-1' specified is out of bounds.???
/*allItems.enableAutoUpdate();
Radiate.log.info("1Length:"+allItems.length);
allItems.refresh();
Radiate.log.info("2Length:"+allItems.length);
allItems.sort = null;
Radiate.log.info("3Length:"+allItems.length);*/
allItems.removeAll();
allItems.refresh();
inheritingStylesList.removeAll();
nonInheritingStylesList.removeAll();
objectPropertiesList.removeAll();
filterInput.text = "";
targetValueText.text = "";
dataProviderProperties = new XMLListCollection();
}
/*
protected function saveSessionHandler(event:GridItemEditorEvent):void {
//trace("Save session");
var item:XML = propertiesGrid.dataProvider.getItemAt(event.rowIndex) as XML;
var value:String = String(item.@value);
var property:String = String(item.@name);
//trace("New Value= ", value);
Radiate.setProperty(target, property, value);
}
protected function startSessionHandler(event:GridItemEditorEvent):void {
//trace("Start session");
}
protected function savingSessionHandler(event:GridItemEditorEvent):void
{
//trace("Saving session");
}
protected function startingSessionHandler(event:GridItemEditorEvent):void
{
//trace("Starting session");
var item:XML = propertiesGrid.dataProvider.getItemAt(event.rowIndex) as XML;
if (String(item.@access).indexOf("write")<0) {
// event.preventDefault();
// NOTIFY OF READ ONLY
}
}*/
protected function propertyChangeHandler(event:RadiateEvent):void
{
// radiate property change event
updateDataGridPropertyValue(event.changes);
//updateSubComponentsValue();
}
protected function targetValueText_keyUpHandler(event:KeyboardEvent):void {
if (event.keyCode==Keyboard.ENTER && !event.shiftKey) {
setTargetValue();
event.preventDefault();
}
else if (event.keyCode==Keyboard.ESCAPE) {
var item:XML = propertiesGrid.selectedItem as XML;
var value:String = String(item.@value);
targetValueText.text = value;
event.preventDefault();
}
}
/**
* Prevents line breaks
* */
protected function targetValueText_changingHandler(event:TextOperationEvent):void {
// prevent line breaks
if(event.operation is SplitParagraphOperation) {
event.preventDefault();
}
}
protected function colorChooser_changeHandler(event:Event):void {
setTargetValue();
}
/**
*
* */
protected function propertiesGrid_selectionChangeHandler(event:GridSelectionEvent = null):void {
updateSubComponentsValue();
}
protected function formatCombobox_changeHandler(event:IndexChangeEvent):void {
setTargetValue();
}
/**
* Update the property value in the datagrid when it changes
* without reloading all the properties.
* */
public function updateDataGridPropertyValue(changes:Array):void {
var length:int = allItems.length;
var itemList:XMLList;
var propertyName:String;
if (length>0 && changes.length>0) {
//itemList = allItems.source;
for each (var node:XML in allItems) {
propertyName = node.@name;
// TypeError: Error #1034: Type Coercion failed:
// cannot convert mx.states::AddItems@1132558c9 to mx.effects.effectClasses.PropertyChanges.
// for each (var change:PropertyChanges in changes) {
for each (var change:Object in changes) {
if (change is mx.effects.effectClasses.PropertyChanges) {
var changeObject:Object = change.end;
for (var property:String in changeObject) {
if (propertyName==property) {
//node.@value = changeObject[property] ? Object(changeObject[property]) : "";
// we probably cleared a style
if (changeObject[property]===undefined) {
setValueOnNode(node);
}
else {
setValueOnNode(node);
//node.@value = Object(changeObject[property]).toString();
filteredPropertiesCollection.itemUpdated(node, "@value");
filteredPropertiesCollection.refresh();
}
}
}
}
}
}
}
}
/**
* Get the value from the datagrid and
* show the correct sub component and
* set it's value.
* */
public function updateSubComponentsValue():void {
var item:XML = propertiesGrid.selectedItem as XML;
var actualValue:*;
var isStyle:Boolean;
var type:String;
var enumeration:Array;
var format:String;
var propertyObject:MetaData;
var property:String;
var value:String;
var nodename:String;
if (settingProperties) return;
if (item) {
nodename = item.name();
if (nodename=="metadata") {
isStyle = true;
propertyObject = new StyleMetaData(item, target);
}
else if (nodename=="accessor") {
propertyObject = new AccessorMetaData(item, target);
}
else {
propertyObject = new MetaData(item, target);
}
value = String(propertyObject.value);
property = propertyObject.name;
type = propertyObject.type;
format = propertyObject.format;
enumeration = propertyObject.enumeration;
if (type=="Boolean") {
currentState = BOOLEAN_STATE;
booleanEnabledRadioButton.selected = false;
booleanDisabledRadioButton.selected = false;
if (value=="true") {
booleanEnabledRadioButton.selected = true;
}
else if (value=="false") {
booleanDisabledRadioButton.selected = true;
}
booleanClearStyleButton.visible = isStyle;
}
else if (format=="Color") {
currentState = COLOR_STATE;
colorChooser.selectedColor = value;
targetValueText.text = value;
clearColorButton.visible = isStyle;
}
else if (enumeration && enumeration.length>0) {
currentState = FORMATS_STATE;
formatsCollection.source = enumeration;
formatCombobox.selectedItem = propertyObject.value;
targetValueText.text = value;
clearFormatButton.visible = isStyle;
}
else if (type=="Number") {
currentState = NUMBER_STATE;
numericStepper.value = propertyObject.value;
if (!isNaN(propertyObject.minValue)) {
numericStepper.minimum = propertyObject.minValue;
}
else {
numericStepper.minimum = Number(int.MIN_VALUE);
}
if (!isNaN(propertyObject.maxValue)) {
numericStepper.maximum = propertyObject.maxValue;
}
else {
numericStepper.maximum = Number(int.MAX_VALUE);
}
if (property.indexOf("lpha")!=-1) {
numericStepper.stepSize = .1;
if (isNaN(propertyObject.minValue)) {
numericStepper.minimum = 0;
}
if (isNaN(propertyObject.maxValue)) {
numericStepper.maximum = 1;
}
}
else {
numericStepper.stepSize = 1;
}
targetValueText.text = value;
booleanNumberStyleButton.visible = isStyle;
}
else {
actualValue = Radiate.getTypedValue(targetValueText.text, type);
currentState = TEXT_STATE;
targetValueText.text= propertyObject.value;
booleanTextStyleButton.visible = isStyle;
}
radiate.dispatchPropertySelectedEvent(property, propertyObject);
}
else {
currentState = NONE_STATE;
}
}
public var settingProperties:Boolean;
/**
* Set the property to the new value
* */
public function setTargetValue(clearStyle:Boolean = false):void {
var item:XML = propertiesGrid.selectedItem as XML;
var actualValue:*;
var isStyle:int;
var styleIsColor:Boolean;
var valueSuccessfullyApplied:Boolean;
var propertyObject:MetaData;
var type:String;
var enumeration:Array;
var format:String;
var inherit:Boolean;
var property:String;
var value:String;
var nodename:String;
var access:String;
if (item) {
nodename = item.name();
if (nodename=="metadata") {
isStyle = 1;
propertyObject = new StyleMetaData(item, target);
}
else if (nodename=="accessor") {
propertyObject = new AccessorMetaData(item, target);
access = AccessorMetaData(propertyObject).access;
}
else {
propertyObject = new MetaData(item, target);
}
// since we try to capture values for history
// ReferenceError: Error #1077: Illegal read of write-only property cacheHeuristic on application.
if (access=="writeonly") {
return;
}
value = String(propertyObject.value);
property = propertyObject.name;
type = propertyObject.type;
format = propertyObject.format;
enumeration = propertyObject.enumeration;
/*
var type:Type = Type.forInstance(target);
var metadataItems:Array = type.metadata;
var b:Array = type.getMetadata("Style");
var metadata:Metadata = Metadata(metadataItems[0]);
var hasFormat:Boolean = metadata.hasArgumentWithKey("type");
var o:MetadataArgument = metadata.getArgument("type");
var xc:XML = MetadataUtils.getFromObject(target);*/
// BEGIN // COPY CHANGES TO OTHER UPDATE FUNCTION
/*var value:String = String(item.@value);
var property:String = String(item.@name);
var args:XMLList = item.arg;
var metadata:Metadata = new Metadata(name);
var argument:MetadataArgument;
for each (var arg:XML in args) {
argument = new MetadataArgument(arg.@key, arg.@value);
metadata.arguments.push(argument);
}
var propertyType:String;
if (isStyle==1 && metadata.hasArgumentWithKey("type")) {
propertyType = metadata.getArgument("type").value;
}
else {
propertyType = String(item.@type);
}
var enumeration:Array;
if (isStyle==1 && metadata.hasArgumentWithKey("enumeration")) {
enumeration = metadata.getArgument("enumeration").value.split(",");
}
var format:String;
if (isStyle==1 && metadata.hasArgumentWithKey("format")) {
format = metadata.getArgument("format").value;
}*/
// END
//var hasAttribute:Boolean = XML(item).attribute("style").length()>0;
//var valuX:String = String(XML(item).attribute("style")).toString();
//var valuX2:String = String(item.@style).toString();
// this is not a reliable way to compare if the value has changed
// probably remove it since setProperty and setStyle will double check
// if value has really changed
//if (value!=targetValueText.text) {
if (type=="Boolean") {
if (isStyle) {
actualValue = booleanGroup.selection==null ? undefined : booleanGroup.selection==booleanEnabledRadioButton;
}
else {
actualValue = booleanGroup.selection==booleanEnabledRadioButton;
}
}
// let SetAction convert 0xFF, #ff and red, ReD
else if (format=="Color") {
actualValue = colorChooser.selectedColor;
}
else if (type=="Number") {
actualValue = numericStepper.value;
if (property.indexOf("lpha")!=-1) {//round down to two places
actualValue = int(actualValue*100)/100;
}
}
else if (type=="String" && enumeration && enumeration.length>0) {
actualValue = formatCombobox.selectedItem;
}
else {
actualValue = Radiate.getTypedValue(targetValueText.text, type);
}
settingProperties = true;
if (isStyle) {
if (clearStyle) {
valueSuccessfullyApplied = Radiate.clearStyle(target, property);
}
else {
valueSuccessfullyApplied = Radiate.setStyle(target, property, actualValue);
}
}
else {
valueSuccessfullyApplied = Radiate.setProperty(target, property, actualValue);
}
}
settingProperties = false;
}
/**
* Set the property to the new value
* */
public function getEditorType(item:XML):String {
var actualValue:*;
var isStyle:Boolean;
var type:String;
var enumeration:Array;
var format:String;
var propertyObject:MetaData;
var property:String;
var value:String;
var nodename:String;
if (item) {
nodename = item.name();
if (nodename=="metadata") {
isStyle = true;
propertyObject = new StyleMetaData(item, target);
}
else if (nodename=="accessor") {
propertyObject = new AccessorMetaData(item, target);
}
else {
propertyObject = new MetaData(item, target);
}
value = String(propertyObject.value);
property = propertyObject.name;
type = propertyObject.type;
format = propertyObject.format;
enumeration = propertyObject.enumeration;
if (type=="Boolean") {
return BOOLEAN_STATE;
}
else if (format=="Color") {
return COLOR_STATE;
}
else if (enumeration && enumeration.length>0) {
return FORMATS_STATE;
}
else if (type=="Number") {
return NUMBER_STATE;
}
else {
return TEXT_STATE;
}
}
return NONE_STATE;
}
protected function clearStyleButton_clickHandler(event:MouseEvent):void {
setTargetValue(true);
}
/**
* Move from search text input to properties grid on down arrow key
* */
protected function filterInput_keyUpHandler(event:KeyboardEvent):void {
if (event.keyCode==Keyboard.DOWN) {
propertiesGrid.setFocus();
if (propertiesGrid.selectedIndex ==-1) {
propertiesGrid.setSelectedIndex(0);
}
}
}
/**
* Move from properties grid to search text input on up arrow key
* */
protected function propertiesGrid_keyUpHandler(event:KeyboardEvent):void {
/*
if (event.keyCode==Keyboard.UP) {
if (propertiesGrid.selectedIndex ==0 ||
filteredPropertiesCollection.length==0) {
filterInput.setFocus();
//filterInput.selectRange(filterInput.text.length,filterInput.text.length);
}
}
else if (event.keyCode==Keyboard.DOWN) {
if (propertiesGrid.selectedIndex==filteredPropertiesCollection.length-1) {
if (!moveDownOnNextDownArrow) {
moveDownOnNextDownArrow = true;
return;
}
moveDownOnNextDownArrow = false;
focusManager.setFocus(focusManager.getNextFocusManagerComponent());
//filterInput.selectRange(filterInput.text.length,filterInput.text.length);
}
}*/
}
/**
*
* */
protected var moveDownOnNextDownArrow:Boolean;
/**
* Updated when the boolean change happens
* */
protected function booleanGroup_changeHandler(event:Event):void {
setTargetValue();
}
/**
*
* */
protected function numericStepper_changeHandler(event:Event):void {
setTargetValue();
}
/**
* Format numeric stepper
* Trim down to 2 decimal places.
* */
public function formatNumericStepper(value:Number):String {
return String(int(value*100)/100);
}
protected function changeStatesButton_clickHandler(event:MouseEvent):void {
if (currentState!=PROPERTIES_STATE) {
currentState = PROPERTIES_STATE;
}
else {
currentState = NONE_STATE;
}
}
public function labelFunction(data:Object, column:GridColumn):String {
StringUtils.prettifyCamelCase(data.@name);
return "";
}
public function propertiesGridItemRendererFunction(item:Object, column:GridColumn):IFactory {
// Create a Class Factory variable
var rendererFactory:ClassFactory;
var type:Class;
var editorState:String = getEditorType(XML(item));
switch (editorState) {
case BOOLEAN_STATE:
type = BooleanRenderer;
break;
case COLOR_STATE:
type = ColorPickerRenderer;
break;
case FORMATS_STATE:
type = ComboBoxRenderer;
break;
case NUMBER_STATE:
type = NumberRenderer;
break;
case TEXT_STATE:
type = com.flexcapacitor.views.renderers.TextRenderer;
break;
default:
type = com.flexcapacitor.views.renderers.TextRenderer;
}
rendererFactory = new ClassFactory(type);
//rendererFactory.properties = { MyBackgroundColor: varDefaultBackgroundColor, MyForegroundColor: varDefaultForegroundColor };
return rendererFactory;
}
]]>
</fx:Script>
<fx:Declarations>
<s:NumberFormatter id="formatter"
useGrouping="false"/>
<s:ArrayCollection id="filteredPropertiesCollection"
list="{dataProviderProperties}"/>
<s:ArrayCollection id="formatsCollection"/>
<fx:Array id="filterArray">
<fx:String>accentColor</fx:String>
<fx:String>visible</fx:String>
</fx:Array>
<!-- SORT BY NAME -->
<handlers:EventHandler eventName="creationComplete" >
<collections:SortCollection target="{filteredPropertiesCollection}"
fields="{['@name']}" />
</handlers:EventHandler>
<!-- FILTER BY NAME -->
<handlers:EventHandler eventName="change"
targets="{[filterInput,showAllItemsOnEmpty,caseSensitive,searchAtStart]}"
>
<collections:FilterCollection target="{filteredPropertiesCollection}"
source="{filterInput}"
sourcePropertyName="text"
fieldName="@name"
showAllItemsOnEmpty="{showAllItemsOnEmpty.selected}"
caseSensitive="{caseSensitive.selected}"
searchAtStart="{searchAtStart.selected}"
/>
</handlers:EventHandler>
<s:RadioButtonGroup id="booleanGroup" change="booleanGroup_changeHandler(event)"/>
</fx:Declarations>
<s:states>
<s:State name="none"/>
<s:State name="number"/>
<s:State name="boolean"/>
<s:State name="int"/>
<s:State name="text"/>
<s:State name="color"/>
<s:State name="formats"/>
<s:State name="properties"/>
<s:State name="dynamicInspector"/>
</s:states>
<mx:VDividedBox id="verticalContainer"
width="100%" height="100%"
top="0"
left="8"
right="8"
bottom="4"
>
<s:Group height="24"
minHeight="24"
maxHeight="60"
width="100%"
excludeFrom="properties"
>
<c:SearchTextInput id="filterInput"
left="0" right="0" top="0"
width="100%"
minWidth="60"
prompt="Search"
styleName="inputStyles"
enter="searchPropertyInput_enterHandler(event)"
keyUp="filterInput_keyUpHandler(event)"
/>
<s:HGroup width="100%" top="32" left="4" verticalAlign="baseline">
<s:CheckBox id="showAllItemsOnEmpty" label="All" selected="true"/>
<s:CheckBox id="caseSensitive" label="Case" selected="false"/>
<s:CheckBox id="searchAtStart" label="At start" selected="false"/>
<s:Spacer width="100%"/>
<s:Label text="Results: {filteredPropertiesCollection.length}"/>
</s:HGroup>
</s:Group>
<!-- PROPERTIES GRID -->
<c:DataGrid id="propertiesGrid"
borderAlpha="0.2"
height="100%"
width="100%"
minWidth="80"
minHeight="48"
rowHeight="26"
variableRowHeight="false"
dataProvider="{filteredPropertiesCollection}"
selectionChange="propertiesGrid_selectionChangeHandler(event)"
click="propertiesGrid_selectionChangeHandler()"
keyUp="propertiesGrid_keyUpHandler(event)"
excludeFrom="properties"
>
<!-- gridItemEditorSessionStart="startSessionHandler(event)"
gridItemEditorSessionSave="saveSessionHandler(event)"
gridItemEditorSessionSaving="savingSessionHandler(event)"
gridItemEditorSessionStarting="startingSessionHandler(event)"-->
<!--<s:layout>
<s:VerticalLayout rowHeight="22" />
</s:layout>-->
<c:columns>
<s:ArrayCollection>
<s:GridColumn dataField="@type"
width="14"
editable="false"
resizable="true"
headerText="PROPERTY">
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer minHeight="14" >
<s:BitmapImage top="8" left="3" source="{Radii8LibraryAssets.propertyIcon}"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
<s:GridColumn dataField="@name"
minWidth="50"
editable="false"
resizable="true"
headerText="PROPERTY" >
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer minHeight="14">
<s:Label id="labelDisplay"
verticalCenter="1" left="0" right="0" top="0" bottom="0"
fontSize="12"
paddingTop="3"
paddingBottom="3"
paddingLeft="3"
paddingRight="3"
textAlign="start"
verticalAlign="middle"
width="100%"
maxDisplayedLines="1"
showTruncationTip="true"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
<s:GridColumn dataField="@value"
minWidth="50"
editable="false"
resizable="true"
headerText="VALUE"
itemRenderer="com.flexcapacitor.views.renderers.PropertiesGridValuesRenderer">
</s:GridColumn>
<!--<s:GridColumn headerText=""
minWidth="30"
width="30"
editable="false"
labelFunction="{function(data:Object, column:GridColumn):String{return'...';}}">
</s:GridColumn>-->
<!--<s:GridColumn width="50"
dataField="@type" editable="false"
headerText="type"/>
<s:GridColumn width="35"
dataField="@style" editable="false"
headerText="Style"/>
<s:GridColumn width="35"
dataField="@inheriting" editable="false"
headerText="Inheriting"/>-->
</s:ArrayCollection>
</c:columns>
</c:DataGrid>
<!-- FORM -->
<!-- PROPERTIES LIST -->
<c:DataGrid id="propertiesList"
borderAlpha="0"
height="100%"
width="100%"
minWidth="80"
minHeight="48"
rowHeight="26"
variableRowHeight="false"
selectionColor="#ffffff"
rollOverColor="#ffffff"
dataProvider="{filteredPropertiesCollection}"
includeIn="properties"
>
<c:columns>
<s:ArrayCollection>
<s:GridColumn dataField="@name"
minWidth="50"
editable="false"
resizable="true"
headerText="PROPERTY">
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer minHeight="14">
<s:Label id="labelDisplay"
verticalCenter="1" left="0" right="0" top="0" bottom="0"
fontSize="12"
paddingTop="3"
paddingBottom="3"
paddingLeft="3"
paddingRight="12"
textAlign="right"
verticalAlign="middle"
width="100%"
maxDisplayedLines="1"
showTruncationTip="true"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
<s:GridColumn dataField="@value"
minWidth="50"
editable="false"
resizable="true"
headerText="VALUE"
itemRendererFunction="propertiesGridItemRendererFunction"
itemRenderer="com.flexcapacitor.views.renderers.ColorPickerRenderer"
>
<!-- labelFunction="labelFunction"-->
</s:GridColumn>
<s:GridColumn minWidth="10"
editable="false"
resizable="false"
headerText=""
>
</s:GridColumn>
<!--<s:GridColumn headerText=""
minWidth="30"
width="30"
editable="false"
labelFunction="{function(data:Object, column:GridColumn):String{return'...';}}">
</s:GridColumn>-->
<!--<s:GridColumn width="50"
dataField="@type" editable="false"
headerText="type"/>
<s:GridColumn width="35"
dataField="@style" editable="false"
headerText="Style"/>
<s:GridColumn width="35"
dataField="@inheriting" editable="false"
headerText="Inheriting"/>-->
</s:ArrayCollection>
</c:columns>
</c:DataGrid>
<s:Group height="28" minHeight="28" width="100%"
clipAndEnableScrolling="true">
<!-- TEXT OPTION -->
<s:Group width="100%"
height="100%"
includeIn="text"
itemCreationPolicy="immediate"
>
<s:TextArea id="targetValueText"
width="100%"
height="100%"
borderAlpha="0.2"
skinClass="spark.skins.spark.TextAreaSkin"
keyUp="targetValueText_keyUpHandler(event)"
changing="targetValueText_changingHandler(event)"
>
</s:TextArea>
<s:Button id="booleanTextStyleButton"
skinClass="com.flexcapacitor.skins.ClearButton"
click="clearStyleButton_clickHandler(event)"
right="0"
verticalCenter="0"/>
</s:Group>
<!-- COLOR OPTION -->
<s:Group width="100%"
height="24"
includeIn="color"
itemCreationPolicy="immediate"
>
<components:ColorChooser id="colorChooser" returnType="hexidecimalHash"
change="colorChooser_changeHandler(event)"/>
<s:Button id="clearColorButton"
skinClass="com.flexcapacitor.skins.ClearButton"
click="clearStyleButton_clickHandler(event)"
right="0"
verticalCenter="0"/>
</s:Group>
<!-- CHOICES OPTION -->
<s:HGroup width="100%"
height="24"
includeIn="formats"
itemCreationPolicy="immediate"
>
<s:DropDownList id="formatCombobox"
width="100%"
change="formatCombobox_changeHandler(event)"
dataProvider="{formatsCollection}"/>
<s:Button id="clearFormatButton"
skinClass="com.flexcapacitor.skins.ClearButton"
click="clearStyleButton_clickHandler(event)"
right="0"
verticalCenter="0"/>
</s:HGroup>
<!-- NUMBERS -->
<s:HGroup width="100%"
height="24"
includeIn="number"
itemCreationPolicy="immediate"
>
<s:NumericStepper id="numericStepper"
width="100%"
valueFormatFunction="formatNumericStepper"
change="numericStepper_changeHandler(event)"/>
<s:Button id="booleanNumberStyleButton"
skinClass="com.flexcapacitor.skins.ClearButton"
click="clearStyleButton_clickHandler(event)"
right="0"
verticalCenter="0"
/>
</s:HGroup>
<!-- BOOLEAN OPTIONS -->
<s:HGroup width="100%"
height="24"
minHeight="24"
includeIn="boolean"
itemCreationPolicy="immediate"
verticalAlign="middle"
>
<s:RadioButton id="booleanEnabledRadioButton"
label="True"
group="{booleanGroup}"
/>
<s:RadioButton id="booleanDisabledRadioButton"
label="False"
group="{booleanGroup}"
/>
<s:Spacer width="100%"/>
<s:Button id="booleanClearStyleButton"
skinClass="com.flexcapacitor.skins.ClearButton"
click="clearStyleButton_clickHandler(event)"
/>
</s:HGroup>
</s:Group>
</mx:VDividedBox>
</s:Group>