blob: e1fd0db825980d4f4d5fad49bba4df22373b0516 [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.controls
{
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.ui.Keyboard;
import mx.core.IFlexDisplayObject;
import mx.core.mx_internal;
import mx.events.FlexEvent;
use namespace mx_internal;
//--------------------------------------
// Styles
//--------------------------------------
/**
* Name of CSS style declaration that specifies styles for the text of the
* selected button.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="selectedButtonTextStyleName", type="String", inherit="no")]
//--------------------------------------
// Excluded APIs
//--------------------------------------
[Exclude(name="horizontalLineScrollSize", kind="property")]
[Exclude(name="horizontalPageScrollSize", kind="property")]
[Exclude(name="horizontalScrollBar", kind="property")]
[Exclude(name="horizontalScrollPolicy", kind="property")]
[Exclude(name="horizontalScrollPosition", kind="property")]
[Exclude(name="maxHorizontalScrollPosition", kind="property")]
[Exclude(name="maxVerticalScrollPosition", kind="property")]
[Exclude(name="verticalLineScrollSize", kind="property")]
[Exclude(name="verticalPageScrollSize", kind="property")]
[Exclude(name="verticalScrollBar", kind="property")]
[Exclude(name="verticalScrollPolicy", kind="property")]
[Exclude(name="verticalScrollPosition", kind="property")]
[Exclude(name="scroll", kind="event")]
[Exclude(name="click", kind="event")]
[Exclude(name="backgroundAlpha", kind="style")]
[Exclude(name="backgroundAttachment", kind="style")]
[Exclude(name="backgroundColor", kind="style")]
[Exclude(name="backgroundImage", kind="style")]
[Exclude(name="backgroundSize", kind="style")]
[Exclude(name="borderColor", kind="style")]
[Exclude(name="borderSides", kind="style")]
[Exclude(name="borderSkin", kind="style")]
[Exclude(name="borderStyle", kind="style")]
[Exclude(name="borderThickness", kind="style")]
[Exclude(name="cornerRadius", kind="style")]
[Exclude(name="dropShadowColor", kind="style")]
[Exclude(name="dropShadowEnabled", kind="style")]
[Exclude(name="horizontalScrollBarStyleName", kind="style")]
[Exclude(name="shadowCapColor", kind="style")]
[Exclude(name="shadowColor", kind="style")]
[Exclude(name="shadowDirection", kind="style")]
[Exclude(name="shadowDistance", kind="style")]
[Exclude(name="verticalScrollBarStyleName", kind="style")]
//--------------------------------------
// Other metadata
//--------------------------------------
[IconFile("ButtonBar.png")]
[Alternative(replacement="spark.components.ButtonBar", since="4.0")]
[DefaultTriggerEvent("itemClick")]
/**
* The ToggleButtonBar control defines a horizontal or vertical
* group of buttons that maintain their selected or deselected state.
* Only one button in the ToggleButtonBar control
* can be in the selected state.
* This means that when a user selects a button in a ToggleButtonBar control,
* the button stays in the selected state until the user selects a different button.
*
* <p>If you set the <code>toggleOnClick</code> property of the
* ToggleButtonBar container to <code>true</code>,
* selecting the currently selected button deselects it.
* By default the <code>toggleOnClick</code> property is set to
* <code>false</code>.</p>
*
* <p>You can use the ButtonBar control to define a group
* of push buttons.</p>
*
* <p>The typical use for a toggle button is for maintaining selection
* among a set of options, such as switching between views in a ViewStack
* container.</p>
*
* <p>The ToggleButtonBar control creates Button controls based on the value of
* its <code>dataProvider</code> property.
* Even though ToggleButtonBar is a subclass of Container, do not use methods such as
* <code>Container.addChild()</code> and <code>Container.removeChild()</code>
* to add or remove Button controls.
* Instead, use methods such as <code>addItem()</code> and <code>removeItem()</code>
* to manipulate the <code>dataProvider</code> property.
* The ToggleButtonBar control automatically adds or removes the necessary children based on
* changes to the <code>dataProvider</code> property.</p>
*
* <p>To control the styling of the buttons of the ToggleButtonBar control,
* use the <code>buttonStyleName</code>, <code>firstButtonStyleName</code>,
* and <code>lastButtonStyleName</code> style properties;
* do not try to style the individual Button controls
* that make up the ToggleButtonBar control.</p>
*
* <p>ToggleButtonBar control has the following default characteristics:</p>
* <table class="innertable">
* <tr>
* <th>Characteristic</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>Preferred size</td>
* <td>Wide enough to contain all buttons with their label text and icons, if any, plus any
* padding and separators, and high enough to accommodate the button height.</td>
* </tr>
* <tr>
* <td>Control resizing rules</td>
* <td>The controls do not resize by default. Specify percentage sizes if you want your
* ToggleButtonBar to resize based on the size of its parent container.</td>
* </tr>
* <tr>
* <td>selectedIndex</td>
* <td>Determines which button will be selected when the control is created. The default value is "0"
* and selects the leftmost button in the bar. Setting the selectedIndex property to "-1" deselects
* all buttons in the bar.</td>
* </tr>
* <tr>
* <td>Padding</td>
* <td>0 pixels for the top, bottom, left, and right properties.</td>
* </tr>
* </table>
*
* @mxml
*
* <p>The <code>&lt;mx:ToggleButtonBar&gt;</code> tag inherits all of the tag attributes
* of its superclass, and adds the following tag attributes:</p>
*
* <pre>
* &lt;mx:ToggleButtonBar
* <b>Properties</b>
* selectedIndex="0"
* toggleOnClick="false|true"
*
* <b>Styles</b>
* selectedButtonTextStyleName="<i>Name of CSS style declaration that specifies styles for the text of the selected button.</i>"&gt;
* ...
* <i>child tags</i>
* ...
* &lt;/mx:ToggleButtonBar&gt;
* </pre>
*
* @includeExample examples/ToggleButtonBarExample.mxml
*
* @see mx.controls.ButtonBar
* @see mx.controls.LinkBar
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class ToggleButtonBar extends ButtonBar
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function ToggleButtonBar()
{
super();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private.
*/
private var initializeSelectedButton:Boolean = true;
/**
* @private
* Name of style used to specify selectedButtonTextStyleName.
* Overridden by TabBar.
*/
mx_internal var selectedButtonTextStyleNameProp:String =
"selectedButtonTextStyleName";
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// selectedIndex
//----------------------------------
/**
* @private
* Storage for the selectedIndex property.
*/
private var _selectedIndex:int = -2;
/**
* @private.
*/
private var selectedIndexChanged:Boolean = false;
[Bindable("click")]
[Bindable("valueCommit")]
[Inspectable(category="General")]
/**
* Index of the selected button.
* Indexes are in the range of 0, 1, 2, ..., n - 1,
* where <i>n</i> is the number of buttons.
*
* <p>The default value is 0.
* A value of -1 deselects all the buttons in the bar.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get selectedIndex():int
{
return super.selectedIndex;
//return _selectedIndex;
}
/**
* @private.
*/
override public function set selectedIndex(value:int):void
{
//#SDK-15690 - if the user has asked for -1 (no child selected) then we must preserve this
if (value == selectedIndex && value != -1)
return;
// If the buttons have not been created yet, store the selectedIndex.
if (numChildren == 0)
{
_selectedIndex = value;
selectedIndexChanged = true;
}
// Otherwise set the index locally and invalidate properties.
if ( value < numChildren)
{
_selectedIndex = value;
selectedIndexChanged = true;
invalidateProperties();
dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
}
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// toggleOnClick
//----------------------------------
/**
* @private
* Storage for the toggleOnClick property.
*/
private var _toggleOnClick:Boolean = false;
[Inspectable(category="General", defaultValue="false")]
/**
* Specifies whether the currently selected button can be deselected by
* the user.
*
* By default, the currently selected button gets deselected
* automatically only when another button in the group is selected.
* Setting this property to <code>true</code> lets the user
* deselect it.
* When the currently selected button is deselected,
* the <code>selectedIndex</code> property is set to <code>-1</code>.
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get toggleOnClick():Boolean
{
return _toggleOnClick;
}
/**
* @private
*/
public function set toggleOnClick(value:Boolean):void
{
_toggleOnClick = value;
}
//--------------------------------------------------------------------------
//
// Overridden methods: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
if (selectedIndexChanged)
{
hiliteSelectedNavItem(_selectedIndex);
// Update parent index.
super.selectedIndex = _selectedIndex;
selectedIndexChanged = false;
}
}
/**
* @private
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
// Select the first selectedIndex in the UI. If the user has defined it,
// the value will be waiting in the selectedIndex property
if (initializeSelectedButton)
{
initializeSelectedButton = false;
var index:int = _selectedIndex;
if (index == -2 )
{
if (numChildren > 0)
index = 0;
else
index = -1;
}
hiliteSelectedNavItem(index);
}
}
/**
* @private
*/
override public function styleChanged(styleProp:String):void
{
var allStyles:Boolean = styleProp == null || styleProp == "styleName";
super.styleChanged(styleProp);
if (allStyles ||
styleProp == selectedButtonTextStyleNameProp)
{
if (selectedIndex != -1 && selectedIndex < numChildren)
{
var child:Button = Button(getChildAt(selectedIndex));
if (child)
{
var selectedButtonTextStyleName:String =
getStyle(selectedButtonTextStyleNameProp);
child.getTextField().styleName =
selectedButtonTextStyleName ?
selectedButtonTextStyleName :
"activeButtonStyle";
}
}
}
}
//--------------------------------------------------------------------------
//
// Overridden methods: NavBar
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function createNavItem(
label:String,
icon:Class = null):IFlexDisplayObject
{
var b:Button = Button(super.createNavItem(label, icon));
b.toggle = true;
return b;
}
/**
* @private
*/
override protected function hiliteSelectedNavItem(index:int):void
{
var child:Button;
// Un-hilite the current selection
if (selectedIndex != -1 && selectedIndex < numChildren)
{
child = Button(getChildAt(selectedIndex));
child.selected = false;
child.getTextField().styleName = child;
child.invalidateDisplayList();
child.invalidateSize();
}
// Set new index
super.selectedIndex = index;
_selectedIndex = index;
if (index > -1)
{
// Hilite the new selection
child = Button(getChildAt(selectedIndex));
child.selected = true;
var selectedButtonTextStyleName:String =
getStyle(selectedButtonTextStyleNameProp);
child.getTextField().styleName =
selectedButtonTextStyleName ?
selectedButtonTextStyleName :
"activeButtonStyle";
child.invalidateDisplayList();
}
}
/**
* @private
*/
override protected function resetNavItems():void
{
var selectedButtonTextStyleName:String =
getStyle(selectedButtonTextStyleNameProp);
// Need to reset the index values, selection state,
// and selected text style...
var n:int = numChildren;
for (var i:int = 0; i < n; i++)
{
var child:Button = Button(getChildAt(i));
if (i == selectedIndex)
{
child.selected = true;
child.getTextField().styleName =
selectedButtonTextStyleName ?
selectedButtonTextStyleName :
"activeButtonStyle";
}
else
{
child.selected = false;
child.getTextField().styleName = child;
}
}
super.resetNavItems();
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Select the button at the specified index.
*/
mx_internal function selectButton(index:int,
updateFocusIndex:Boolean = false,
trigger:Event = null):void
{
// 143958
_selectedIndex = index;
if (updateFocusIndex)
{
drawButtonFocus(focusedIndex, false);
focusedIndex = index;
drawButtonFocus(focusedIndex, false);
}
var child:Button = Button(getChildAt(index));
simulatedClickTriggerEvent = trigger;
child.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
simulatedClickTriggerEvent = null;
}
//--------------------------------------------------------------------------
//
// Overridden event handlers: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function keyDownHandler(event:KeyboardEvent):void
{
var targetIndex:int = -1;
var updateFocusIndex:Boolean = true;
var n:int = numChildren;
switch (event.keyCode)
{
case Keyboard.PAGE_DOWN:
{
targetIndex = nextIndex(selectedIndex);
break;
}
case Keyboard.PAGE_UP:
{
if (selectedIndex != -1)
targetIndex = prevIndex(selectedIndex);
else if (n > 0)
targetIndex = 0;
break;
}
case Keyboard.HOME:
{
if (n > 0)
targetIndex = 0;
break;
}
case Keyboard.END:
{
if (n > 0)
targetIndex = n - 1;
break;
}
case Keyboard.SPACE:
case Keyboard.ENTER:
{
if (focusedIndex != -1)
{
targetIndex = focusedIndex;
updateFocusIndex = false;
}
break;
}
default:
{
super.keyDownHandler(event);
}
}
if (targetIndex != -1)
{
selectButton(targetIndex, updateFocusIndex, event);
}
event.stopPropagation();
}
/**
* @private
*/
override protected function keyUpHandler(event:KeyboardEvent):void
{
// Override superclass's keyUpHandler() but do nothing.
}
//--------------------------------------------------------------------------
//
// Overridden event handlers: NavBar
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function clickHandler(event:MouseEvent):void
{
var index:int = getChildIndex(Button(event.currentTarget));
// 143958
_selectedIndex = index;
if (_toggleOnClick && index == selectedIndex)
{
selectedIndex = -1;
hiliteSelectedNavItem(-1);
}
else
hiliteSelectedNavItem(index);
super.clickHandler(event);
}
}
}