SpriteVisualElement doesn't take mouse input - apache-flex

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>

Related

Disable mouse wheel scroll on child component

here is the layout:
<s:Scroller>
<s:VGroup>
<s:List id="list1"/>
<s:List id="list2"/>
<component:ThirdPartyComponent/>
</s:VGroup>
</s:Scroller>
so, my application should only showing 1 scroll to the right which is the scoller, list1, list2 etc should not showing scroll.
it was working fine for a moment until we found that mouse wheel scroll not working. seem the mouseWheel event captured by the child component (list and thirdparty).
searched through the net to find solution, there is solution to stopImmediatePropagation() of the child mouseWheel event, but seems it is not a good solution. beside the part of ThirdPartyCompoent that doing scroll is a private member, so no way to listen mouseWheel from ThirdPartyCompoent
any idea?
CASE CLOSE
so far, the case solved by listen the mouseWheel event and disable root VGroup mouseChildren there, then on the root VGroup click handler i enable the mouseChildren. but if there is more elegan solution please comment.
Maybe these options could help you
OPTION#: 1
<component:ThirdPartyComponent
creationComplete = "afterCreation()"
id = "TPComponent">
<fx:Script>
// You could use initialize or creationComplete to handle MouseWheel Listener
private function afterCreation():void{
this.addEventListener(MouseEvent.MOUSE_WHEEL, hoverHandler);
}
private function hoverHandler(e:MouseEvent):void{
if(!e.isDefaultPrevented()){
e.preventDefault();
e.cancelBubble = true;
e.cancel = true;
e.returnValue = false;
}
return false;
}
</fx:Script>
</component:ThirdPartyComponent>
But I suggest you to disable MouseWheel in children components with MouseEvent.ROLL_OVER, because it covers all area of any child object of the display object container. The buble event should return to false so that the children would have no chance to dispatch any mouse event including the MOUSE_WHEEL.
OPTION#: 2
<component:ThirdPartyComponent
creationComplete = "afterCreation()"
id = "TPComponent">
<fx:Script>
private function afterCreation():void{
this.mouseChildren = false;
}
</fx:Script>
</component:ThirdPartyComponent>
By setting mouseChildren to false, events on any mouseChildren are given to the parent magically. mouseChildren is not equal to mouseEnabled, so any given return of it will give different impact :)
You could combine Option# 1 & Option# 2 or choose one of them, which is the best for you :)
you can access third party components children via recursion
How to access children of children recursively?
and add mouse wheel block handler to children you need

Hiding a tab in a Spark TabBar

I have a spark TabBar and I want to hide and show some elements of it from an external user input (namely a checkbox check)
I am having trouble changing the tabs visibility. They are currently always shown.
Does anyone have any idea? I have seen a getTabAt on the mx TabBar but the look of the tab is important and the requirement is for it to look like a tab bar rather than a button bar.
My code for the tabs and for hiding and showing is below:
<fx:Script>
<![CDATA[
import mx.containers.VBox;
import mx.controls.Label;
private function onCreationComplete():void {
var vbox1:VBox = new VBox();
vbox1.label = "Tab 1";
var lbl1:Label = new Label()
lbl1.text = "Panel1";
vbox1.addChild(lbl1);
dp.addChild(vbox1);
var vbox2:VBox = new VBox();
vbox2.label = "Tab 2";
var lbl2:Label = new Label()
lbl2.text = "Panel 2";
vbox2.addChild(lbl2);
dp.addChild(vbox2);
}
private function showTab(event:MouseEvent):void {
makeVisible(true);
}
private function hideTab(event:MouseEvent):void {
makeVisible(false);
}
private function makeVisible(vis:Boolean):void {
VBox(dp.getChildAt(0)).visible = vis;
VBox(dp.getChildAt(0)).enabled = vis;
VBox(dp.getChildAt(0)).includeInLayout = vis;
}
]]>
</fx:Script>
<s:VGroup>
<s:TabBar id="tabNavigator" width="100%" height="100%" dataProvider="{dp}"/>
<mx:ViewStack width="100%" height="100%" id="dp" borderStyle="solid"/>
<s:Button click="showTab(event)" label="show Tab"/>
<s:Button click="hideTab(event)" label="hide Tab"/>
</s:VGroup>
Any advice greatly received
Thanks
Yea, it's really nasty to not be documented such a trivial task. I come upon this post but i'm on Flex builder 4.6 and targeting mobile application (flex mobile). There is Spark TabbedViewNavigatorApplication which has TabbedViewNavigator as its child. The adobe forums and help show only how to hide the entire tabBar, which is really obvious, but not how to hide distinct options inside the tabBar.
Some places i visited suggested to remove items from the TabbedViewNavigator when you want to hide them, and then put them back again with removeItemAt, addItemAt combination ... but you really don't want to do that.
The first reason is that with removing items from tabBar you are removing ViewNavigators which form the View stack of certain section.
With removing one of the navigators, you are messing with this stack, and if your application tends to be on the complex side, or tends to grow that way, you will find yourself in trouble writing code that manages all those removal and adding processes, keeping in mind that your indexes in the navigators Vector in TabbedViewNavigator don't get messed.
Furthermore, if you do some caching, or custom handle navigator properties, restoring them to the state where they been in the moment you removed them from the tab stack will give you a lot of headache.
Following the solution from the original post, and with little experimenting the solution is quite simple:
// let say that the instance of Tabbed view navigator look like this:
// appRef is reference to TabbedViewNavigatorApplication (if you are in the main mxml, just put "this" as reference)
....
var myTabbedViewNavigator : TabbedViewNavigator = appRef.tabbedNavigator;
var index : int = 0; // we take item at index 0 for example (1st option in tabBar)
var dg : DataGroup = myTabbedViewNavigator.tabBar.dataGroup;
dg.getElementAt(index).visible = false;
dg.getElementAt(index).includeInLayout = false;
....
To show the tab again put true, and that's it, your navigators will still be there inside your TabbedViewNavigator but their visual representation in the tabBar will be invisible.
This function will hide a tab at a particular index. If you do not have the includeInLayout then the tab disappears and leaves a hole.
private function setTabEnabled(index:int, enabled:Boolean):void {
var theTab:UIComponent = tabNavigator.dataGroup.getElementAt(index) as UIComponent;
if (theTab)
theTab.visible = enabled;
theTab.includeInLayout = enabled;
}
}

