http://git-wip-us.apache.org/repos/asf/flex-examples/blob/539852e9/tourdeflexmobile/src/spark/skins/android4/SpinnerListSkin.as ---------------------------------------------------------------------- diff --git a/tourdeflexmobile/src/spark/skins/android4/SpinnerListSkin.as b/tourdeflexmobile/src/spark/skins/android4/SpinnerListSkin.as new file mode 100644 index 0000000..006fddb --- /dev/null +++ b/tourdeflexmobile/src/spark/skins/android4/SpinnerListSkin.as @@ -0,0 +1,293 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.android4 +{ + import flash.display.InteractiveObject; + + import mx.core.ClassFactory; + import mx.core.DPIClassification; + import mx.core.mx_internal; + + import spark.components.DataGroup; + import spark.components.Scroller; + import spark.components.SpinnerList; + import spark.components.SpinnerListItemRenderer; + import spark.layouts.VerticalSpinnerLayout; + import spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; + import spark.skins.mobile.supportClasses.MobileSkin; + + + use namespace mx_internal; + /** + * ActionScript-based skin for the SpinnerList in mobile applications. + * + * @see spark.components.SpinnerList + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public class SpinnerListSkin extends MobileSkin + { + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public function SpinnerListSkin() + { + super(); + + selectionIndicatorClass = spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; + borderThickness = 1; + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + selectionIndicatorHeight = 182; + minWidth = 64; + borderThickness = 3; + break; + } + case DPIClassification.DPI_480: + { + selectionIndicatorHeight = 144; + minWidth = 48; + borderThickness = 2; + break; + } + case DPIClassification.DPI_320: + { + selectionIndicatorHeight = 96; + minWidth = 32; + borderThickness = 2; + break; + } + case DPIClassification.DPI_240: + { + selectionIndicatorHeight = 72; + minWidth = 24; + borderThickness = 1; + break; + } + case DPIClassification.DPI_120: + { + selectionIndicatorHeight = 36; + minWidth = 12; + borderThickness = 0; + break; + } + default: + { + selectionIndicatorHeight = 48; + minWidth = 16; + borderThickness = 1; + } + } + + } + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * Scroller skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var scroller:Scroller; + + /** + * DataGroup skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var dataGroup:DataGroup; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var hostComponent:SpinnerList; + + /** + * Pixel size of the border. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var borderThickness:int; + + /** + * Class for the selection indicator skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var selectionIndicatorClass:Class; + + /** + * Selection indicator skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var selectionIndicator:InteractiveObject; + + /** + * Height of the selection indicator. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var selectionIndicatorHeight:Number; + + //-------------------------------------------------------------------------- + // + // Overridden Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + alpha = currentState.indexOf("disabled") == -1 ? 1 : 0.5; + } + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (!dataGroup) + { + // Create data group layout + var layout:VerticalSpinnerLayout = new VerticalSpinnerLayout(); + layout.requestedRowCount = 5; + // Create data group + dataGroup = new DataGroup(); + dataGroup.id = "dataGroup"; + dataGroup.layout = layout; + + dataGroup.itemRenderer = new ClassFactory(spark.components.SpinnerListItemRenderer); + } + + if (!scroller) + { + // Create scroller + scroller = new Scroller(); + scroller.id = "scroller"; + scroller.hasFocusableChildren = false; + scroller.ensureElementIsVisibleForSoftKeyboard = false; + // Only support vertical scrolling + scroller.setStyle("verticalScrollPolicy","on"); + scroller.setStyle("horizontalScrollPolicy", "off"); + scroller.setStyle("skinClass", spark.skins.android4.SpinnerListScrollerSkin); + + addChild(scroller); + } + + if (!selectionIndicator) + { + // Selection indicator is on top + selectionIndicator = new selectionIndicatorClass(); + selectionIndicator.mouseEnabled = false; + addChild(selectionIndicator); + } + + // Associate scroller with data group + if (!scroller.viewport) + scroller.viewport = dataGroup; + } + + /** + * @private + */ + override protected function measure():void + { + measuredWidth = scroller.getPreferredBoundsWidth() + borderThickness * 2; + measuredHeight = scroller.getPreferredBoundsHeight(); + //add in for selection indicator + measuredMinHeight = selectionIndicatorHeight + borderThickness * 4; + minHeight = measuredMinHeight; + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + // Scroller + setElementSize(scroller, unscaledWidth - borderThickness * 2, unscaledHeight); + setElementPosition(scroller, borderThickness, 0); + //selection indicator + unscaledHeight = Math.max(unscaledHeight, selectionIndicatorHeight + borderThickness * 4); + + setElementSize(selectionIndicator, unscaledWidth, selectionIndicatorHeight); + setElementPosition(selectionIndicator, 0, Math.floor((unscaledHeight - selectionIndicatorHeight) / 2)); + } + + /** + * @private + */ + override public function styleChanged(styleProp:String):void + { + // Reinitialize the typical element so it picks up the latest styles + // Font styles might impact the size of the SpinnerList + if (styleProp != "color" && styleProp != "accentColor") + { + if (dataGroup) + dataGroup.invalidateTypicalItemRenderer(); + } + + super.styleChanged(styleProp); + } + + } +} +
http://git-wip-us.apache.org/repos/asf/flex-examples/blob/539852e9/tourdeflexmobile/src/spark/skins/android4/SplitViewNavigatorSkin.as ---------------------------------------------------------------------- diff --git a/tourdeflexmobile/src/spark/skins/android4/SplitViewNavigatorSkin.as b/tourdeflexmobile/src/spark/skins/android4/SplitViewNavigatorSkin.as new file mode 100644 index 0000000..ee06eea --- /dev/null +++ b/tourdeflexmobile/src/spark/skins/android4/SplitViewNavigatorSkin.as @@ -0,0 +1,157 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.android4 +{ + import mx.core.mx_internal; + + import spark.components.Callout; + import spark.components.Group; + import spark.components.SkinnablePopUpContainer; + import spark.components.SplitViewNavigator; + import spark.layouts.HorizontalLayout; + import spark.skins.mobile.supportClasses.MobileSkin; + + use namespace mx_internal; + + /** + * The default skin for the SplitViewNavigator component. This skin is + * chromeless and doesn't draw a background, border or separator. + * It only contains a single content group with a horizontal layout to hold + * the navigators, and a Callout component. + * The Callout component uses the default values for its layout properties. + * + * @see spark.components.Callout + * @see spark.components.SplitViewNavigator + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public class SplitViewNavigatorSkin extends MobileSkin + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public function SplitViewNavigatorSkin() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // SkinParts + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.components.SkinnableContainer#contentGroup + */ + public var contentGroup:Group; + + /** + * @copy spark.components.SplitViewNavigator#viewNavigatorPopUp + */ + public var viewNavigatorPopUp:SkinnablePopUpContainer; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:SplitViewNavigator; + + //-------------------------------------------------------------------------- + // + // Overridden Methods + // + //-------------------------------------------------------------------------- + /** + * @private + */ + override protected function createChildren():void + { + // Create the layout for the content group + var hLayout:HorizontalLayout = new HorizontalLayout(); + + // A gap of 1 is used to reveal a thin line of the background between + // each child navigator. This serves as a divider. To change the look + // of the divider, change the backgroundColor style. + hLayout.gap = 1; + + // Create the contentGroup + contentGroup = new Group(); + contentGroup.id = "contentGroup"; + contentGroup.layout = hLayout; + addChild(contentGroup); + + // Create the callout but don't add it to display list + viewNavigatorPopUp = new Callout(); + viewNavigatorPopUp.id = "viewNavigatorPopUp"; + } + + /** + * @private + */ + override protected function measure():void + { + super.measure(); + + measuredWidth = contentGroup.getPreferredBoundsWidth(); + measuredHeight = contentGroup.getPreferredBoundsHeight(); + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + contentGroup.setLayoutBoundsSize(unscaledWidth, unscaledHeight); + contentGroup.setLayoutBoundsPosition(0, 0); + } + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + // Background is used to draw the divider between each navigator + var color:uint = getStyle("backgroundColor"); + graphics.beginFill(color); + graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); + graphics.endFill(); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/539852e9/tourdeflexmobile/src/spark/skins/android4/StageTextAreaSkin.as ---------------------------------------------------------------------- diff --git a/tourdeflexmobile/src/spark/skins/android4/StageTextAreaSkin.as b/tourdeflexmobile/src/spark/skins/android4/StageTextAreaSkin.as new file mode 100644 index 0000000..97daa08 --- /dev/null +++ b/tourdeflexmobile/src/spark/skins/android4/StageTextAreaSkin.as @@ -0,0 +1,185 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.android4 +{ + +import mx.core.DPIClassification; +import mx.core.mx_internal; +import mx.utils.Platform; + +import spark.components.TextArea; +import spark.components.supportClasses.StyleableTextField; +import spark.skins.android4.supportClasses.StageTextSkinBase; + +use namespace mx_internal; + +/** + * ActionScript-based skin for TextArea controls in mobile applications that uses a + * StyleableStageText class for the text display. + * + * @see spark.components.TextArea + * @see spark.components.supportClasses.StyleableStageText + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ +public class StageTextAreaSkin extends StageTextSkinBase +{ + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * The underlying native text control on iOS has internal margins of its + * own. In order to remain faithful to the paddingTop and paddingBottom + * style values that developers may specify, those internal margins need to + * be compensated for. This variable contains size of that compensation in + * pixels. + */ + mx_internal static var iOSVerticalPaddingAdjustment:Number = 5; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + public function StageTextAreaSkin() + { + super(); + multiline = true; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + measuredDefaultHeight = 212; + break; + } + case DPIClassification.DPI_480: + { + measuredDefaultHeight = 140; + break; + } + case DPIClassification.DPI_320: + { + measuredDefaultHeight = 106; + break; + } + case DPIClassification.DPI_240: + { + measuredDefaultHeight = 70; + break; + } + case DPIClassification.DPI_120: + { + measuredDefaultHeight = 35; + break; + } + default: + { + measuredDefaultHeight = 48; + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:TextArea; // SkinnableComponent will populate + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + // base class handles border position & size + super.layoutContents(unscaledWidth, unscaledHeight); + + // 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 = Math.max(0, unscaledWidth - paddingLeft - paddingRight); + var unscaledTextHeight:Number = Math.max(0, unscaledHeight - paddingTop - paddingBottom); + + if (textDisplay) + { + var verticalPosAdjustment:Number = 0; + var heightAdjustment:Number = 0; + + if (Platform.isIOS) + { + verticalPosAdjustment = Math.min(iOSVerticalPaddingAdjustment, paddingTop); + heightAdjustment = verticalPosAdjustment + Math.min(iOSVerticalPaddingAdjustment, paddingBottom); + } + + textDisplay.commitStyles(); + setElementSize(textDisplay, unscaledTextWidth, unscaledTextHeight + heightAdjustment); + setElementPosition(textDisplay, paddingLeft, paddingTop - verticalPosAdjustment); + } + + if (promptDisplay) + { + if (promptDisplay is StyleableTextField) + StyleableTextField(promptDisplay).commitStyles(); + + setElementSize(promptDisplay, unscaledTextWidth, unscaledTextHeight); + setElementPosition(promptDisplay, paddingLeft, paddingTop); + } + } + + /** + * @private + */ + override protected function measure():void + { + super.measure(); + measureTextComponent(hostComponent); + } +} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/539852e9/tourdeflexmobile/src/spark/skins/android4/StageTextInputSkin.as ---------------------------------------------------------------------- diff --git a/tourdeflexmobile/src/spark/skins/android4/StageTextInputSkin.as b/tourdeflexmobile/src/spark/skins/android4/StageTextInputSkin.as new file mode 100644 index 0000000..4bcc4d9 --- /dev/null +++ b/tourdeflexmobile/src/spark/skins/android4/StageTextInputSkin.as @@ -0,0 +1,130 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.android4 +{ +import spark.components.TextInput; +import spark.components.supportClasses.StyleableTextField; +import spark.skins.android4.supportClasses.StageTextSkinBase; + +/** + * ActionScript-based skin for TextInput controls in mobile applications that uses a + * StyleableStageText class for the text input. + * + * @see spark.components.TextInput + * @see spark.components.supportClasses.StyleableStageText + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ +public class StageTextInputSkin extends StageTextSkinBase +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + public function StageTextInputSkin() + { + super(); + multiline = false; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:TextInput; // SkinnableComponent will populate + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function measure():void + { + super.measure(); + measureTextComponent(hostComponent); + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, + unscaledHeight:Number):void + { + // base class handles border position & size + super.layoutContents(unscaledWidth, unscaledHeight); + + // 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 = Math.max(0, unscaledWidth - paddingLeft - paddingRight); + var unscaledTextHeight:Number = Math.max(0, unscaledHeight - paddingTop - paddingBottom); + + // default vertical positioning is centered + var textHeight:Number = getElementPreferredHeight(textDisplay); + var textY:Number = Math.round(0.5 * (unscaledTextHeight - textHeight)) + paddingTop; + + if (textDisplay) + { + textDisplay.commitStyles(); + setElementSize(textDisplay, unscaledTextWidth, unscaledTextHeight); + setElementPosition(textDisplay, paddingLeft, textY); + } + + if (promptDisplay) + { + if (promptDisplay is StyleableTextField) + StyleableTextField(promptDisplay).commitStyles(); + + var promptHeight:Number = getElementPreferredHeight(promptDisplay); + var promptY:Number = Math.round(0.5 * (unscaledTextHeight - promptHeight)) + paddingTop; + + setElementSize(promptDisplay, unscaledTextWidth, promptHeight); + setElementPosition(promptDisplay, paddingLeft, promptY); + } + } + + /** + * @private + */ +} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/539852e9/tourdeflexmobile/src/spark/skins/android4/TabbedViewNavigatorTabBarSkin.as ---------------------------------------------------------------------- diff --git a/tourdeflexmobile/src/spark/skins/android4/TabbedViewNavigatorTabBarSkin.as b/tourdeflexmobile/src/spark/skins/android4/TabbedViewNavigatorTabBarSkin.as new file mode 100644 index 0000000..eea5948 --- /dev/null +++ b/tourdeflexmobile/src/spark/skins/android4/TabbedViewNavigatorTabBarSkin.as @@ -0,0 +1,122 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.android4 +{ +import spark.components.ButtonBarButton; +import spark.components.DataGroup; +import spark.skins.mobile.supportClasses.ButtonBarButtonClassFactory; +import spark.skins.mobile.supportClasses.TabbedViewNavigatorTabBarHorizontalLayout; + +/** + * The Android 4.x specific skin class for the Spark TabbedViewNavigator tabBar skin part. + * It uses the ButtonBarFirstButtonSkin and ButtonBarMiddleButtonSkin as skins for first + * middle buttons + * + * @see spark.components.TabbedViewNavigator#tabBar + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class TabbedViewNavigatorTabBarSkin extends ButtonBarSkin +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function TabbedViewNavigatorTabBarSkin() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + if (!firstButton) + { + firstButton = new ButtonBarButtonClassFactory(ButtonBarButton); + firstButton.skinClass = spark.skins.android4.ButtonBarFirstButtonSkin; + } + + if (!lastButton) + { + lastButton = new ButtonBarButtonClassFactory(ButtonBarButton); + lastButton.skinClass = spark.skins.android4.ButtonBarFirstButtonSkin; + } + + if (!middleButton) + { + middleButton = new ButtonBarButtonClassFactory(ButtonBarButton); + middleButton.skinClass = spark.skins.android4.ButtonBarMiddleButtonSkin; + } + + if (!dataGroup) + { + // TabbedViewNavigatorButtonBarHorizontalLayout for even percent layout + var tabLayout:TabbedViewNavigatorTabBarHorizontalLayout = + new TabbedViewNavigatorTabBarHorizontalLayout(); + tabLayout.useVirtualLayout = false; + + dataGroup = new DataGroup(); + dataGroup.layout = tabLayout; + addChild(dataGroup); + } + } + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + super.drawBackground(unscaledWidth, unscaledHeight); + + // backgroundAlpha style is not supported by ButtonBar + // TabbedViewNavigatorSkin sets a hard-coded value to support + // overlayControls + var backgroundAlphaValue:* = getStyle("backgroundAlpha"); + var backgroundAlpha:Number = (backgroundAlphaValue === undefined) + ? 1 : getStyle("backgroundAlpha"); + + graphics.beginFill(getStyle("chromeColor"), backgroundAlpha); + graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); + graphics.endFill(); + } +} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/539852e9/tourdeflexmobile/src/spark/skins/android4/TextAreaHScrollBarSkin.as ---------------------------------------------------------------------- diff --git a/tourdeflexmobile/src/spark/skins/android4/TextAreaHScrollBarSkin.as b/tourdeflexmobile/src/spark/skins/android4/TextAreaHScrollBarSkin.as new file mode 100644 index 0000000..540318e --- /dev/null +++ b/tourdeflexmobile/src/spark/skins/android4/TextAreaHScrollBarSkin.as @@ -0,0 +1,105 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.android4 +{ +import mx.core.DPIClassification; +import mx.core.mx_internal; + +use namespace mx_internal; + + /** + * The default skin class for the Spark TextAreaHScrollBar component in mobile + * applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class TextAreaHScrollBarSkin extends HScrollBarSkin +{ + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function TextAreaHScrollBarSkin() + { + super(); + + thumbSkinClass = TextAreaHScrollBarThumbSkin; + var paddingBottom:int; + var paddingHorizontal:int; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + minHeight = 30; + paddingBottom = TextAreaHScrollBarThumbSkin.PADDING_BOTTOM_320DPI; + paddingHorizontal = TextAreaHScrollBarThumbSkin.PADDING_HORIZONTAL_320DPI; + break; + } + case DPIClassification.DPI_480: + { + minHeight = 22; + paddingBottom = TextAreaHScrollBarThumbSkin.PADDING_BOTTOM_480DPI; + paddingHorizontal = TextAreaHScrollBarThumbSkin.PADDING_HORIZONTAL_480DPI; + break; + } + case DPIClassification.DPI_320: + { + minHeight = 15; + paddingBottom = TextAreaHScrollBarThumbSkin.PADDING_BOTTOM_320DPI; + paddingHorizontal = TextAreaHScrollBarThumbSkin.PADDING_HORIZONTAL_320DPI; + break; + } + case DPIClassification.DPI_240: + { + minHeight = 11; + paddingBottom = TextAreaHScrollBarThumbSkin.PADDING_BOTTOM_240DPI; + paddingHorizontal = TextAreaHScrollBarThumbSkin.PADDING_HORIZONTAL_240DPI; + break; + } + case DPIClassification.DPI_120: + { + minHeight = 11; + paddingBottom = TextAreaHScrollBarThumbSkin.PADDING_BOTTOM_120DPI; + paddingHorizontal = TextAreaHScrollBarThumbSkin.PADDING_HORIZONTAL_120DPI; + break; + } + default: + { + // default DPI_160 + minHeight = 9; + paddingBottom = TextAreaHScrollBarThumbSkin.PADDING_BOTTOM_DEFAULTDPI; + paddingHorizontal = TextAreaHScrollBarThumbSkin.PADDING_HORIZONTAL_DEFAULTDPI; + break; + } + } + + // The minimum width is set such that, at it's smallest size, the thumb appears + // as wide as it is high. + minThumbWidth = (minHeight - paddingBottom) + (paddingHorizontal * 2); + } +} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/539852e9/tourdeflexmobile/src/spark/skins/android4/TextAreaHScrollBarThumbSkin.as ---------------------------------------------------------------------- diff --git a/tourdeflexmobile/src/spark/skins/android4/TextAreaHScrollBarThumbSkin.as b/tourdeflexmobile/src/spark/skins/android4/TextAreaHScrollBarThumbSkin.as new file mode 100644 index 0000000..5d3fec8 --- /dev/null +++ b/tourdeflexmobile/src/spark/skins/android4/TextAreaHScrollBarThumbSkin.as @@ -0,0 +1,116 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.android4 +{ +import mx.core.DPIClassification; +import mx.core.mx_internal; + +use namespace mx_internal; + +/** + * The ActionScript-based skin used for TextAreaHScrollBarThumb components + * in mobile applications. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ +public class TextAreaHScrollBarThumbSkin extends HScrollBarThumbSkin +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + // These constants are also accessed from HScrollBarSkin + mx_internal static const PADDING_BOTTOM_640DPI:int = 16; + mx_internal static const PADDING_HORIZONTAL_640DPI:int = 16; + mx_internal static const PADDING_BOTTOM_480DPI:int = 12; + mx_internal static const PADDING_HORIZONTAL_480DPI:int = 12; + mx_internal static const PADDING_BOTTOM_320DPI:int = 8; + mx_internal static const PADDING_HORIZONTAL_320DPI:int = 12; + mx_internal static const PADDING_BOTTOM_240DPI:int = 6; + mx_internal static const PADDING_HORIZONTAL_240DPI:int = 6; + mx_internal static const PADDING_BOTTOM_120DPI:int = 3; + mx_internal static const PADDING_HORIZONTAL_120DPI:int = 3; + mx_internal static const PADDING_BOTTOM_DEFAULTDPI:int = 4; + mx_internal static const PADDING_HORIZONTAL_DEFAULTDPI:int = 6; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function TextAreaHScrollBarThumbSkin() + { + super(); + + // Depending on density set padding + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + paddingBottom = PADDING_BOTTOM_640DPI; + paddingHorizontal = PADDING_HORIZONTAL_640DPI; + break; + } + case DPIClassification.DPI_480: + { + paddingBottom = TextAreaHScrollBarThumbSkin.PADDING_BOTTOM_480DPI; + paddingHorizontal = TextAreaHScrollBarThumbSkin.PADDING_HORIZONTAL_480DPI; + break; + } + case DPIClassification.DPI_320: + { + paddingBottom = PADDING_BOTTOM_320DPI; + paddingHorizontal = PADDING_HORIZONTAL_320DPI; + break; + } + case DPIClassification.DPI_240: + { + paddingBottom = PADDING_BOTTOM_240DPI; + paddingHorizontal = PADDING_HORIZONTAL_240DPI; + break; + } + case DPIClassification.DPI_120: + { + paddingBottom = PADDING_BOTTOM_120DPI; + paddingHorizontal = PADDING_HORIZONTAL_120DPI; + break; + } + default: + { + paddingBottom = PADDING_BOTTOM_DEFAULTDPI; + paddingHorizontal = PADDING_HORIZONTAL_DEFAULTDPI; + break; + } + } + } +} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/539852e9/tourdeflexmobile/src/spark/skins/android4/TextAreaSkin.as ---------------------------------------------------------------------- diff --git a/tourdeflexmobile/src/spark/skins/android4/TextAreaSkin.as b/tourdeflexmobile/src/spark/skins/android4/TextAreaSkin.as new file mode 100644 index 0000000..6d1fa52 --- /dev/null +++ b/tourdeflexmobile/src/spark/skins/android4/TextAreaSkin.as @@ -0,0 +1,892 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.android4 +{ + + import flash.events.Event; + import flash.events.FocusEvent; + import flash.events.KeyboardEvent; + import flash.events.MouseEvent; + import flash.events.SoftKeyboardEvent; + import flash.geom.Point; + import flash.geom.Rectangle; + import flash.system.Capabilities; + import flash.text.TextLineMetrics; + import flash.ui.Keyboard; + + import mx.core.DPIClassification; + import mx.core.EventPriority; + import mx.core.FlexGlobals; + import mx.core.mx_internal; + import mx.events.FlexEvent; + import mx.utils.Platform; + + import spark.components.Group; + import spark.components.Scroller; + import spark.components.TextArea; + import spark.components.supportClasses.StyleableTextField; + import spark.events.CaretBoundsChangeEvent; + import spark.skins.android4.supportClasses.TextSkinBase; + + use namespace mx_internal; + + /** + * ActionScript-based skin for TextArea components in mobile applications. + * + * @see spark.components.TextArea + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class TextAreaSkin extends TextSkinBase + { + /** + * @private + * Right-margin of iOS native text control when editing on a retina display + * based on fontSize 32. + */ + mx_internal static var IOS_RIGHT_MARGIN_320:Number = 19; + + /** + * @private + * Right-margin of iOS native text control when editing on a retina display + * based on fontSize 16 scaling from applicationDPI 160. + */ + mx_internal static var IOS_RIGHT_MARGIN_160_SCALED_TO_320:Number = 9.4; + + /** + * @private + * Right-margin of iOS native text control when editing on a standard display + * based on fontSize 16 and runtimeDPI 160. + */ + mx_internal static var IOS_RIGHT_MARGIN_160:Number = 20.6; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function TextAreaSkin() + { + super(); + + addEventListener(Event.RESIZE, resizeHandler); + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + measuredDefaultWidth = 1024; + measuredDefaultHeight = 212; + layoutBorderSize = 4; + flatheight = 9; + break; + } + case DPIClassification.DPI_480: + { + measuredDefaultWidth = 880; + measuredDefaultHeight = 140; + layoutBorderSize = 3; + flatheight = 7; + break; + } + case DPIClassification.DPI_320: + { + measuredDefaultWidth = 612; + measuredDefaultHeight = 106; + layoutBorderSize = 2; + flatheight = 6; + break; + } + case DPIClassification.DPI_240: + { + measuredDefaultWidth = 440; + measuredDefaultHeight = 70; + layoutBorderSize = 2; + flatheight = 5; + break; + } + case DPIClassification.DPI_120: + { + measuredDefaultWidth = 220; + measuredDefaultHeight = 35; + layoutBorderSize = 1; + flatheight = 2; + break; + } + default: + { + measuredDefaultWidth = 306; + measuredDefaultHeight = 53; + layoutBorderSize = 1; + flatheight = 3; + break; + } + } + addEventListener(FocusEvent.FOCUS_IN, focusChangeHandler); + addEventListener(FocusEvent.FOCUS_OUT, focusChangeHandler); + } + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * Scroller skin part. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var scroller:Scroller; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:TextArea; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + protected var isFocused:Boolean = false; + + protected var flatheight:uint; + + /** + * @private + * The width of the component on the previous layout manager + * pass. This gets set in updateDisplayList() and used in measure() on + * the next layout pass. This is so our "guessed width" in measure() + * will be as accurate as possible since textDisplay is multiline and + * the textDisplay height is dependent on the width. + * + * In the constructor this is actually set based on the DPI. + */ + mx_internal var oldUnscaledWidth:Number; + + private var textDisplayGroup:Group; + private var _isIOS:Boolean; + private var invalidateCaretPosition:Boolean = true; + private var oldCaretBounds:Rectangle = new Rectangle(-1, -1, -1, -1); + private var lastTextHeight:Number; + private var lastTextWidth:Number; + + private var isTextDisplayTall:Boolean = true; + private var growTextDisplay:Boolean = false; + private var shrinkTextDisplay:Boolean = false; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + if (!textDisplay) + { + // wrap StyleableTextField in UIComponent + textDisplay = StyleableTextField(createInFontContext(StyleableTextField)); + textDisplay.styleName = this; + textDisplay.multiline = true; + textDisplay.editable = true; + textDisplay.lineBreak = getStyle("lineBreak"); + textDisplay.useTightTextBounds = false; + textDisplay.scrollToRangeDelegate = scrollToRange; + + // on iOS, resize the TextField and let the native control handle scrolling + _isIOS = Platform.isIOS; + + if (_isIOS) + { + // hard-coded rightMargin for iOS native text control + // this value is independent of the paddingRight style + var rightMargin:Number = 0; + var isRetina:Boolean = false; + var isScaling160to320:Boolean = false; + + // check for scaling + if ("runtimeDPI" in FlexGlobals.topLevelApplication) + { + var runtimeDPI:Number = FlexGlobals.topLevelApplication.runtimeDPI as Number; + isRetina = (runtimeDPI == DPIClassification.DPI_320); + isScaling160to320 = isRetina + && (applicationDPI == DPIClassification.DPI_160); + } + + if (isRetina && !isScaling160to320) + rightMargin = IOS_RIGHT_MARGIN_320; + else if (isRetina && isScaling160to320) + rightMargin = IOS_RIGHT_MARGIN_160_SCALED_TO_320; + else + rightMargin = IOS_RIGHT_MARGIN_160; + + textDisplay.rightMargin = rightMargin; + } + else + { + textDisplay.addEventListener(KeyboardEvent.KEY_DOWN, textDisplay_keyHandler); + } + + textDisplay.addEventListener(Event.CHANGE, textDisplay_changeHandler); + textDisplay.addEventListener(FlexEvent.VALUE_COMMIT, textDisplay_changeHandler); + textDisplay.addEventListener(Event.SCROLL, textDisplay_scrollHandler); + // Use a lower priority so that the StyleableTextField event handler is called first. + // That handler cancels the event and we need to check for that case + textDisplay.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING, textDisplay_softKeyboardActivatingHandler, false, EventPriority.DEFAULT_HANDLER); + textDisplay.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE, textDisplay_softKeyboardActivateHandler); + textDisplay.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, textDisplay_softKeyboardDeactivateHandler); + + textDisplay.left = getStyle("paddingLeft"); + textDisplay.top = getStyle("paddingTop"); + textDisplay.right = getStyle("paddingRight"); + textDisplay.bottom = getStyle("paddingBottom"); + + // wrap StyleableTextComponent in Group for viewport + textDisplayGroup = new Group(); + textDisplayGroup.clipAndEnableScrolling = true; + textDisplayGroup.addElement(textDisplay); + } + + if (!scroller) + { + scroller = new Scroller(); + scroller.minViewportInset = 0; + scroller.measuredSizeIncludesScrollBars = false; + scroller.ensureElementIsVisibleForSoftKeyboard = false; + + addChild(scroller); + } + + if (!scroller.viewport) + scroller.viewport = textDisplayGroup; + + super.createChildren(); + } + + /** + * @private + * TextArea prompt supports wrapping and multiline + */ + override protected function createPromptDisplay():StyleableTextField + { + var prompt:StyleableTextField = super.createPromptDisplay(); + prompt.editable = true; + prompt.wordWrap = true; + + return prompt; + } + + /** + * @private + */ + override protected function measure():void + { + super.measure(); + + var paddingTop:Number = getStyle("paddingTop"); + var paddingBottom:Number = getStyle("paddingBottom"); + var paddingLeft:Number = getStyle("paddingLeft"); + var paddingRight:Number = getStyle("paddingRight"); + + // TextDisplay always defaults to 440 pixels wide (the value is DPI dependent), + // and tall enough to show all text. + // + // You can set an explicit width and the height will adjust accordingly. The opposite + // is not true: setting an explicit height will not adjust the width accordingly. + + measuredWidth = measuredDefaultWidth; + + // now we need to measure textDisplay's height. Unfortunately, this is tricky and + // is dependent on textDisplay's width. Let's use the heuristic that our width + // is the same as our last width. + // We don't use layoutMeasuredWidth, because that value is just a constant and doesn't + // take into account the fact that the TextArea could have an explicitWidth or could + // be constrained by some value. However, we still default oldTextDisplayWidth to + // be layoutMeasuredWidth the first time through. + var textDisplayEstimatedWidth:Number = oldUnscaledWidth - paddingLeft - paddingRight; + + // now we need to measure textDisplay's height. Unfortunately, this is tricky and + // is dependent on textDisplay's width. + // Use the old textDisplay width as an estimte for the new one. + // If we are wrong, we'll find out in updateDisplayList() + textDisplay.commitStyles(); + + // Clear min sizes first. + textDisplay.minWidth = textDisplay.minHeight = NaN; + + // If lineBreak == explicit, always use NaN for estimated width + if (getStyle("lineBreak") == "explicit") + textDisplayEstimatedWidth = NaN; + + setElementSize(textDisplay, textDisplayEstimatedWidth, NaN); + + measuredHeight = getElementPreferredHeight(textDisplay) + paddingTop + 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); + } + + setElementSize(scroller, unscaledWidth, unscaledHeight); + setElementPosition(scroller, 0, 0); + + // position & size the text + var explicitLineBreak:Boolean = getStyle("lineBreak") == "explicit"; + 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; + var textHeight:Number; + var textWidth:Number = explicitLineBreak ? textDisplay.measuredTextSize.x : unscaledTextWidth; + + var lineIndex:int; + var topCharIndex:int; + var charBounds:Rectangle; + + // grab old measured textDisplay height before resizing it + var oldPreferredTextHeight:Number = getElementPreferredHeight(textDisplay); + + // set width first to measure height correctly + textDisplay.commitStyles(); + textDisplay.setLayoutBoundsSize(textWidth, NaN); + + // In iOS, when we go into editing mode, the runtime overlays a native + // text control over the textDisplay. In order to prevent the text + // from overflowing the component and to get scrolling support, the + // native text control must be the same size as the TextArea + if (_isIOS) + { + if (shrinkTextDisplay) + { + // Switching to edit mode. Convert from viewport scrolling to + // TextField scrolling + var vsp:Number = textDisplayGroup.verticalScrollPosition; + + var lineMetrics:TextLineMetrics = textDisplay.getLineMetrics(0); + var lineHeight:Number = lineMetrics.ascent + lineMetrics.descent; + + // TODO Figure out how to get the x offset. Right now is hard coded to 2 + // At least half the line should be showing before we scroll to that line + // This makes the conversion from pixel to line based scrolling a little less jumpy + lineIndex = textDisplay.getLineIndexAtPoint(2, vsp + lineHeight / 2) + 1; + textDisplayGroup.verticalScrollPosition = 0; + isTextDisplayTall = false; + //trace("TAS.layoutContents shrinkText vsp",vsp,"lineIndex",lineIndex); + } + + else if (growTextDisplay) + { + // Leaving edit mode. Convert from TextField scrolling to + // viewport scrolling + var scrollV:Number = textDisplay.scrollV; + + // TODO (jszeto) investigate using lineMetrics.lineHeight * scrollV instead of getCharBoundaries + topCharIndex = textDisplay.getLineOffset(scrollV - 1); + charBounds = textDisplay.getCharBoundaries(topCharIndex); + // If the charBounds is null, just set vsp to 0 + if (charBounds == null) + charBounds = new Rectangle(0, 0, 0, 0); + textDisplay.scrollV = 1; + isTextDisplayTall = true; + //trace("TAS.layoutContents growText scrollV",scrollV,"topCharIndex",topCharIndex,"charBounds",charBounds); + } + } + + // TextField height should match its content or the TextArea bounds at minimum + // iOS special case to prevent Flex Scroller scrolling when editable + if (isTextDisplayTall) + textHeight = Math.max(textDisplay.measuredTextSize.y, unscaledTextHeight); + else + textHeight = unscaledTextHeight; + + // FIXME (jasonsj): iOS native scroll bar appears even when explictHeight + // is not specified. Focus-in is jumpy. + + if (promptDisplay) + { + promptDisplay.commitStyles(); + setElementSize(promptDisplay, unscaledTextWidth, textHeight); + setElementPosition(promptDisplay, paddingLeft, paddingTop); + + // no need to update textDisplay if promptDisplay is present + return; + } + + // keep track of oldUnscaledWidth so we have a good guess as to the width + // of the textDisplay on the next measure() pass + oldUnscaledWidth = unscaledWidth; + + // set the width of textDisplay to textWidth. + // set the height to oldTextHeight. If the height's actually wrong, + // we'll invalidateSize() and go through this layout pass again anyways + setElementSize(textDisplay, textWidth, textHeight); + + // Set minWidth/Height on the text so the textDisplayGroup sizes accordingly + textDisplay.minWidth = textWidth; + textDisplay.minHeight = textHeight; + textDisplayGroup.invalidateDisplayList(); + + // grab new measured textDisplay height after the textDisplay has taken its final width + var newPreferredTextHeight:Number = getElementPreferredHeight(textDisplay); + + // if the resize caused the textDisplay's height to change (because of + // text reflow), then we need to remeasure ourselves with our new width + if (oldPreferredTextHeight != newPreferredTextHeight) + invalidateSize(); + + if (_isIOS) + { + if (shrinkTextDisplay) + { + scroller.validateNow(); + textDisplay.scrollV = lineIndex; + } + else if (growTextDisplay) + { + scroller.validateNow(); + textDisplayGroup.verticalScrollPosition = charBounds.y; + } + + shrinkTextDisplay = false; + growTextDisplay = false; + } + + //trace("TAS.layoutContents tH",textHeight,"tW",textWidth,"invalidateCaret",invalidateCaretPosition); + + // checking if text fits in TextArea + // does not apply to iOS due to native text editing and scrolling + // invalidateCaretPosition will never be true for iOS + if (invalidateCaretPosition && isTextDisplayTall) + { + // if the caret is outside the viewport, update the Group verticalScrollPosition + var charIndex:int = textDisplay.selectionBeginIndex; + var caretBounds:Rectangle = textDisplay.getCharBoundaries(charIndex); + lineIndex = textDisplay.getLineIndexOfChar(charIndex); + + // getCharBoundaries() returns null for new lines + if (!caretBounds) + { + // temporarily insert a character at the caretIndex + textDisplay.replaceText(charIndex, charIndex, "W"); + caretBounds = textDisplay.getCharBoundaries(charIndex); + lineIndex = textDisplay.getLineIndexOfChar(charIndex); + textDisplay.replaceText(charIndex, charIndex + 1, ""); + } + + if (caretBounds) + { + // Scroll the internal Scroller to ensure the caret is visible + if (textHeight > unscaledTextHeight) + { + + if (charIndex == textDisplay.text.length) + { + // Make sure textDisplayGroup is validated, otherwise the + // verticalScrollPosition may be out of bounds, which will + // cause a bounce effect. + textDisplayGroup.validateNow(); + textDisplayGroup.verticalScrollPosition = textHeight; + } + else + { + // caretTopPositon and caretBottomPosition are TextField-relative positions + // the TextField is inset by padding styles of the TextArea (via the VGroup) + + // adjust top position to 0 when on the first line + // caretTopPosition will be negative when off stage + var caretTopPosition:Number = ((caretBounds.y) < 0 || (lineIndex == 0)) + ? 0 : caretBounds.y; + + // caretBottomPosition is the y coordinate of the bottom bounds of the caret + var caretBottomPosition:Number = caretBounds.y + caretBounds.height; + + // note that verticalScrollPosition min/max do not account for padding + var vspTop:Number = textDisplayGroup.verticalScrollPosition; + + // vspBottom should be the max visible Y in the TextField + // coordinate space. + // remove paddingBottom for some clearance between caret and border + var vspBottom:Number = vspTop + unscaledHeight - paddingTop - paddingBottom; + + // is the caret in or below the padding and viewport? + if (caretBottomPosition > vspBottom) + { + // adjust caretBottomPosition to max scroll position when on the last line + if (lineIndex + 1 == textDisplay.numLines) + { + // use textHeight+paddings instead of textDisplayGroup.contentHeight + // Group has not been resized by this point + textDisplayGroup.verticalScrollPosition = (textHeight + paddingTop + paddingBottom) - textDisplayGroup.height; + } + else + { + // bottom edge of the caret moves just inside the bottom edge of the scroller + // add delta between caret and vspBottom + textDisplayGroup.verticalScrollPosition = vspTop + (caretBottomPosition - vspBottom); + } + } + // is the caret above the viewport? + else if (caretTopPosition < vspTop) + { + // top edge of the caret moves inside the top edge of the scroller + textDisplayGroup.verticalScrollPosition = caretTopPosition; + } + } + + scroller.validateNow(); + } + + // Convert to local coordinates + // Dispatch an event for an ancestor Scroller + // It will scroll the TextArea so the caret is in view + convertBoundsToLocal(caretBounds); + if (oldCaretBounds == null || caretBounds.bottom != oldCaretBounds.bottom || caretBounds.top != oldCaretBounds.top) + { + //trace("TAS.layoutContents send caret CHANGE"); + dispatchEvent(new CaretBoundsChangeEvent(CaretBoundsChangeEvent.CARET_BOUNDS_CHANGE,true,true,oldCaretBounds,caretBounds)); + } + + oldCaretBounds = caretBounds; + } + + invalidateCaretPosition = false; + } + + // Make sure final scroll position is valid + if (isTextDisplayTall) + snapTextScrollPosition(); + } + + 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 selectWidth:uint = isFocused ? layoutBorderSize + 1 : layoutBorderSize; + if (isNaN(contentBackgroundAlpha)) + { + contentBackgroundAlpha = 1; + } + if (getStyle("contentBackgroundBorder") == "flat") + { + var halfGap:int = flatheight * 2; + //background + graphics.beginFill(contentBackgroundColor, contentBackgroundAlpha); + graphics.drawRect(0, 0, unscaledWidth, unscaledHeight - flatheight); + graphics.endFill(); + //begin flat border + graphics.beginFill(borderColor, 1); + //left half border + graphics.drawRect(0, unscaledHeight - halfGap, selectWidth, flatheight ); + //bottom border + graphics.drawRect(0, unscaledHeight - flatheight, unscaledWidth, selectWidth); + //right border + graphics.drawRect(unscaledWidth - selectWidth, unscaledHeight - halfGap, selectWidth, flatheight); + graphics.endFill(); + } + else if (getStyle("contentBackgroundBorder") == "rectangle") + { + var borderWidth:uint = layoutBorderSize * 2; + //rectangle border and background + graphics.lineStyle(selectWidth, borderColor, 1); + graphics.beginFill(contentBackgroundColor, contentBackgroundAlpha); + graphics.drawRect(layoutBorderSize, layoutBorderSize, unscaledWidth - borderWidth, unscaledHeight - borderWidth); + graphics.endFill(); + } + } + + /** + * @private + * Make sure the scroll positions are valid, and adjust if needed. + */ + private function snapTextScrollPosition():void + { + var maxHsp:Number = textDisplayGroup.contentWidth > textDisplayGroup.width ? + textDisplayGroup.contentWidth-textDisplayGroup.width : 0; + textDisplayGroup.horizontalScrollPosition = + Math.min(Math.max(0,textDisplayGroup.horizontalScrollPosition),maxHsp); + + var maxVsp:Number = textDisplayGroup.contentHeight > textDisplayGroup.height ? + textDisplayGroup.contentHeight-textDisplayGroup.height : 0; + + textDisplayGroup.verticalScrollPosition = + Math.min(Math.max(0,textDisplayGroup.verticalScrollPosition),maxVsp); + } + + /** + * @private + * Get the bounds of the caret + */ + private function getCaretBounds():Rectangle + { + var charIndex:int = textDisplay.selectionBeginIndex; + var caretBounds:Rectangle = textDisplay.getCharBoundaries(charIndex); + + if (!caretBounds) + { + textDisplay.replaceText(charIndex, charIndex, "W"); + caretBounds = textDisplay.getCharBoundaries(charIndex); + textDisplay.replaceText(charIndex, charIndex + 1, ""); + } + + return caretBounds; + } + + /** + * @private + * Convert bounds from textDisplay to local coordinates + */ + private function convertBoundsToLocal(bounds:Rectangle):void + { + if (bounds) + { + var position:Point = new Point(bounds.x, bounds.y); + position = textDisplay.localToGlobal(position); + position = globalToLocal(position); + bounds.x = position.x; + bounds.y = position.y; + } + } + + /** + * @private + */ + private function scrollToRange(anchorPosition:int, activePosition:int):void + { + var pos:int = Math.min(anchorPosition, activePosition); + var bounds:Rectangle = textDisplay.getCharBoundaries(pos); + var vsp:int = textDisplayGroup.verticalScrollPosition; + var paddingTop:Number = getStyle("paddingTop"); + var paddingBottom:Number = getStyle("paddingBottom"); + + if (bounds && (bounds.top < vsp - paddingTop || + bounds.bottom > vsp + unscaledHeight - paddingTop - paddingBottom)) + { + textDisplayGroup.verticalScrollPosition = bounds.top + paddingTop; + snapTextScrollPosition(); + } + } + + /** + * @private + * Handle size and caret position changes that occur when text content + * changes. + */ + private function textDisplay_changeHandler(event:Event):void + { + var tH:Number = textDisplay.textHeight; + var tW:Number = textDisplay.textWidth; + var explicitLineBreak:Boolean = getStyle("lineBreak") == "explicit"; + + // Size and caret position have changed if the text height is different or + // the text width is different and we aren't word wrapping + if (tH != lastTextHeight || ( explicitLineBreak && tW != lastTextWidth)) + { + invalidateSize(); + invalidateDisplayList(); + invalidateCaretPosition = true; + } + + lastTextHeight = tH; + lastTextWidth = tW; + } + + /** + * @private + * Cancels any native scroll that the Flash Player attempts to do + */ + private function textDisplay_scrollHandler(event:Event):void + { + // if iOS, let the OS handle scrolling + if (_isIOS) + return; + + // If not IOS, we will handle scrolling, so don't let the native + // flash textfield scroll at all. + if (textDisplay.scrollV > 1) + textDisplay.scrollV = 1; + if (textDisplay.scrollH > 0) + textDisplay.scrollH = 0; + } + + /** + * @private + * Adjust viewport when using key navigation + */ + private function textDisplay_keyHandler(event:KeyboardEvent):void + { + // update scroll position when caret changes + if ((event.keyCode == Keyboard.UP + || event.keyCode == Keyboard.DOWN + || event.keyCode == Keyboard.LEFT + || event.keyCode == Keyboard.RIGHT)) + { + invalidateDisplayList(); + invalidateCaretPosition = true; + } + + // Change event is not always sent when delete key is pressed, so + // invalidate the size here + if (event.keyCode == Keyboard.BACKSPACE) + { + invalidateSize(); + } + } + + /** + * @private + * When entering edit mode on iOS, we need to shrink the textDisplay to + * the size of the TextArea + */ + private function textDisplay_softKeyboardActivatingHandler(event:SoftKeyboardEvent):void + { + if (event.isDefaultPrevented()) + return; + + if (_isIOS && isTextDisplayTall) + { + //trace("TAS.SK ACTIVATING targ",event.target); + shrinkTextDisplay = true; + invalidateDisplayList(); + validateNow(); + } + } + + /** + * @private + * Send a caret change event to an ancestor Scroller + */ + private function textDisplay_softKeyboardActivateHandler(event:SoftKeyboardEvent):void + { + var keyboardRect:Rectangle = stage.softKeyboardRect; + + if (keyboardRect.width > 0 && keyboardRect.height > 0) + { + var newCaretBounds:Rectangle = getCaretBounds(); + convertBoundsToLocal(newCaretBounds); + + if (oldCaretBounds != newCaretBounds) + { + //trace("TAS.SK ACTIVATE",keyboardRect,"dispatch caret CHANGE","newCaretBounds",newCaretBounds); + dispatchEvent(new CaretBoundsChangeEvent(CaretBoundsChangeEvent.CARET_BOUNDS_CHANGE,true,true,oldCaretBounds,newCaretBounds)); + oldCaretBounds = newCaretBounds; + } + } + } + + /** + * @private + * On iOS, when leaving edit mode, we need to restore the textDisplay to the + * height of the text. + */ + private function textDisplay_softKeyboardDeactivateHandler(event:SoftKeyboardEvent):void + { + if (_isIOS && !isTextDisplayTall) + { + growTextDisplay = true; + invalidateDisplayList(); + } + } + + /** + * @private + */ + private function resizeHandler(event:Event):void + { + // Resizing needs to tickle the TextArea's internal auto-scroll logic + invalidateCaretPosition = true; + invalidateDisplayList(); + } + + /** + * @private + */ + override public function styleChanged(styleProp:String):void + { + super.styleChanged(styleProp); + + // propogate styleChanged explicitly to textDisplay + if (textDisplay) + textDisplay.styleChanged(styleProp); + + // Check for padding style changes + if (!styleProp || styleProp == "styleName" || styleProp.indexOf("padding") >= 0) + { + if (textDisplay) + { + textDisplay.left = getStyle("paddingLeft"); + textDisplay.top = getStyle("paddingTop"); + textDisplay.right = getStyle("paddingRight"); + textDisplay.bottom = getStyle("paddingBottom"); + } + } + } + + private function focusChangeHandler(event:FocusEvent):void + { + isFocused = event.type == FocusEvent.FOCUS_IN; + invalidateDisplayList(); + } + + } +} http://git-wip-us.apache.org/repos/asf/flex-examples/blob/539852e9/tourdeflexmobile/src/spark/skins/android4/TextAreaVScrollBarSkin.as ---------------------------------------------------------------------- diff --git a/tourdeflexmobile/src/spark/skins/android4/TextAreaVScrollBarSkin.as b/tourdeflexmobile/src/spark/skins/android4/TextAreaVScrollBarSkin.as new file mode 100644 index 0000000..f65b679 --- /dev/null +++ b/tourdeflexmobile/src/spark/skins/android4/TextAreaVScrollBarSkin.as @@ -0,0 +1,105 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.android4 +{ +import mx.core.DPIClassification; +import mx.core.mx_internal; + +use namespace mx_internal; + +/** + * ActionScript-based skin for TextAreaVScrollBar components in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10.1 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class TextAreaVScrollBarSkin extends VScrollBarSkin +{ + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function TextAreaVScrollBarSkin() + { + super(); + + thumbSkinClass = TextAreaVScrollBarThumbSkin; + var paddingRight:int; + var paddingVertical:int; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + minWidth = 30; + paddingRight = TextAreaVScrollBarThumbSkin.PADDING_RIGHT_640DPI; + paddingVertical = TextAreaVScrollBarThumbSkin.PADDING_VERTICAL_640DPI; + break; + } + case DPIClassification.DPI_480: + { + minWidth = 22; + paddingRight = TextAreaVScrollBarThumbSkin.PADDING_RIGHT_480DPI; + paddingVertical = TextAreaVScrollBarThumbSkin.PADDING_VERTICAL_480DPI; + break; + } + case DPIClassification.DPI_320: + { + minWidth = 15; + paddingRight = TextAreaVScrollBarThumbSkin.PADDING_RIGHT_320DPI; + paddingVertical = TextAreaVScrollBarThumbSkin.PADDING_VERTICAL_320DPI; + break; + } + case DPIClassification.DPI_240: + { + minWidth = 11; + paddingRight = TextAreaVScrollBarThumbSkin.PADDING_RIGHT_240DPI; + paddingVertical = TextAreaVScrollBarThumbSkin.PADDING_VERTICAL_240DPI; + break; + } + case DPIClassification.DPI_120: + { + minWidth = 6; + paddingRight = TextAreaVScrollBarThumbSkin.PADDING_RIGHT_120DPI; + paddingVertical = TextAreaVScrollBarThumbSkin.PADDING_VERTICAL_120DPI; + break; + } + default: + { + // default DPI_160 + minWidth = 9; + paddingRight = TextAreaVScrollBarThumbSkin.PADDING_RIGHT_DEFAULTDPI; + paddingVertical = TextAreaVScrollBarThumbSkin.PADDING_VERTICAL_DEFAULTDPI; + break; + } + } + + // The minimum height is set such that, at it's smallest size, the thumb appears + // as high as it is wide. + minThumbHeight = (minWidth - paddingRight) + (paddingVertical * 2); + } +} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/539852e9/tourdeflexmobile/src/spark/skins/android4/TextAreaVScrollBarThumbSkin.as ---------------------------------------------------------------------- diff --git a/tourdeflexmobile/src/spark/skins/android4/TextAreaVScrollBarThumbSkin.as b/tourdeflexmobile/src/spark/skins/android4/TextAreaVScrollBarThumbSkin.as new file mode 100644 index 0000000..5cc1ea0 --- /dev/null +++ b/tourdeflexmobile/src/spark/skins/android4/TextAreaVScrollBarThumbSkin.as @@ -0,0 +1,117 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.android4 +{ +import mx.core.DPIClassification; +import mx.core.mx_internal; + +use namespace mx_internal; + +/** + * The ActionScript-based skin used for TextAreaHScrollBarThumb components + * in mobile applications. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ +public class TextAreaVScrollBarThumbSkin extends VScrollBarThumbSkin +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + // These constants are also accessed from TextAreaVScrollBarSkin + mx_internal static const PADDING_RIGHT_640DPI:int = 16; + mx_internal static const PADDING_VERTICAL_640DPI:int = 24; + mx_internal static const PADDING_RIGHT_480DPI:int = 12; + mx_internal static const PADDING_VERTICAL_480DPI:int = 18; + mx_internal static const PADDING_RIGHT_320DPI:int = 8; + mx_internal static const PADDING_VERTICAL_320DPI:int = 12; + mx_internal static const PADDING_RIGHT_240DPI:int = 4; + mx_internal static const PADDING_VERTICAL_240DPI:int = 6; + mx_internal static const PADDING_RIGHT_120DPI:int = 2; + mx_internal static const PADDING_VERTICAL_120DPI:int = 3; + mx_internal static const PADDING_RIGHT_DEFAULTDPI:int = 4; + mx_internal static const PADDING_VERTICAL_DEFAULTDPI:int = 6; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function TextAreaVScrollBarThumbSkin() + { + super(); + + // Depending on density set padding + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + paddingRight = PADDING_RIGHT_640DPI; + paddingVertical = PADDING_VERTICAL_640DPI; + break; + } + case DPIClassification.DPI_480: + { + paddingRight = PADDING_RIGHT_480DPI; + paddingVertical = PADDING_VERTICAL_480DPI; + break; + } + case DPIClassification.DPI_320: + { + paddingRight = PADDING_RIGHT_320DPI; + paddingVertical = PADDING_VERTICAL_320DPI; + break; + } + case DPIClassification.DPI_240: + { + paddingRight = PADDING_RIGHT_240DPI; + paddingVertical = PADDING_VERTICAL_240DPI; + break; + } + case DPIClassification.DPI_120: + { + paddingRight = PADDING_RIGHT_120DPI; + paddingVertical = PADDING_VERTICAL_120DPI; + break; + } + default: + { + paddingRight = PADDING_RIGHT_DEFAULTDPI; + paddingVertical = PADDING_VERTICAL_DEFAULTDPI; + break; + } + } + } +} +} \ No newline at end of file
