How does one smooth an image used as a control skin? - apache-flex

I'm embedding an image like this:
[Embed(source="/tool_deleteUp.png")]
private static const c_deleteButton_styleUp:Class;
I'm using it like this:
_removeButton = new Button();
_removeButton.setStyle('upSkin', c_deleteButton_styleUp);
When I rotate the button, the image doesn't scale smoothly. I know the tricks one uses to scale an image loaded in an Image control, but I'm banging my head against a wall trying to figure out how to do it here.
Help!

a hacky way would be to traverse the children/grandchildren of the button, to find the corresponding Bitmap that is of type c_deleteButton_styleUp, and set its smoothing to true ... it is a big flaw of flex, that sometimes it requires classes for styling although some IDisplayObjectFactory would completely suffice for that purpose and would make your life a lot easier ... but life is life ...
don't know of a clean flex only way ... the only possibility i could think of, is to create an SWF, that contains your asset as a symbol, with smoothing turned on, and embed this symbol from that SWF ...
hope that helps ...
greetz
back2dos

A better, more general solution. Not only does it handle the above case, but it does it
Without subclasses
It works with any UIComponent, including IRawChildContainers (like Container), which hide skin children in rawChildren
It only smooths newly added items, instead of running every time the control updates.
public static function smoothChildBitmaps(object:UIComponent):void{
// Define a nested smooth method
function smoothChildren(val:UIComponent):void{
var childList:IChildList;
if(val is IRawChildrenContainer){
childList = (val as IRawChildrenContainer).rawChildren;
}
else{
childList = object;
}
for(var i:int = 0; i < childList.numChildren; i++){
var child:Bitmap = childList.getChildAt(i) as Bitmap;
if(child != null){
child.smoothing = true;
}
}
};
// Call the nested method on the object right away
smoothChildren(object);
// Set up an event handler to re-call the method when a child is added
object.addEventListener(
Event.ADDED,
function(args:Event):void{
smoothChildren(object);
}
);
}

Silly, but it works.
import flash.display.Bitmap;
import mx.controls.Button;
public class SmoothButton extends Button{
protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
for(var i:int = 0; i < this.numChildren; i++){
var child:Bitmap = this.getChildAt(i) as Bitmap;
if(child != null){
child.smoothing = true;
}
}
}
}

I do not know what you folks banged your head on no offense, just joking. I had a desk :-P
Anyway, those scripts presented are totally useless! For one thing you cannot cast a uicomponent to bitmap! The other thing is, that the seconds script with rawchildren ist more ore less the same. rawchildren gets your the children and the chrome of the container - that's it. If you have a button in a container you can try to smooth the chrome of the container - the rest stays the same since all casts to bitmap will fail.
So, did anyon rdfm?
On the other hand. One could try out stage.quality to stagequality.best. If you check the adobe forums http://forums.adobe.com/thread/427899 you will see that flex runs well - just air kind of ignores this :-(
If those two scripts ever worked - esp. the part with button as bitmap - I'll go and smash my head somewhere :-). I see NO WAY in the class hierarchy that this could work. After all, numChildren only gives you uicomponents or displayobjects but nothing more!

Related

Flex validation popups no longer showing up in popup dialogs?

I am working on an application which has quite a bit of field-validation in it. The validation works great and I am 1000000% sure the validation message popups were appearing earlier. Now I did quite a bit of work and refactoring. One of the things I changed was the way I open up Popups/Dialog. In order to have these centered over the entire application instead of the opening component I refactored the way I open dialogs. I used the source of the Alert as a base for this but extended it quite a bit as I was having other issues (Focus Manager etc.) (I am just mentioning this as I am assuming that my missing popups are related to this).
Here comes the code responsible for opening popups in my application:
public function show(realParent:Sprite,
displayParent:Sprite = null,
closeHandler:Function = null,
moduleFactory:IFlexModuleFactory = null):Dialog {
// Get the parent ...
// If none is set, use the top-level-application.
if (!displayParent) {
var sm:ISystemManager = ISystemManager(FlexGlobals.topLevelApplication.systemManager);
// no types so no dependencies
var mp:Object = sm.getImplementation("mx.managers.IMarshallPlanSystemManager");
if (mp && mp.useSWFBridge())
displayParent = Sprite(sm.getSandboxRoot());
else
displayParent = Sprite(FlexGlobals.topLevelApplication);
}
// Register for close-events, making sure the pop-up is closed.
if (closeHandler != null) {
this.addEventListener(CloseEvent.CLOSE, closeHandler);
}
// Setting a module factory allows the correct embedded font to be found.
if (moduleFactory) {
this.moduleFactory = moduleFactory;
} else if (realParent is IFlexModule) {
this.moduleFactory = IFlexModule(realParent).moduleFactory;
} else {
if (realParent is IFlexModuleFactory) {
this.moduleFactory = IFlexModuleFactory(realParent);
} else {
this.moduleFactory = FlexGlobals.topLevelApplication.moduleFactory;
}
// also set document if parent isn't a UIComponent
if (!parent is UIComponent) {
this.document = FlexGlobals.topLevelApplication.document;
}
}
// Make the dialog center itself relative to the parent.
PopUpManager.addPopUp(this, displayParent, true);
PopUpManager.centerPopUp(this);
return this;
}
What could be responsible for the Validation popups not showing up any more? Where should I look?
Chris
Ok ... so I figgured this out by myself again. I coould bang my head at the wall for taking so long for finding it though.
If I use the Spart forms, the FormItems and Forms themselves can define error text areas in order to output error messages. So as soon as the FormItem posesses a skin part with the id "errorTextDisplay" the error messages go there. I was now expecting that if there was no such part, the old notifications would be used ... nope.
After about 2-3 Hours of messing around with the code of FormItem and it's skins, I noticed that the "contentGroup" explicitly defined an attribute to suppress error tooltyips by setting showErrorTip to false. Simply removing the "errorTextDisplay" from the skin and changing the showErrorTip to true made my popups appear nicely :-)
Hopefully this post might help someone with the same problems.

