Last week I added an autoScroll property to that internal form, using code from Daryl Bantarri's AutoScroll utility class (a class that has a single static method). I had a slight problem with it that required a slight edit or two (breaking the method into a few more manageable functions). (and, of course, my edits had to conform to our internal architectural standards and naming conventions)... so if the "doFunctionName," "canRuleName" or redundant returns in the rule are annoying -- that's my fault --> I wrote a lot of those ridiculous rules over here.
Also, in the comments of Daryl's post, some talked of removing the updateComplete listener. We add a lot of form components dynamically to forms, so listening for anything after an updateDisplayList, etc., is a necessity for our framework. Feel free to get rid of that updateComplete listener if need be, especially if your intended use is to hard-code your components in the form.
A shout out to Patrick who is part owner of Webapper... fellow Northern Coloradan whom I've met a small handful of times at random local tech meetups.
private var _autoScroll : Boolean = false;
public function get autoScroll():Boolean
{
return _autoScroll;
}
public function set autoScroll(value:Boolean):void
{
_autoScroll = value;
if(value)
activateAutoscrollListeners();
}
private function activateAutoscrollListeners():void
{
var container : DisplayObjectContainer = this.parent;
if(container)
{
container.addEventListener(FlexEvent.UPDATE_COMPLETE, doAutoScroll);
container.addEventListener(FocusEvent.FOCUS_IN, doAutoScroll);
}
}
also, on creationComplete (or earlier, since we only need the this.parent in activeAutoScrollLIsteners()):
if(autoScroll)
activateAutoscrollListeners();
private function canAutoScroll(container:Container, focusObject:DisplayObject):Boolean
{
if(!focusObject)
return false;
if(!container)
return false;
if(!container.verticalScrollBar)
return false;
if(!this.contains(focusObject))
return false;
if(focusObject == this)
return false;
return true;
}
/**
* doAutoScroll is the eventListener for the parent container's updateComplete and focusIn. if(canAutoScroll), this method calls
* completeAutoScroll and updates the parent container's verticalScrollPosition.
*
*/
private function doAutoScroll(event:Event):void
{
var container : Container = Container(event.currentTarget);
//Don't want displayObject.Y of event.target. getFocus() is more accurate (i.e., mx.controls.combobox).
var focusObject : DisplayObject = DisplayObject(container.focusManager.getFocus());
if(canAutoScroll(container,focusObject))
{
var itemParent : DisplayObjectContainer = focusObject.parent;
var focusObjectTopY : int = (focusObject.parent == this) ? focusObject.y : addTotalYoffSets(focusObject);
if(container.verticalScrollPosition != focusObjectTopY)
{
completeAutoScroll(container, focusObjectTopY, focusObject);
}
}
}
private function addTotalYoffSets(focusObject:DisplayObject):int
{
var parentContainer : DisplayObjectContainer = focusObject.parent;
var focusObjectTopY : int = focusObject.y ;
while(parentContainer != this.parent) {
focusObjectTopY += parentContainer.y;
parentContainer = parentContainer.parent;
}
return focusObjectTopY;
}
private function completeAutoScroll(container:Container, focusObjectTopY:int, focusObject:DisplayObject):void
{
var focusObjectBottomY : int = focusObjectTopY + focusObject.height;
var lastVisibleY : int = container.height + container.verticalScrollPosition;
if(container.horizontalScrollBar)
lastVisibleY -= container.horizontalScrollBar.height;
if(focusObjectTopY < container.verticalScrollPosition)
{
container.verticalScrollPosition = Math.max( 0, focusObjectTopY - 5 );
}
else if(focusObjectBottomY > lastVisibleY)
{
//scroll down. +5 pixels for good measure.
var newPosition : int = Math.min( container.verticalScrollBar.maxScrollPosition, (container.verticalScrollPosition + (focusObjectBottomY - lastVisibleY)) );
container.verticalScrollPosition = newPosition + 5;
}
}