Displaying black and red ToolTips as speech baloons - apache-flex

I'm porting a card game from pure Flash/AS3 to Flex 4.5:
I'm almost done, but the "speech baloons" marked by the blue color in the screenshot above are missing.
Those "speech baloons" fade in, display red (if they contain hearts or diamonds char) or black text and finally fade out.
I'm trying to implement those as mx.controls.ToolTips and have prepared a simple test case, where 3 users are represented by smiley-buttons and you can push a "Talk"-button to make them talk:
<?xml version="1.0"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
width="400" height="300"
initialize="init();">
<fx:Declarations>
<s:Fade id="fadeIn" alphaFrom="0" alphaTo="1" duration="2000"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.managers.ToolTipManager;
private var i:uint = 0;
private function init():void {
ToolTipManager.enabled = false;
ToolTipManager.showEffect = fadeIn;
}
private function talk():void {
var str:String = 'Me plays 10' + (i % 2 ? '♥' : '♠');
// this does not make the ToolTip appear?
this['user' + (++i % 3)].toolTip = str;
// how to set color according to isRed(str)?
}
private function isRed(str:String):Boolean {
return (str.indexOf('♦') > 0 || str.indexOf('♥') > 0);
}
]]>
</fx:Script>
<s:Button id="user0" horizontalCenter="0" bottom="0" label=":-)" />
<s:Button id="user1" left="0" top="0" label=":-)" />
<s:Button id="user2" right="0" top="0" label=":-)" />
<s:Button right="0" bottom="0" label="Talk!" click="talk()" />
</s:Application>
Can anybody please give me hints?
How to make ToolTips appear at will? (and not just on mouse hover)
How to change their color (I only found how to set it once by CSS)
UPDATE:
I've tried the following
private var tip0:ToolTip;
private var tip1:ToolTip;
private var tip2:ToolTip;
private function talk():void {
var str:String = 'Me plays 10' + (++i % 2 ? '♥' : '♠');
var btn:Button = this['user' + (i % 3)];
var tip:ToolTip = this['tip' + (i % 3)];
tip = ToolTipManager.createToolTip(str, btn.x + 10, btn.y + 10, "errorTipBelow", IUIComponent(btn)) as ToolTip;
}
but this does not work too well - no effects, no disappearing (I guess I have to call destroyToolTip myself). I wonder if ToolTip can be (ab)used for my purpose of representing "speech baloons" in an elegant way at all...

Personally, I have found the tool tip system rather limiting, and any time I want to do something a bit more different, it just seems easier to implement it manually. Generally in this case I would add a PopUpAnchor control to the components that need these overlay displays. Then you have full manual control over what is shown, and exactly how it is shown.
http://blog.flexexamples.com/category/spark/popupanchor-spark/
There are quite a few ways to do this though as well as just building the tooltip component as a subclass of Group, adding it as a child, and keeping track of it.

Related

Flex spark list to render images

I am new in flex. Currently i am going to build a flex album, but I get a problem about
the render in image by spark list.
the problem is this last 2~3 thumbs can not be properly displayed, as you can see as follows:
http://www.j-rich.com/Problem
and it will look like:
http://www.j-rich.com/Problem/show.jpg
for the source code, you can right click and choose view source in the demo
Any suggestion will be appreciated, thank you very much.
Sincerely,
Yuan-Hsu Liao
Add cachePolicy="on" to your Image control in ItemRenderer. But I don't recomend to use this kind of huge images as thumbnails. Looks like Flash has some limitations in this field.
This is correct for me, my resolution is 1920*1080. I use FLEX SDK4.5.1, flashplayer 11.8.
By the way, you should better override the set data function in the ItemRenderer, and set img source in the function, like this:
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.FlexEvent;
override public function set data(value:Object):void {
super.data = value;
var url:String = value as String;
img.source = url;
positionImg();
}
private function positionImg():void
{
this.ima.width = 136;
this.ima.height = 105;
this.ima.x = 0;
this.ima.y = 0;
}
]]>
</fx:Script>
<s:Group id="group" x="0" y="0" width="170" height="85">
<s:Image id="img" x="0" y="0" scaleMode="letterbox"/>
</s:Group>
</s:ItemRenderer>
ItemRenderer is recycled, so it's better to do some stuff in the set data()
Check whether ur Padding given is Correct...or else Adjust the same.

How to detect if font is applicable on text layer or not before applying the font?

