http://git-wip-us.apache.org/repos/asf/flex-radii8/blob/f370bfcf/Radii8Library/src/com/flexcapacitor/views/panels/EffectsInspector.mxml ---------------------------------------------------------------------- diff --git a/Radii8Library/src/com/flexcapacitor/views/panels/EffectsInspector.mxml b/Radii8Library/src/com/flexcapacitor/views/panels/EffectsInspector.mxml new file mode 100644 index 0000000..7922a41 --- /dev/null +++ b/Radii8Library/src/com/flexcapacitor/views/panels/EffectsInspector.mxml @@ -0,0 +1,951 @@ +<?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:controls="com.flexcapacitor.controls.*" + + width="400" + height="200" + > + + <fx:Script> + <![CDATA[ + import mx.core.mx_internal; + + use namespace mx_internal; + + [Bindable] + public var openAllBranchesAtStart:Boolean = true; + + + public function set targets(value:Array):void { + + effectsCollection.source = value; + effectsCollection.refresh(); + + if (openAllBranchesAtStart) { + callLater(openAllBranches); + } + + } + + public function openAllBranches():void { + var items:Array = effectsCollection.toArray(); + + effectsTree.validateNow(); + effectsTree.collectionLength; + + if (items) { + var length:int = items.length; + + for (var i:int; i < length; i++) { + effectsTree.expandItem(items[i], true); + // effectsTree.expandChildrenOf(items[i], true); + effectsTree.validateNow(); + } + } + } + + protected function effectsTree_enterFrameHandler(event:Event):void { + var newTreeHeight:int = effectsTree.collectionLength*effectsTree.rowHeight; + newTreeHeight = Math.max(newTreeHeight, effectsTree.minHeight); + //trace("new tree height=" + newTreeHeight); + + if (newTreeHeight!=treeHeight) { + treeHeight = Math.max(newTreeHeight, effectsTree.minHeight); + trace("updating tree height"); + } + } + + ]]> + </fx:Script> + + + <fx:Declarations> + <s:ArrayCollection id="effectsCollection"/> + <fx:int id="treeHeight">200</fx:int> + </fx:Declarations> + + + <s:VGroup width="100%" height="100%"> + + <!--<s:HGroup verticalAlign="middle" width="100%"> + <s:Spacer width="100%"/> + <s:Label text="Total ({effectsCollection.length})"/> + </s:HGroup>--> + + <mx:Tree id="effectsTree" + width="100%" + minHeight="100" + height="{treeHeight}" + borderVisible="false" + borderColor="#CCCCCC" + dataProvider="{effectsCollection}" + rollOverColor="#FFFFFF88" + selectionColor="#FFFFFFaa" + enterFrame="effectsTree_enterFrameHandler(event)" + rowHeight="30"> + + <mx:itemRenderer> + <fx:Component> + <s:MXTreeItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" + xmlns:s="library://ns.adobe.com/flex/spark" + xmlns:mx="library://ns.adobe.com/flex/mx" + autoDrawBackground="false" + > + + <fx:Script> + <![CDATA[ + import com.flexcapacitor.utils.StringUtils; + + import mx.core.mx_internal; + import mx.effects.CompositeEffect; + import mx.effects.Effect; + import mx.effects.Pause; + import mx.effects.Sequence; + import mx.events.EffectEvent; + + use namespace mx_internal; + + /** + * + * */ + [Bindable] + private var item:Effect; + + /** + * + * */ + [Bindable] + private var compositeEffect:CompositeEffect; + + /** + * + * */ + [Bindable] + private var sequenceEffect:Sequence; + + /** + * + * */ + [Bindable] + private var isPaused:Boolean; + + /** + * + * */ + private var effectStartCount:int; + + /** + * + * */ + private var effectUpdateCount:int; + + /** + * + * */ + private var effectStopCount:int; + + /** + * + * */ + private var effectRepeatCount:int; + + /** + * + * */ + private var effectEndCount:int; + + /** + * + * */ + private var effectEnabledAlpha:int = 1; + + /** + * + * */ + private var effectDisabledAlpha:int = .9; + + /** + * + * */ + public var playedOnceCharacter:String = " "; + + /** + * + * */ + public var eventPlayedColor:Number = 0x00FF00; + + /** + * + * */ + public var eventColor:Number = 0x888888; + + /** + * Show playhead time label + * */ + public var showPlayheadTimeLabel:Boolean = true; + + /** + * Show duration time label + * */ + public var showDurationTimeLabel:Boolean = true; + + /** + * + * */ + override public function set data(value:Object):void { + super.data = value; + + + // remove previous listeners + if (item) { + removeListeners(item); + compositeEffect = null; + sequenceEffect = null; + item = null; + } + + if (value==null) { + return; + } + + if (value is Effect) { + item = value as Effect; + } + + if (value is CompositeEffect) { + compositeEffect = value as CompositeEffect; + } + + if (value is Sequence) { + sequenceEffect = value as Sequence; + } + + nameLabel.text = item.className; + + try { + var idname:String = item["id"]; + if (idname) { + nameLabel.text = item.className + "." + idname; + } + trace("************found id=" + idname); + } + catch (e:*) { + + } + + resetEffectEvents(); + + // remove these at some point + addListeners(item); + + updateEffectStatus(); + } + + /** + * Updates the controls as the effect or effect parent is playing + * */ + private function updateEffectStatus(event:EffectEvent=null):void { + var type:String = event ? event.type : ""; + var duration:int = compositeEffect ? compositeEffect.compositeDuration : item.duration; + var playheadTime:int = item.playheadTime; + var itemPlaying:Boolean = item.isPlaying; + var ancestorPlaying:Boolean = isAncestorPlaying(item); + var ancesterRoot:Effect = getItemRoot(item); + + //playingLabel.text = ""; + + updateEffectEventCount(type); + + + // if composite or pause listen for start and end of child effects + if (compositeEffect || item is Pause) { + + if (type==EffectEvent.EFFECT_START) { + addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true); + } + + if (type==EffectEvent.EFFECT_END) { + removeEventListener(Event.ENTER_FRAME, enterFrameHandler); + } + } + else if (type==EffectEvent.EFFECT_START) { + if (item.parentCompositeEffect && + item.parentCompositeEffect.isPlaying) { + //resetEffectEvents(); + } + } + + + + // get position in time + playheadTimeLabel.text = sanitizeTime(playheadTime); + durationLabel.text = sanitizeTime(duration, "seconds", 1); + + + // show or hide playhead time label + if (showPlayheadTimeLabel) { + if (itemPlaying) { + playheadTimeLabel.visible = true; + } + else { + playheadTimeLabel.visible = false; + } + } + else { + playheadTimeLabel.visible = false; + } + + + // show or hide duration time label + if (showDurationTimeLabel) { + if (duration==0) { + durationLabel.visible = false; + } + else { + durationLabel.visible = true; + } + } + else { + durationLabel.visible = false; + } + + + // update divider position + if (itemPlaying) { + var position:int = (effectDurationRect.width*item.playheadTime)/duration; + + if (isNaN(position)) { + position = 0; + } + + divider.x = position - divider.width/2; + divider.visible = true; + } + else { + divider.visible = false; + } + + + // check if need to show playing indicator + if (itemPlaying) { + playingIndicator.visible = true; + + if (isPaused) { + //playingLabel.text = "Paused"; + playImage.enableBlink = true; + } + else { + //playingLabel.text = "Playing"; + playImage.enableBlink = false; + } + + // if part of a composite effect that is also playing then disable controls + if (ancestorPlaying) { + + if (playImage.source != Radii8LibraryEffectsAssets.pause) { + playImage.source = Radii8LibraryEffectsAssets.pause; + } + + //playingLabel.text += " (parent playing)"; + playImage.enabled = false; + reverseImage.enabled = false; + stopImage.enabled = false; + endImage.enabled = false; + } + else { + + if (playImage.source != Radii8LibraryEffectsAssets.pause) { + playImage.source = Radii8LibraryEffectsAssets.pause; + } + + playImage.enabled = true; + reverseImage.enabled = true; + stopImage.enabled = true; + endImage.enabled = true; + } + + // select list item? + //ListBase(treeListData.owner).selectedItem = data; + } + + // effect is not playing + else { + + playingIndicator.visible = false; + + // if part of a composite effect that is playing then disable controls + if (ancestorPlaying) { + + if (playImage.source != Radii8LibraryEffectsAssets.play) { + playImage.source = Radii8LibraryEffectsAssets.play; + } + + //playingLabel.text += "(parent playing)"; + + stopImage.enabled = false; + endImage.enabled = false; + playImage.enableBlink = false; + playImage.enabled = false; + reverseImage.enabled = false; + } + // no parent effect or parent effect is not playing + else { + + if (playImage.source != Radii8LibraryEffectsAssets.play) { + playImage.source = Radii8LibraryEffectsAssets.play; + } + + //playingLabel.text += "Idle"; + stopImage.enabled = false; + endImage.enabled = false; + playImage.enableBlink = false; + playImage.enabled = true; + reverseImage.enabled = true; + } + } + + + + // effect ended - enable controls or wait and then enable controls + if (event && event.type==EffectEvent.EFFECT_END) { + //trace("effect type=" + item.className); + //trace("effect end"); + + if (sequenceEffect && sequenceEffect.children.length>4) { + //trace("more than one"); + } + + // set back to play icon + if (playImage.source != Radii8LibraryEffectsAssets.play) { + playImage.source = Radii8LibraryEffectsAssets.play; + } + + isPaused = false; + playImage.enableBlink = false; + playingIndicator.visible = false; + + // if part of a composite effect that is also playing then disable controls until it's done + if (ancestorPlaying) { + + // add listeners to ancestor effects to know when to enable controls again + if (item.parentCompositeEffect.isPlaying) { + item.parentCompositeEffect.addEventListener(EffectEvent.EFFECT_END, enterFrameHandler); + //playImage.enabled = true; + } + else { + item.parentCompositeEffect.removeEventListener(EffectEvent.EFFECT_END, enterFrameHandler); + playImage.enabled = true; + reverseImage.enabled = true; + } + } + + // no ancestor playing enable controls + else { + playImage.enabled = true; + reverseImage.enabled = true; + } + + } + + //playingLabel.text += " " + type; + } + + /** + * + * */ + protected function playImage_clickHandler(event:MouseEvent):void { + if (item.parentCompositeEffect) { + //item.pause(); + } + + if (item.isPlaying) { + + if (!isPaused) { + item.pause(); + isPaused = true; + } + else { + item.resume(); + isPaused = false; + } + } + else { + resetEffectEvents(); + item.play(); + } + + } + + /** + * + * */ + protected function stopImage_clickHandler(event:MouseEvent):void { + item.stop(); + } + + /** + * + * */ + protected function endImage_clickHandler(event:MouseEvent):void { + item.end(); + } + + /** + * + * */ + protected function reverseImage_clickHandler(event:MouseEvent):void { + if (item.isPlaying) { + item.reverse(); + } + else { + resetEffectEvents(); + item.play(null, true); + } + } + + /** + * + * */ + public function sanitizeTime(value:Number, units:String = "seconds", places:int = 2):String { + + if (units=="seconds") { + value = value / 1000; + } + + return StringUtils.padLeft(value.toFixed(places), places, "0", true); + } + + /** + * + * */ + private function enterFrameHandler(event:Event):void { + updateEffectStatus(); + //trace("enterframe"); + } + + /** + * + * */ + private function resetEffectEvents():void { + + /* effectStartRect.alpha = effectDisabledAlpha; + effectUpdateRect.alpha = effectDisabledAlpha; + effectStopRect.alpha = effectDisabledAlpha; + effectRepeatRect.alpha = effectDisabledAlpha; + effectEndRect.alpha = effectDisabledAlpha; */ + + effectStartLabel.text = ""; + effectUpdateLabel.text = ""; + effectEndLabel.text = ""; + effectStopLabel.text = ""; + effectRepeatLabel.text = ""; + effectDurationRectFill.color = eventColor; + + effectStartLabel.setStyle("backgroundColor", eventColor); + effectUpdateLabel.setStyle("backgroundColor", eventColor); + effectEndLabel.setStyle("backgroundColor", eventColor); + effectStopLabel.setStyle("backgroundColor", eventColor); + effectRepeatLabel.setStyle("backgroundColor", eventColor); + + effectStartCount = 0; + effectUpdateCount = 0; + effectStopCount = 0; + effectRepeatCount = 0; + effectEndCount = 0; + } + + /** + * + * */ + private function updateEffectEventCount(type:String):void { + + if (type==EffectEvent.EFFECT_START) { + effectStartCount++; + effectStartLabel.text = effectStartCount>1 ? String(effectStartCount) : playedOnceCharacter; + effectStartLabel.setStyle("backgroundColor", eventPlayedColor); + } + else if (type==EffectEvent.EFFECT_UPDATE) { + effectUpdateCount++; + //effectUpdateLabel.text = effectUpdateCount>1 ? String(effectUpdateCount) : playedOnceCharacter; + effectUpdateLabel.text = effectUpdateCount>1000 ? String(effectUpdateCount) : playedOnceCharacter; + effectUpdateLabel.setStyle("backgroundColor", eventPlayedColor); + } + else if (type==EffectEvent.EFFECT_STOP) { + effectStopCount++; + effectStopLabel.text = effectStopCount>1 ? String(effectStopCount) : playedOnceCharacter; + effectStopLabel.setStyle("backgroundColor", eventPlayedColor); + } + else if (type==EffectEvent.EFFECT_REPEAT) { + effectRepeatCount++; + effectRepeatLabel.text = effectRepeatCount>1 ? String(effectRepeatCount) : playedOnceCharacter; + effectRepeatLabel.setStyle("backgroundColor", eventPlayedColor); + } + else if (type==EffectEvent.EFFECT_END) { + effectEndCount++; + effectEndLabel.text = effectEndCount>1 ? String(effectEndCount) : playedOnceCharacter; + effectEndLabel.setStyle("backgroundColor", eventPlayedColor); + } + } + + /** + * + * */ + private function removeListeners(item:Effect):void { + + item.removeEventListener(EffectEvent.EFFECT_START, updateEffectStatus); + item.removeEventListener(EffectEvent.EFFECT_UPDATE, updateEffectStatus); + item.removeEventListener(EffectEvent.EFFECT_END, updateEffectStatus); + item.removeEventListener(EffectEvent.EFFECT_REPEAT, updateEffectStatus); + item.removeEventListener(EffectEvent.EFFECT_STOP, updateEffectStatus); + + + if (item.parentCompositeEffect) { + item.parentCompositeEffect.removeEventListener(EffectEvent.EFFECT_START, parentStartEffectHandler); + } + + } + + /** + * + * */ + private function addListeners(item:Effect):void { + + item.addEventListener(EffectEvent.EFFECT_START, updateEffectStatus, false, 0, true); + item.addEventListener(EffectEvent.EFFECT_UPDATE, updateEffectStatus, false, 0, true); + item.addEventListener(EffectEvent.EFFECT_END, updateEffectStatus, false, 0, true); + item.addEventListener(EffectEvent.EFFECT_REPEAT, updateEffectStatus, false, 0, true); + item.addEventListener(EffectEvent.EFFECT_STOP, updateEffectStatus, false, 0, true); + + if (item.parentCompositeEffect) { + item.parentCompositeEffect.addEventListener(EffectEvent.EFFECT_START, parentStartEffectHandler, false, 0, true); + } + } + + /** + * Reset effect counters when parent effect plays for the first time + * */ + protected function parentStartEffectHandler(event:EffectEvent):void { + resetEffectEvents(); + } + + /** + * Reset effect counters when parent effect plays for the first time + * */ + protected function clearImage_clickHandler(event:MouseEvent):void { + resetEffectEvents(); + } + + /** + * Check if ancestor effect is playing + * */ + private function isAncestorPlaying(item:Effect):Boolean { + + while (item.parentCompositeEffect) { + + if (item.parentCompositeEffect.isPlaying) { + return true; + } + item = item.parentCompositeEffect; + } + return false; + } + + /** + * Get greatest ancestor effect + * */ + private function getItemRoot(item:Effect, isAlsoPlaying:Boolean = false):Effect { + var parentCompositeEffect:Effect = item.parentCompositeEffect; + var isPlaying:Boolean; + + + if (isAlsoPlaying) { + + isPlaying = parentCompositeEffect ? parentCompositeEffect.isPlaying : false; + + while (isPlaying) { + + if (parentCompositeEffect.parentCompositeEffect && parentCompositeEffect.parentCompositeEffect.isPlaying) { + parentCompositeEffect = parentCompositeEffect.parentCompositeEffect; + } + else { + return parentCompositeEffect; + } + } + + if (isPlaying) { + return parentCompositeEffect; + } + else { + return null; + } + } + else { + + while (parentCompositeEffect) { + + if (parentCompositeEffect.parentCompositeEffect) { + parentCompositeEffect = parentCompositeEffect.parentCompositeEffect; + } + else { + return parentCompositeEffect; + } + } + return parentCompositeEffect; + } + + return null; + } + + protected function loopButton_changeHandler(event:Event):void { + + if (loopButton.selected) { + item.repeatCount = 0; + } + else { + item.repeatCount = 1; + } + } + + ]]> + </fx:Script> + + <s:states> + <s:State name="normal" /> + <s:State name="hovered" /> + <s:State name="selected" /> + </s:states> + + <s:HGroup left="1" right="1" top="1" bottom="1" verticalAlign="middle"> + + <s:Rect id="indentationSpacer" width="{treeListData.indent}" percentHeight="100" alpha="0"> + <s:fill> + <s:SolidColor color="0xFFFFFF" /> + </s:fill> + </s:Rect> + + <s:Group id="disclosureGroup" + useHandCursor="true" + buttonMode="true"> + <s:BitmapImage source="{treeListData.disclosureIcon}" + visible="{treeListData.hasChildren}" + /> + </s:Group> + + <!--<s:HGroup id="controlsGroup" + height="100%" + paddingRight="8" + verticalAlign="middle" + width="80" + > + + </s:HGroup> + --> + + <s:Label id="nameLabel" + text="" + paddingTop="2"/> + + <s:Spacer width="100%"/> + + <!-- Events--> + <s:Group height="100%" + width="100%"> + + <s:HGroup width="100%" + height="100%" + paddingRight="8" + paddingTop="1" + paddingBottom="1" + verticalAlign="middle" + fontSize="10" + gap="1"> + + <controls:BlinkingImageButton id="playImage" + dim="true" + click="playImage_clickHandler(event)" + useHandCursor="true" + buttonMode="true"/> + <s:Image id="reverseImage" + click="reverseImage_clickHandler(event)" + source="{Radii8LibraryEffectsAssets.reverse}" + useHandCursor="true" + buttonMode="true"/> + + <s:Image id="stopImage" + click="stopImage_clickHandler(event)" + source="{Radii8LibraryEffectsAssets.stop}" + useHandCursor="true" + buttonMode="true" /> + + <s:Image id="endImage" + click="endImage_clickHandler(event)" + source="{Radii8LibraryEffectsAssets.end}" + useHandCursor="true" + buttonMode="true"/> + + <s:Image id="clearImage" + click="clearImage_clickHandler(event)" + source="{Radii8LibraryEffectsAssets.clear}" + useHandCursor="true" + buttonMode="true"/> + + <s:ToggleButton id="loopButton" + skinClass="com.flexcapacitor.views.skins.LoopButton" + change="loopButton_changeHandler(event)" + useHandCursor="true" + buttonMode="true"/> + + <s:Spacer width="10" /> + + <s:BitmapImage id="playingIndicator" source="{Radii8LibraryEffectsAssets.selected}" /> + + + <s:Label id="effectStartLabel" + backgroundColor="#00ff00" + height="100%" + width="10" + paddingTop="2" + textAlign="center" + verticalAlign="middle"/> + + <s:Group width="250" + height="100%" + > + + <s:Rect id="effectDurationRect" width="100%" percentHeight="100"> + <s:fill> + <s:SolidColor id="effectDurationRectFill" color="0x00FF00" /> + </s:fill> + </s:Rect> + + <s:BitmapImage id="divider" source="{Radii8LibraryEffectsAssets.playhead}" + height="100%" + bottom="0"/> + + <s:Spacer width="10"/> + + <s:Label id="playheadTimeLabel" + text="" + paddingTop="2" + left="8" + height="100%" + verticalAlign="middle" + color="white"/> + + + <s:Label id="durationLabel" + text="" + paddingTop="2" + right="8" + height="100%" + verticalAlign="middle" + color="white"/> + + <s:HGroup width="100%" + height="100%" + horizontalAlign="right" + verticalAlign="middle" + > + </s:HGroup> + </s:Group> + + <s:Label id="effectEndLabel" + backgroundColor="#00ff00" + paddingTop="2" + height="100%" + width="10" + textAlign="center" + verticalAlign="middle"/> + + <s:Spacer width="10" /> + + <s:Label id="effectStopLabel" + backgroundColor="#00ff00" + paddingTop="2" + height="100%" + width="10" + textAlign="center" + verticalAlign="middle"/> + <s:Label id="effectRepeatLabel" + backgroundColor="#00ff00" + paddingTop="2" + height="100%" + width="10" + textAlign="center" + verticalAlign="middle"/> + <s:Label id="effectUpdateLabel" + backgroundColor="#00ff00" + paddingTop="2" + height="100%" + width="10" + includeInLayout="false" + visible="false" + maxDisplayedLines="1" + textAlign="center" + verticalAlign="middle"/> + </s:HGroup> + </s:Group> + + </s:HGroup> + + + + <s:HGroup width="100%" + height="100%" + paddingRight="8" + gap="1" + visible="false" + includeInLayout="false"> + + <s:Rect id="effectStartRect" width="10" percentHeight="100"> + <s:fill> + <s:SolidColor color="0x00FF00" /> + </s:fill> + </s:Rect> + <s:Rect id="effectUpdateRect" width="10" percentHeight="100"> + <s:fill> + <s:SolidColor color="0x00FF00" /> + </s:fill> + </s:Rect> + <s:Rect id="effectEndRect" width="10" percentHeight="100"> + <s:fill> + <s:SolidColor color="0x00FF00" /> + </s:fill> + </s:Rect> + <s:Rect id="effectStopRect" width="10" percentHeight="100"> + <s:fill> + <s:SolidColor color="0x00FF00" /> + </s:fill> + </s:Rect> + <s:Rect id="effectRepeatRect" width="10" percentHeight="100"> + <s:fill> + <s:SolidColor color="0x00FF00" /> + </s:fill> + </s:Rect> + </s:HGroup> + </s:MXTreeItemRenderer> + </fx:Component> + </mx:itemRenderer> + </mx:Tree> + </s:VGroup> +</s:Group>
http://git-wip-us.apache.org/repos/asf/flex-radii8/blob/f370bfcf/Radii8Library/src/com/flexcapacitor/views/panels/EventListeners.mxml ---------------------------------------------------------------------- diff --git a/Radii8Library/src/com/flexcapacitor/views/panels/EventListeners.mxml b/Radii8Library/src/com/flexcapacitor/views/panels/EventListeners.mxml new file mode 100644 index 0000000..ad1e691 --- /dev/null +++ b/Radii8Library/src/com/flexcapacitor/views/panels/EventListeners.mxml @@ -0,0 +1,241 @@ +<?xml version="1.0" encoding="utf-8"?> +<s:Group xmlns:fc="com.flexcapacitor.controls.*" + xmlns:fx="http://ns.adobe.com/mxml/2009" + xmlns:local="*" + xmlns:mx="library://ns.adobe.com/flex/mx" + xmlns:s="library://ns.adobe.com/flex/spark" + xmlns:utils="com.flexcapacitor.utils.*" + xmlns:handlers="com.flexcapacitor.handlers.*" + xmlns:supportClasses="com.flexcapacitor.effects.supportClasses.*" + + styleName="constraintStyles" + creationComplete="creationCompleteHandler(event)" + width="100%" + height="100%" + > + <!--- + http://dougmccune.com/blog/source-code-license/ + + Any source code released on this blog, unless otherwise explicitly noted, is licensed under the MIT license: + Copyright (c) 2013 Doug McCune + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the âSoftwareâ), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED âAS ISâ, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + http://dougmccune.com/blog/2008/02/21/monkey-patching-flexsprite-to-list-all-event-listeners-on-any-flex-component/ + --> + + + <fx:Style> + @namespace s "library://ns.adobe.com/flex/spark"; + @namespace fc "com.flexcapacitor.controls.*"; + @namespace local "*"; + @namespace mx "library://ns.adobe.com/flex/mx"; + @namespace utils "com.flexcapacitor.utils.*"; + @namespace handlers "com.flexcapacitor.handlers.*"; + @namespace supportClasses "com.flexcapacitor.effects.supportClasses.*"; + + .constraintStyles { + fontSize:12; + } + .inputStyles { + fontSize:12; + focusAlpha : 0; + borderColor: #b8b8b8; + } + </fx:Style> + + <fx:Script> + <![CDATA[ + import com.flexcapacitor.controller.Radiate; + import com.flexcapacitor.events.RadiateEvent; + + import mx.core.FlexSprite; + import mx.core.IVisualElement; + import mx.events.FlexEvent; + + + public var radiate:Radiate; + + protected function creationCompleteHandler(event:FlexEvent):void { + target = null; + + var radiate:Radiate = Radiate.getInstance(); + radiate.addEventListener(RadiateEvent.TARGET_CHANGE, targetChangeHandler); + radiate.addEventListener(RadiateEvent.PROPERTY_CHANGED, propertyChangeHandler); + radiate.addEventListener(RadiateEvent.HISTORY_CHANGE, historyChangeHandler); + + if (radiate.target) { + target = radiate.target as FlexSprite; + + if (target is FlexSprite) { + showEventListeners(target as FlexSprite); + } + } + } + + private var _target:FlexSprite; + private var _visualElement:IVisualElement; + public function get target():FlexSprite { + return _target; + } + + [Bindable] + public function set target(value:FlexSprite):void { + + if (!(value is FlexSprite)) { + _target = null; + return; + } + + _target = value; + } + + /** + * Get current target + * */ + protected function targetChangeHandler(event:RadiateEvent):void { + target = event.selectedItem as FlexSprite; + + showEventListeners(target); + } + + /** + * Updates the view when a property is changed externally + * */ + protected function propertyChangeHandler(event:RadiateEvent):void { + var properties:Array = event.properties; + var length:int = event.properties.length; + + if (!target) return; + + for (var i:int;i<length;i++) { + if (watchedProperties.indexOf(properties[i])!=-1) { + //updateView(); + break; + } + } + } + + protected function historyChangeHandler(event:RadiateEvent):void { + if (event.newIndex==-1 || !event.historyEventItem) return; + var properties:Array = event.historyEventItem.properties; + var length:int = properties ? properties.length : 0; + + if (!target) return; + + for (var i:int;i<length;i++) { + if (watchedProperties.indexOf(properties[i])!=-1) { + //updateView(); + break; + } + } + } + + private const watchedProperties:Array = [ "width", "height", "percentWidth", "percentHeight", "x", + "y", "top", "left", "right", "bottom", "verticalCenter", + "horizontalCenter", "baseline"] + + /** + * Show event listeners on target. + * */ + public function showEventListeners(sprite:FlexSprite): void { + this.sprite = sprite; + grid.dataProvider = sprite != null ? Object(sprite).eventListeners : null; + grid.selectedIndex = 0; + + if (grid.dataProvider.length>0) { + selectedTracker = grid.dataProvider[0];// as ListenerTracker; + } + } + + private var sprite:FlexSprite; + + public function removeListener(listener:Object):void { + //public function removeListener(listener:ListenerTracker):void { + sprite.removeEventListener(listener.type, listener.listener, listener.useCapture); + + grid.dataProvider = Object(sprite).eventListeners; + } + + private function removeAll():void { + if (sprite) { + Object(sprite).removeAllEventListeners(); + grid.dataProvider = Object(sprite).eventListeners; + } + } + + [Bindable] + //private var selectedTracker:ListenerTracker; + private var selectedTracker:Object; + ]]> + </fx:Script> + + <s:layout> + <s:VerticalLayout paddingRight="10"/> + </s:layout> + + <s:Button label="Remove All Event Listeners" click="removeAll()" /> + + <mx:DataGrid id="grid" + width="100%" + minWidth="100" + minHeight="100" + change="selectedTracker = grid.selectedItem" + > + <!--change="selectedTracker = grid.selectedItem as ListenerTracker"--> + <mx:columns> + <mx:DataGridColumn dataField="type" headerText="Type" /> + <mx:DataGridColumn dataField="callingClassName" headerText="Added By" /> + + <mx:DataGridColumn dataField="type" headerText="" width="50" > + <mx:itemRenderer> + <fx:Component> + <!--<mx:Button label="Remove" click="outerDocument.removeListener(data as mx.core.ListenerTracker)" />--> + <mx:Button label="Remove" click="outerDocument.removeListener(data)" /> + </fx:Component> + </mx:itemRenderer> + </mx:DataGridColumn> + </mx:columns> + </mx:DataGrid> + + <mx:Panel title="Details: {selectedTracker.type}" + width="100%" + height="100%" + minHeight="150" + minWidth="100" + dropShadowVisible="false" + > + <mx:Form width="100%" height="100%"> + <mx:FormItem label="Type"> + <mx:Label text="{selectedTracker.type}" /> + </mx:FormItem> + <mx:FormItem label="Added by"> + <mx:Label text="{selectedTracker.callingClassName}" /> + </mx:FormItem> + <mx:FormItem label="Method"> + <mx:Label text="{selectedTracker.callingMethod}" /> + </mx:FormItem> + <mx:FormItem label="Line"> + <mx:Label text="{selectedTracker.callingLineNumber}" /> + </mx:FormItem> + <mx:FormItem label="Priority"> + <mx:Label text="{selectedTracker.priority}" /> + </mx:FormItem> + <mx:FormItem label="Use Capture"> + <mx:Label text="{selectedTracker.useCapture}" /> + </mx:FormItem> + <mx:FormItem label="Weak Reference"> + <mx:Label text="{selectedTracker.useWeakReference}" /> + </mx:FormItem> + <mx:FormItem label="Time Created"> + <mx:Label text="{selectedTracker.timeCreated}" /> + </mx:FormItem> + </mx:Form> + </mx:Panel> + +</s:Group> http://git-wip-us.apache.org/repos/asf/flex-radii8/blob/f370bfcf/Radii8Library/src/com/flexcapacitor/views/panels/Events.mxml ---------------------------------------------------------------------- diff --git a/Radii8Library/src/com/flexcapacitor/views/panels/Events.mxml b/Radii8Library/src/com/flexcapacitor/views/panels/Events.mxml new file mode 100644 index 0000000..276147a --- /dev/null +++ b/Radii8Library/src/com/flexcapacitor/views/panels/Events.mxml @@ -0,0 +1,832 @@ +<?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.*" + + minWidth="200" + minHeight="100" + creationComplete="group1_creationCompleteHandler(event)" + > + + + <!-- + TODO: + + + --> + + <fx:Script> + <![CDATA[ + import com.flexcapacitor.controller.Radiate; + import com.flexcapacitor.events.RadiateEvent; + import com.flexcapacitor.model.EventMetaData; + import com.flexcapacitor.model.MetaData; + import com.flexcapacitor.utils.ClassUtils; + import com.flexcapacitor.utils.InspectorUtils; + import com.flexcapacitor.utils.TypeUtils; + + import mx.collections.XMLListCollection; + import mx.core.IDataRenderer; + import mx.core.UIComponent; + import mx.effects.effectClasses.PropertyChanges; + import mx.events.FlexEvent; + import mx.utils.ObjectUtil; + + import spark.collections.Sort; + import spark.collections.SortField; + import spark.components.gridClasses.DefaultGridItemEditor; + import spark.components.gridClasses.GridColumn; + import spark.events.GridItemEditorEvent; + import spark.events.GridSelectionEvent; + import spark.events.TextOperationEvent; + + import flashx.textLayout.operations.SplitParagraphOperation; + + + public var CUSTOM_ITEM_SORT_CHARACTER:String = "~"; + + private var allItems:XMLListCollection = new XMLListCollection(); + private var objectPropertiesList:XMLListCollection = new XMLListCollection(); + private var _dataProviderProperties:XMLListCollection; + + public var describedTypeXML:XML; + + + + protected function group1_creationCompleteHandler(event:FlexEvent):void { + radiate = Radiate.getInstance(); + + if (!showHeader) { + //propertiesGrid.columnHeaderGroup.visible = showHeader; + eventsGrid.columnHeaderGroup.height = hiddenHeaderHeight; + } + + radiate.addEventListener(RadiateEvent.TARGET_CHANGE, handleTargetChange); + radiate.addEventListener(RadiateEvent.PROPERTY_CHANGED, propertyChangeHandler); + + 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 eventsList: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; + + if (target != null) { + //describedTypeXML = describeType(target); + //var type:Type = Type.forInstance(target); + //var type2:Type = Type.forName(type.extendsClasses[0]); + //describedTypeXML = describeType(getDefinitionByName(type.extendsClasses[0])); + //var describedType:XML = ClassUtils.describeTypeCache(target); + var data:XMLList = ClassUtils.concatenateMetaDataXMLItems(target, "Event", new XMLList()); + + //describedTypeXML = describeType(target); + eventsList = new XMLListCollection(data); + + // ADD ACCESSORS LIST + allItems.addAll(eventsList); + + // if item is an object enumerate + /*if (describedTypeXML.@name=="Object") { + for (property in target) { + item = createXMLItem(property, target[property], 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); + + // attempts to get the values of the properties on the current target + for each (var node:XML in allItems) { + propertyName = node.@name; + + if (node.@access != "writeonly") { + + if (propertyName in target) { + + try { + node.@value = target[propertyName]; + node.@isObject = (target[propertyName] is Object); + } + catch (error:Error) { + node.@value = error.message; + node.@valueError = true; + } + } + } + } + + // Add in inheriting styles + /*if ("inheritingStyles" in target) { + inheritingStyles = target.inheritingStyles as Object; + + for (property in inheritingStyles) { + item = createXMLItem(property, inheritingStyles[property], 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], true, false); + nonInheritingStylesList.addItem(item); + } + + //trace("Non-Inheriting style count", nonInheritingStylesList.length); + + //allItems.addAll(nonInheritingStylesList); + }*/ + + // create an item for unnamed style + var customItem:XML = createXMLItem(CUSTOM_ITEM_SORT_CHARACTER, ""); + customItem.@search = true; + //allItems.addItem(customItem); + + dataProviderProperties = allItems; + } + else { + dataProviderProperties = new XMLListCollection(); + } + } + + private var _target:Object; + + public function get target():Object { + return _target; + } + + [Bindable] + public function set target(value:Object):void { + + _target = value; + + if (_target) { + clear(); + populatePropertiesGrid(_target); + } + else { + clear(); + } + } + + public function createXMLItem(name:String, value:*, style:Boolean=false, inheriting:Boolean=false):XML { + var xml:XML = <accessor />; + xml.@name = name; + xml.@style = style; + xml.@inheriting = inheriting; + xml.@access = "readwrite"; + xml.@type = InspectorUtils.getValueType(value); + xml.@value = String(value); + return xml; + } + + protected function filterDisplayObjectChangeHandler(item:XML):Boolean { + var itemName:String = item.attribute("name") ? item.attribute("name") : ""; + var value:String = filterInput.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 = filterInput.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") { + 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 { + + return; + // 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; + } + + protected function filterInput_enterHandler(event:FlexEvent):void { + var searchText:String = filterInput.text; + var item:XML; + + if (filteredPropertiesCollection.length==1) { + item = filteredPropertiesCollection.getItemAt(0) as XML; + if (item) { + item.@value = target is UIComponent ? UIComponent(target).getStyle(searchText) : ""; + filteredPropertiesCollection.itemUpdated(item, "@value"); + filteredPropertiesCollection.refresh(); + } + } + } + + /** + * Called when... + * */ + protected function changeHandler(event:GridItemEditorEvent):void { + + // This could probably all be rewritten + // we could use inspector utils setProperty + // ... + //trace(""); + var column:GridColumn = event.column; + if (column.grid.dataGrid.itemEditorInstance==null) return; + var data:XML = event.currentTarget.selectedItem as XML; //event.itemRenderer.data as XML; + var currentValue:String = String(data[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 newValue:* = DefaultGridItemEditor(column.grid.dataGrid.itemEditorInstance).value; + + if (isSearchField) { + propertyName = propertyName.replace(CUSTOM_ITEM_SORT_CHARACTER, ""); + isStyle = true; + } + + if (target) { + newValue = TypeUtils.getTypedValue(newValue, type) + + InspectorUtils.setTargetProperty(target, propertyName, newValue, type, isStyle); + } + + } + + [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; + + + /** + * + * */ + public function clear():void { + + allItems.removeAll(); + allItems.refresh(); + filterInput.text = ""; + targetValueText.text = ""; + dataProviderProperties = new XMLListCollection(); + } + /* + protected function saveSessionHandler(event:GridItemEditorEvent):void { + //trace("Save session"); + var item:XML = eventsGrid.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 = eventsGrid.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 + updatePropertyChangeValue(event.changes); + } + + protected function targetValueText_keyUpHandler(event:KeyboardEvent):void { + if (event.keyCode==Keyboard.ENTER) { + updateTargetValue(); + event.preventDefault(); + } + else if (event.keyCode==Keyboard.ESCAPE) { + var item:XML = eventsGrid.selectedItem as XML; + var value:String = String(item.@value); + targetValueText.text = value; + event.preventDefault(); + } + } + + /** + * Set the property to the value in the textarea. + * */ + public function updateTargetValue():void { + var item:XML = eventsGrid.selectedItem as XML; + var actualValue:*; + + if (item) { + var value:String = String(item.@value); + var property:String = String(item.@name); + var propertyType:String = String(item.@type); + + if (value!=targetValueText.text) { + actualValue = Radiate.getTypedValue(targetValueText.text, propertyType); + //trace("New Value= ", value); + Radiate.setProperty(target, property, actualValue); + } + } + } + + protected function targetValueText_changingHandler(event:TextOperationEvent):void { + + // prevent line breaks + if(event.operation is SplitParagraphOperation) { + event.preventDefault(); + } + } + + /** + * Update the property value in the datagrid when it changes + * without reloading all the properties. + * */ + public function updatePropertyChangeValue(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]) : ""; + node.@value = Object(changeObject[property]).toString(); + filteredPropertiesCollection.itemUpdated(node, "@value"); + filteredPropertiesCollection.refresh(); + } + } + } + } + } + } + } + + protected function filterInput_clearTextHandler(event:TextOperationEvent):void { + + } + + /** + * Dispatch a property selected event + * */ + protected function eventsGrid_clickHandler(event:MouseEvent):void { + dispatchPropertySelected(); + } + + protected function eventsGrid_selectionChangeHandler(event:GridSelectionEvent):void { + dispatchPropertySelected(); + } + + + + private function dispatchPropertySelected():void { + var item:XML = eventsGrid.selectedItem as XML; + var propertyObject:MetaData; + var property:String; + var nodename:String; + + + if (item) { + nodename = item.name(); + + propertyObject = new EventMetaData(item, target); + + radiate.dispatchPropertySelectedEvent(property, propertyObject); + } + } + + ]]> + </fx:Script> + + + <fx:Declarations> + + <s:NumberFormatter id="formatter" + useGrouping="false"/> + + <s:ArrayCollection id="filteredPropertiesCollection" + list="{dataProviderProperties}"/> + + <!-- 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> + </fx:Declarations> + + <fx:Style> + @namespace s "library://ns.adobe.com/flex/spark"; + @namespace c "com.flexcapacitor.controls.*"; + @namespace mx "library://ns.adobe.com/flex/mx"; + @namespace collections "com.flexcapacitor.effects.collections.*"; + @namespace handlers "com.flexcapacitor.handlers.*"; + + .eventsGrid { + + } + </fx:Style> + + + <mx:VDividedBox id="verticalContainer" + width="100%" height="100%" + top="0" + left="8" + right="8" + bottom="8" + > + <s:Group height="24" width="100%"> + + <c:SearchTextInput id="filterInput" + top="0" + width="100%" + minWidth="60" + borderAlpha=".2" + color="#2F3030" + focusAlpha="0" + fontWeight="normal" + prompt="Search" + enter="filterInput_enterHandler(event)" + clearText="filterInput_clearTextHandler(event)" + /> + <s:HGroup width="100%" top="32" paddingLeft="2"> + <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:HGroup> + </s:Group> + + <c:DataGrid id="eventsGrid" + styleName="eventsGrid" + borderAlpha=".2" + height="100%" + width="100%" + minWidth="80" + minHeight="48" + rowHeight="18" + variableRowHeight="true" + click="eventsGrid_clickHandler(event)" + selectionChange="eventsGrid_selectionChangeHandler(event)" + dataProvider="{filteredPropertiesCollection}" + > + <c:columns> + <s:ArrayCollection> + <s:GridColumn dataField="@name" + minWidth="50" + editable="false" + resizable="true" + headerText="Name"/> + + <s:GridColumn dataField="@className" + editable="false" + resizable="true" + headerText="DISPATCHED BY" + visible="false"/> + </s:ArrayCollection> + </c:columns> + + <!-- + <c:columns> + <s:ArrayCollection> + + <s:GridColumn dataField="@name" + minWidth="50" + editable="false" + resizable="true" + headerText="PROPERTY"> + <s:itemRenderer> + <fx:Component className="defaultItemRenderer"> + <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"> + <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" + maxDisplayedLines="1" + showTruncationTip="true" /> + </s:GridItemRenderer> + </fx:Component> + </s:itemRenderer> + </s:GridColumn> + + <s:GridColumn headerText="" + minWidth="30" + width="30" + editable="false" + labelFunction="{function(data:Object, column:GridColumn):String{return'...';}}"> + </s:GridColumn> + + + + </s:ArrayCollection> + </c:columns>--> + </c:DataGrid> + + <s:TextArea id="targetValueText" + text="{XML(eventsGrid.selectedItem).@value}" + width="100%" + heightInLines="2" + skinClass="spark.skins.spark.TextAreaSkin" + minHeight="10" + visible="false" + includeInLayout="false" + keyUp="targetValueText_keyUpHandler(event)" + changing="targetValueText_changingHandler(event)"> + </s:TextArea> + + </mx:VDividedBox> + +</s:Group> http://git-wip-us.apache.org/repos/asf/flex-radii8/blob/f370bfcf/Radii8Library/src/com/flexcapacitor/views/panels/HistoryInspector.mxml ---------------------------------------------------------------------- diff --git a/Radii8Library/src/com/flexcapacitor/views/panels/HistoryInspector.mxml b/Radii8Library/src/com/flexcapacitor/views/panels/HistoryInspector.mxml new file mode 100644 index 0000000..8271b6b --- /dev/null +++ b/Radii8Library/src/com/flexcapacitor/views/panels/HistoryInspector.mxml @@ -0,0 +1,495 @@ +<?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:filters="com.flexcapacitor.filters.*" + xmlns:controls="com.flexcapacitor.controls.*" + xmlns:renderers="com.flexcapacitor.views.renderers.*" + + styleName="radiateInspector" + width="100%" + minHeight="20" + creationComplete="creationCompleteHandler(event)" + > + + <fx:Script> + <![CDATA[ + import com.flexcapacitor.controller.Radiate; + import com.flexcapacitor.events.HistoryEvent; + import com.flexcapacitor.events.HistoryEventItem; + import com.flexcapacitor.events.RadiateEvent; + + import flash.utils.getTimer; + + import mx.collections.ArrayCollection; + import mx.events.FlexEvent; + + import spark.core.NavigationUnit; + import spark.events.IndexChangeEvent; + + /** + * Not used. Possible use store sets of changes between targets + * */ + [Bindable] + public var history:ArrayCollection = new ArrayCollection(); + + /** + * When true removes all the items in the history when the target is null + * */ + public var clearOnTargetChange:Boolean = true; + + private var radiate:Radiate = Radiate.instance; + + public var debug:Boolean = false; + + private var _target:*; + + public function get target():* { + return _target; + } + + [Bindable] + public function set target(value:*):void { + _target = value; + + if (value==null) { + if (clearOnTargetChange) { + Radiate.removeAllHistory(); + } + } + } + + protected function creationCompleteHandler(event:FlexEvent):void { + radiate.addEventListener(RadiateEvent.TARGET_CHANGE, targetChangeHandler, false, 0, true); + radiate.addEventListener(RadiateEvent.HISTORY_CHANGE, historyChangeHandler, false, 0, true); + radiate.addEventListener(RadiateEvent.DOCUMENT_CHANGE, documentChangeHandler, false, 0, true); + + + //Radiate.history.refresh(); + + list.selectedIndex = Radiate.historyIndex; + + scrollToBottom(); + + updateNavigationButtons(); + } + + /** + * + * */ + protected function targetChangeHandler(event:RadiateEvent):void { + target = event.selectedItem; + } + + /** + * + * */ + public function redo():void { + var historyIndex:int = Radiate.redo(true); + + if (historyIndex>-1) { + list.ensureIndexIsVisible(historyIndex); + } + else { + list.ensureIndexIsVisible(0); + } + + + //Radiate.history.refresh(); + + //list.selectedIndex = historyIndex; + + updateNavigationButtons(); + + if (debug) Radiate.log.info("3 SELECTED ITEM INDEX: " + list.selectedIndex); + } + + /** + * + * */ + public function undo():void { + var historyIndex:int = Radiate.undo(true); + + //Radiate.history.refresh(); + + if (historyIndex>-1) { + list.ensureIndexIsVisible(historyIndex); + } + else { + list.ensureIndexIsVisible(0); + } + + //list.selectedIndex = Radiate.historyIndex; + + updateNavigationButtons(); + if (debug) Radiate.log.info("4 SELECTED ITEM INDEX: " + list.selectedIndex); + } + + /** + * Label for the history item + * */ + public function labelFunction(item:Object):String { + var historyItem:HistoryEvent = item as HistoryEvent; + var historyEvents:Array = historyItem.historyEventItems; + var historyEvent:HistoryEventItem = historyEvents && historyEvents.length ? historyEvents[0] : null; + var label:String; + + + if (historyEvent.action==RadiateEvent.PROPERTY_CHANGED) { + if (historyEvent.properties.length) { + label = historyEvent.description || historyEvent.properties + " changed"; + } + else { + label = historyEvent.description || historyEvent.styles + " changed"; + } + } + else if (historyEvent.action==RadiateEvent.ADD_ITEM) { + label = historyEvent.description || "Item added"; + } + else if (historyEvent.action==RadiateEvent.REMOVE_ITEM) { + label = historyEvent.description || "Item removed"; + } + else if (historyEvent.action==RadiateEvent.MOVE_ITEM) { + label = historyEvent.description || "Moved"; + } + else { + label = "Something Changed"; + } + + if (!historyEvent) return ""; + + return label; + } + + /** + * Show the last item in the list + * */ + private function scrollToBottom():void { + // update the verticalScrollPosition to the end of the List + // virtual layout may require us to validate a few times + // code posted by a Flex SDK developer + var delta:Number = 0; + var count:int = 0; + var iterations:int = 10; + + while (count++ < iterations) { + list.validateNow(); + delta = list.layout.getVerticalScrollPositionDelta(NavigationUnit.END); + list.layout.verticalScrollPosition += delta; + + if (delta == 0) { + break; + } + } + } + + protected function list1_clickHandler(event:MouseEvent):void { + if (list.selectedItem) { + //Radiate.history.refresh(); + } + } + + + public var updatingChanges:Boolean; + + /** + * + * */ + protected function changeHandler(event:IndexChangeEvent):void { + if (debug) Radiate.log.info("SELECTED ITEM INDEX: " + list.selectedIndex); + if (debug && updatingChanges) Radiate.log.info("ALREADY APPLYING CHANGES. SELECTED ITEM INDEX: " + list.selectedIndex); + + if (updatingChanges) { + //event.preventDefault(); + return; + } + var newIndex:int = event.newIndex; + var oldIndex:int = Radiate.historyIndex; + var dispatch:Boolean = false; + var time:int = getTimer(); + var historyIndex:int; + var length:int; + + updatingChanges = true; + + //ensureIndexIsVisible(newIndex); + + if (debug) Radiate.log.info("goToHistoryIndex: " + newIndex); + + Radiate.goToHistoryIndex(newIndex); + //trace("changes:"+(getTimer()-time)); + + //list.selectedIndex = Radiate.historyIndex; + + + //Radiate.history.refresh(); + + //updateNavigationButtons(); + + if (debug) Radiate.log.info("2 SELECTED ITEM INDEX: " + list.selectedIndex); + + updatingChanges = false; + + //ensureIndexIsVisible(newIndex); + } + + + public function updateNavigationButtons():void { + var historyIndex:int = Radiate.historyIndex; + var totalItems:int = Radiate.history ? Radiate.history.length : 0; + var hasItems:Boolean = totalItems>0; + + // has forward history + if (hasItems && historyIndex+1<totalItems) { + forwardButton.enabled = true; + } + else { + forwardButton.enabled = false; + } + + // has previous items + if (hasItems && historyIndex>-1) { + backButton.enabled = true; + } + else { + backButton.enabled = false; + } + } + + /** + * Document change handler. Clear history + * */ + protected function documentChangeHandler(event:Event):void { + if (debug) Radiate.log.info("DOCUMENT CHANGE"); + + list.selectedIndex = Radiate.historyIndex; + + scrollToBottom(); + + updateNavigationButtons(); + + // Radiate.removeAllHistory(); + } + + /** + * + * */ + public function ensureIndexIsVisible(index:int=0):void { + + if (index!=-1) { + list.ensureIndexIsVisible(index); + } + else { + list.dataGroup.verticalScrollPosition = 0; + } + //list.validateNow(); + //list.ensureIndexIsVisible(index); + //list.validateNow(); + } + + /** + * + * */ + public function ensureLastItemVisible():void { + if (list.dataProvider.length>0) { + ensureIndexIsVisible(list.dataProvider.length-1); + scrollToBottom(); + } + } + + /** + * + * */ + protected function revertHandler(event:MouseEvent):void { + var length:int = Radiate.history.length; + var dispatch:Boolean = false; + var time:int = getTimer(); + var i:int; + + //trace("revert start: " + time); + + // it is 22 times faster to not dispatch events until the end + // each change takes 85ms per change with dispatch events + // and 2.58ms without + + Radiate.goToHistoryIndex(-1); + + //ensureIndexIsVisible(0); + + //Radiate.history.refresh(); + + //trace("revert start: " + (getTimer()-time)); + //list.selectedIndex = -1; + + //Radiate.history.refresh(); + + //updateNavigationButtons(); + if (debug) Radiate.log.info("5 SELECTED ITEM INDEX: " + list.selectedIndex); + } + + protected function list_valueCommitHandler(event:FlexEvent):void { + //Radiate.log.info("Value commit. Index : " + list.selectedIndex); + //Radiate.log.info("History Index : " + Radiate.historyIndex); + + if (list.selectedIndex!= Radiate.historyIndex) { + //list.selectedIndex = Radiate.historyIndex; + //Radiate.history.refresh(); + //Radiate.log.info("History OUT OF SYNC"); + } + } + + protected function list_caretChangeHandler(event:IndexChangeEvent):void { + //Radiate.log.info("Caret changed. Index : " + list.selectedIndex); + //Radiate.log.info("History Index : " + Radiate.historyIndex); + } + + /** + * History changed. Select the last applied item in the history array. + * */ + protected function historyChangeHandler(event:Event):void { + if (debug) Radiate.log.info("HISTORY CHANGE"); + + //Radiate.log.info("historyChangeHandler. Index : " + list.selectedIndex); + //Radiate.log.info("History Index : " + Radiate.historyIndex); + + // must call validate now since history.refresh() may have been called + list.validateNow(); + + if (Radiate.historyIndex==-1) { + list.selectedItem = null; + } + else { + list.selectedIndex = Radiate.historyIndex; + } + + ensureIndexIsVisible(Radiate.historyIndex); + updateNavigationButtons(); + } + + + ]]> + </fx:Script> + + <fx:Declarations> + + </fx:Declarations> + + <s:Group width="100%" height="100%" left="5" right="5" bottom="5"> + + <s:List id="list" + top="26" + focusAlpha="0" + minHeight="20" + width="100%" height="100%" + dataProvider="{Radiate.history}" + labelFunction="labelFunction" + borderVisible="false" + change="changeHandler(event)" + valueCommit="list_valueCommitHandler(event)" + caretChange="list_caretChangeHandler(event)" + itemRenderer="com.flexcapacitor.views.renderers.HistoryItemRenderer" + > + + <s:layout> + <s:VerticalLayout useVirtualLayout="true" gap="1"/> + </s:layout> + + </s:List> + + <renderers:HistoryItemRenderer id="openLabel" + label="Open" + width="100%" + right="100" + useHandCursor="true" + buttonMode="true" + autoDrawBackground="true" + click="revertHandler(event)" /> + + <controls:HorizontalLine top="22" + strokeColor="#000000" + alpha=".15" + /> + + <!--- triangle --> + <s:Group height="14" width="10" top="4" visible="{Radiate.historyIndex==-1}"> + <s:Path data="M 0 0 L 0 16 L 16 8 z" width="100%" height="100%"> + <s:fill> + <s:SolidColor color="#000000" alpha=".25"/> + </s:fill> + <s:stroke> + <s:SolidColorStroke weight="0" color="#ffffff" /> + </s:stroke> + </s:Path> + <!--<s:Line width="40" x="13" y="5" > + <s:stroke> + <s:SolidColorStroke alpha=".15"/> + </s:stroke> + </s:Line>--> + </s:Group> + + + <!--- UNDO REDO BUTTONS --> + <s:HGroup width="100%" right="0" top="0" verticalAlign="top"> + + <!--- revert to beginning --> + <!--<s:Group width="50%" height="8" > + <s:Rect width="100%" height="8" > + <s:fill> + <s:SolidColor alpha="0"/> + </s:fill> + </s:Rect> + </s:Group>--> + + <s:Spacer width="100%"/> + + <!--- go back --> + <s:Group id="backButton" + click="undo()" + width="20" + height="15" + useHandCursor="true" + buttonMode="true" + > + <s:Path data="M 8 0 L 8 16 L 0 8 Z" height="14" width="14"> + <s:fill> + <s:SolidColor color="#000000" alpha=".5"/> + </s:fill> + </s:Path> + </s:Group> + + + <!--- go forward --> + <s:Group id="forwardButton" + click="redo()" + width="20" + height="15" + useHandCursor="true" + buttonMode="true"> + <s:Path data="M 0 0 L 0 16 L 16 8 L 0 0 Z" height="14" width="14"> + <s:fill> + <s:SolidColor color="#000000" alpha=".5"/> + </s:fill> + </s:Path> + </s:Group> + </s:HGroup> + </s:Group> + +</s:Group>