how to find height of dynamic flex component used as a popup

I've created a custom component with several inline item renderers that I use as a tooltip. The height of the component is unknown, as the data contents of the component are not known until runtime.
However, when displaying the tooltip, occasionally it extends beyond the boundaries of the flash application, thus, I'd like to be able to detect this occurrence and reposition the tip.
The problem is that the height and width of the component are, apparently, not available until after being rendered by the popup manager. (i.e. they are always 0)
But, I do not know any way of finding out when the popup is actually rendered and, therefore, the height/width values available.
I tried adding a resize event listener to the component, but it doesn't appear to work, though I most certainly could be doing something wrong since it seems to me that the resize event only gives you the "oldWidth" and "oldHeight" of the object, which, at first display, would be 0...and useless to me.
Any ideas about how to proceed?
-----Edit-----
I have a base class like this:
public class TTComponent extends Canvas
{
var _parentC:UIComponent;
var popped:Boolean = false;
var timer:Timer;
var _comp:UIComponent;
public function set parentComponent(pC:UIComponent):void
{
_parentC = pc;
_parentC.addEventListener(MouseEvent.MOUSE_OUT, mouseOut);
_parentC.addEventListener(MouseEvent.MOUSE_OVER, mouseOver);
}
public function mouseOver(evt:MouseEvent):void
{
if (_parentC != null)
{
timer = new Timer(150,1);
_comp = this;
timer.addEventListener(TimerEvent.TIMER_COMPLETE, function( tevt:TimerEvent ):void
{
this.move( somex, somey);
if (popped != true)
{
PopUpManager.addPopUp(_comp, parentComponent );
popped = true;
});
timer.start();
}
}
public function mouseOut(evt:MouseEvent ):void
{
if ( timer )
{
timer.stop();
timer = null;
}
//If we popped up, remove the popup
if ( popped )
{
PopUpManager.removePopUp( _comp );
popped = false;
parentC .removeEventListener(MouseEvent.MOUSE_OUT, mouseOut);
parentC .removeEventListener(MouseEvent.MOUSE_OVER, mouseOver);
}
}
}
Then, an extended renderer like this:
<c:TTComponent name="T" 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="components.*">
<s:BorderContainer>
...about 30 labels grouped in various manners
...2 lists with inline item renderers
</s:BorderContainer>
</c:TTComponent>
Now, the code is called like this:
var w = new TTComponent();
w.data = data;
win.parentComponent = this;
This will add listeners to the mouse over and mouse out events on the parent, whatever it is, and then show or hide the tooltip accordingly.
------Edit------
Using a portion of what a commenter below suggested, this is the solution I came up with:
Inside the TTComponent class:
import flash.events.Event;
import mx.binding.utils.ChangeWatcher;
private var heightWatcher:ChangeWatcher;
public function set parentComponent
{
...
heightWatcher = ChangeWatcher.watch(this,'height',onSizeChange);
}
public function onSizeChange(evt:Event):void
{
if (this.height != 0)
{
....calculate the new component coords
this.move(newx, newy);
}
}
Note that this additional code doesn't bind to any component variable, it just adds a watcher on the component property.
You could also try binding your width and height. If these are made bindable in your class, flex will automatically adjust your popup's width and height.
When using mxml for your binding, you can just do something like this
<mx:YourComponent height="{HeightOfYourTooltip}" width="{WidthOfYourTooltip}"></mx:YourComponent>
You can also add a eventListener that listens to the change event if you want to reposition you component, like so
<mx:YourComponent height="{HeightOfYourTooltip}" width="{WidthOfYourTooltip}" change="yourComponentResizeHandler()"></mx:YourComponent>
If you are using a programmed approach, you should should use the changewatcher. Below is shown how you can use that.
ChangeWatcher.watch(YourComponent, "width", repositionHandler);
ChangeWatcher.watch(YourComponent, "height", repositionHandler);
If you want to watch for other variables or properties to change, be sure to add the [Bindable]-tag above your variables in your class, like this
[Bindable]
var myVariable:SomeVariable;
I hope this helps.
For displaying toolTip which controls you are using in itemRenderer? Text or Label?
Try to Listen update complete Event of that component. May this Help you. :)
This might be messy, but on the pop up component, you could add an event listener after complete is fired, if the height or width == 0 then you setTimeout() to a function after say 100ms until you get valid data.
Yes, I know it is a bit of a hack, but those will eventually report correctly measured values so it's not going to call that many times.
Just an idea if you are against a deadline or something like this isn't critical. :)