I created a panel in flex which will have 2 combo-boxes that will display system font family and font styles and when user selects any font family or style from the combo-box the font gets applied on the selected text layer. But when I try to apply few fonts for eg Euro sign then the text in the selected text layers become all rectangles. I found that Character panel of Photoshop does not apply the font(Euro sign) on the selected text layer.
Is there any method in flex which can help us detect whether a font is applicable on the selected text layer or not?
Thanks
For your Q. I need a method which can tell me either a system font can be applied or not?
Here I have sample where you can read System Font and check on combobox if that font is available in system font or not. Please find below code: -
<?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">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
private var systemFontsCollection:ArrayCollection = new ArrayCollection();
private function getSystemFont():void
{
var fonts:Array = Font.enumerateFonts(true).sortOn("fontName");
for (var i:int = 0; i < fonts.length; i++) {
systemFontsCollection.addItem(new String(fonts[i].fontName));
}
}
private function init():void
{
var fontName:String = fontName.selectedLabel;
if(systemFontsCollection.contains(fontName))
{
trace(" ----System Font is applied ----");
}
else{
trace(" ----System Font is NOT applied ----");
}
}
]]>
</fx:Script>
<mx:Form>
<mx:FormItem label="Font name:">
<mx:ComboBox id="fontName" change="init();" creationComplete="getSystemFont()">
<mx:dataProvider>
<fx:Array>
<fx:String>Arial</fx:String>
<fx:String>ArialEmbedded</fx:String>
<fx:String>Verdana</fx:String>
<fx:String>VerdanaEmbedded</fx:String>
</fx:Array>
</mx:dataProvider>
</mx:ComboBox>
</mx:FormItem>
</mx:Form>
</s:Application>

How do I prevent memory leaks in Flex when rerunning effects?

This application is a much simplified version of what I am trying to accomplish, some smoothing of some motion on the screen by using a composite animation and rerunning it as things change, and using bindings to set some of the values of the animation. The animation is periodically stopped, and then restarted. valueFrom is not set so a property value just continues from where it was towards a new goal. In this example, I'm animating a Rect's left/top towards mouse's X/Y to make it chase the mouse.
Problem is the memory of this application continues to grow if you wait a little while and continue to move the mouse. So what should I be doing that I am not?
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication
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="creationCompleteHandler(event)"
mouseMove="mouseMoveHandler(event)"
mouseOut="mouseOutHandler(event)"
>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
private static const RECT_HALF_SIZE:int = 5;
private static const NO_CHASE:int = -1000;
[Bindable]
private var lastMouseX:int = NO_CHASE;
[Bindable]
private var lastMouseY:int = NO_CHASE;
private var evalTimer:Timer = new Timer(100);
protected function creationCompleteHandler(event:FlexEvent):void
{
evalTimer.addEventListener(TimerEvent.TIMER, function(event:TimerEvent):void { evalChase(); });
evalTimer.start();
}
protected function mouseOutHandler(event:MouseEvent):void
{
lastMouseX = lastMouseY = NO_CHASE;
evalChase();
}
protected function mouseMoveHandler(event:MouseEvent):void
{
const reStart:Boolean = (lastMouseX == NO_CHASE);
lastMouseX = event.localX;
lastMouseY = event.localY;
if(reStart)
evalChase();
}
private function evalChase():void
{
doChase.stop();
doChase.end();
if(lastMouseX == NO_CHASE)
return;
doChase.play();
}
]]>
</fx:Script>
<fx:Declarations>
<s:Linear id="linearEaser" />
<s:Parallel
id="doChase"
duration="2000"
>
<s:Animate target="{chaser}" easer="{linearEaser}" >
<s:SimpleMotionPath property="left" valueTo="{lastMouseX-RECT_HALF_SIZE}" />
</s:Animate>
<s:Animate target="{chaser}" easer="{linearEaser}" >
<s:SimpleMotionPath property="top" valueTo="{lastMouseY-RECT_HALF_SIZE}" />
</s:Animate>
</s:Parallel>
</fx:Declarations>
<s:Rect
id="chaser"
top="0" left="0"
width="{2*RECT_HALF_SIZE}" height="{2*RECT_HALF_SIZE}"
>
<s:fill>
<s:SolidColor color="red" />
</s:fill>
</s:Rect>
</s:WindowedApplication>
Don't think you're actually doing anything wrong in this code, I copied into a new Flex 4.6 project and ran it, I could see what you're saying with regard to the ADL process itself increasing slightly in memory consumption over time, particularly after it stops motion then begins again, however I tried running this in Flash Builder with the profiler and it seems the swf itself isn't really leaking any memory, it stayed right around 1.1 MB for me (up to about 1.4, but then back down) it seems it had a fair amount of Vector.<*> objects in memory though I'm not clear on what is being stored in those objects.

