blob: f2b243e03d056b508056b3e8b977601746a76137 [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 spark.skins.ios7
{
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.SoftKeyboardEvent;
import flash.system.Capabilities;
import mx.core.DPIClassification;
import mx.core.EventPriority;
import mx.core.mx_internal;
import mx.events.FlexEvent;
import mx.utils.Platform;
import spark.components.TextInput;
import spark.components.supportClasses.StyleableTextField;
import spark.skins.ios7.supportClasses.TextSkinBase;
use namespace mx_internal;
/**
* ActionScript-based skin for TextInput controls in mobile applications.
*
* @see spark.components.TextInput
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public class TextInputSkin extends TextSkinBase
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function TextInputSkin()
{
super();
// on iOS, make adjustments for native text rendering
_isIOS = Platform.isIOS;
switch (applicationDPI)
{
case DPIClassification.DPI_640:
{
measuredDefaultWidth = 1200;
measuredDefaultHeight = 132;
layoutBorderSize = 3;
roundheight = 24;
break;
}
case DPIClassification.DPI_480:
{
measuredDefaultWidth = 880;
measuredDefaultHeight = 100;
layoutBorderSize = 2;
roundheight = 18;
break;
}
case DPIClassification.DPI_320:
{
measuredDefaultWidth = 600;
measuredDefaultHeight = 66;
layoutBorderSize = 1.5;
roundheight = 14;
break;
}
case DPIClassification.DPI_240:
{
measuredDefaultWidth = 440;
measuredDefaultHeight = 50;
layoutBorderSize = 1;
roundheight = 10;
break;
}
case DPIClassification.DPI_120:
{
measuredDefaultWidth = 220;
measuredDefaultHeight = 25;
layoutBorderSize = .5;
roundheight = 5;
break;
}
default:
{
measuredDefaultWidth = 300;
measuredDefaultHeight = 33;
layoutBorderSize = .5;
roundheight = 7;
break;
}
}
addEventListener(FocusEvent.FOCUS_IN, focusChangeHandler);
addEventListener(FocusEvent.FOCUS_OUT, focusChangeHandler);
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
protected var isFocused:Boolean = false;
protected var roundheight:uint;
/**
* @copy spark.skins.spark.ApplicationSkin#hostComponent
*/
public var hostComponent:TextInput; // SkinnableComponent will populate
/**
* @private
*/
private var _isIOS:Boolean;
/**
* @private
*/
private var _isEditing:Boolean;
/**
* @private
*/
override protected function createChildren():void
{
super.createChildren();
textDisplay.addEventListener("editableChanged", editableChangedHandler);
textDisplay.addEventListener(FlexEvent.VALUE_COMMIT, valueCommitHandler);
// remove hit area improvements on iOS when editing
if (_isIOS)
{
textDisplay.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING, textDisplay_softKeyboardActivatingHandler, false, EventPriority.DEFAULT_HANDLER);
textDisplay.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, textDisplay_softKeyboardDeactivateHandler);
}
}
/**
* @private
*/
override protected function measure():void
{
super.measure();
var paddingLeft:Number = getStyle("paddingLeft");
var paddingRight:Number = getStyle("paddingRight");
var paddingTop:Number = getStyle("paddingTop");
var paddingBottom:Number = getStyle("paddingBottom");
var textHeight:Number = getStyle("fontSize") as Number;
if (textDisplay)
{
// temporarily change text for measurement
var oldText:String = textDisplay.text;
// commit styles so we can get a valid textHeight
textDisplay.text = "Wj";
textDisplay.commitStyles();
textHeight = textDisplay.measuredTextSize.y;
textDisplay.text = oldText;
}
// width is based on maxChars (if set)
if (hostComponent && hostComponent.maxChars)
{
// Grab the fontSize and subtract 2 as the pixel value for each character.
// This is just an approximation, but it appears to be a reasonable one
// for most input and most font.
var characterWidth:int = Math.max(1, (getStyle("fontSize") - 2));
measuredWidth = (characterWidth * hostComponent.maxChars) +
paddingLeft + paddingRight + StyleableTextField.TEXT_WIDTH_PADDING;
}
measuredHeight = paddingTop + textHeight + paddingBottom;
}
/**
* @private
*/
override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void
{
super.layoutContents(unscaledWidth, unscaledHeight);
// position & size border
if (border)
{
setElementSize(border, unscaledWidth, unscaledHeight);
setElementPosition(border, 0, 0);
}
// position & size the text
var paddingLeft:Number = getStyle("paddingLeft");
var paddingRight:Number = getStyle("paddingRight");
var paddingTop:Number = getStyle("paddingTop");
var paddingBottom:Number = getStyle("paddingBottom");
var unscaledTextWidth:Number = unscaledWidth - paddingLeft - paddingRight;
var unscaledTextHeight:Number = unscaledHeight - paddingTop - paddingBottom;
// default vertical positioning is centered
var textHeight:Number = getElementPreferredHeight(textDisplay);
var textY:Number = Math.round(0.5 * (unscaledTextHeight - textHeight)) + paddingTop;
// On iOS the TextField top and bottom edges are bounded by the padding.
// On all other platforms, the height of the textDisplay is
// textHeight + paddingBottom to increase hitArea on bottom.
// Note: We don't move the Y position upwards because TextField
// has way to set vertical positioning.
// Note: iOS is a special case due to the clear button provided by the
// native text control used while editing.
var adjustedTextHeight:Number = (_isIOS && _isEditing) ? textHeight : textHeight + paddingBottom;
if (textDisplay)
{
// We're going to do a few tricks to try to increase the size of our hitArea to make it
// easier for users to select text or put the caret in a certain spot. To do that,
// rather than set textDisplay.x=paddingLeft, we are going to set
// textDisplay.leftMargin = paddingLeft. In addition, we're going to size the height
// of the textDisplay larger than just the size of the text inside to increase the hitArea
// on the bottom. We'll also assign textDisplay.rightMargin = paddingRight to increase the
// the hitArea on the right. Unfortunately, there's no way to increase the hitArea on the top
// just yet, but these three tricks definitely help out with regards to user experience.
// See http://bugs.adobe.com/jira/browse/SDK-29406 and http://bugs.adobe.com/jira/browse/SDK-29405
// set leftMargin, rightMargin to increase the hitArea. Need to set it before calling commitStyles().
var marginChanged:Boolean = ((textDisplay.leftMargin != paddingLeft) ||
(textDisplay.rightMargin != paddingRight));
textDisplay.leftMargin = paddingLeft;
textDisplay.rightMargin = paddingRight;
// need to force a styleChanged() after setting leftMargin, rightMargin if they
// changed values. Then we can validate the styles through commitStyles()
if (marginChanged)
textDisplay.styleChanged(null);
textDisplay.commitStyles();
setElementSize(textDisplay, unscaledWidth, adjustedTextHeight);
// set x=0 since we're using textDisplay.leftMargin = paddingLeft
setElementPosition(textDisplay, 0, textY);
}
if (promptDisplay)
{
promptDisplay.commitStyles();
setElementSize(promptDisplay, unscaledTextWidth, adjustedTextHeight);
setElementPosition(promptDisplay, paddingLeft, textY);
}
}
override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void
{
super.drawBackground(unscaledWidth, unscaledHeight);
var contentBackgroundColor:uint = getStyle("contentBackgroundColor");
var contentBackgroundAlpha:Number = getStyle("contentBackgroundAlpha");
//change border color and thickness when in focus
var borderColor:uint = isFocused ? getStyle("focusColor") : getStyle("borderColor");
var borderWidth:uint = layoutBorderSize * 2;
if (isNaN(contentBackgroundAlpha))
{
contentBackgroundAlpha = 1;
}
if (getStyle("contentBackgroundBorder") == "roundedrect")
{
graphics.lineStyle(layoutBorderSize, borderColor, 1, true);
graphics.beginFill(contentBackgroundColor, contentBackgroundAlpha);
graphics.drawRoundRectComplex(layoutBorderSize, layoutBorderSize, unscaledWidth - borderWidth, unscaledHeight - borderWidth, roundheight, roundheight, roundheight, roundheight);
graphics.endFill();
}
if (getStyle("contentBackgroundBorder") == "rectangle")
{
//rectangle border and background
graphics.lineStyle(layoutBorderSize, borderColor, 1);
graphics.beginFill(contentBackgroundColor, contentBackgroundAlpha);
graphics.drawRect(layoutBorderSize, layoutBorderSize, unscaledWidth - borderWidth, unscaledHeight - borderWidth);
graphics.endFill();
}
else if (getStyle("contentBackgroundBorder") == "none")
{
//rectangle border and background
graphics.beginFill(contentBackgroundColor, contentBackgroundAlpha);
graphics.drawRect(0, 0, unscaledWidth - borderWidth, unscaledHeight - borderWidth);
graphics.endFill();
}
}
/**
* @private
*/
private function editableChangedHandler(event:Event):void
{
invalidateDisplayList();
}
/**
* @private
* The text changed in some way.
*
* Dynamic fields (ie !editable) with no text measure with width=0 and height=0.
* If the text changed, need to remeasure the text to get the correct height so it
* will be laid out correctly.
*/
private function valueCommitHandler(event:Event):void
{
if (textDisplay && !textDisplay.editable)
invalidateDisplayList();
}
/**
* @private
*/
private function textDisplay_softKeyboardActivatingHandler(event:SoftKeyboardEvent):void
{
if (event.isDefaultPrevented())
return;
_isEditing = true;
invalidateDisplayList();
}
/**
* @private
*/
private function textDisplay_softKeyboardDeactivateHandler(event:SoftKeyboardEvent):void
{
_isEditing = false;
invalidateDisplayList();
}
private function focusChangeHandler(event:FocusEvent):void
{
isFocused = event.type == FocusEvent.FOCUS_IN;
invalidateDisplayList();
}
}
}