tags:

views:

32

answers:

2

When writing a custom layout for a List, what considerations need to be given for using the horizontalScrollPosition / verticalScrollPosition properties on the LayoutBase class itself (as opposed to the corresponding properties on the layoutTarget)

Looking at the HorizontalLayout class as an example, it always references and updates layoutTarget.horizontalScrollPosition. Is the horizontalScrollPosition of the LayoutBase class ever referenced / used?

CLARIFICATION: What I'm really asking here is: What's the relationship between the property on the layout instance, vs the property on the layoutTarget instance, and when should each be used?

A: 

Is the horizontalScrollPosition of the LayoutBase class ever referenced / used?

yes it can be used.

please look! in the code find horizontalScrollPosition there are at least 3 functions where this param used.

It will be better, if you will tell what exactly you want to do with it?

Eugene
I can be used...but what does it do? What's the relationship between the property on the layout instance, vs the property on the layoutTarget instance, and when should each be used?
Marty Pitt
did you read this http://docs.huihoo.com/flex/4/spark/layouts/supportClasses/LayoutBase.html#horizontalScrollPosition ?
Eugene
Yes, but unfortunately that is copied directly from the `IViewport` definition, which is just the standard ASDoc for horizontalScrollPosition. It doesn't tell me anything about how the property is supposed to be used in relation to the layout.
Marty Pitt
A: 

layoutTarget

/**
 *  @private
 *  Work-around the Player globalToLocal and scrollRect changing before
 *  a frame is updated. 
 */
private function globalToLocal(x:Number, y:Number):Point
{
    var layoutTarget:GroupBase = target;
    var parent:DisplayObject = layoutTarget.parent;
    var local:Point = parent.globalToLocal(new Point(x, y));
    local.x -= layoutTarget.x;
    local.y -= layoutTarget.y;

    var scrollRect:Rectangle = getScrollRect();
    if (scrollRect)
    {
        local.x += scrollRect.x;
        local.y += scrollRect.y;
    }
    return local;
}

and this too

/**
 *  Calculates how much to scroll for the specified <code>dropLocation</code>
 *  during a drag and drop gesture.
 *
 *  Called by the <code>showDropIndicator()</code> method to calculate the scroll 
 *  during drag-scrolling.
 *
 *  @param context A valid DropLocation object previously obtained
 *  by calling the <code>calculateDropLocation()</code> method.
 *
 *  @param elapsedTime The duration, in milliseconds, since the drag scrolling start.
 *
 *  @return How much to drag scroll, or null if drag-scrolling is not needed.
 *
 *  @see spark.layouts.supportClasses.DropLocation 
 *  @see #calculateDropIndex()
 *  @see #calculateDropIndicatorBounds()
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
protected function calculateDragScrollDelta(dropLocation:DropLocation, elapsedTime:Number):Point
{
    var layoutTarget:GroupBase = target;
    if (layoutTarget.numElements == 0)
        return null;

    var scrollRect:Rectangle = getScrollRect();
    if (!scrollRect)
        return null;

    // Make sure that the drag-scrolling regions don't overlap 
    var x:Number = dropLocation.dropPoint.x;
    var y:Number = dropLocation.dropPoint.y;

    var horizontalRegionSize:Number = Math.min(dragScrollRegionSizeHorizontal, layoutTarget.width/2);
    var verticalRegionSize:Number = Math.min(dragScrollRegionSizeVertical, layoutTarget.height/2);
    // Return early if the mouse is outside of the drag-scroll region.
    if (scrollRect.left + horizontalRegionSize < x && x < scrollRect.right - horizontalRegionSize &&
        scrollRect.top + verticalRegionSize < y && y < scrollRect.bottom - verticalRegionSize )
        return null;

    if (elapsedTime < dragScrollInitialDelay)
        return new Point(); // Return zero point to continue firing events, but not actually scroll.
    elapsedTime -= dragScrollInitialDelay;

    // Speedup based on time elapsed
    var timeSpeedUp:Number = Math.min(elapsedTime, 2000) / 2000;
    timeSpeedUp *= 3;
    timeSpeedUp += 1;
    timeSpeedUp *= timeSpeedUp * dragScrollSpeed * dragScrollInterval / 50;

    var minDeltaX:Number = -scrollRect.left;
    var minDeltaY:Number = -scrollRect.top;
    var maxDeltaY:Number = target.contentHeight - scrollRect.bottom;
    var maxDeltaX:Number = target.contentWidth - scrollRect.right;

    var deltaX:Number = 0;
    var deltaY:Number = 0;

    if (minDeltaX != 0 && x - scrollRect.left < horizontalRegionSize)
    {
        // Scroll left
        deltaX = 1 - (x - scrollRect.left) / horizontalRegionSize;
        deltaX *=  deltaX * timeSpeedUp;
        deltaX = -Math.round(deltaX) - 1;
    }
    else  if (maxDeltaX != 0 && scrollRect.right - x < horizontalRegionSize)
    {
        // Scroll right
        deltaX = 1 - (scrollRect.right - x) / horizontalRegionSize;
        deltaX *= deltaX * timeSpeedUp;
        deltaX = Math.round(deltaX) + 1;
    }

    if (minDeltaY != 0 && y - scrollRect.top < verticalRegionSize)
    {
        // Scroll up
        deltaY = 1 - (y - scrollRect.top) / verticalRegionSize;
        deltaY *=  deltaY * timeSpeedUp;
        deltaY = -Math.round(deltaY) - 1;
    }
    else  if (maxDeltaY != 0 && scrollRect.bottom - y < verticalRegionSize)
    {
        // Scroll down
        deltaY = 1 - (scrollRect.bottom - y) / verticalRegionSize;
        deltaY *= deltaY * timeSpeedUp;
        deltaY = Math.round(deltaY) + 1;
    }

    deltaX = Math.max(minDeltaX, Math.min(maxDeltaX, deltaX));
    deltaY = Math.max(minDeltaY, Math.min(maxDeltaY, deltaY));

    if (deltaX == 0 && deltaY == 0)
        return null;
    return new Point(deltaX, deltaY);
}

horizontalScrollPosition

/**
 *  @private
 *  Updates the scroll position and dispatches a DragEvent.
 */