Flex 4 lifecycle and drawing stuff manually

I have a component which inherrits Group. I made a property called dataSource:ArrayList. I wish to draw a Line for each of the entries.
When the 'function set dataSource' -method is invoked I do the following (simplified):
var newLine:Line = new Line();
newLine.stroke = new SolidColorStroke();
newLine.xFrom = 0;
newLine.yFrom = 0;
newLine.xTo = 0;
newLine.yTo = height;
this.addElement(newLine);
The line doesn't stretch itself to the very bottom of the parent. I'm guessing I'm messing up life cycle, but I'm not finding flex life cycle particular easy to understand, so I'm not sure how to go about this.
If you don't want to interact with the line as an object on the display list, I'd simply draw it in updateDisplayList() using the Graphics api, and call invalidateDisplayList() from set dataSource()
The "right" way is slightly more verbose ;-)
private var dataSourceValid = true;
public function set dataSource(value:FooData):void {
_dataSource = foo;
dataSourceValid = false;
invalidateProperties();
}
override protected function commitProperties():void {
if (!dataSourceValid)
commitDataSource();
// Do it later in case we've invalidated something
// belonging to Flex while validating our stuff
super.commitProperties();
}
protected function commitDataSource():void {
// Do whatever we need to with our datasource,
// including adding or removing child elements.
// ...
// If we also need to re-draw something, then
// invalidateDisplayList();
dataSourceValid = true;
}
(All code typed in TextMate, so it's probably full of spelling errors and doesn't compile, but you get the idea)
I agree with you, it probably has to do with the component not being properly measured yet when you create your lines. You could try overriding updateDisplayList and setting the height of the lines you created to be the height parameter supplied to the updateDisplayList method. Don't create the lines in updateDisplayList since it can get called multiple times during the component life cycle. Regarding the life cycle in general, here's a link to a chart I've found helpful in the past: http://danorlando.com/?p=122 Hope that helps.
It isn't entirely clear what you're looking to do, but by putting a dataSource property on Group it appears as if you're trying to re-invent DataGroup. Perhaps you should consider just using the latter instead, along with a custom ItemRenderer within which you could draw a line?

unable to get focus on canvas