Passing a state to the child button of a Flex4 ButtonBar

I have a Spark ButtonBar that has a custom skin, which defines a custom skin for the "middleButton" requirement. My CustomButtonBarSkin has a custom state, minimized, which I want to pass into my middleButton skin so it can modify its design.
Is it possible to do this? I can see that my button skin could use parentDocument.currentState to get the minimized state, but that's really ugly. Any way to pass a skin from the bar to the child button(s)?
I think you should extend default ButtonBar. Something like this:
package
{
import mx.core.IFactory;
import spark.components.ButtonBar;
[SkinState("minimized")]
[SkinState("minimizedDisabled")]
public class MinimizableButtonBar extends ButtonBar
{
public function MinimizableButtonBar()
{
super();
itemRendererFunction = defaultButtonBarItemRendererFunction;
}
[SkinPart(required="true", type="mx.core.IVisualElement")]
public var middleButtonMinimized:IFactory;
private var _minimized:Boolean;
[Bindable]
public function get minimized():Boolean
{
return _minimized;
}
public function set minimized(value:Boolean):void
{
if (_minimized == value)
return;
_minimized = value;
invalidateSkinState();
itemRendererFunction = defaultButtonBarItemRendererFunction;
}
override protected function getCurrentSkinState():String
{
if (_minimized)
return enabled ? "minimized" : "minimizedDisabled";
return super.getCurrentSkinState();
}
private function defaultButtonBarItemRendererFunction(data:Object):IFactory
{
var i:int = dataProvider.getItemIndex(data);
if (i == 0)
return firstButton ? firstButton : (_minimized ? middleButtonMinimized : middleButton);
var n:int = dataProvider.length - 1;
if (i == n)
return lastButton ? lastButton : (_minimized ? middleButtonMinimized : middleButton);
return (_minimized ? middleButtonMinimized : middleButton);
}
}
}
So using this code you can declare your custom skin with the following way:
<?xml version="1.0" encoding="utf-8"?>
<s:Skin alpha.disabledGroup="0.5" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Metadata>[HostComponent("MinimizableButtonBar")]</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" stateGroups="disabledGroup" />
<s:State name="minimized" stateGroups="minimizedGroup" />
<s:State name="minimizedDisabled" stateGroups="disabledGroup,minimizedGroup" />
</s:states>
<fx:Declarations>
<fx:Component id="firstButton">
<s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarFirstButtonSkin" />
</fx:Component>
<fx:Component id="middleButton">
<s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarMiddleButtonSkin" />
</fx:Component>
<fx:Component id="middleButtonMinimized">
<s:ButtonBarButton skinClass="MinimazedButtonBarMiddleButtonSkin" />
</fx:Component>
<fx:Component id="lastButton">
<s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarLastButtonSkin" />
</fx:Component>
</fx:Declarations>
<s:DataGroup height="100%" id="dataGroup" width="100%">
<s:layout>
<s:ButtonBarHorizontalLayout gap="-1" />
</s:layout>
<s:layout.minimizedGroup>
<s:VerticalLayout />
</s:layout.minimizedGroup>
</s:DataGroup>
</s:Skin>
Hope this solves your problem.
And if your minimized state is only about changing middle button skin you can remove all states related code both from custom component and from skin.
I was working with skinning the button bar recently and wanted to expand on/remove some of the default behavior. Rather than extend & overwrite or copy/paste the ButtonBar code I just rolled my own minimalistic component:.
public class HButtonBarGroup extends HGroup {
public function HButtonBarGroup() {
addEventListener(ElementExistenceEvent.ELEMENT_ADD, refreshSkins);
super();
gap = -1;
}
private function refreshSkins(event : * = null) : void {
var buttonCount : int = numElements;
for (var i : int = 0; i < buttonCount; i++) {
var button : Button = getElementAt(i) as Button;
var skinClass : Class
if ((buttonCount == 0) || (buttonCount > 2 && (i != 0 && i != buttonCount)))
skinClass = GreyButtonBarMiddleButtonSkin;
else if (i == 0)
skinClass = GreyButtonBarFirstButtonSkin;
else
skinClass = GreyButtonBarLastButtonSkin;
Button(getElementAt(i)).setStyle("skinClass", skinClass);
}
}
}
This would give you the ability to do most anything you want without having to tiptoe around ButtonBar, ButtonBarBase, and ButtonBarSkin - all unnecessary unless you want togglebutton/selectedIndex. IMO it is a pain to create buttons based on a dataProvider instead of just declaring buttons in MXML and assigning handlers and other properties there.
I recently needed to change skin on a component based on its parents state. I used the same solution I would have used in HTML, using CSS. In your case, something like:
s|ButtonBar:minimized s|ButtonBarButton {
skinClass: ClassReference("CustomButtonBarSkin");
}
s|ButtonBarButton {
skinClass: ClassReference("spark.skins.spark.ButtonBarMiddleButtonSkin");
}
:minimized is the Pseudo Selector (for States).
Unfortunately, this didn't seem to get picked up by child (bug?) unless I changed styleName on parent element on state change:
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"
styleName.normal="foo" styleName.minimized="foo"
>
Maybe there is some invalidate-method I should have called on parents state change instead to make child pick up the change, but merely change the styleName to something bogus did the trick.
This is maybe not a widely used technique in Flex due to the fact that Flex 3 only supported basic CSS selectors.
Maybe I'm not getting what you're trying to do exactly, but it seems fairly obvious and easy to me. Just have your custome button bar skin set the state of your middle button when the minimized state is active:
<s:Skin>
<s:states>
<s:State name="minimized" />
</s:states>
<s:ButtonBarButton currentState.minimized="someState" />
</s:Skin>
Get it?