private function dragScroll(event:TimerEvent):void
{
    // Scroll the target
    horizontalScrollPosition += _dragScrollDelta.x;
    verticalScrollPosition += _dragScrollDelta.y;

    // Validate target before dispatching the event
    target.validateNow();

    // Re-dispatch the event so that the drag initiator handles it as if
    // the DragProxy is dispatching in response to user input.
    // Always switch over to DRAG_OVER, don't re-dispatch DRAG_ENTER
    var dragEvent:DragEvent = new DragEvent(DragEvent.DRAG_OVER,
                                            _dragScrollEvent.bubbles,
                                            _dragScrollEvent.cancelable, 
                                            _dragScrollEvent.dragInitiator, 
                                            _dragScrollEvent.dragSource, 
                                            _dragScrollEvent.action, 
                                            _dragScrollEvent.ctrlKey, 
                                            _dragScrollEvent.altKey, 
                                            _dragScrollEvent.shiftKey);

    dragEvent.draggedItem = _dragScrollEvent.draggedItem;
    dragEvent.localX = _dragScrollEvent.localX;
    dragEvent.localY = _dragScrollEvent.localY;
    dragEvent.relatedObject = _dragScrollEvent.relatedObject;
    _dragScrollEvent.target.dispatchEvent(dragEvent);
}

and this too:

public function set horizontalScrollPosition(value:Number):void 
{
    if (value == _horizontalScrollPosition) 
        return;

    _horizontalScrollPosition = value;
    scrollPositionChanged();
}

lets look too scrollPositionChanged();

 /**
 *  Called when the <code>verticalScrollPosition</code> or 
 *  <code>horizontalScrollPosition</code> properties change.
 *
 *  <p>The default implementation updates the target's <code>scrollRect</code> property by
 *  calling <code>updateScrollRect()</code>.
 *  Subclasses can override this method to compute other values that are
 *  based on the current <code>scrollPosition</code> or <code>scrollRect</code>.</p>
 *
 *  @see #updateScrollRect()
 *
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */  
protected function scrollPositionChanged():void
{
    var g:GroupBase = target;
    if (!g)
        return;

    updateScrollRect(g.width, g.height);
}

also lets look at this part of code

/**
*  Computes the <code>verticalScrollPosition</code> and 
*  <code>horizontalScrollPosition</code> deltas needed to 
*  scroll the element at the specified index into view.
* 
*  <p>This method attempts to minimize the change to <code>verticalScrollPosition</code>
*  and <code>horizontalScrollPosition</code>.</p>
* 
*  <p>If <code>clipAndEnableScrolling</code> is <code>true</code> 
*  and the element at the specified index is not
*  entirely visible relative to the target's scroll rectangle, then 
*  return the delta to be added to <code>horizontalScrollPosition</code> and
*  <code>verticalScrollPosition</code> that scrolls the element completely 
*  within the scroll rectangle's bounds.</p>
* 
*  @param index The index of the element to be scrolled into view.
*
*  @return A Point that contains offsets to horizontalScrollPosition 
*      and verticalScrollPosition that will scroll the specified
*      element into view, or null if no change is needed. 
*      If the specified element is partially visible and larger than the
*      scroll rectangle, meaning it is already the only element visible, then
*      return null.
*      If the specified index is invalid, or target is null, then
*      return null.
*      If the element at the specified index is null or includeInLayout
*      false, then return null.
* 
*  @see #clipAndEnableScrolling
*  @see #verticalScrollPosition
*  @see #horizontalScrollPosition
*  @see #udpdateScrollRect()
*  
*  @langversion 3.0
*  @playerversion Flash 10
*  @playerversion AIR 1.5
*  @productversion Flex 4
*/
public function getScrollPositionDeltaToElement(index:int):Point

