blob: 5ab99da045ebb372a507cc45d8766dfef4f50090 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.core
{
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.display.Shape;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import mx.controls.HScrollBar;
import mx.controls.VScrollBar;
import mx.controls.scrollClasses.ScrollBar;
import mx.core.IToolTip;
import mx.events.ScrollEvent;
import mx.events.ScrollEventDetail;
import mx.events.ScrollEventDirection;
import mx.managers.ToolTipManager;
import mx.styles.ISimpleStyleClient;
use namespace mx_internal;
/**
* Dispatched when the content is scrolled.
*
* @eventType mx.events.ScrollEvent.SCROLL
* @helpid 3269
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="scroll", type="mx.events.ScrollEvent")]
include "../styles/metadata/BackgroundStyles.as"
include "../styles/metadata/BorderStyles.as"
include "../styles/metadata/FocusStyles.as"
include "../styles/metadata/LeadingStyle.as"
include "../styles/metadata/TextStyles.as"
/**
* Accent color used by component skins. The default button skin uses this color
* to tint the background. Slider track highlighting uses this color.
*
* @default #0099FF
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="accentColor", type="uint", format="Color", inherit="yes", theme="spark")]
/**
* The main color for a component.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="chromeColor", type="uint", format="Color", inherit="yes", theme="spark")]
/**
* The alpha of the content background for this component.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="contentBackgroundAlpha", type="Number", inherit="yes", theme="spark")]
/**
* Color of the content area of the component.
*
* @default 0xFFFFFF
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="contentBackgroundColor", type="uint", format="Color", inherit="yes", theme="spark")]
/**
* Radius of component corners.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="cornerRadius", type="Number", format="Length", inherit="no", theme="halo")]
/**
* Color of focus ring when the component is in focus
*
* @default 0x70B2EE
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="focusColor", type="uint", format="Color", inherit="yes", theme="spark")]
/**
* Style name for horizontal scrollbar. This allows more control over
* the appearance of the scrollbar.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="horizontalScrollBarStyleName", type="String", inherit="no")]
/**
* Color of any symbol of a component. Examples include the check mark of a CheckBox or
* the arrow of a ScrollBar button.
*
* @default 0x000000
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="symbolColor", type="uint", format="Color", inherit="yes", theme="spark")]
/**
* Style name for vertical scrollbar. This allows more control over
* the appearance of the scrollbar.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="verticalScrollBarStyleName", type="String", inherit="no")]
/**
* The ScrollControlBase class is the base class for controls
* with scroll bars.
* The user interacts with the scroll bar or the developer accesses
* methods and properties that alter the viewable area.
* The ScrollControlBase takes a single child object and positions and
* masks or sizes that object to display the viewable content.
* All items to be scrolled must be children of that content object
*
* @mxml
*
* <p>The <code>&lt;ScrollControlBase&gt;</code> tag inherits all the tag attributes
* of its superclass, and adds the following tag attributes:</p>
*
* <pre>
* &lt;mx:<i>tagname</i>
* <b>Properties</b>
* border="<i>object of border skin style</i>"
* horizontalScrollPolicy="off|on|auto"
* horizontalScrollPosition="0"
* liveScrolling="true|false"
* maxHorizontalScrollPosition="NaN"
* maxVerticalScrollPosition="NaN"
* scrollTipFunction="undefined"
* showScrollTips="false|true"
* verticalScrollPolicy="auto|off|on"
* verticalScrollPosition="0"
* <b>Styles</b>
* backgroundAlpha="1.0"
* backgroundColor="undefined"
* backgroundImage="undefined"
* backgroundSize="auto"
* borderColor="0xAAB3B3"
* borderSides="left top right bottom"
* borderSkin="ClassReference('mx.skins.halo.HaloBorder')"
* borderStyle="inset"
* borderThickness="1"
* color="0x0B333C"
* cornerRadius="0"
* disabledColor="0xAAB3B3"
* dropShadowColor="0x000000"
* dropShadowEnabled="false"
* fontFamily="Verdana"
* fontSize="10"
* fontStyle="normal|italic"
* fontWeight="normal|bold"
* horizontalScrollBarStyleName=""
* leading="2"
* shadowDirection="center"
* shadowDistance="2"
* textAlign="<i>value; see detail.</i>"
* textDecoration="none|underline"
* textIndent="0"
* verticalScrollBarStyleName=""
*
* <b>Events</b>
* scroll="<i>No default</i>"
* /&gt;
* </pre>
*
* @helpid 3270
* @tiptext base class for views/containers that support scrolling
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class ScrollControlBase extends UIComponent
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function ScrollControlBase()
{
super();
_viewMetrics = EdgeMetrics.EMPTY;
// Listen for "mouseWheel" events on myself or any of my children
addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* The border object.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var border:IFlexDisplayObject;
/**
* @private
* Offsets including borders and scrollbars.
*/
private var _viewMetrics:EdgeMetrics;
/**
* The mask. This property may be undefined if no scroll bars
* are currently displayed or in some subclasses which
* have a different masking mechanism.
* A mask defined the viewable area of a displayable object.
* Thus this mask is used to hide the portions of the content
* that are not currently viewable.
* In general you do not access this property directly.
* Manipulation of the <code>horizontalScrollPolicy</code>,
* <code>verticalScrollPolicy</code>, <code>horizontalScrollPosition</code>,
* and <code>verticalScrollPosition</code> properties
* should provide sufficient control over the mask.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var maskShape:Shape;
/**
* The horizontal scroll bar.
* This property is null if no horizontal scroll bar
* is currently displayed.
* In general you do not access this property directly.
* Manipulation of the <code>horizontalScrollPolicy</code>
* and <code>horizontalScrollPosition</code>
* properties should provide sufficient control over the scroll bar.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var horizontalScrollBar:ScrollBar;
/**
* The vertical scroll bar.
* This property is null if no vertical scroll bar
* is currently displayed.
* In general you do not access this property directly.
* Manipulation of the <code>verticalScrollPolicy</code>
* and <code>verticalScrollPosition</code>
* properties should provide sufficient control over the scroll bar.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var verticalScrollBar:ScrollBar;
/**
* @private
*/
private var numberOfCols:Number = 0;
/**
* @private
*/
private var numberOfRows:Number = 0;
/**
* @private
*/
mx_internal var _maxVerticalScrollPosition:Number;
mx_internal var _maxHorizontalScrollPosition:Number;
/**
* @private
*/
private var viewableRows:Number;
private var viewableColumns:Number;
/**
* @private
*/
private var propsInited:Boolean;
/**
* A flag that the scrolling area changed due to the appearance or disappearance of
* scrollbars. Used by most layout methods to re-adjust the scrolling properties again.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var scrollAreaChanged:Boolean;
/**
* @private
*/
private var invLayout:Boolean;
/**
* @private
* Instance of the scrollTip. (There can be only one.)
*/
private var scrollTip:IToolTip;
/**
* @private
* Base position for the scrollTip.
*/
private var scrollThumbMidPoint:Number;
/**
* @private
* Keep track of the whether the ToolTipManager was enabled
* before dealing with scroll tips.
*/
private var oldTTMEnabled:Boolean;
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// enabled
//----------------------------------
/**
* @private
* Scrollbars must be enabled/disabled when we are.
*/
override public function set enabled(value:Boolean):void
{
super.enabled = value;
if (horizontalScrollBar)
horizontalScrollBar.enabled = value;
if (verticalScrollBar)
verticalScrollBar.enabled = value;
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// borderMetrics
//----------------------------------
/**
* Returns an EdgeMetrics object that has four properties:
* <code>left</code>, <code>top</code>, <code>right</code>,
* and <code>bottom</code>.
* The value of each property is equal to the thickness of one side
* of the border, expressed in pixels.
*
* @return EdgeMetrics object with the left, right, top,
* and bottom properties.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get borderMetrics():EdgeMetrics
{
return (border && border is IRectangularBorder) ?
IRectangularBorder(border).borderMetrics : EdgeMetrics.EMPTY;
}
//----------------------------------
// horizontalScrollPosition
//----------------------------------
/**
* @private
* Storage for the horizontalScrollPosition property.
*/
mx_internal var _horizontalScrollPosition:Number = 0;
[Bindable("scroll")]
[Bindable("viewChanged")]
[Inspectable(defaultValue="0")]
/**
* The offset into the content from the left edge. This can
* be a pixel offset in some subclasses or some other metric
* like the number of columns in a DataGrid or number of items
* in a HorizontalList or TileList.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get horizontalScrollPosition():Number
{
return _horizontalScrollPosition;
}
/**
* @private
* This only moves the scrollBars -- assumes the call emanated
* from the scrollable content.
*/
public function set horizontalScrollPosition(value:Number):void
{
_horizontalScrollPosition = value;
if (horizontalScrollBar)
horizontalScrollBar.scrollPosition = value;
dispatchEvent(new Event("viewChanged"));
}
//----------------------------------
// horizontalScrollPolicy
//----------------------------------
/**
* @private
* Storage for the horizontalScrollPolicy property.
*/
mx_internal var _horizontalScrollPolicy:String = ScrollPolicy.OFF;
[Bindable("horizontalScrollPolicyChanged")]
[Inspectable(enumeration="off,on,auto", defaultValue="off")]
/**
* A property that indicates whether the horizontal scroll
* bar is always on, always off,
* or automatically changes based on the parameters passed to the
* <code>setScrollBarProperties()</code> method.
* Allowed values are <code>ScrollPolicy.ON</code>,
* <code>ScrollPolicy.OFF</code>, and <code>ScrollPolicy.AUTO</code>.
* MXML values can be <code>"on"</code>, <code>"off"</code>,
* and <code>"auto"</code>.
*
* <p>Setting this property to <code>ScrollPolicy.OFF</code> for ListBase
* subclasses does not affect the <code>horizontalScrollPosition</code>
* property; you can still scroll the contents programmatically.</p>
*
* <p>Note that the policy can affect the measured size of the component
* If the policy is <code>ScrollPolicy.AUTO</code> the
* scrollbar is not factored in the measured size. This is done to
* keep the layout from recalculating when the scrollbar appears. If you
* know that you will have enough data for scrollbars you should set
* the policy to <code>ScrollPolicy.ON</code>. If you
* don't know, you may need to set an explicit width or height on
* the component to allow for scrollbars to appear later.</p>
*
* @default ScrollPolicy.OFF
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get horizontalScrollPolicy():String
{
return _horizontalScrollPolicy;
}
/**
* @private
*/
public function set horizontalScrollPolicy(value:String):void
{
var newPolicy:String = value.toLowerCase();
if (_horizontalScrollPolicy != newPolicy)
{
_horizontalScrollPolicy = newPolicy;
invalidateDisplayList();
dispatchEvent(new Event("horizontalScrollPolicyChanged"));
}
}
//----------------------------------
// liveScrolling
//----------------------------------
[Inspectable(defaultValue="true")]
/**
* A flag that indicates whether scrolling is live as the
* scrollbar thumb is moved
* or the view is not updated until the thumb is released.
* The default value is <code>true</code>.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var liveScrolling:Boolean = true;
//----------------------------------
// maxHorizontalScrollPosition
//----------------------------------
[Bindable("maxHorizontalScrollPositionChanged")]
[Inspectable(defaultValue="0")]
/**
* The maximum value for the <code>horizontalScrollPosition</code> property.
* Note that this is not the width of the content because the
* <code>maxHorizontalScrollPosition</code> property contains the width
* of the content minus the width of the displayable area.
*
* <p>In most components, the value of the
* <code>maxHorizontalScrollPosition</code> property is computed from the
* data and size of component, and must not be set by
* the application code.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get maxHorizontalScrollPosition():Number
{
if (!isNaN(_maxHorizontalScrollPosition))
return _maxHorizontalScrollPosition;
var m:Number = horizontalScrollBar ?
horizontalScrollBar.maxScrollPosition :
0;
return m;
}
/**
* @private
*/
public function set maxHorizontalScrollPosition(value:Number):void
{
_maxHorizontalScrollPosition = value;
dispatchEvent(new Event("maxHorizontalScrollPositionChanged"));
}
//----------------------------------
// maxVerticalScrollPosition
//----------------------------------
[Bindable("maxVerticalScrollPositionChanged")]
[Inspectable(defaultValue="0")]
/**
* The maximum value for the <code>verticalScrollPosition</code> property.
* Note that this is not the height of the content because the
* <code>maxVerticalScrollPosition</code> property contains the height
* of the content minus the height of the displayable area.
*
* <p>The value of the
* <code>maxVerticalScrollPosition</code> property is computed from the
* data and size of component, and must not be set by
* the application code.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get maxVerticalScrollPosition():Number
{
if (!isNaN(_maxVerticalScrollPosition))
return _maxVerticalScrollPosition;
var m:Number = verticalScrollBar ?
verticalScrollBar.maxScrollPosition :
0;
return m;
}
/**
* @private
*/
public function set maxVerticalScrollPosition(value:Number):void
{
_maxVerticalScrollPosition = value;
dispatchEvent(new Event("maxVerticalScrollPositionChanged"));
}
//----------------------------------
// scrollTipFunction
//----------------------------------
/**
* @private
* Storage for the scrollTipFunction property.
*/
private var _scrollTipFunction:Function;
[Bindable("scrollTipFunctionChanged")]
[Inspectable(defaultValue="")]
/**
* A function that computes the string to be displayed as the ScrollTip.
* This function is called if the <code>showScrollTips</code> property
* is set to <code>true</code> and the scroll thumb is being dragged.
* The function should return a String that used as a ScrollTip.
* The function is passed two parameters.
* The first is the <code>direction</code> of the scroll bar.
* The second is its <code>scrollPosition</code>, as the following example shows:
*
* <pre>
* function scrollTipFunction(direction:String, position:Number):String
* {
* if (direction == "vertical") return myToolTips[position];
* else return "";
* }</pre>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get scrollTipFunction():Function
{
return _scrollTipFunction;
}
/**
* @private
*/
public function set scrollTipFunction(value:Function):void
{
_scrollTipFunction = value;
dispatchEvent(new Event("scrollTipFunctionChanged"));
}
//----------------------------------
// showScrollTips
//----------------------------------
[Inspectable(defaultValue="false")]
/**
* A flag that indicates whether a tooltip should appear
* near the scroll thumb when it is being dragged.
* The default value is <code>false</code> to disable the tooltip.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var showScrollTips:Boolean = false;
//----------------------------------
// viewMetrics
//----------------------------------
/**
* An EdgeMetrics object taking into account the scroll bars,
* if visible.
*
* @return EdgeMetrics object with the thickness, in pixels,
* of the left, top, right, and bottom edges.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get viewMetrics():EdgeMetrics
{
_viewMetrics = borderMetrics.clone();
if (!horizontalScrollBar &&
(horizontalScrollPolicy == ScrollPolicy.ON))
{
createHScrollBar(true);
horizontalScrollBar.addEventListener(ScrollEvent.SCROLL, scrollHandler);
horizontalScrollBar.addEventListener(ScrollEvent.SCROLL, scrollTipHandler);
horizontalScrollBar.scrollPosition = _horizontalScrollPosition;
invalidateDisplayList();
}
if (!verticalScrollBar && verticalScrollPolicy == ScrollPolicy.ON)
{
createVScrollBar(true);
verticalScrollBar.addEventListener(ScrollEvent.SCROLL, scrollHandler);
verticalScrollBar.addEventListener(ScrollEvent.SCROLL, scrollTipHandler);
verticalScrollBar.scrollPosition = _verticalScrollPosition;
invalidateDisplayList();
}
if (verticalScrollBar && verticalScrollBar.visible)
_viewMetrics.right += verticalScrollBar.minWidth;
if (horizontalScrollBar && horizontalScrollBar.visible)
_viewMetrics.bottom += horizontalScrollBar.minHeight;
return _viewMetrics;
}
//----------------------------------
// verticalScrollPosition
//----------------------------------
/**
* @private
* Storage for the verticalScrollPosition property.
*/
mx_internal var _verticalScrollPosition:Number = 0;
[Bindable("scroll")]
[Bindable("viewChanged")]
[Inspectable(defaultValue="0")]
/**
* The offset into the content from the top edge. This can
* be a pixel offset in some subclasses or some other metric
* like number of lines in a List or number of tiles in
* a TileList.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get verticalScrollPosition():Number
{
return _verticalScrollPosition;
}
/**
* @private
*/
public function set verticalScrollPosition(value:Number):void
{
_verticalScrollPosition = value;
if (verticalScrollBar)
verticalScrollBar.scrollPosition = value;
dispatchEvent(new Event("viewChanged"));
}
//----------------------------------
// verticalScrollPolicy
//----------------------------------
/**
* @private
* Storage for the verticalScrollPolicy property.
*/
mx_internal var _verticalScrollPolicy:String = ScrollPolicy.AUTO;
[Bindable("verticalScrollPolicyChanged")]
[Inspectable(enumeration="off,on,auto", defaultValue="auto")]
/**
* A property that indicates whether the vertical scroll bar is always on, always off,
* or automatically changes based on the parameters passed to the
* <code>setScrollBarProperties()</code> method.
* Allowed values are <code>ScrollPolicy.ON</code>,
* <code>ScrollPolicy.OFF</code>, and <code>ScrollPolicy.AUTO</code>.
* MXML values can be <code>"on"</code>, <code>"off"</code>,
* and <code>"auto"</code>.
*
* <p>Setting this property to <code>ScrollPolicy.OFF</code> for ListBase
* subclasses does not affect the <code>verticalScrollPosition</code>
* property; you can still scroll the contents programmatically.</p>
*
* <p>Note that the policy can affect the measured size of the component
* If the policy is <code>ScrollPolicy.AUTO</code> the
* scrollbar is not factored in the measured size. This is done to
* keep the layout from recalculating when the scrollbar appears. If you
* know that you will have enough data for scrollbars you should set
* the policy to <code>ScrollPolicy.ON</code>. If you
* don't know, you may need to set an explicit width or height on
* the component to allow for scrollbars to appear later.</p>
*
* @default ScrollPolicy.AUTO
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get verticalScrollPolicy():String
{
return _verticalScrollPolicy;
}
/**
* @private
*/
public function set verticalScrollPolicy(value:String):void
{
var newPolicy:String = value.toLowerCase();
if (_verticalScrollPolicy != newPolicy)
{
_verticalScrollPolicy = newPolicy;
invalidateDisplayList();
dispatchEvent(new Event("verticalScrollPolicyChanged"));
}
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* Creates objects that are children of this ScrollControlBase,
* which in this case are the border and mask.
* Flex calls this method when the ScrollControlBase is first created.
* If a subclass overrides this method, the subclass should call
* the <code>super.createChildren()</code> method so that the logic
* in the <code>ScrollControlBase.createChildren()</code> method is executed.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override protected function createChildren():void
{
super.createChildren();
createBorder();
if (!maskShape)
{
maskShape = new FlexShape();
maskShape.name = "mask";
var g:Graphics = maskShape.graphics;
g.beginFill(0xFFFFFF);
g.drawRect(0, 0, 10, 10);
g.endFill();
addChild(maskShape);
}
maskShape.visible = false;
}
/**
* @private
* Sets the position and size of the scroll bars and content
* and adjusts the mask.
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
layoutChrome(unscaledWidth, unscaledHeight);
var w:Number = unscaledWidth;
var h:Number = unscaledHeight;
invLayout = false;
var vm:EdgeMetrics = _viewMetrics = viewMetrics;
if (horizontalScrollBar && horizontalScrollBar.visible)
{
horizontalScrollBar.setActualSize(w - vm.left - vm.right,
horizontalScrollBar.minHeight);
horizontalScrollBar.move(vm.left,
h - vm.bottom);
horizontalScrollBar.enabled = enabled;
}
if (verticalScrollBar && verticalScrollBar.visible)
{
verticalScrollBar.setActualSize(verticalScrollBar.minWidth,
h - vm.top - vm.bottom);
verticalScrollBar.move(w - vm.right, vm.top);
verticalScrollBar.enabled = enabled;
}
var mask:DisplayObject = maskShape;
var wd:Number = w - vm.left - vm.right;
var ht:Number = h - vm.top - vm.bottom;
mask.width = wd < 0 ? 0 : wd;
mask.height = ht < 0 ? 0 : ht;
mask.x = vm.left;
mask.y = vm.top;
}
/**
* @private
*/
override public function styleChanged(styleProp:String):void
{
var allStyles:Boolean = (styleProp == null || styleProp == "styleName");
super.styleChanged(styleProp);
if (allStyles || styleProp == "horizontalScrollBarStyleName")
{
if (horizontalScrollBar)
{
var horizontalScrollBarStyleName:String =
getStyle("horizontalScrollBarStyleName");
horizontalScrollBar.styleName = horizontalScrollBarStyleName;
}
}
if (allStyles || styleProp == "verticalScrollBarStyleName")
{
if (verticalScrollBar)
{
var verticalScrollBarStyleName:String =
getStyle("verticalScrollBarStyleName");
verticalScrollBar.styleName = verticalScrollBarStyleName;
}
}
// Replace the borderSkin
if (allStyles || styleProp == "borderSkin")
{
if (border)
{
removeChild(DisplayObject(border));
border = null;
createBorder();
}
}
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* Responds to size changes by setting the positions and sizes
* of this control's borders.
*
* <p>The <code>ScrollControlBase.layoutChrome()</code> method sets the
* position and size of the ScrollControlBase's border.
* In every subclass of ScrollControlBase, the subclass's <code>layoutChrome()</code>
* method should call the <code>super.layoutChrome()</code> method,
* so that the border is positioned properly.</p>
*
* @param unscaledWidth Specifies the width of the component, in pixels,
* in the component's coordinates, regardless of the value of the
* <code>scaleX</code> property of the component.
*
* @param unscaledHeight Specifies the height of the component, in pixels,
* in the component's coordinates, regardless of the value of the
* <code>scaleY</code> property of the component.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function layoutChrome(unscaledWidth:Number,
unscaledHeight:Number):void
{
// Border covers the whole thing.
if (border)
{
border.move(0, 0);
border.setActualSize(unscaledWidth, unscaledHeight);
}
}
/**
* Creates the border for this component.
* Normally the border is determined by the
* <code>borderStyle</code> and <code>borderSkin</code> styles.
* It must set the border property to the instance
* of the border.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function createBorder():void
{
if (!border && isBorderNeeded())
{
var borderClass:Class = getStyle("borderSkin");
if (borderClass != null)
{
border = new borderClass();
if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0)
{
if (border is IUIComponent)
IUIComponent(border).enabled = enabled;
}
if (border is ISimpleStyleClient)
ISimpleStyleClient(border).styleName = this;
// Add the border behind all the children.
addChildAt(DisplayObject(border), 0);
invalidateDisplayList();
}
}
}
/**
* Returns <code>true</code> if a border is needed for this component based
* on the borderStyle and whether or not there is a background
* for the component.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
private function isBorderNeeded():Boolean
{
//trace("isBorderNeeded",this,"ms",getStyle("mouseShield"),"borderStyle",getStyle("borderStyle"));
var v:Object = getStyle("borderStyle");
if (v)
{
// If borderStyle is "none", then only create a border if the mouseShield style is true
// (meaning that there is a mouse event listener on this view). We don't create a border
// if our parent's mouseShieldChildren style is true.
if ((v != "none") || (v == "none" && getStyle("mouseShield")))
{
return true;
}
}
v = getStyle("backgroundColor");
if (v !== null && v !== "")
return true;
v = getStyle("backgroundImage");
return v != null && v != "";
}
/**
* Causes the ScrollControlBase to show or hide scrollbars based
* on the parameters passed in. If a TextArea can only show 100 pixels
* across and 5 lines of text, but the actual text to display is 200 pixels wide
* and 30 lines of text, then the <code>setScrollBarProperties()</code> method
* is called as
* <code>setScrollBarProperties(200, 100, 30, 5)</code>.
*
* @param totalColumns The number of horizontal units that need to be displayed.
*
* @param visibleColumns The number of horizontal units that can be displayed at one time.
*
* @param totalRows The number of vertical units that need to be displayed.
*
* @param visibleRows The number of vertical units that can be displayed at one time
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function setScrollBarProperties(totalColumns:int, visibleColumns:int,
totalRows:int, visibleRows:int):void
{
var horizontalScrollPolicy:String = this.horizontalScrollPolicy;
var verticalScrollPolicy:String = this.verticalScrollPolicy;
var shouldBeVisible:Boolean;
scrollAreaChanged = false;
// scrollbars when the scrollPolicy is ON are calculated in viewMetrics
if (horizontalScrollPolicy == ScrollPolicy.ON ||
(visibleColumns < totalColumns && totalColumns > 0 &&
(horizontalScrollPolicy == ScrollPolicy.AUTO)))
{
// We need a horizontal scrollBar.
// Nope, need to add it.
if (!horizontalScrollBar)
{
createHScrollBar(false);
horizontalScrollBar.addEventListener(ScrollEvent.SCROLL, scrollHandler);
horizontalScrollBar.addEventListener(ScrollEvent.SCROLL, scrollTipHandler);
horizontalScrollBar.scrollPosition = _horizontalScrollPosition;
}
shouldBeVisible = roomForScrollBar(horizontalScrollBar, unscaledWidth, unscaledHeight);
if (shouldBeVisible != horizontalScrollBar.visible)
{
horizontalScrollBar.visible = shouldBeVisible;
scrollAreaChanged = true;
}
if (horizontalScrollBar && horizontalScrollBar.visible && (numberOfCols != totalColumns ||
viewableColumns != visibleColumns || scrollAreaChanged))
{
horizontalScrollBar.setScrollProperties(
visibleColumns, 0, totalColumns - visibleColumns);
if (horizontalScrollBar.scrollPosition != _horizontalScrollPosition)
horizontalScrollBar.scrollPosition = _horizontalScrollPosition;
viewableColumns = visibleColumns;
numberOfCols = totalColumns;
}
}
else if ((horizontalScrollPolicy == ScrollPolicy.AUTO ||
horizontalScrollPolicy == ScrollPolicy.OFF) &&
horizontalScrollBar && horizontalScrollBar.visible)
{
// We need to remove this scrollBar.
horizontalScrollPosition = 0;
horizontalScrollBar.setScrollProperties(
visibleColumns, 0, 0);
horizontalScrollBar.visible = false;
viewableColumns = NaN;
scrollAreaChanged = true;
}
if (verticalScrollPolicy == ScrollPolicy.ON ||
(visibleRows < totalRows && totalRows > 0 &&
(verticalScrollPolicy == ScrollPolicy.AUTO)))
{
// We need a vertical scrollBar. Does it exist?
if (!verticalScrollBar)
{
// No it doesn't, and we're allowed to add it.
createVScrollBar(false);
verticalScrollBar.addEventListener(ScrollEvent.SCROLL, scrollHandler);
verticalScrollBar.addEventListener(ScrollEvent.SCROLL, scrollTipHandler);
verticalScrollBar.scrollPosition = _verticalScrollPosition;
}
shouldBeVisible = roomForScrollBar(verticalScrollBar, unscaledWidth, unscaledHeight);
if (shouldBeVisible != verticalScrollBar.visible)
{
verticalScrollBar.visible = shouldBeVisible;
scrollAreaChanged = true;
}
if (verticalScrollBar && verticalScrollBar.visible && (numberOfRows != totalRows ||
(viewableRows != visibleRows) || scrollAreaChanged))
{
verticalScrollBar.setScrollProperties(
visibleRows, 0, totalRows - visibleRows);
if (verticalScrollBar.scrollPosition != _verticalScrollPosition)
verticalScrollBar.scrollPosition = _verticalScrollPosition;
viewableRows = visibleRows;
numberOfRows = totalRows;
}
}
else if ((verticalScrollPolicy == ScrollPolicy.AUTO ||
verticalScrollPolicy == ScrollPolicy.OFF) &&
verticalScrollBar && verticalScrollBar.visible)
{
verticalScrollPosition = 0;
verticalScrollBar.setScrollProperties(
visibleRows, 0, 0);
verticalScrollBar.visible = false;
viewableRows = NaN;
scrollAreaChanged = true;
}
// Now, if any scrollBar came into or left existence,
// it's possible that the content is occluded or revealed enough
// that the other scrollBar needs to appear or go away.
if (scrollAreaChanged)
{
// This is for content that conforms to the (discrete)
// scrollable interface. It just falls through on analog content.
// (I hope.)
updateDisplayList(unscaledWidth, unscaledHeight);
}
}
//--------------------------------------------------------------------------
//
// Private methods
//
//--------------------------------------------------------------------------
/**
* @private
* Create the HScrollBar
*/
private function createHScrollBar(visible:Boolean):ScrollBar
{
horizontalScrollBar = new HScrollBar();
horizontalScrollBar.visible = visible;
horizontalScrollBar.enabled = enabled;
var horizontalScrollBarStyleName:String =
getStyle("horizontalScrollBarStyleName");
horizontalScrollBar.styleName = horizontalScrollBarStyleName;
// Place the new scrollbar in front of other children
// in the ScrollControlBase
addChild(horizontalScrollBar);
horizontalScrollBar.validateNow();
return horizontalScrollBar;
}
/**
* @private
* Create the VScrollBar
*/
private function createVScrollBar(visible:Boolean):ScrollBar
{
verticalScrollBar = new VScrollBar();
verticalScrollBar.visible = visible;
verticalScrollBar.enabled = enabled;
var verticalScrollBarStyleName:String =
getStyle("verticalScrollBarStyleName");
verticalScrollBar.styleName = verticalScrollBarStyleName;
// Place the new scrollbar in front of other children
// in the ScrollControlBase
addChild(verticalScrollBar);
//need verticalScrollBar.validateNow()?
UIComponentGlobals.layoutManager.validateClient(verticalScrollBar, true);
return verticalScrollBar;
}
/**
* Determines if there is enough space in this component to display
* a given scrollbar.
*
* @param bar The scrollbar
*
* @param unscaledWidth Specifies the width of the component, in pixels,
* in the component's coordinates, regardless of the value of the
* <code>scaleX</code> property of the component.
*
* @param unscaledHeight Specifies the height of the component, in pixels,
* in the component's coordinates, regardless of the value of the
* <code>scaleY</code> property of the component.
*
* @return Returns true if there is enough space for a scrollbar.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function roomForScrollBar(bar:ScrollBar,
unscaledWidth:Number, unscaledHeight:Number):Boolean
{
var bm:EdgeMetrics = borderMetrics;
return (unscaledWidth >= bar.minWidth + bm.left + bm.right) &&
(unscaledHeight >= bar.minHeight + bm.top + bm.bottom)
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* Default event handler for the <code>scroll</code> event.
*
* @param event The event object.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function scrollHandler(event:Event):void
{
// TextField.scroll bubbles so you might see it here
if (event is ScrollEvent)
{
var scrollBar:ScrollBar = ScrollBar(event.target);
var pos:Number = scrollBar.scrollPosition;
var prop:QName;
if (scrollBar == verticalScrollBar)
prop = new QName(mx_internal, "_verticalScrollPosition");
else if (scrollBar == horizontalScrollBar)
prop = new QName(mx_internal, "_horizontalScrollPosition");
dispatchEvent(event);
if (prop)
this[prop] = pos;
}
}
/**
* @private
*/
private function scrollTipHandler(event:Event):void
{
// TextField.scroll bubbles so you might see it here
if (event is ScrollEvent)
{
if (!showScrollTips)
return;
if (ScrollEvent(event).detail == ScrollEventDetail.THUMB_POSITION)
{
if (scrollTip)
{
ToolTipManager.destroyToolTip(scrollTip);
scrollTip = null;
ToolTipManager.enabled = oldTTMEnabled;
}
}
else if (ScrollEvent(event).detail == ScrollEventDetail.THUMB_TRACK)
{
var scrollBar:ScrollBar = ScrollBar(event.target);
var isVertical:Boolean = scrollBar == verticalScrollBar;
var dir:String = isVertical ? "vertical" : "horizontal";
var pos:Number = scrollBar.scrollPosition;
if (!scrollTip)
{
scrollTip = ToolTipManager.createToolTip("", 0, 0, null, this);
scrollThumbMidPoint = scrollBar.scrollThumb.height / 2;
oldTTMEnabled = ToolTipManager.enabled;
ToolTipManager.enabled = false;
}
var tip:String = pos.toString();
if (_scrollTipFunction != null)
tip = _scrollTipFunction(dir, pos);
if (tip == "")
{
scrollTip.visible = false;
}
else
{
scrollTip.text = tip;
ToolTipManager.sizeTip(scrollTip);
var pt:Point = new Point();
if (isVertical)
{
pt.x = -3 - scrollTip.width;
pt.y = scrollBar.scrollThumb.y + scrollThumbMidPoint -
scrollTip.height / 2;
}
else
{
// The scrollbar is rotated so we kind of reverse things
// around with width and height.
pt.x = -3 - scrollTip.height;
pt.y = scrollBar.scrollThumb.y + scrollThumbMidPoint -
scrollTip.width / 2;
}
pt = scrollBar.localToGlobal(pt);
scrollTip.move(pt.x, pt.y);
scrollTip.visible = true;
}
}
}
}
/**
* Event handler for the mouse wheel scroll event.
*
* @param event The event object.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function mouseWheelHandler(event:MouseEvent):void
{
// If this Container has a vertical scrollbar,
// then handle the event and prevent further bubbling.
if (verticalScrollBar && verticalScrollBar.visible && !event.isDefaultPrevented())
{
var scrollDirection:int = event.delta <= 0 ? 1 : -1;
// Make sure we scroll by at least one line
var scrollAmount:Number = Math.max(Math.abs(event.delta),
verticalScrollBar.lineScrollSize);
// Multiply by 3 to make scrolling a little faster
var oldPosition:Number = verticalScrollPosition;
verticalScrollPosition += 3 * scrollAmount * scrollDirection;
var scrollEvent:ScrollEvent = new ScrollEvent(ScrollEvent.SCROLL);
scrollEvent.direction = ScrollEventDirection.VERTICAL;
scrollEvent.position = verticalScrollPosition;
scrollEvent.delta = verticalScrollPosition - oldPosition;
dispatchEvent(scrollEvent);
event.preventDefault();
}
}
/**
* @private
*/
mx_internal function get scroll_verticalScrollBar():ScrollBar
{
return verticalScrollBar;
}
/**
* @private
*/
mx_internal function get scroll_horizontalScrollBar():ScrollBar
{
return horizontalScrollBar;
}
}
}