Showing posts with label work. Show all posts
Showing posts with label work. Show all posts

Wednesday, June 3, 2009

A better way to stop ComboBox from resizing itself automatically...

Adobe Flex 3

This is an update to my earlier post about mx:ComboBox which, if set to a percentage width, will by default always take the width of the widest line it contains, widening possibly its container and/or changing the container to be scrollable.

The solution I suggested earlier was not enough for example in case the ComboBox was on a panel which was not visible when it was created. Now I am using a different solution.

Basically you extend the ComboBox and override the protected measure() method to cancel out the setting of minimum width set by the original code which is the cause the ComboBox will force its width upon its container.

The following code has some more changes, like resizing the dropdown list to the width of the longest line it will display and also refusing even programmatic focusing when either disabled or hidden.

Did you know, that if the focus already is on the standard ComboBox, then disabling or even hiding it does not block the keyboard input? For example, the [Down] and [Up] keys still change the items within the invisible ComboBox with all the corresponding events triggering, and [CTRL-Down] will still open the dropdown list even though the ComboBox itself is hidden and disabled?

public class ComboBoxEx extends ComboBox
{
private var initDone:Boolean;
public var preferredDropdownWidth:Number;

public function ComboBoxEx()
{
super();
this.preferredDropdownWidth = NaN;
this.initDone = false;

this.addEventListener("dropdownWidthChanged",
function(event:Event):void { if (!initDone) preferredDropdownWidth = event.target.dropdownWidth; });

this.addEventListener(FlexEvent.INITIALIZE,
function(event:Event):void { initDone = true; });

}

/** A property indicating if the combobox should resize itself to the width of its contents */
[Inspectable(category="General", enumeration="true,false", defaultValue="false")]
public var resizeWidthToContent:Boolean = false;

/**
* A property indicating if the dropdown of the combobox should resize its width to the width of the longest
* label within the combobox'es data, though never below the width of the combobox itself.
* NOTE! that if you leave this property to its default value of true then the property dropdownWidth
* will not be respected if it happens to be smaller than the width of the longest label in the combobox.
*/

[Inspectable(category="General, enumeration="true,false", defaultValue="false")]
public var resizeDropdownWidthToContent:Boolean = true;

/**
* @private
*/

override protected function keyDownHandler(event:KeyboardEvent):void
{
// Handle keyboard only if we are enabled and visible!
if (super.enabled && super.visible)
super.keyDownHandler(event);
}

/**
* @private
*/

override public function setFocus():void
{
// Take focus only if we are enabled and visible!
if (super.enabled && super.visible)
super.setFocus();
}

/**
* The super method determines the measuredWidth and measuredHeight
* properties of the control.
* This version will reset the measuredWidth property to the value of
* UIComponent.DEFAULT_MEASURED_WIDTH unless the resizeWidthToContent
* property is set.
* @see mx.core.ComboBase#measure()
*/

override protected function measure():void
{
super.measure();
if (!this.resizeWidthToContent) measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH;
}

override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (this.resizeDropdownWidthToContent && isNaN(this.preferredDropdownWidth))
{
var width:Number = getContentMaxWidth().width + getStyle("arrowButtonWidth");
this.dropdownWidth = Math.max(this.width, width);
}
}

public function getContentMaxWidth():Object
{
return calculatePreferredSizeFromData(this.collection.length);
}

}

I also opened a bug about it.

Wednesday, January 21, 2009

How to stop ComboBox from resizing itself automatically...

Adobe Flex 3

One of the many grievances I have with the otherwise wonderful Adobe Flex is the way the ComboBox component with percentage width resizes itself to accommodate the data which is loaded into it. There seems to be no switch to disable this behavior and yet retain the scalability of the ComboBox control itself.

The problem manifests itself best when you have, for example, two adjacent ComboBox widgets both set to be 50% width and you assign to the first a list of rows with short names and to the other a list of rows with long names - both widgets will resize themselves to a different width to accommodate their respective contents.

After searching for and failing to find a solution to this, I decided to try and write a workaround myself. I needed to fixate the width of the comboboxes right after they were first displayed and before they got their data. There was no need for the combobox to be resized later. In order to do this, I extended the original ComboBox and added the following code to it:

First I added a property via which you can turn on or off the new functionality:
/**
* Property indicating if the initially measured width of the combobox will be
* fixed as soon as the combobox has been layed out.
* Only applies to combobox'es which have their width specified as a percentage.
*/

[Inspectable(category="General", enumeration="true,false", defaultValue="false")]
public var fixPercentWidthAfterInitialMeasurement:Boolean = false;

Second, to the constructor of the combobox, I added an event listener:
public function AComboBox()
{
super();
this.addEventListener(FlexEvent.UPDATE_COMPLETE, handleUpdateComplete);
}

Finally, I added the event handler method:
/**
* This eventhandler is called right before the widget is drawn on screen - it's size has already been calculated,
* but it has not yet received its contents, therefore now it is good time to convert its percentage width into
* an explicit width, so that future data loaded into the combobox would not resize the compobox causing scrolling.
*/

private function handleUpdateComplete(event:FlexEvent):void
{
if (this.fixPercentWidthAfterInitialMeasurement &&
(!isNaN(this.percentWidth))) this.explicitWidth = this.width;
}

The idea here is to capture the event which is dispatched after the ComboBox size has been determined, right before the combobox is drawn on screen. Each ComboBox can have its width specified either explicitly in pixels or as a percentage of the containers width. When the width of the ComboBox is set as a percentage, I now assign its actual pixel width to the explicitWidth property. This will fixate the width and also stop the ComboBox from automatically resizing itself.

That solved the situation for me - the ComboBox retained its size after the initial layout.

Tuesday, December 2, 2008

How to release software...

Today, in our team, we had an argument about how to manage the software quality.

I've previously worked for another organization where the releasing was done very differently. In there, the software was released file-by-file as opposed to by package. When you needed to make some changes to already existing piece of software, you checked out the relevant file(s), modified it (them) and committed the change(s) back, noting what revision did the new file(s) get. After that you created a change document and filled it with the name(s) of the file(s) which were changed, the new revision and the reason for the change. The software releasing team got the change document, checked out the necessary revisions you had written in the document, compiled them and released them.

In my current organization, the releasing is done monthly (sometimes even less frequently) in one big package. In addition to that, our Flex application is submitted also as a package, which we compile ourselves (the resulting SWF is released).

The problem we are facing has to do with release quality. Since the software is released as a package, it becomes vitally important to track which changes to the individual Flex source files get to go into the package and which must be left out, since they are not yet complete. Still, in development environment we need the latest state there is (so called nightly build). The question is - how to track the changes? How to differentiate the changes of the future from the immediate changes scheduled to be released next?

Normally, the development environment is compiled from the HEAD of the CVS - with all the latest changes. Up until recently the same HEAD build was also released. Since we now are doing more and more future developments that do not need to get in anytime soon, that started to create problems for us. For example, in order to demonstrate my new development, I had to commit the changes, so that the nightly build process would build it into the development environment. But this can no longer be the version that goes out as a release version, since my new development should not be released yet... How to solve this?

Some parts of our organization are using extensive branching. Any bugfixing to production code is performed on a branch which is later merged with the HEAD code... this is a very difficult process with lots of errors which we hoped to avoid.

My suggestion was to use a special tag to track the revisions of the source files which are used to compile the release SWF. The tag name might be anything, for example "GO". This tag is given to all the files that are scheduled to go out next - the release build process will only check out files with the "GO" tag (as opposed to the nightly build process, which checks out HEAD).

If there are any future changes which should not go into the next release, then those files are not to be tagged with the "GO" tag, so that tag will remain on the older revision of these files. Hence the release build process will ignore the future changes, while the nightly build process will compile them into the development environment.

In case there are any production bugfixing changes, which must go into the next release, then the new (fixed) revisions of these files should be given the "GO" tag - the release build process will then pick these changes up and compile them for the release SWF.

A more difficult situation is when there are future changes and immediate bugfixes to be performed on the same files. In such situation, branching might be the only answer, though I would do things differently:
  1. commit any uncommitted future changes (if any) to CVS;
  2. get the contents of the file revision with the "GO" tag into the current file;
  3. perform the bugfixing on that code, test the bugfix;
  4. commit the changes and move the "GO" tag to the new revison you just committed;
  5. now get the contents of the previous HEAD revision (before the commit in 4 above);
  6. perform the same bugfixing in this version and commit the changes back to the HEAD.

The result in CVS might look like this:
  • r1.4 - the old "GO" revision of the file
  • r1.5 - future changes without a tag
  • r1.6 - some more future changes without a tag
  • r1.7 - the r1.4 revision with the bugfix and with "GO" tag
  • r1.8 - the r1.6 revision with the bugfix, with the latest future changes, but without a tag

This however is complicated if the future changes encompass more than one file, in which case the "GO" revision contents must be restored for all the files involved. In order to make this easier, first tag the future changes with some other tag, such as "NEW", so you'll know which files to restore after you've done bugfixing.

Thats it. I don't know if the above made any sense at all, but this is how I would tackle this code quality issue while keeping a grip on my senses at the same time...