and so, what if we will talk about difference of these params in LayoutBase and HorizontalLayout?

in HL it used in private function updateDisplayListVirtual():void but there are no such function in LayoutBase, because LayoutBase just a Base class as it is, but it have another one which called as public function updateDisplayList(width:Number, height:Number):void but its empty.

So, if we will try to think what does all these functions do, imho, HL just extending standart updateDisplayList to two functions one is the same and another one is virtual.

Whats for HL needs virtual? Lets see:

override public function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
    super.updateDisplayList(unscaledWidth, unscaledHeight);

    var layoutTarget:GroupBase = target; 
    if (!layoutTarget)
        return;

    if ((layoutTarget.numElements == 0) || (unscaledWidth == 0) || (unscaledHeight == 0))
    {
        setColumnCount(0);
        setIndexInView(-1, -1);
        if (layoutTarget.numElements == 0)
            layoutTarget.setContentSize(paddingLeft + paddingRight, paddingTop + paddingBottom);
        return;         
    }

    if (useVirtualLayout) 
        updateDisplayListVirtual();
    else
        updateDisplayListReal();
}

so all stuff is useVirtualLayout, but its from LayoutBase and lets see:

 /**
 *  A container can hold any number of children. 
 *  However, each child requires an instance of an item renderer. 
 *  If the container has many children, you might notice performance degradation 
 *  as you add more children to the container. 
 *
 *  <p>Instead of creating an item renderer for each child, 
 *  you can configure the container to use a virtual layout. 
 *  With virtual layout, the container reuses item renderers so that it only creates 
 *  item renderers for the currently visible children of the container. 
 *  As a child is moved off the screen, possible by scrolling the container, 
 *  a new child being scrolled onto the screen can reuse its item renderer. </p>
 *  
 *  <p>To configure a container to use virtual layout, set the <code>useVirtualLayout</code> property 
 *  to <code>true</code> for the layout associated with the container. 
 *  Only the DataGroup or SkinnableDataContainer with the VerticalLayout, 
 *  HorizontalLayout, and TileLayout supports virtual layout. 
 *  Layout subclasses that do not support virtualization must prevent changing
 *  this property.</p>
 *
 *  <p><b>Note: </b>The BasicLayout class throws a run-time error if you set 
 *  <code>useVirtualLayout</code> to <code>true</code>.</p>
 * 
 *  <p>When <code>true</code>, layouts that support virtualization must use 
 *  the <code>target.getVirtualElementAt()</code> method, 
 *  rather than <code>getElementAt()</code>, and must only get the 
 *  elements they anticipate will be visible given the value of <code>getScrollRect()</code>.</p>
 * 
 *  <p>When <code>true</code>, the layout class must be able to compute
 *  the indices of the layout elements that overlap the <code>scrollRect</code> in its 
 *  <code>updateDisplayList()</code> method based exclusively on cached information, not
 *  by getting layout elements and examining their bounds.</p>
 * 
 *  <p>Typically virtual layouts update their cached information 
 *  in the <code>updateDisplayList()</code> method,
 *  based on the sizes and locations computed for the elements in view.</p>
 * 
 *  <p>Similarly, in the <code>measure()</code> method, virtual layouts should update the target's 
 *  measured size properties based on the <code>typicalLayoutElement</code> and other
 *  cached layout information, not by measuring elements.</p>
 * 
 *  <p>Containers cooperate with layouts that have <code>useVirtualLayout</code> = <code>true</code> by 
 *  recycling item renderers that were previously constructed, but are no longer in use.
 *  An item is considered to be no longer in use if its index is not
 *  within the range of <code>getVirtualElementAt()</code> indices requested during
 *  the container's most recent <code>updateDisplayList()</code> invocation.</p>
 *
 *  @default false
 * 
 *  @see #getScrollRect
 *  @see #typicalLayoutElement
 *
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */

so it is your answer, i think, also i think that people who wrote this Spark system will explain more widely, and this is only my little pleasure to help you to get the right answers.

Regards
Eugene

Eugene