I am creating a canvas in actionscript like :
private var cvs_preview:Canvas = null;
private function show_preview():void
{
this.cvs_preview = new Canvas();
this.cvs_preview.id = "cvs_preview_1";
this.cvs_preview.setStyle('backgroundColor', 0x000000);
this.cvs_preview.setStyle('backgroundAlpha', 1);
this.cvs_preview.setStyle('borderColor', 0x417FDD);
this.cvs_preview.setStyle('cornerRadius', 10);
this.cvs_preview.setStyle('borderStyle', 'solid');
this.cvs_preview.setStyle('dropShadowEnabled', true);
var pt:Point = image.localToGlobal(new Point(image.x, image.y));
this.cvs_preview.x = pt.x - 50;
this.cvs_preview.y = pt.y - 50;
this.cvs_preview.height = 200;
this.cvs_preview.width = 250;
//this.cvs_preview.addEventListener(FlexEvent.CREATION_COMPLETE, get_focus_on_canvas);
//this.cvs_preview.focusManager.setFocus(
//this.cvs_preview.addEventListener(MouseEvent.CLICK, end_preview_on_focus_change);
this.cvs_preview.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, end_preview_on_focus_change);
Application.application.addChild(this.cvs_preview); //add as top-most visible container
btn_mini_preview.enabled = false;
}
So on the focus change i want to run the "end_preview_on_focus_change()"
but this is not working.
As per my understanding, i think the canvas not getting any focus in the first place. I was trying to use focusManager.setFocus to do that after the canvas's creation complete. but even that is giving me an error.
the code i was trying on Creation.Complete is :
private function get_focus_on_canvas(e:FlexEvent)
{
focusManager.setFocus(e.target);
//Alert.show("testing img complete");
}
this is giving me an error "1118: Implicit coercion of a value with static type Object to a possibly unrelated type mx.managers:IFocusManagerComponent."
basically i just want to use the focus out event of the canvas.
Can someone help me with this...
I have been on this issue since a long time.
Regards
Zeeshan
The error is correct. You have an object of type Object which you are trying to use as an IFocusManagerComponent. This will not work. To accomplish that line of code, you need to do something like
focusManager.setFocus( IFocusManagerComponent( e.target ) );
This, of course, assumes that the target implements IFocusManagerComponent. It will give you an error otherwise (and likely will in this case because Canvas is not listed as an IFocusManagerComponent). The good news is that Canvas does have a drawFocus method which will accomplish the same thing.
As to your MOUSE_FOCUS_CHANGE event, that will only be fired if an object already HAS focus and then loses it. I think you are better off using FlexEvent.CREATION_COMPLETE. This will ensure that the component has registered itself with all of the appropriate classes in the Flex SDK so that the FocusManager can even be aware of the new object. Whatever you do, do not try to set focus on something which has not been added to the stage (ie: Event.ADDED has been called).
As another piece of advice -- Event.ADDED bubbles, make sure that event.currentTarget == event.target to make sure that you are listening to the correct object. Otherwise, you might be calling the same function multiple times erroneously.
Only a few classes implement IFocusManagerComponent as others mentioned and Canvas is not one of them. If you really must call FocusManager.setFocus() you will have to extend the canvas class to implement this interface and use that class instead. You don't have to write any methods to implement this interface, all methods have already been implemented by UIComponent itself
//FocusableCanvas.as (include appropriate package and import statements)
public class FocusableCanvas extends Canvas implements IFocusManagerComponent
{
public function FocusableCanvas()
{
super();
}
}
//Now use this class instead of Canvas
this.cvs_preview = new FocusableCanvas();
//setFocus in creation complete handler
FocusManager.setFocus(IFocusManagerComponent(e.target));
But if all you want to do is to set focus on the canvas upon it's creation, you can call canvas.setFocus() from the creationComplete handler instead.
private function get_focus_on_canvas(e:FlexEvent)
{
Canvas(e.currentTarget).setFocus();
trace("done");
}
I see two problems, and no perfect solutions. With any luck, this can help you out.
First of all, e.target returns an object typecast with type Object. This explains your implict coercion error, because Object does not implement IFocusManagerComponent.
Second, iFocusManagerComponent is only implemented by Accordion, AdvancedListBase, Button, ButtonBar, ChartBase, ComboBase, DateChooser, DateField, HTML, ListBase, MenuBar, NumericStepper, TabNavigator, TextArea, TextInput, UIMovieClip as per this entry in the Flex 3.4 AS3 Reference.
This leads me to believe that a Canvas element cannot take focus and has simply inherited access to the FocusManager through inheritance of UIComponent.
The only solutions I can see are to utilize something other than Canvas to handle your focus related concerns, or subclass Canvas and implement iFocusManagerComponent, though that looks fairly complex.
Edit
Apologies for missing drawFocus in the above solution.
Please try;
private function get_focus_on_canvas(e:FlexEvent)
{
this.cvs_preview.setFocus();
}

drag-and-drop problem in a Tilelist using an ItemRenderer

in my flex application, I created a Tilelist. In this Tilelist, I am using an ItemRenderer to create a box consisting of an image and an VSlider in each tile.
The tile need to be dragable when you click on the image, but not dragable when you slide the slider. How can I achieve this ? I have been scratching my head searching on Google for one day and I have really no idea.
I look forward to your help.
Thank you.
I found a solution to my problem, however it may not be the best one.
Using this :
public var overImage:Boolean = false;
public function checkAllow(evt:DragEvent):void {
if(overImage == false)
{
evt.preventDefault()
}
}
public function isOverImage():void {
overImage = true;
}
public function isOutImage():void {
overImage = false;
}
I call those functions like this :
On my image component
mouseOver="outerDocument.isOverImage()" mouseOut="outerDocument.isOutImage()"
And for my tilelist I did this
Tiles.addEventListener(DragEvent.DRAG_START, checkAllow);
Hope it helps some people.
if(event.target is ScrollThumb )
{
return;
}
problem solved by returning the scrollThumb property of sroller in imageDragStart method...
BackGround:My TileList is provided with mouseDown ="event.currentTarget.addEventListener(MouseEvent.MOUSE_MOVE, imageDragStart )";
which was effecting complete TileList along with scroller ,
This above was the temp fix, but expecting experts suggestions.
Basically this is for Native application (AIR), used NativeDragStart. but am forced to use mouseDown over my TileList to invoke imageStartDrag() method of mine....

Resources