view sitching how to?

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)" />

How do I set focus and selection on a TextField?

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.

Flex Cursor management question

I have a spark borderContainer that contains a spark TextInput.
I have an mouse_down and mouse_up event handlers on the borderContainer in order to drag it around the screen; while dragging I change the cursor.
I would like to make the textInput behave as a "standard" textInput, i.e. when clicking on the textInput the user should not be able to drag the whole component but simply interact with text as he/she would normally. Also, I'd like the textInput cursor to look like a normal textInput cursor.
I'm not sure I'm doing this right. My assumption is that I need to stop the propagation of mouse_down and mouse_up on the textInput to inhibit the drag behavior from its parent, and manage rollOver and rollOut in order for the cursor to look ok. See below an example of my code (to simplify it there is no borderContainer or drag - but the code for that would be very simple - just a bit longer).
So here's the issue: if one clicks on the spark textInput and then rolls out of it the cursor turns into a combination of a textInput cursor + the standard cursor set for for the borderContainer. This doesn't seem to happen on mx textInput components (thus the two boxes), but unfortunately I need to use spark components. My guess is that I'm either not calling the cursorManager correctly or I'm not stopping the propagation of mouse_up properly - it seems like it should hit the textInput but not propagate to the borderContainer. I tried stopPropagation() as well but no luck.
Would love any advice / constructive criticism.
thank you!
f
<?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"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
creationComplete="application1_creationCompleteHandler(event)"
mouseDown="application1_mouseDownHandler(event)"
mouseUp="application1_mouseUpHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.managers.CursorManager;
[Bindable] [Embed(source="../resources/hand.png")] private var _handIcon:Class;
[Bindable] [Embed(source="../resources/Fist.png")] private var _fistIcon:Class;
private var _cursorID:int;
protected function textinput1_rollOutHandler(e:MouseEvent):void
{
e.stopImmediatePropagation();
CursorManager.removeCursor(_cursorID);
_cursorID = CursorManager.setCursor(_handIcon);
}
protected function textinput1_rollOverHandler(e:MouseEvent):void
{
e.stopImmediatePropagation();
CursorManager.removeCursor(_cursorID);
}
protected function application1_creationCompleteHandler(e:FlexEvent):void
{
_cursorID = CursorManager.setCursor(_handIcon);
}
private function stopPropagation(event:MouseEvent):void
{
event.preventDefault();
event.stopImmediatePropagation();
}
protected function textinput1_mouseDownHandler(event:MouseEvent):void
{
stopPropagation(event);
}
protected function textinput1_mouseUpHandler(event:MouseEvent):void
{
stopPropagation(event);
}
protected function application1_mouseDownHandler(event:MouseEvent):void
{
CursorManager.removeCursor(_cursorID);
_cursorID = CursorManager.setCursor(_fistIcon);
}
protected function application1_mouseUpHandler(event:MouseEvent):void
{
CursorManager.removeCursor(_cursorID);
_cursorID = CursorManager.setCursor(_handIcon);
}
]]>
</fx:Script>
<s:TextInput x="43" y="30"
rollOut="textinput1_rollOutHandler(event)"
rollOver="textinput1_rollOverHandler(event)"
mouseDown="textinput1_mouseDownHandler(event)"
mouseUp="textinput1_mouseUpHandler(event)"/>
<mx:TextInput x="43" y="70"
rollOut="textinput1_rollOutHandler(event)"
rollOver="textinput1_rollOverHandler(event)"
mouseDown="textinput1_mouseDownHandler(event)"
mouseUp="textinput1_mouseUpHandler(event)"/>
You can simply do not start drag and do not change the cursor if user clicks on input:
protected function application1_mouseDownHandler(event:MouseEvent):void
{
var container:DisplayObjectContainer = event.target as DisplayObjectContainer;
if (!container || container == textInput || textInput.contains(container))
return;
// start drag and change the cursor
}
I had a similar problem but I have several TextInput fields in the container. So to avoid checking every single of them I used this version of the idea:
if (event.target is RichEditableText) return;
Works perfectly...
Greetings, J!

Resources