CommitProperties in a custom component doesn't work with percent width - apache-flex

My problem is pretty much described in the title. Just a few details .. I'm creating a custom component (based on ComboBox) and overriding some base methods. Recently I've found it acts weird when I set it's width in percents, rather than a fixed width. It tends to continuously call the commitProperties method and fail in the end. When I set it's width to fixed value - all works like a charm. What am I missing to implement? Here's some code ..
override protected function commitProperties():void {
super.commitProperties();
//some stuff ...
TextInput(textInput).setSelection(cursorPosition, cursorPosition);
if (cursorPosition == textInput.text.length || cursorPosition == 0) {
TextInput(textInput).horizontalScrollPosition = (cursorPosition == 0) ? 0 : textInput.width;
}
}
Here, the textInput.width properly returning a calculated value, in both cases.
And, just for a case, if it's needed:
override protected function measure():void {
super.measure();
measuredWidth = 160;
}
I'm not sure if something else is needed .. just ask if so.
Thanks in advance :)

The issue is not obvious but it is generally a good practise to call super.commitProperties(); at the end of overridden commitProperties() implementation rather than at the beginning.
If your //some stuff... block invalidates properties it won't be handled correctly if you called super.commitProperties(); on the first line.

Unfortunately it's not possible to debug this kind of issue without a full code...
If you are extending Flex 3 MX ComboBox have a look at ComboBase.updateDisplayList() to understand when / how textInput is sized.
The textInput.width is updated in ComboBase.updateDisplayList() so you shouldn't rely on this value during commitProperties(). You can try moving your scroll position logic to the end of updateDisplayList() of your component (at this point width should be updated).
Good luck!

Related

Alternating row colors for GWT CellTable

I'm extending the GWT celltable, and I'd like to override the default row striping with my own styles. The documentation seems to indicate that the celltable should implement RowStyles interface, which I have:
#Override
public String getStyleNames(Object row, int rowIndex) {
if(rowIndex == 0 || rowIndex % 2 == 0)
return "even_row";
return "odd_row";
}
However, it's not applying the style - It's not even hitting the method. If I'm extending the GWT CellTable, then it should be calling this method to apply the row style, right?
Anyone have this working and can tell me what I'm missing...?
Have a look at this page, specifically the last post on the page. It gives a good example of how to do this.
Implement the RowStyles interface and call this:
this.setRowStyles(this);

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?

How can I get a datagrid to behave like the ctrl key is active?

I want my data grid to behave by default as if the user is holding the control key down. So when an item is clicked, then another item they are both part of the selection, clicking them again removes them from the selection.
I already have allowMultipleSelection = true but I can't seem to find any setting that does this. I'm working on the itemclick event in the meantime, but it seems like there might be an easy to use setting I'm missing.
Any thoughts?
You could also extend DataGrid and override the selectItem method like so:
override protected function selectItem(item:IListItemRenderer, shiftKey:Boolean, ctrlKey:Boolean, transition:Boolean = true):Boolean
{
return super.selectItem(item, shiftKey, true, transition )
}
Less code and less likely to have impact on other elements that might be listening for that MouseEvent.
You could try adding event listeners to the grid for MouseEvents (UP and/or DOWN) with the highest priority, stopping propagation, and redispatching a new MouseEvent with the same properties on the original event.target but this time with ctrlKey=true.
I'm not sure if it'll cause 10,000 other things to break.
I tried Nalandial's idea but had no luck...can't really intercept those events, but it got me going in the right direction. Worked a lot on this then found that the solution was a lot simpler than I was making it. I just needed to extend the dataGrid class and override two functions (mouseDownHandler and mouseClickHandler) adding the ctrlKey = true there then calling the rest of the function workes perfectly. In case you want to implement it, here's the code:
package com{
import flash.events.MouseEvent;
import mx.controls.DataGrid;
public class ForceCtrlDataGrid extends DataGrid{
public function ForceCtrlDataGrid(){
super();
}
override protected function mouseClickHandler(event:MouseEvent):void{
event.ctrlKey = true;
super.mouseClickHandler(event);
}
override protected function mouseDownHandler(event:MouseEvent):void{
event.ctrlKey = true;
super.mouseDownHandler(event);
}
}
}

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

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!

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();
}

Resources