Spark TextArea or RichText autosize

I have done lots of searching on this subject, but it seems what I am finding is either out of date or just does not seem to work.
With TextFields in the past, you could set the TextField to a certain width, set wordWrap to true and you would end up with a textfield that changed height according to the text you added.
Now I am trying to do this with either the Spark TextArea or RichText.
I tried this HeightInLines = NAN, but that seems to be out of date.
I also tried this routine:
var totalHeight:uint = 10;
this.validateNow();
var noOfLines:int = this.mx_internal::getTextField().numLines;
for (var i:int = 0; i < noOfLines; i++)
{
var textLineHeight:int =
this.mx_internal::getTextField().getLineMetrics(i).height;
totalHeight += textLineHeight;
}
this.height = totalHeight;
But the mx_internal is not in the Spark components.
I am trying to do this with AS3, not MXML. If anyone has any suggestions or links that could help me figure this out using AS3, I'd really appreciate it.
Been struggling with this all afternoon. But it looks like the RichEditableText component will autosize if you set its width and leave its height undefined.
This works fine:
<?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">
<s:TextArea updateComplete="event.currentTarget.heightInLines = NaN" />
</s:Application>
Found in comments here. You can do the same in ActionScript using the same updateComplete event.
This is how I set the height of a TextArea to fit its content when used inside an ItemRenderer (e.g. for a List component):
private function onUpdateComplete( e: Event ): void
{
// autoresize the text area
if ( theText ) {
var actualNumOfLines: int = theText.textFlow.flowComposer.numLines;
theText.heightInLines = actualNumOfLines;
invalidateSize();
}
}
ItemRenderer must have this property set:
<s:ItemRenderer ... updateComplete="onUpdateComplete(event)>
Maybe the updateComplete event is not the optimal trigger for auto-resize actions but works fine for me.
You can remove scrollers from TextArea's skin and it becomes autoresizable. You can download completed skin here: http://www.yumasoft.com/node/126
Here's a solution for spark text areas (it works as mx text areas do):
var ta_height:int;
for(var i:int=0; i < StyleableTextField(myTextArea.textDisplay).numLines; i++) {
ta_height += StyleableTextField(myTextArea.textDisplay).getLineMetrics(i).height;
}
myTextArea.height = ta_height;
This seems to work for me:
<s:TextArea id="testTextArea"
change="testTextArea_changeHandler(event)"
updateComplete="testTextArea_updateCompleteHandler(event)"/>
<fx:Script>
<![CDATA[
protected function testTextArea_changeHandler(event:TextOperationEvent):void
{
testTextArea.height = RichEditableText(testTextArea.textDisplay).contentHeight + 2;
}
protected function testTextArea_updateCompleteHandler(event:FlexEvent):void
{
testTextArea.height = RichEditableText(testTextArea.textDisplay).contentHeight + 2;
}
]]>
</fx:Script>
Been doing the same head banging over that s:TextArea, and then found out that this gets the job done :
<s:RichEditableText id="txtArea" left="0" right="0" backgroundColor="#F7F2F2"
change="textChanged()" />

Resources