I am trying to programmatically pass focus to a newly created TextField, but for some reason setSelection and setFocus do not work. For example, see my test code below:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="_init()">
<mx:Button click="{tf.setSelection(1,2)}" />
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
public var tf:TextField
private function _init():void{
tf = new TextField();
tf.text = "abcdefghijk";
tf.type = TextFieldType.INPUT;
tf.autoSize = TextFieldAutoSize.LEFT;
var ui:UIComponent = new UIComponent();
ui.addChild(tf);
addChild(ui);
tf.setSelection(0,1);
ui.setFocus();
ui.addEventListener(MouseEvent.MOUSE_DOWN, function():void{
tf.setSelection(0,3);
});
}
]]>
</mx:Script>
</mx:Application>
The only setSelection that does anything is the 0,3 one on MOUSE_DOWN. I assume this all has something to do with the text field receiving focus with the mouse click, but I cannot for the life of me figure out how to do this manually.
setFocus works on components that implement the mx.managers.IFocusManagerComponent. Textfield is not a Flex component and doesn't implement this interface, that's why it doesn't work. If I were you, I would use a TextInput instead seeing that you need an input control
On investigating other classes, motivated by Florian's suggestion, I came across UITextField which subclasses TextField. Though it does not implement the IFocusManagerComponent interface, it does have a setFocus method, which at this moment in time seems to be working.
As an added benefit, it can be added to a container directly, that is, without having to construct a UIComponent parent first.
Related
I'm trying to use one great example of using SpriteVisualElement for item renderers from here:
The issue i have is it's impossible to detect the mouse click event when click points to the area of the renderer which doesn't have any child components. For example: if I click on the textfield, then it works and i see the mouse even dispatched. If I click on an empty spot on the renderer then no mouse event is dispatched. I've tried mouseEnabled=true (which is true by default any way) with no luck. I see from the Flex doc:
the click event is inherited from InteractiveObject. So maybe this has something to do with the focus (see the tread at the and of the page). Looking for an explanation why InteractiveObject behaves that way. Thanks!
What is happening is that you do not have anything in the renderer to click on so click will fall through your renderer, by adding and image or graphic you are creating a clickable area.
The best thing to do is to tell the render that it does not have any mouseChildren which will then make it respond to any click on it.
change this method
public function TweetRenderer()
{
this.mouseChildren = false;
percentWidth = 100;
}
I think is getting a bit clear now. The mouseChildren is a property on DisplayObjectContainer. And as the following example shows DisplayObjectContainer doesn't dispatch any mouse click events, when click occur on the area which is not taken by any of it's children. This is unintuitive because DisplayObjectContainer has a click event inherited from InteractiveObject, so one (a newbe like me) would expect it to dispatch an event if i click on the container. Setting mouseChildren=false kind of flattens the DisplayObjectContainer, so the click event on any of the children will be dispatched having target as a container. But!!! This still assumes that you click on the child, not on the empty area. There is no way to dispatch it when click is done inside the area which is not taken by the child. This example shows this: If you click on either TextField or on fill, then even is dispatched with SpriteVisualElement as target. If you click elsewhere the event is not dispatched. I'm still unclear on why this is an intended behavior, taking into account the presence of click event on the DisplayObjectContainer. Maybe because containers don't meant to detect the mouse clicks at all, but rather their children are? This is a bit unintuitive to me.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="creationCompleteHandler(event)" >
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function creationCompleteHandler(event:FlexEvent):void {
var tf:TextField = new TextField();
tf.text = "I'm inside SpriteVisualElement!";
tf.background = true;
tf.backgroundColor = 0xff0000;
tf.alpha = 0.75;
tf.selectable = false;
tf.width = 150;
tf.height = 25;
uic.addChild(tf);
uic.addEventListener(MouseEvent.CLICK, clickHandler);
uic.mouseChildren = false;
uic.mouseEnabled = true;
uic.graphics.lineStyle(1, 0xFF0000);
uic.graphics.moveTo(0,0);
uic.graphics.lineTo(uic.width,0);
uic.graphics.lineTo(uic.width,uic.height);
uic.graphics.lineTo(0,uic.height);
uic.graphics.lineTo(0,0);
uic.graphics.beginFill(0x00FF00,1);
uic.graphics.drawRect(12, 12, 178, 28);
uic.graphics.endFill();
}
protected function clickHandler(e:MouseEvent):void {
trace("click detected, target:",e.target);
}
]]>
</fx:Script>
<s:SpriteVisualElement id="uic" horizontalCenter="0" verticalCenter="0" width="200" height="50" />
</s:Application>
I have a mxml flex application where I have to launch a VideoPlayer on button click event. Any idea what solutions I can use to open a new "frame" or "view" (I'm not sure what the right terminology is) with the VideoPlayer playing a media clip so that it wouldn't interfere with the original "view"?
What I would do is create a component (like a TitleWindow, Group, Panel, etc.) that has your VideoPlayer added to it and then use the PopUpManager to display it on screen when your button is clicked. Make sure you add a method to close the pop up when you're done with it.
Some links on the PopUpManager to get you started:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/managers/PopUpManager.html
http://blog.flexexamples.com/category/popupmanager/
http://blog.flexexamples.com/2008/03/20/creating-custom-pop-up-windows-with-the-popupmanager-class-redux/
A (really) rough example:
<fx:Script>
<![CDATA[
private var myVideoPlayerComponent:VideoPlayer;
protected function btnHistory_clickHandler(event:MouseEvent):void
{
myVideoPlayerComponent = PopUpManager.createPopUp(this, VideoPlayer, false);
PopUpManager.centerPopUp(myVideoPlayerComponent);
}
]]>
</fx:Script>
<s:Button label="Play" id="myButton" click="myButton_clickHandler(event)" />
this seems to be an interesting question to be discovered in Flex.
I registered a very simple button event listener in A.mxml:
<mx:Script><![CDATA[
import mx.controls.Alert;
public function Handler():void
{
Alert.show('click event occured');
}
]]></mx:Script>
<mx:Button label="{resourceManager.getString('resources', 'button.startLab')}"
id="nextStepButton" click="Handler()" />
It works fine when clicking the button everytime.
Now I want to have something interesting,that is,I want to capture this buttonClick Event in another mxml file,say B.mxml and do something in B.mxml instead of A.
I am bit stuck on this,hope you could give me some hint and help,thanks a lot.
There are a number of approaches to this problem. The simplest (and least object-oriented) is to have A be aware of B, or vice versa. In that case you can just add a listener. In B you could say a.nextStepButton.addEventListener(MouseEvent.CLICK, myHandler), or in A you could do this.nextStepButton.addEventListener(MouseEvent.CLICK, b.myHandler). (When one component is instantiated, you have to set a reference to it on the other component.)
One step better would be to dispatch a custom event that bubbles, with one of the components still aware of the other. In B: a.addEventListener(CustomNavigationEvent.NEXT_CLICK, myHandler), or in A: b.addEventListener(CustomNavigationEvent.NEXT_CLICK, myHandler).
Taking it further, you could just let the event bubble to the top (the SystemManager) and add your listener to the SystemManager. This way B is not aware of A at all. In B: this.systemManager.addEventListener(CustomNavigationEvent.NEXT_CLICK, myHandler).
Taking it even further, you can implement your own version of an event broadcaster, which is just a third object that is accessible by any component, usually implemented as a singleton, that takes listener registrations and accepts event dispatches, then broadcasts that event to registered listeners.
Hope that helps.
EDIT: Here's some code for doing it the first way:
In A.mxml:
<?xml version="1.0" encoding="utf-8"?>
<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" creationComplete="onCreationComplete(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
public var b:B;
private function onCreationComplete(e:FlexEvent):void {
// Note that you have to have a public onClick handler in B
this.myButton.addEventListener(MouseEvent.CLICK, b.onClick);
}
]]>
</fx:Script>
<s:Button id="myButton"/>
</s:Group>
You need to make A aware of B in the container that declares instances of both A and B:
MXML:
<mypackage:A id="aComponent" b="bComponent"/>
<mypackage:B id="bComponent"/>
ActionScript equivalent:
var aComponent:A = new A();
var bComponent:B = new B();
aComponent.b = bComponent;
The concept of this seems easy, but I'm having trouble getting it right and can't find anything to help me on this.
I have a panel I need to perform a drag and drop operation on, but I only want to perform that if the user mouses down on a particular area of the panel. I can add an Icon to the panel by doing this:
[Embed("/img/icon.png")]
[Bindable]
public var dragIcon:Class;
newPanel.titleIcon = dragIcon;
But what I really want to add is a box, which I can then add my listeners to for the drag and mouse down like I do on some canvases created in actionscript like so
var tempBox:Box = new Box;
tempBox.x=0;
tempBox.y=0;
tempBox.width = 20;
tempBox.height = 44;
tempBox.setStyle("horizontalAlign","center");
tempBox.setStyle("verticalAlign","middle");
tempBox.addEventListener(MouseEvent.ROLL_OVER,over);
tempBox.addEventListener(MouseEvent.ROLL_OUT,out);
tempBox.addEventListener(MouseEvent.MOUSE_DOWN,mouseDownAnswer);
var tempImg:Image = new Image();
tempImg.source = grabbableItem;
tempBox.addChild(tempImg);
myCanvas.addChild(tempBox);
So what do I need to do to use that tempBox and turn it into a class to be used as my panels titleIcon?
Edit 12/29/09:
So I came up with something where I'm extending the panel class (shown below) but all this is really doing is covering up the icon with something I can access publicly. I'm sure there's a better way out there right?
package custClass
{
import mx.containers.Box;
import mx.containers.Panel;
import mx.controls.Image;
public class DragPanel extends Panel
{
[Bindable] public var iconBox:Box = new Box();
[Embed("../img/doc_page.png")] [Bindable] public var grabbableItem:Class;
public function DragPanel()
{
super();
}
override protected function createChildren():void{
super.createChildren();
iconBox.x = 10
iconBox.y = 4
iconBox.width = 20;
iconBox.height = 20;
iconBox.setStyle("horizontalAlign","center");
iconBox.setStyle("verticalAlign","middle");
iconBox.setStyle("borderStyle","solid");
iconBox.setStyle("backgroundColor",0x000000);
var tempImg:Image = new Image();
tempImg.source = grabbableItem;
iconBox.addChild(tempImg);
this.rawChildren.addChild(iconBox);
}
}
}
EDIT 1/7/10 (or 16 according to my windows mobile phones text messages):
Using Chaims help from below here is my new answer.
Create a box mxml component like Chaim says but also add the following script block to it.
<mx:Script>
<![CDATA[
import mx.core.Application;
[Embed("/img/doc_page.png")]
[Bindable]
public var grabbableItem:Class;
public function init():void{
this.addEventListener(MouseEvent.MOUSE_DOWN,Application.application.mouseDownSection);
this.addEventListener(MouseEvent.ROLL_OVER,Application.application.over);
this.addEventListener(MouseEvent.ROLL_OUT,Application.application.out);
}
]]>
</mx:Script>
This adds in all the event listeners I want on the Box that will be used as my icon. Now just add the box as an Icon and it's good to go.
panel.titleIcon = DraggableBox;
I guess since it's a separate mxml component it is now a class, though I don't think I understand why.
The Panel expecting titleIcon property value to be a IFactory and create an instance by himself.
Make your box a component (lets name it DraggableBox.mxml):
<?xml version="1.0" encoding="utf-8"?>
<mx:Box xmlns:mx="http://www.adobe.com/2006/mxml"
x="0" y="0" width="20" height="44"
horizontalAlign="center" verticalAlign="middle">
<mx:Image source="{grabbableItem}"/>
</mx:Box>
And assign it to titleIcon:
<mx:Panel titleIcon="{DraggableBox}" >
...
</mx:Panel>
If you want do it in ActionScript use ClassFactory:
panel.titleIcon = new ClassFactory(DraggableBox);
I have a Flex3 project with 2 HorizontalList controls; one of which has drag & drop enabled. Both controls will always have the same number of items, and are related... index 0 from the 1st control matches index 0 from the 2nd control, and so on.
Naturally, when using drag and drop to reorder the 2nd control, I want the items in the 1st control to reflect the same reordering. I think I can handle the actual updating of the control, but I'm having trouble finding the from and to indexes of the item that has been moved via drag & drop.
Using the dragDrop method on the control that allows drag and drop, and perusing the contents of event.currentTarget (instead of event.target because the docs say so) in the debugger shows a public property named selectedIndex which does seem to hold the from index, but I don't see any properties that will tell me the to index.
Am I overlooking the property? Do I have to infer it based on something else? Is there a better way to do this?
update: An acceptable solution might also be to iterate over the contents of the HorizontalList and save the internal index value to an array that could then be used to re-sort the dataProvider for the other list; but I can't figure out how to iterate over the list contents, so I'm just as stuck. Any help there?
Here's my code:
main.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:com="components.*"
layout="absolute"
>
<mx:Script>
<![CDATA[
import mx.events.DragEvent;
import mx.controls.Alert;
import mx.collections.ArrayCollection;
//these two variables are junk data to represent
//what we will get back from our data services...
[Bindable]
private var btnData:ArrayCollection = new ArrayCollection(
[
{title:'title1', index:0},
{title:'title2', index:1},
{title:'title3', index:2},
{title:'title4', index:3}
]
);
[Bindable]
private var chainData:ArrayCollection = new ArrayCollection(
[
{tmp:'testing 1'},
{tmp:'testing 2'},
{tmp:'testing 3'},
{tmp:'testing 4'}
]
);
//handle button re-ordering
private function handleBtnReorder(event:DragEvent):void{
Alert.show(event.action.toString());
}
]]>
</mx:Script>
<mx:HorizontalList
id="ChainView"
dataProvider="{chainData}"
itemRenderer="components.ItemRenderers.ChainLinkRenderer"
left="20"
right="20"
top="20"
height="300"
rowHeight="300"
columnWidth="500"
rollOverColor="#ffffff"
selectionColor="#eeeeee"
/>
<mx:HorizontalList
id="btnList"
dataProvider="{btnData}"
dragEnabled="true"
dropEnabled="true"
dragMoveEnabled="true"
dragDrop="handleBtnReorder(event)"
itemRenderer="components.ItemRenderers.BtnListRenderer"
horizontalCenter="true"
left="20"
right="20"
top="320"
height="50"
rowHeight="35"
rollOverColor="#ffffff"
selectionColor="#ffffff"
/>
</mx:Application>
The Alert.show(...) isn't there to show anything useful, I've set a breakpoint on that line so that I can inspect the event argument.
I don't think the custom itemRenderer's are important (there's not much code to them, yet), so I'll leave them out for brevity's sake. If you think they are important, let me know and I'll edit them in here.
It turns out the solution was to switch from using the dragDrop event to the dragComplete event; at which point, the binded dataProvider has been updated to reflect the reordering.
Thats why the Model Locator pattern is made for!
You must move your dataProvider to a singleton class (Model for instance) than your item renderers can easily access to it. There is no point that your item has the property Index since its already given by a [ArrayCollection].getItemIndex(..)