Skip to content

Instantly share code, notes, and snippets.

@newtriks
Created November 29, 2011 13:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save newtriks/1404826 to your computer and use it in GitHub Desktop.
Save newtriks/1404826 to your computer and use it in GitHub Desktop.
package
{
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import mx.controls.List;
import mx.controls.listClasses.ListRowInfo;
import mx.core.mx_internal;
import mx.events.ScrollEvent;
import mx.events.ScrollEventDetail;
use namespace mx_internal;
public class CustomList extends List
{
public function CustomList()
{
super();
offscreenExtraRowsOrColumns = 2;
}
override public function get verticalScrollPosition():Number
{
if ( !isNaN( fudge ) )
{
var vsp:Number = super.verticalScrollPosition + fudge;
fudge = NaN;
return vsp;
}
return Math.floor( super.verticalScrollPosition );
}
protected function getMeasuredHeight( maxHeight:int ):Number
{
/*if the collection has only one row, we ignore the
maxHeight by setting it back to its default. One row
cannot scroll, setting a max on that for one very
large row (that is more than maxHeight pixels long)
will force the row to be clipped */
var count:int = ( collection )?collection.length:0;
if( count < 0 ) count = 0;
if( collection && count == 1 ) maxHeight = DEFAULT_MAX_HEIGHT;
if( contentHeight >= maxHeight ) return maxHeight;
var hh:int = 0;
if( !rowInfo || rowInfo.length == 0 )
{
if( collection )
contentHeight = Math.min(maxHeight,count * 20);
else
contentHeight = 0;
return contentHeight;
}
/* keep on increasing the height until either we run out
of rows to draw or maxHeight is reached */
var len:int = Math.min( rowInfo.length, count );
for( var i:int=0;i<len;i++ )
{
if( rowInfo[i] && ListRowInfo( rowInfo[i]).uid )
hh += ListRowInfo( rowInfo[i] ).height;
}
/* if hh is less than maxHeight and we still have rows to
show, increase the height */
if( hh < maxHeight && rowInfo.length < count )
{
/* if we have already drawn all the rows without
hitting the maxHeight, we are good to go */
hh = Math.min( maxHeight,hh + ( count - rowInfo.length ) * 20 );
}
contentHeight = Math.min( maxHeight, hh );
return contentHeight;
}
protected function measureHeight():void
{
var buffer:int = (this.horizontalScrollBar!=null ) ? this.horizontalScrollBar.height : 0;
var maxContentHeight:int = maxHeight - buffer;
var listContentHeight:int = buffer + getMeasuredHeight( maxContentHeight );
var hh:int = listContentHeight + 2;
if( hh == this.height ) return;
listContent.height = listContentHeight;
this.height = hh;
if( height >= maxHeight )
this.verticalScrollPolicy = "auto";
else
this.verticalScrollPolicy = "off";
}
/**
* Override of the corresponding method in mx.controls.ListGrid.
* After drawing the rows, it calls measureHeight to figure out
* if the height of the grid still needs to be adjusted.
*/
protected override function makeRowsAndColumns( left:Number, top:Number, right:Number, bottom:Number, firstCol:int, firstRow:int, byCount:Boolean=false, rowsNeeded:uint=0.0 ):Point
{
var p:Point = super.makeRowsAndColumns( left, top, right, bottom, firstCol, firstRow, byCount, rowsNeeded );
measureHeight();
return p;
}
/**
* Override of the method from ListBase.configureScrollBars.
* We copy the method but make one significant change - we comment
* out the code that pushes the scroll bar up if any filler rows
* are present. With our variableRowHeights, this code sometimes
* pushes up the vertical scroll when the user is trying to scroll
* down. In the worst case, it doesn't allow the user to see the
* last few rows.
*/
override protected function configureScrollBars():void
{
var rowCount:int = listItems.length;
if( rowCount == 0 ) return;
// ignore nonvisible rows off the top
var yy:Number;
var i:int;
var n:int = listItems.length;
// if there is more than one row and it is a partial row we dont count it
while( rowCount > 1 && rowInfo[n - 1].y + rowInfo[n-1].height > listContent.height - listContent.bottomOffset )
{
rowCount--;
n--;
}
/* offset, when added to rowCount, is the index of the dataProvider
item for that row. IOW, row 10 in listItems is showing dataProvider
item 10 + verticalScrollPosition - lockedRowCount - 1; */
var offset:int = verticalScrollPosition - lockedRowCount - 1;
// don't count filler rows at the bottom either.
var fillerRows:int = 0;
// don't count filler rows at the bottom either.
while( rowCount && listItems[rowCount - 1].length == 0 )
{
if( collection && rowCount + offset >= collection.length )
{
rowCount--;
++fillerRows;
}
else
{
break;
}
}
if( listContent.topOffset )
{
yy = Math.abs( listContent.topOffset );
i = 0;
while( rowInfo[i].y + rowInfo[i].height <= yy )
{
rowCount--;
i++;
if( i == rowCount )
break;
}
}
var colCount:int = listItems[0].length;
var oldHorizontalScrollBar:Object = horizontalScrollBar;
var oldVerticalScrollBar:Object = verticalScrollBar;
var roundedWidth:int = Math.round(unscaledWidth);
var length:int = collection ? collection.length - lockedRowCount: 0;
var numRows:int = rowCount - lockedRowCount;
setScrollBarProperties( ( isNaN( _maxHorizontalScrollPosition ) ) ?
Math.round( listContent.width ) :
Math.round( _maxHorizontalScrollPosition + roundedWidth ),
roundedWidth, length, numRows );
maxVerticalScrollPosition = Math.max( length - numRows, 0 );
}
/**
* displayWidth is a private variable in mx.controls.ListBase. We
* need to create it here so that we can use it
*/
protected var displayWidth:Number;
/**
* We need to override the updateDisplayList so that we can set the
* displayWidth.
* See the displayWidth variable in mx.controls.ListBase
*/
protected override function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void
{
if( displayWidth != unscaledWidth - viewMetrics.right - viewMetrics.left )
displayWidth = unscaledWidth - viewMetrics.right - viewMetrics.left + 5;
super.updateDisplayList( unscaledWidth, unscaledHeight );
}
override protected function mouseDownHandler( event:MouseEvent ):void
{
// Stop RB deselection by cancelling this event
if( event.target is mx.controls.listClasses.ListBaseContentHolder )
event.stopImmediatePropagation();
else super.mouseDownHandler( event );
}
override protected function scrollHandler( event:Event ):void
{
/**
* Going backward is trickier. When you cross from, for instance 2.1 to 1.9, you need to
* convince the superclass that it is going from 2 to 1 so the delta is -1 and not -.2.
* We do this by adding a fudge factor to the first return from verticalScrollPosition
* which is used by the superclass logic.
*/
var last:Number = super.verticalScrollPosition;
var vsp:Number = verticalScrollBar.scrollPosition;
if ( vsp < last )
{
if ( last != Math.floor( last ) || vsp != Math.floor( vsp ) )
{
if ( Math.floor( vsp ) < Math.floor( last ) )
{
fudge = Math.floor( last ) - Math.floor( verticalScrollBar.scrollPosition );
}
}
}
super.scrollHandler( event );
var pos:Number = super.verticalScrollPosition;
/**
* If we get a THUMB_TRACK, then we need to calculate the position
* because it gets rounded to an int by the ScrollThumb code, and
* we want fractional values.
*/
if ( event is ScrollEvent )
{
var se:ScrollEvent = ScrollEvent( event );
if ( se.detail == ScrollEventDetail.THUMB_TRACK )
{
if ( verticalScrollBar.numChildren == 4 )
{
var downArrow:DisplayObject = verticalScrollBar.getChildAt( 3 );
var thumb:DisplayObject = verticalScrollBar.getChildAt( 2 );
pos = ( thumb.y - downArrow.height ) / ( downArrow.y - thumb.height - downArrow.height ) * maxVerticalScrollPosition;
// round to nearest lineScrollSize;
pos /= verticalScrollBar.lineScrollSize;
pos = Math.round( pos );
pos *= verticalScrollBar.lineScrollSize;
}
}
}
var fraction:Number = pos - verticalScrollPosition;
fraction *= rowHeight;
listContent.move( listContent.x, viewMetrics.top + listContent.topOffset - fraction );
}
protected var fudge:Number;
protected var contentHeight:int = 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment