I want to have a tooltip that shows different things when the mouse goes over a different part of a component. For example if it is the top half of a component it will show one tooltip. If the mouse is on the bottom half of the segment then the tooltip will be another. I have some code I have written that returns a panel with string in. This code is on another computer so I'll post code tomorrow.
Is it possible in ActionScript to have different tooltips (or rather differnt values in a tooltip) for different parts of a segment?
The code I have so far is:
MyToolTip.mxml
<?xml version="1.0"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
implements="mx.core.IToolTip"
alpha=".9" width="325" borderColor="black" borderStyle="solid"
cornerRadius="10" horizontalAlign="center">
<mx:Script><![CDATA[
[Bindable]
public var toolTipText:String = "";
public var _text:String;
[Bindable]
public function get text():String { return _text; }
public function set text(value:String):void {}
]]></mx:Script>
<mx:HBox width="100%" height="100%">
<mx:Text text = "Text here" width = "50%"/>
<mx:Text text = "{toolTipText}" width = "50%"/>
</mx:HBox>
</mx:Panel>
And then my action script class component that I want the tooltip to be against.
public class MyComponent extends mx.containers.VBox {
private var tt:MyToolTip
public function MyComponent() {
this.addEventListener(ToolTipEvent.TOOL_TIP_CREATE, toolTipCreateHandler);
this.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
tt = new MyToolTip();
}
override protected function drawFigure():void {
//Need to kick the TOOL_TIP_CREATE event...and needs to be a value (eg a SPACE).
//If blank then no tooltip is created
this.toolTip = " ";
super.drawFigure();
}
private function toolTipCreateHandler(event:ToolTipEvent):void {
var toolTipText:String = "tooltip1";
eventToolTip.toolTipText = toolTipText;
event.toolTip = tt;
}
private function mouseOverHandler(event:MouseEvent):void {
//perhaps I need to be more efficient here and only fire
//when the mouse goes into top half or bottom half
//This does not appear to update the toolTipText in the view
var halfwayUp:Number = getBounds(this).height / 2;
if (event.localY < halfwayUp) {
eventToolTip.toolTipText = "tooltip2";
}
else {
eventToolTip.toolTipText = "tooltip1";
}
}
}
}
Any help or pointers in how to update the tooltip when it is already displaying would be great.
Yes, its possible, the trick is to know how tooltips work:
Tooltips get created, if you mouse over a component, and are destroyed if you mouse out. So if you change the text on a tooltip while its displayed, then you wont see the change, because the set toolTip() function does not creates a new tooltip, if one already exists. So the solution is to destroy the currently showing tooltip, and make a new one. To destroy a tooltip, you can set its value to an empty string.
Here is a sample code:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
mouseMove="application1_mouseMoveHandler(event)">
<mx:Script>
<![CDATA[
import mx.managers.ToolTipManager;
protected function application1_mouseMoveHandler(event:MouseEvent):void{
if (mouseX < 100) {
testButton.toolTip = ""
testButton.toolTip = "on the left side";
} else {
testButton.toolTip = ""
testButton.toolTip = "on the right side";
}
}
]]>
</mx:Script>
<mx:Button id="testButton" label="test" width="200" height="200" />
</mx:Application>
Note: If you want to mess more with tooltips in Flex, you can get the current tooltip with ToolTipManager.currentToolTip (and modify its properties without destroying it).
Related
My datagrid prints starting at the very top of the page. I can't figure out how to move the datagrid down. I don't see anything in FlexPrintJob or PrintAdvancedDataGrid that will do this. Do I have to create a blank object to add to the top of my FlexPrintJob object?
Any help or a link that will help.
Thanks,
John
After trying a lot of examples form adobe and others that did not work at all for me, I figured out that I had to put the datagrid in a vBox where I could put a blank header above the datagrid. As all of the examples showed, this was best done with a vBox component.
Below is a working example of what I came up with, which is a bit different than the examples I found. In the vBox component you can do a lot of things to enhance the print job. In my case I added a title and date.
Hope this helps someone out there,
John
I am tusing a generic function to print any AdvancedDataGrid I might have in my application...
public function printAdvancedDataGridContents(advDG:AdvancedDataGrid, xmlListCollection:XMLListCollection, headerText:String):void
{
const printJob:FlexPrintJob = new FlexPrintJob();
if ( printJob.start() ) {
//create an instance of the FormPrintView_ADG component containing the datagrid
var thePrintView:FormPrintView_ADG = new FormPrintView_ADG();
//add the component to my application
FlexGlobals.topLevelApplication.addChild(thePrintView);
//load the datagrid
thePrintView.printDataGrid.dataProvider = xmlListCollection.copy();
//format the datagrid
thePrintView.printDataGrid.width = printJob.pageWidth-45; //set a left margin for the dg in a right justiified vBox
thePrintView.printDataGrid.height = printJob.pageHeight-73; //page adjusted for header title and date
thePrintView.printDataGrid.setStyle("fontSize", 8);
thePrintView.printDataGrid.columns = advDG.columns;
thePrintView.printDataGrid.setStyle("fontFamily", 'Times');
thePrintView.printDataGrid.setStyle("color", 000000);
//set the header text
thePrintView.headerText.height = 45;
thePrintView.headerText.width = printJob.pageWidth-20;
thePrintView.headerText.text = "\r"+headerText;
//add the first page to the print job
printJob.addObject(thePrintView, FlexPrintJobScaleType.NONE);
while (thePrintView.printDataGrid.validNextPage) {
// Move the next page of data to the top of the PrintDataGrid and add it to the printjob
thePrintView.printDataGrid.nextPage();
printJob.addObject(thePrintView, FlexPrintJobScaleType.NONE);
}
//print it and remove the component from my application
printJob.send();
FlexGlobals.topLevelApplication.removeChild(thePrintView);
}
}
Here is the vBox component I am using...
<?xml version="1.0"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
horizontalAlign="right"
creationComplete="init();">
<mx:Script>
<![CDATA[
[Bindable] private var date:String;
private function init():void{
date = new Date().toString();
date = df.format(date);
}
]]>
</mx:Script>
<mx:DateFormatter id="df" formatString="EEEE, MMMM D, YYYY"/>
<!-- this header can contain a title or be left blank to create a top margin -->
<mx:TextArea id="headerText" borderThickness="0" color="#000000" fontWeight="bold"
textAlign="center" textDecoration="none"/>
<!-- date label. set visible to false in the calling function if not needed -->
<mx:Label color="#000000" fontSize="8" fontStyle="normal" fontWeight="normal" text="{date}"/>
<!-- the data grid -->
<mx:PrintAdvancedDataGrid id="printDataGrid"/>
</mx:VBox>
Reward: Has been claimed.
Overview: The code giving me the problem is deployed here: http://www.johnuckele.com/MastersOfTime.html
The problem I am running into is that a certain sequence of actions (I don't fully understand how or why) is causing my calls to invalidateDisplayList to fail to produce a subsequent call to updateDisplayList. What I know is that during this period, some other visual effects will fail to occur (such as changing the width of a component or the addition of a new child).
Example: The program below draws two columns of horizontal lines. The column on the left is drawn during commitProperties, the column on the right is drawn during updateDisplayList. A certain sequence of actions can cause the right column to stop updating.
To trigger this bug: First add a new item. Now hit the start button and a bar starts filling up. If you press the add row button, the right column and the filling bar both stop growing. The left column continues unfettered. The extra component won't appear until the last line of the if statement in TEComputeRow.tick() doesn't execute for a frame. Click on the stop button to halt the execution of the block inside the if statement in TEComputeRow.tick() and everything goes back to normal.
Question: What is going on here?
I can force it to behave by using validate now but it doesn't cure the problem, it merely covers it up for a frame. It also seems like a really sloppy hack. Is there a nicer way to deal with the loss of updateDisplayList than using validateNow? Are there any ways to accurately identify the state of the world?
MastersOfTime.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
backgroundColor="white"
backgroundGradientAlphas="[1,1]"
initialize="init()"
enterFrame="tick()"
creationComplete="addComputeArray()">
<mx:Script>
<![CDATA[
import mx.containers.HBox;
import mx.controls.Button;
import mx.containers.VBox;
import flash.utils.getTimer;
private var global:int = 0;
private function addComputeArray():void
{
var addButton:Button = new Button;
addButton.label = "Add Row Item";
addButton.addEventListener(MouseEvent.CLICK, addComputeBox);
box.addChild(addButton);
}
private function addComputeBox(a:* = null):void
{
box.addChild(new TEComputeRow());
}
private function init():void
{
box.clipContent = false;
box.graphics.lineStyle(1);
}
private function tick():void
{
global++;
this.invalidateDisplayList();
this.invalidateProperties();
//this.validateNow();
}
protected override function commitProperties():void
{
super.commitProperties();
box.graphics.moveTo(100, (global*3)%800);
box.graphics.lineTo(200, (global*3)%800);
}
protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
box.graphics.moveTo(200, (global*3)%800);
box.graphics.lineTo(300, (global*3)%800);
}
]]>
</mx:Script>
<mx:VBox id="box"/>
</mx:Application>
TEComputeRow.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
height="60"
width="352"
verticalGap="0"
borderStyle="solid"
enterFrame="tick()">
<mx:Script>
<![CDATA[
public var doStuff:Boolean = false;
private var parameter:Number = 0;
private function tick(e:Event = null):void
{
var value:*;
if(doStuff)
{
parameter = parameter+1;
value = parameter;
fill.width = value;
}
}
]]>
</mx:Script>
<mx:Button label="turn on" click="{doStuff = true;}" height="20"/>
<mx:Container id="fill" x="7" width="0" height="20" backgroundColor="0x8888AA"/>
<mx:Button label="turn off" click="{doStuff = false;}" height="20"/>
</mx:VBox>
Well for starters, you are abusing the Flex life-cycle horribly, and doing things you are not meant to do... Changing the width of the fill in the tick of the row kicks off another invalidation cycle is the one that immediately jumps out. If you drove things via the Timer rather than on enterFrame, you'd immediately be better off.
My guess is that you spend so much time per frame re-invalidating the properties (changing the width will invalidate properties), the player never fits in the updateDisplayList.
Read up on both the elastic race track, and Deepa's presentation on the Flex 3 lifecycle.
The use of .width is what is triggering this problem. If I replace .width with .setActualSize the problem stops. These pieces of code travel through separate paths and .width and .height apparently have the capacity to skip part of the frame cycle (the updateDisplayList part).
I'm displaying a list of buttons, some of which might be disabled. I need to show a tooltip on the disabled buttons with an explanation of why it's disabled, but it seems I can't disable the button without disabling the tooltip. Is there a simple way around this?
Wrap the Button in a Group, and apply the toolTip to the group instead.
<s:Group toolTip="My toolTip">
<s:Button enabled="false"/>
</s:Group>
It's a bit ugly, but it works.
One way to do this is to override the enabled getter and setter to do what you want. So in my case, I still wanted most mouse events to fire, just not the click event.
<?xml version="1.0" encoding="utf-8"?>
<s:Button buttonMode="true" click="handleClick(event)" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
public var data:Object;
private var _enabled:Boolean = true;
public override function get enabled():Boolean
{
return _enabled;
}
public override function set enabled(value:Boolean):void
{
_enabled = value;
invalidateDisplayList();
dispatchEvent(new Event("enabledChanged"));
invalidateSkinState();
}
protected function handleClick(event:MouseEvent):void
{
if (!_enabled)
{
event.stopPropagation();
}
}
]]>
</fx:Script>
</s:Button>
Since mouse events now fire, the tooltips work again.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.managers.ToolTipManager;
import mx.controls.ToolTip;
private var tooltip:ToolTip;
private var p:Point;
private function whyDisable():void
{
//calculate the button position , so that roll over shows the tooltip
p=new Point();
p=localToGlobal(new Point(btn.x,btn.y));
if(btn.enabled==false)
tooltip = ToolTipManager.createToolTip('Button is disabled',p.x+(btn.width/2),p.y-20,'errorTipAbove') as ToolTip;
else
tooltip=ToolTipManager.createToolTip('Button is enabled',p.x+(btn.width/2),p.y-20,'errorTipAbove') as ToolTip;
}
]]>
</mx:Script>
<mx:VBox height="100%" width="100%" horizontalAlign="center" verticalAlign="middle">
<mx:Button id="btn" label="Show Tooltip" buttonDown="trace('ankur')" autoRepeat="true" enabled="true" rollOver="whyDisable();" rollOut="{ToolTipManager.destroyToolTip(tooltip);}"/>
</mx:VBox>
</mx:Application>
Hi, this application works on the disabled button,I used ToolTipManager to do this,
i hope this works for you
have a gr8 time
Ankur Sharma
The best choice for me was to put a void label around and in front of the element. Then, if necessary, I set the element to disable and the tooltip works in the label. If not, I put sent the label to back. It works pretty well.
if (new MainListsAdmin(this.mainApp).temInvestimentoComAqueleTipo(t)) {
deletarGroupInto.setTooltip(new Tooltip("Há investimentos vinculados a Tipo de Investimento.\nDeleção bloqueada."));
this.deletarButton.setDisable(true);
}else{
deletarGroupInto.toBack();
}
You will need to use the ToolTipManager class to create and destroy the tool tips manually.
This article should give you all the info you need to accomplish this:
http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf60d65-7ff6.html
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 many buttons in Main.mxml. I'm trying to move the button functionality into a Class and have Event Listeners inside the class respond to Click and call other functions. I have written:
Main.mxml
<mx:Button x="23.5" y="10" label="checker" click="{goListen()}" />
<mx:Button id="btnT1" x="252.5" y="10" label="t1" />
<mx:Button id="btnT2" x="309" y="10" label="t2"/>
<mx:Button id="btnT3" x="366" y="10" label="t3"/>
Main.as
private function goListen():void
{
var t:ButtonListener = new ButtonListener(btnT1, btnT2, btnT3);
}
ButtonListener.mxml
package com.util
{
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import mx.controls.Alert;
import mx.controls.Button;
public final class ButtonListener extends EventDispatcher
{
private var __btnArray:Array;
public function ButtonListener(...btnList)
{
__btnArray = new Array();
for each (var item:Button in btnList)
{
__btnArray.push(item);
}
buildListeners();
}
private function buildListeners():void
{
for each (var item:Button in __btnArray)
{
item.addEventListener(MouseEvent.CLICK, traceMe, false, 0, true);
}
}
private function traceMe(event:MouseEvent):void
{
trace(event.target.label + " was clicked");
}
}
}
so when I debug, I see the array filling up with the buttons, but the traceMe() function won't work. Not sure how I can get this to work. Or do I just have to have 30 event listeners and corresponding functions in the main class.
It looks like you have two different options or problems. If you change the last parameter in:
item.addEventListener(MouseEvent.CLICK, traceMe, false, 0, true);
to false, then everything should work because your event listener will stick around to handle the mouse clicks. Of courses, this means that if you click on your "checker" button a second time, you'll then have two sets of listeners responding to mouse clicks of buttons one, two, and three.
So, it's likely that the real solution you're interested in is leaving the line quoted above the same and instead changing the following line:
var t:ButtonListener = new ButtonListener(btnT1, btnT2, btnT3);
If you change the above line to store your button listener as a part of your class it will be available to respond to the mouse clicks, rather than having been garbage collected:
_buttonListener = new ButtonListener(btnT1, btnT2, btnT3);
That, of course, assumes that you have defined _buttonListener within an mx:script block:
<mx:Script><![CDATA[
var _buttonListener:ButtonListener;
]]></mx:Script>
EDIT per comment:
In the code provided, t, the ButtonListener, goes out of scope. When it does, it is garbage collected unless you use strong references, which you do not per the last parameter in your addEventListener call.
Thus, make the button listener a member of the main class:
Main.mxml would then read:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Button x="23.5" y="10" label="checker" click="{goListen()}" />
<mx:Button id="btnT1" x="252.5" y="10" label="t1" />
<mx:Button id="btnT2" x="309" y="10" label="t2"/>
<mx:Button id="btnT3" x="366" y="10" label="t3"/>
<mx:Script>
<![CDATA[
private var _buttonListener:ButtonListener;
private function goListen():void
{
_buttonListener = new ButtonListener(btnT1, btnT2, btnT3);
}
]]>
</mx:Script>
</mx:Application>
Since the event listener will no longer go out of scope, the weak references used by the event listeners will work as expected, being garbage collected when __buttonListener goes out of scope.
Since the click event of Button bubbles, you can just listen for a click event on the main application file and delegate to a handler function in a class.
Or you can call the handler directly on the click of your button.
private var controller:ButtonListener = new ButtonListener();
<mx:Button id="btnT1" x="252.5" y="10" label="t1" click="controller.handleClick(event)"/>