From 1513ffd0897824ad0a70aa2e3278f22b9f4c0e4f Mon Sep 17 00:00:00 2001 From: Om Date: Wed, 25 Sep 2013 02:27:18 -0700 Subject: [PATCH 001/392] First few fxg skins for the Android 4+ skins. Included: ActionBarBackground, Button and Checkbox skins. --- .../android4/assets/ActionBarBackground.fxg | 34 +++++++++++ .../skins/android4/assets/Button_down.fxg | 58 +++++++++++++++++++ .../spark/skins/android4/assets/Button_up.fxg | 49 ++++++++++++++++ .../skins/android4/assets/CheckBox_down.fxg | 39 +++++++++++++ .../android4/assets/CheckBox_downSymbol.fxg | 30 ++++++++++ .../assets/CheckBox_downSymbolSelected.fxg | 27 +++++++++ .../skins/android4/assets/CheckBox_up.fxg | 27 +++++++++ .../android4/assets/CheckBox_upSymbol.fxg | 30 ++++++++++ .../assets/CheckBox_upSymbolSelected.fxg | 30 ++++++++++ 9 files changed, 324 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg new file mode 100644 index 0000000000..23da060b17 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg new file mode 100644 index 0000000000..d41282a48f --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg new file mode 100644 index 0000000000..6b3469854b --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg new file mode 100644 index 0000000000..69b5c0a559 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg new file mode 100644 index 0000000000..5190e607c8 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg new file mode 100644 index 0000000000..ee22c4adde --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg @@ -0,0 +1,27 @@ + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg new file mode 100644 index 0000000000..53e87d4133 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg @@ -0,0 +1,27 @@ + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg new file mode 100644 index 0000000000..e23b0f7bda --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg new file mode 100644 index 0000000000..5190e607c8 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + \ No newline at end of file From 73ee3c38118212b7691368ba0f89be3695e88a3f Mon Sep 17 00:00:00 2001 From: Om Date: Sat, 28 Sep 2013 01:50:15 -0700 Subject: [PATCH 002/392] Better skins for ActionBarBackground, Button and Checkbox. This time, we have registration point at top/left and scalegrid values from Illustrator. This replaces the earlier ones which came from Fireworks. --- .../android4/assets/ActionBarBackground.fxg | 12 ++-- .../skins/android4/assets/Button_down.fxg | 58 +++++++++---------- .../spark/skins/android4/assets/Button_up.fxg | 26 +++++---- .../skins/android4/assets/CheckBox_down.fxg | 17 +++--- .../android4/assets/CheckBox_downSymbol.fxg | 19 +++--- .../assets/CheckBox_downSymbolSelected.fxg | 15 +++-- .../skins/android4/assets/CheckBox_up.fxg | 15 +++-- .../android4/assets/CheckBox_upSymbol.fxg | 21 ++++--- .../assets/CheckBox_upSymbolSelected.fxg | 21 ++++--- 9 files changed, 106 insertions(+), 98 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg index 23da060b17..5ba85f8550 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg @@ -18,17 +18,13 @@ --> - - - + + + - - - - - + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg index d41282a48f..31bd28e261 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg @@ -18,41 +18,39 @@ --> - - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + - + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg index 6b3469854b..0e335badc3 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg @@ -18,32 +18,34 @@ --> - - - - - - - - + + + + + + + + + - + - + - + - + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg index 69b5c0a559..e8a55a141e 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg @@ -18,22 +18,23 @@ --> - - - + + + - - - + + + - - + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg index 5190e607c8..3882781c6b 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg @@ -1,4 +1,4 @@ - + - - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg index ee22c4adde..03fdd86642 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg @@ -18,10 +18,13 @@ --> - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg index 53e87d4133..52d7b1dd0c 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg @@ -18,10 +18,13 @@ --> - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg index e23b0f7bda..b6c84feaa1 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg @@ -18,13 +18,16 @@ --> - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg index 5190e607c8..0f613f07dd 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg @@ -18,13 +18,16 @@ --> - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file From 952531e049295cad95c493217ba8633e3cb2ddd7 Mon Sep 17 00:00:00 2001 From: Om Date: Tue, 18 Mar 2014 18:13:55 -0700 Subject: [PATCH 003/392] Added cleaner versions of ActionBarBackground.fxg, Button_down.fxg and Button_up.fxt Added skins for Android 4.x versions of ActionBarSkin.as and ButtonSkin.as --- .../src/spark/skins/android4/ActionBarSkin.as | 743 ++++++++++++++++++ .../src/spark/skins/android4/ButtonSkin.as | 397 ++++++++++ .../android4/assets/ActionBarBackground.fxg | 35 +- .../skins/android4/assets/Button_down.fxg | 77 +- .../spark/skins/android4/assets/Button_up.fxg | 67 +- .../src/spark/skins/mobile/ActionBarSkin.as | 15 +- 6 files changed, 1269 insertions(+), 65 deletions(-) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonSkin.as diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as new file mode 100644 index 0000000000..7b64f06894 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as @@ -0,0 +1,743 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.GradientType; +import flash.events.Event; +import flash.text.TextFormatAlign; + +import mx.core.DPIClassification; +import mx.core.mx_internal; +import mx.utils.ColorUtil; + +import spark.components.ActionBar; +import spark.components.Group; +import spark.components.supportClasses.StyleableTextField; +import spark.core.SpriteVisualElement; +import spark.layouts.HorizontalAlign; +import spark.layouts.HorizontalLayout; +import spark.layouts.VerticalAlign; +import spark.skins.android4.assets.ActionBarBackground; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * The default skin class for the Spark ActionBar component in mobile + * applications. + * + * @see spark.components.ActionBar + * @see spark.skins.mobile.TransparentNavigationButtonSkin + * @see spark.skins.mobile.BeveledBackButtonSkin + * @see spark.skins.mobile.TransparentActionButtonSkin + * @see spark.skins.mobile.BeveledActionButtonSkin + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class ActionBarSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + mx_internal static const ACTIONBAR_CHROME_COLOR_RATIOS:Array = [0, 80]; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ActionBarSkin() + { + super(); + + borderClass = spark.skins.android4.assets.ActionBarBackground; + + switch (applicationDPI) + { + + case DPIClassification.DPI_640: + { + borderSize = 2; + layoutShadowHeight = 12; + layoutContentGroupHeight = 172; + layoutTitleGroupHorizontalPadding = 52; + + break; + } + case DPIClassification.DPI_480: + { + // Note provisional may need changes + borderSize = 2; + layoutShadowHeight = 9; + layoutContentGroupHeight = 130; + layoutTitleGroupHorizontalPadding = 40; + + + break; + } + case DPIClassification.DPI_320: + { + borderSize = 2; + layoutShadowHeight = 6; + layoutContentGroupHeight = 86; + layoutTitleGroupHorizontalPadding = 26; + + break; + } + case DPIClassification.DPI_240: + { + borderSize = 1; + layoutShadowHeight = 3; + layoutContentGroupHeight = 65; + layoutTitleGroupHorizontalPadding = 20; + + break; + } + case DPIClassification.DPI_120: + { + borderSize = 1; + layoutShadowHeight = 1.5; + layoutContentGroupHeight = 32; + layoutTitleGroupHorizontalPadding = 10; + + break; + } + default: + { + // default DPI_160 + borderSize = 1; + layoutShadowHeight = 3; + layoutContentGroupHeight = 43; + layoutTitleGroupHorizontalPadding = 13; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Graphics variables + // + //-------------------------------------------------------------------------- + + /** + * FXG Class reference for the ActionBar background border graphic. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var borderClass:Class; + + //-------------------------------------------------------------------------- + // + // Layout variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var borderSize:uint; + + /** + * Height of shadow embedded in borderClass graphic. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var layoutShadowHeight:uint; + + /** + * Default height for navigationGroup, titleGroup and actionGroup. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var layoutContentGroupHeight:uint; + + /** + * Default horizontal padding for the titleGroup and titleDisplay. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var layoutTitleGroupHorizontalPadding:uint; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:ActionBar; + + /** + * @private + */ + private var _navigationVisible:Boolean = false; + + /** + * @private + */ + private var _titleContentVisible:Boolean = false; + + /** + * @private + */ + private var _actionVisible:Boolean = false; + + /** + * @private + */ + private var border:SpriteVisualElement; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.components.ActionBar#navigationGroup + */ + public var navigationGroup:Group; + + /** + * @copy spark.components.ActionBar#titleGroup + */ + public var titleGroup:Group; + + /** + * @copy spark.components.ActionBar#actionGroup + */ + public var actionGroup:Group; + + /** + * @copy spark.components.ActionBar#titleDisplay + * + * @private + * Wraps a StyleableTextField in a UIComponent to be compatible with + * ViewTransition effects. + */ + public var titleDisplay:TitleDisplayComponent; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + if (borderClass) + { + border = new borderClass(); + addChild(border); + } + + navigationGroup = new Group(); + var hLayout:HorizontalLayout = new HorizontalLayout(); + hLayout.horizontalAlign = HorizontalAlign.LEFT; + hLayout.verticalAlign = VerticalAlign.MIDDLE; + hLayout.gap = 0; + hLayout.paddingLeft = hLayout.paddingTop = hLayout.paddingRight = + hLayout.paddingBottom = 0; + navigationGroup.layout = hLayout; + navigationGroup.id = "navigationGroup"; + + titleGroup = new Group(); + hLayout = new HorizontalLayout(); + hLayout.horizontalAlign = HorizontalAlign.LEFT; + hLayout.verticalAlign = VerticalAlign.MIDDLE; + hLayout.gap = 0; + hLayout.paddingLeft = hLayout.paddingRight = layoutTitleGroupHorizontalPadding; + hLayout.paddingTop = hLayout.paddingBottom = 0; + titleGroup.layout = hLayout; + titleGroup.id = "titleGroup"; + + actionGroup = new Group(); + hLayout = new HorizontalLayout(); + hLayout.horizontalAlign = HorizontalAlign.RIGHT; + hLayout.verticalAlign = VerticalAlign.MIDDLE; + hLayout.gap = 0; + hLayout.paddingLeft = hLayout.paddingTop = hLayout.paddingRight = + hLayout.paddingBottom = 0; + actionGroup.layout = hLayout; + actionGroup.id = "actionGroup"; + + titleDisplay = new TitleDisplayComponent(); + titleDisplay.id = "titleDisplay"; + + // initialize titleAlign style (center is managed explicitly in layoutContents) + var titleAlign:String = getStyle("titleAlign"); + titleAlign = (titleAlign == "center") ? TextFormatAlign.LEFT : titleAlign; + titleDisplay.setStyle("textAlign", titleAlign); + + addChild(navigationGroup); + addChild(titleGroup); + addChild(actionGroup); + addChild(titleDisplay); + } + + /** + * @private + */ + override protected function measure():void + { + var titleWidth:Number = 0; + var titleHeight:Number = 0; + + if (_titleContentVisible) + { + titleWidth = titleGroup.getPreferredBoundsWidth(); + titleHeight = titleGroup.getPreferredBoundsHeight(); + } + else + { + // use titleLayout for paddingLeft and paddingRight + var layoutObject:Object = hostComponent.titleLayout; + titleWidth = ((layoutObject.paddingLeft) ? Number(layoutObject.paddingLeft) : 0) + + ((layoutObject.paddingRight) ? Number(layoutObject.paddingRight) : 0) + + titleDisplay.getPreferredBoundsWidth(); + + titleHeight = titleDisplay.getPreferredBoundsHeight(); + } + + measuredWidth = + getStyle("paddingLeft") + + navigationGroup.getPreferredBoundsWidth() + + titleWidth + + actionGroup.getPreferredBoundsWidth() + + getStyle("paddingRight"); + + // measuredHeight is contentGroupHeight, 2x border on top and bottom + measuredHeight = + getStyle("paddingTop") + + Math.max(layoutContentGroupHeight, + navigationGroup.getPreferredBoundsHeight(), + actionGroup.getPreferredBoundsHeight(), + titleHeight) + + getStyle("paddingBottom"); + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + _titleContentVisible = currentState.indexOf("titleContent") >= 0; + _navigationVisible = currentState.indexOf("Navigation") >= 0; + _actionVisible = currentState.indexOf("Action") >= 0; + + invalidateSize(); + invalidateDisplayList(); + } + + /** + * @private + */ + override public function styleChanged(styleProp:String):void + { + if (titleDisplay) + { + var allStyles:Boolean = !styleProp || styleProp == "styleName"; + + if (allStyles || (styleProp == "titleAlign")) + { + var titleAlign:String = getStyle("titleAlign"); + + if (titleAlign == "center") + { + // If the title align is set to center, the alignment is set to LEFT + // so that the skin can manually center the component in layoutContents + titleDisplay.setStyle("textAlign", TextFormatAlign.LEFT); + } + else + { + titleDisplay.setStyle("textAlign", titleAlign); + } + } + } + + super.styleChanged(styleProp); + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + var navigationGroupWidth:Number = 0; + + var paddingLeft:Number = getStyle("paddingLeft"); + var paddingRight:Number = getStyle("paddingRight"); + var paddingTop:Number = getStyle("paddingTop"); + var paddingBottom:Number = getStyle("paddingBottom"); + + var titleCompX:Number = paddingLeft; + var titleCompWidth:Number = 0; + + var actionGroupX:Number = unscaledWidth; + var actionGroupWidth:Number = 0; + + // remove top and bottom padding from content group height + var contentGroupsHeight:Number = Math.max(0, unscaledHeight - paddingTop - paddingBottom); + + if (border) + { + // FXG uses scale-9, drop shadow is drawn outside the bounds + setElementSize(border, unscaledWidth, unscaledHeight + layoutShadowHeight); + } + + // position groups, overlap of navigation and action groups is allowed + // when overlap occurs, titleDisplay/titleGroup is not visible + if (_navigationVisible) + { + navigationGroupWidth = navigationGroup.getPreferredBoundsWidth(); + titleCompX += navigationGroupWidth; + + setElementSize(navigationGroup, navigationGroupWidth, contentGroupsHeight); + setElementPosition(navigationGroup, paddingLeft, paddingTop); + } + + if (_actionVisible) + { + // actionGroup x position can be negative + actionGroupWidth = actionGroup.getPreferredBoundsWidth(); + actionGroupX = unscaledWidth - actionGroupWidth - paddingRight; + + setElementSize(actionGroup, actionGroupWidth, contentGroupsHeight); + setElementPosition(actionGroup, actionGroupX, paddingTop); + } + + // titleGroup or titleDisplay is given remaining width after navigation + // and action groups preferred widths + titleCompWidth = unscaledWidth - navigationGroupWidth - actionGroupWidth + - paddingLeft - paddingRight; + + if (titleCompWidth <= 0) + { + titleDisplay.visible = false; + titleGroup.visible = false; + } + else if (_titleContentVisible) + { + titleDisplay.visible = false; + titleGroup.visible = true; + + // use titleGroup for titleContent + setElementSize(titleGroup, titleCompWidth, contentGroupsHeight); + setElementPosition(titleGroup, titleCompX, paddingTop); + } + else + { + // use titleDisplay for title text label + titleGroup.visible = false; + + // use titleLayout for paddingLeft and paddingRight + var layoutObject:Object = hostComponent.titleLayout; + var titlePaddingLeft:Number = (layoutObject.paddingLeft) ? Number(layoutObject.paddingLeft) : 0; + var titlePaddingRight:Number = (layoutObject.paddingRight) ? Number(layoutObject.paddingRight) : 0; + + // align titleDisplay to the absolute center + var titleAlign:String = getStyle("titleAlign"); + + // check for available width after padding + if ((titleCompWidth - titlePaddingLeft - titlePaddingRight) <= 0) + { + titleCompX = 0; + titleCompWidth = 0; + } + else if (titleAlign == "center") + { + // use LEFT instead of CENTER + titleCompWidth = titleDisplay.getExplicitOrMeasuredWidth(); + + // use x position of titleDisplay to implement CENTER + titleCompX = Math.round((unscaledWidth - titleCompWidth) / 2); + + var navigationOverlap:Number = navigationGroupWidth + titlePaddingLeft - titleCompX; + var actionOverlap:Number = (titleCompX + titleCompWidth + titlePaddingRight) - actionGroupX; + + // shrink and/or move titleDisplay width if there is any + // overlap after centering + if ((navigationOverlap > 0) && (actionOverlap > 0)) + { + // remaining width + titleCompX = navigationGroupWidth + titlePaddingLeft; + titleCompWidth = unscaledWidth - navigationGroupWidth - actionGroupWidth - titlePaddingLeft - titlePaddingRight; + } + else if ((navigationOverlap > 0) || (actionOverlap > 0)) + { + if (navigationOverlap > 0) + { + // nudge to the right + titleCompX += navigationOverlap; + } + else if (actionOverlap > 0) + { + // nudge to the left + titleCompX -= actionOverlap; + + // force left padding + if (titleCompX < (navigationGroupWidth + titlePaddingLeft)) + titleCompX = navigationGroupWidth + titlePaddingLeft; + } + + // recompute action overlap and force right padding + actionOverlap = (titleCompX + titleCompWidth + titlePaddingRight) - actionGroupX; + + if (actionOverlap > 0) + titleCompWidth -= actionOverlap; + } + } + else + { + // implement padding by adjusting width and position + titleCompX += titlePaddingLeft; + titleCompWidth = titleCompWidth - titlePaddingLeft - titlePaddingRight; + } + + // check for negative width + titleCompWidth = (titleCompWidth < 0) ? 0 : titleCompWidth; + + setElementSize(titleDisplay, titleCompWidth, contentGroupsHeight); + setElementPosition(titleDisplay, titleCompX, paddingTop); + + titleDisplay.visible = true; + } + } + + /** + * @private + */ +/* override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + super.drawBackground(unscaledWidth, unscaledHeight); + + var chromeColor:uint = getStyle("chromeColor"); + var backgroundAlphaValue:Number = getStyle("backgroundAlpha"); + var colors:Array = []; + + // apply alpha to chromeColor fill only + var backgroundAlphas:Array = [backgroundAlphaValue, backgroundAlphaValue]; + + // exclude top and bottom 1px borders + colorMatrix.createGradientBox(unscaledWidth, unscaledHeight - (borderSize * 2), Math.PI / 2, 0, 0); + + colors[0] = ColorUtil.adjustBrightness2(chromeColor, 20); + colors[1] = chromeColor; + + graphics.beginGradientFill(GradientType.LINEAR, colors, backgroundAlphas, ACTIONBAR_CHROME_COLOR_RATIOS, colorMatrix); + graphics.drawRect(0, borderSize, unscaledWidth, unscaledHeight - (borderSize * 2)); + graphics.endFill(); + }*/ + +} +} +import flash.events.Event; + +import mx.core.UIComponent; +import mx.core.mx_internal; +import mx.events.FlexEvent; + +import spark.components.supportClasses.StyleableTextField; +import spark.core.IDisplayText; + +use namespace mx_internal; + +/** + * @private + * Component that holds StyleableTextFields to produce a drop shadow effect. + * Combines label and shadow into a single component to allow transitions to + * target them both. + */ +class TitleDisplayComponent extends UIComponent implements IDisplayText +{ + private var titleDisplay:StyleableTextField; + private var titleDisplayShadow:StyleableTextField; + private var title:String; + private var titleChanged:Boolean; + + public function TitleDisplayComponent() + { + super(); + title = ""; + } + + override public function get baselinePosition():Number + { + return titleDisplay.baselinePosition; + } + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + titleDisplay = StyleableTextField(createInFontContext(StyleableTextField)); + titleDisplay.styleName = this; + titleDisplay.editable = false; + titleDisplay.selectable = false; + titleDisplay.multiline = false; + titleDisplay.wordWrap = false; + titleDisplay.addEventListener(FlexEvent.VALUE_COMMIT, + titleDisplay_valueCommitHandler); + + titleDisplayShadow = + StyleableTextField(createInFontContext(StyleableTextField)); + titleDisplayShadow.styleName = this; + titleDisplayShadow.colorName = "textShadowColor"; + titleDisplayShadow.editable = false; + titleDisplayShadow.selectable = false; + titleDisplayShadow.multiline = false; + titleDisplayShadow.wordWrap = false; + + addChild(titleDisplayShadow); + addChild(titleDisplay); + } + + /** + * @private + */ + override protected function commitProperties():void + { + super.commitProperties(); + + if (titleChanged) + { + titleDisplay.text = title; + + invalidateSize(); + invalidateDisplayList(); + + titleChanged = false; + } + } + + /** + * @private + */ + override protected function measure():void + { + // reset text if it was truncated before. + if (titleDisplay.isTruncated) + titleDisplay.text = title; + + measuredWidth = titleDisplay.getPreferredBoundsWidth(); + + // tightTextHeight + measuredHeight = titleDisplay.getPreferredBoundsHeight(); + } + + /** + * @private + */ + override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void + { + super.updateDisplayList(unscaledWidth, unscaledHeight); + + // reset text if it was truncated before. + if (titleDisplay.isTruncated) + titleDisplay.text = title; + titleDisplay.commitStyles(); + + // use preferred height, setLayoutBoundsSize will accommodate for tight + // text adjustment + var tightHeight:Number = titleDisplay.getPreferredBoundsHeight(); + var tightY:Number = (unscaledHeight - tightHeight) / 2; + + titleDisplay.setLayoutBoundsSize(unscaledWidth, tightHeight); + titleDisplay.setLayoutBoundsPosition(0, (unscaledHeight - tightHeight) / 2); + + // now truncate the text + titleDisplay.truncateToFit(); + + titleDisplayShadow.commitStyles(); + titleDisplayShadow.setLayoutBoundsSize(unscaledWidth, tightHeight); + titleDisplayShadow.setLayoutBoundsPosition(0, tightY + 1); + + titleDisplayShadow.alpha = getStyle("textShadowAlpha"); + + // if labelDisplay is truncated, then push it down here as well. + // otherwise, it would have gotten pushed in the labelDisplay_valueCommitHandler() + if (titleDisplay.isTruncated) + titleDisplayShadow.text = titleDisplay.text; + } + + /** + * @private + */ + private function titleDisplay_valueCommitHandler(event:Event):void + { + titleDisplayShadow.text = titleDisplay.text; + } + + public function get text():String + { + return title; + } + + public function set text(value:String):void + { + title = value; + titleChanged = true; + + invalidateProperties(); + } + + public function get isTruncated():Boolean + { + return titleDisplay.isTruncated; + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonSkin.as new file mode 100644 index 0000000000..43fb108c61 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonSkin.as @@ -0,0 +1,397 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + +import mx.core.DPIClassification; +import mx.core.mx_internal; +import mx.events.FlexEvent; + +import spark.components.supportClasses.StyleableTextField; +import spark.skins.android4.assets.Button_down; +import spark.skins.android4.assets.Button_up; +import spark.skins.mobile.supportClasses.ButtonSkinBase; + + +use namespace mx_internal; + +/** + * ActionScript-based skin for Button controls in mobile applications. The skin supports + * iconClass and labelPlacement. It uses FXG classes to + * implement the vector drawing. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class ButtonSkin extends ButtonSkinBase +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * An array of color distribution ratios. + * This is used in the chrome color fill. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + mx_internal static const CHROME_COLOR_RATIOS:Array = [0, 127.5]; + + /** + * An array of alpha values for the corresponding colors in the colors array. + * This is used in the chrome color fill. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + mx_internal static const CHROME_COLOR_ALPHAS:Array = [1, 1]; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ButtonSkin() + { + super(); + upBorderSkin = spark.skins.android4.assets.Button_up; + downBorderSkin = spark.skins.android4.assets.Button_down; + layoutCornerEllipseSize = 0; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + + layoutGap = 20; + layoutPaddingLeft = 40; + layoutPaddingRight = 40; + layoutPaddingTop = 40; + layoutPaddingBottom = 40; + layoutBorderSize = 2; + measuredDefaultWidth = 128; + measuredDefaultHeight = 172; + + break; + } + case DPIClassification.DPI_480: + { + + layoutGap = 14; + layoutPaddingLeft = 30; + layoutPaddingRight = 30; + layoutPaddingTop = 30; + layoutPaddingBottom = 30; + layoutBorderSize = 2; + measuredDefaultWidth = 96; + measuredDefaultHeight = 130; + + break; + } + case DPIClassification.DPI_320: + { + + layoutGap = 10; + layoutPaddingLeft = 20; + layoutPaddingRight = 20; + layoutPaddingTop = 20; + layoutPaddingBottom = 20; + layoutBorderSize = 2; + measuredDefaultWidth = 64; + measuredDefaultHeight = 86; + + break; + } + case DPIClassification.DPI_240: + { + + layoutGap = 7; + layoutPaddingLeft = 15; + layoutPaddingRight = 15; + layoutPaddingTop = 15; + layoutPaddingBottom = 15; + layoutBorderSize = 1; + measuredDefaultWidth = 48; + measuredDefaultHeight = 65; + + break; + } + case DPIClassification.DPI_120: + { + + layoutGap = 4; + layoutPaddingLeft = 8; + layoutPaddingRight = 8; + layoutPaddingTop = 8; + layoutPaddingBottom = 8; + layoutBorderSize = 1; + measuredDefaultWidth = 24; + measuredDefaultHeight = 33; + + break; + } + default: + { + + layoutGap = 5; + layoutPaddingLeft = 10; + layoutPaddingRight = 10; + layoutPaddingTop = 10; + layoutPaddingBottom = 10; + layoutBorderSize = 1; + measuredDefaultWidth = 32; + measuredDefaultHeight = 43; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Layout variables + // + //-------------------------------------------------------------------------- + + /** + * Defines the corner radius. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var layoutCornerEllipseSize:uint; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + private var _border:DisplayObject; + + private var changeFXGSkin:Boolean = false; + + private var borderClass:Class; + + mx_internal var fillColorStyleName:String = "chromeColor"; + + /** + * Defines the shadow for the Button control's label. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var labelDisplayShadow:StyleableTextField; + + /** + * Read-only button border graphic. Use getBorderClassForCurrentState() + * to specify a graphic per-state. + * + * @see #getBorderClassForCurrentState() + */ + protected function get border():DisplayObject + { + return _border; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * Class to use for the border in the up state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + * @default Button_up + */ + protected var upBorderSkin:Class; + + /** + * Class to use for the border in the down state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + * @default Button_down + */ + protected var downBorderSkin:Class; + + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (!labelDisplayShadow && labelDisplay) + { + labelDisplayShadow = StyleableTextField(createInFontContext(StyleableTextField)); + labelDisplayShadow.styleName = this; + labelDisplayShadow.colorName = "textShadowColor"; + labelDisplayShadow.useTightTextBounds = false; + + // add shadow before display + addChildAt(labelDisplayShadow, getChildIndex(labelDisplay)); + } + + setStyle("textAlign", "center"); + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + borderClass = getBorderClassForCurrentState(); + + if (!(_border is borderClass)) + changeFXGSkin = true; + + // update borderClass and background + invalidateDisplayList(); + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + // size the FXG background + if (changeFXGSkin) + { + changeFXGSkin = false; + + if (_border) + { + removeChild(_border); + _border = null; + } + + if (borderClass) + { + _border = new borderClass(); + addChildAt(_border, 0); + } + } + + layoutBorder(unscaledWidth, unscaledHeight); + + // update label shadow + labelDisplayShadow.alpha = getStyle("textShadowAlpha"); + labelDisplayShadow.commitStyles(); + + // don't use tightText positioning on shadow + setElementPosition(labelDisplayShadow, labelDisplay.x, labelDisplay.y + 1); + setElementSize(labelDisplayShadow, labelDisplay.width, labelDisplay.height); + + // if labelDisplay is truncated, then push it down here as well. + // otherwise, it would have gotten pushed in the labelDisplay_valueCommitHandler() + if (labelDisplay.isTruncated) + labelDisplayShadow.text = labelDisplay.text; + } + + /** + * Position the background of the skin. Override this function to re-position the background. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + mx_internal function layoutBorder(unscaledWidth:Number, unscaledHeight:Number):void + { + setElementSize(border, unscaledWidth, unscaledHeight); + setElementPosition(border, 0, 0); + } + + /** + * Returns the borderClass to use based on the currentState. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected function getBorderClassForCurrentState():Class + { + if (currentState == "down") + return downBorderSkin; + else + return upBorderSkin; + } + + //-------------------------------------------------------------------------- + // + // Event Handlers + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function labelDisplay_valueCommitHandler(event:FlexEvent):void + { + super.labelDisplay_valueCommitHandler(event); + labelDisplayShadow.text = labelDisplay.text; + } + +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg index 5ba85f8550..febd3f71aa 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ActionBarBackground.fxg @@ -18,13 +18,38 @@ --> - - - + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg index 31bd28e261..79a535c0db 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_down.fxg @@ -18,39 +18,56 @@ --> - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg index 0e335badc3..af7d033a3f 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/Button_up.fxg @@ -18,34 +18,43 @@ --> - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/mobile/ActionBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ActionBarSkin.as index 43f7bf673e..5ee87e7f96 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/mobile/ActionBarSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ActionBarSkin.as @@ -40,6 +40,7 @@ import spark.skins.mobile160.assets.ActionBarBackground; import spark.skins.mobile240.assets.ActionBarBackground; import spark.skins.mobile320.assets.ActionBarBackground; import spark.skins.mobile480.assets.ActionBarBackground; +import spark.skins.mobile640.assets.ActionBarBackground; use namespace mx_internal; @@ -89,6 +90,18 @@ public class ActionBarSkin extends MobileSkin switch (applicationDPI) { + + case DPIClassification.DPI_640: + { + borderSize = 2; + layoutShadowHeight = 12; + layoutContentGroupHeight = 172; + layoutTitleGroupHorizontalPadding = 52; + + borderClass = spark.skins.mobile640.assets.ActionBarBackground; + + break; + } case DPIClassification.DPI_480: { // Note provisional may need changes @@ -97,7 +110,7 @@ public class ActionBarSkin extends MobileSkin layoutContentGroupHeight = 130; layoutTitleGroupHorizontalPadding = 40; - borderClass = spark.skins.mobile240.assets.ActionBarBackground; + borderClass = spark.skins.mobile480.assets.ActionBarBackground; break; } From 90ad7ff23fee2577a27d5f8097fee13b673429cc Mon Sep 17 00:00:00 2001 From: Om Date: Tue, 18 Mar 2014 18:37:55 -0700 Subject: [PATCH 004/392] Resolve conflict --- .../src/spark/skins/mobile/ActionBarSkin.as | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/mobile/ActionBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ActionBarSkin.as index 2a41a028d5..6e1bba3bfc 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/mobile/ActionBarSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ActionBarSkin.as @@ -90,15 +90,9 @@ public class ActionBarSkin extends MobileSkin switch (applicationDPI) { -<<<<<<< HEAD - - case DPIClassification.DPI_640: - { -======= case DPIClassification.DPI_640: { // Note provisional may need changes ->>>>>>> develop borderSize = 2; layoutShadowHeight = 12; layoutContentGroupHeight = 172; @@ -115,11 +109,6 @@ public class ActionBarSkin extends MobileSkin layoutShadowHeight = 9; layoutContentGroupHeight = 130; layoutTitleGroupHorizontalPadding = 40; -<<<<<<< HEAD - -======= - ->>>>>>> develop borderClass = spark.skins.mobile480.assets.ActionBarBackground; break; From a4290b8793778ee42cba91cfdd9b7b97ed2816d7 Mon Sep 17 00:00:00 2001 From: Om Date: Wed, 19 Mar 2014 12:25:40 -0700 Subject: [PATCH 005/392] We do need to draw the background of ActionBarSkin --- .../mobiletheme/src/spark/skins/android4/ActionBarSkin.as | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as index 7b64f06894..d8fbd8fcb8 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as @@ -559,7 +559,7 @@ public class ActionBarSkin extends MobileSkin /** * @private */ -/* override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void { super.drawBackground(unscaledWidth, unscaledHeight); @@ -579,7 +579,7 @@ public class ActionBarSkin extends MobileSkin graphics.beginGradientFill(GradientType.LINEAR, colors, backgroundAlphas, ACTIONBAR_CHROME_COLOR_RATIOS, colorMatrix); graphics.drawRect(0, borderSize, unscaledWidth, unscaledHeight - (borderSize * 2)); graphics.endFill(); - }*/ + } } } From d1fa5de19271166b0b5f5a66b080b0fc4471ed46 Mon Sep 17 00:00:00 2001 From: Om Date: Sat, 22 Mar 2014 22:52:20 -0700 Subject: [PATCH 006/392] Added CheckBox skins and assets --- .../src/spark/skins/android4/CheckBoxSkin.as | 267 ++++++++++++++++++ .../android4/TransparentActionButtonSkin.as | 108 +++++++ .../TransparentNavigationButtonSkin.as | 109 +++++++ .../skins/android4/assets/CheckBox_down.fxg | 45 +-- .../android4/assets/CheckBox_downSymbol.fxg | 29 -- .../assets/CheckBox_downSymbolSelected.fxg | 30 +- .../assets/CheckBox_downUnselected.fxg | 15 + .../skins/android4/assets/CheckBox_up.fxg | 49 ++-- .../android4/assets/CheckBox_upSymbol.fxg | 33 --- .../assets/CheckBox_upSymbolSelected.fxg | 33 +-- .../supportClasses/ActionBarButtonSkinBase.as | 2 +- 11 files changed, 541 insertions(+), 179 deletions(-) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/CheckBoxSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentActionButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentNavigationButtonSkin.as delete mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downUnselected.fxg delete mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/CheckBoxSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/CheckBoxSkin.as new file mode 100644 index 0000000000..416d85fed3 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/CheckBoxSkin.as @@ -0,0 +1,267 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + +import mx.core.DPIClassification; + +import spark.skins.android4.assets.CheckBox_up; +import spark.skins.mobile.supportClasses.SelectableButtonSkinBase; +import spark.skins.mobile120.assets.CheckBox_down; +import spark.skins.mobile120.assets.CheckBox_downSymbol; +import spark.skins.mobile120.assets.CheckBox_downSymbolSelected; +import spark.skins.mobile120.assets.CheckBox_up; +import spark.skins.mobile120.assets.CheckBox_upSymbol; +import spark.skins.mobile120.assets.CheckBox_upSymbolSelected; +import spark.skins.mobile160.assets.CheckBox_down; +import spark.skins.mobile160.assets.CheckBox_downSymbol; +import spark.skins.mobile160.assets.CheckBox_downSymbolSelected; +import spark.skins.mobile160.assets.CheckBox_up; +import spark.skins.mobile160.assets.CheckBox_upSymbol; +import spark.skins.mobile160.assets.CheckBox_upSymbolSelected; +import spark.skins.mobile240.assets.CheckBox_down; +import spark.skins.mobile240.assets.CheckBox_downSymbol; +import spark.skins.mobile240.assets.CheckBox_downSymbolSelected; +import spark.skins.mobile240.assets.CheckBox_up; +import spark.skins.mobile240.assets.CheckBox_upSymbol; +import spark.skins.mobile240.assets.CheckBox_upSymbolSelected; +import spark.skins.mobile320.assets.CheckBox_down; +import spark.skins.mobile320.assets.CheckBox_downSymbol; +import spark.skins.mobile320.assets.CheckBox_downSymbolSelected; +import spark.skins.mobile320.assets.CheckBox_up; +import spark.skins.mobile320.assets.CheckBox_upSymbol; +import spark.skins.mobile320.assets.CheckBox_upSymbolSelected; +import spark.skins.mobile480.assets.CheckBox_down; +import spark.skins.mobile480.assets.CheckBox_downSymbol; +import spark.skins.mobile480.assets.CheckBox_downSymbolSelected; +import spark.skins.mobile480.assets.CheckBox_up; +import spark.skins.mobile480.assets.CheckBox_upSymbol; +import spark.skins.mobile480.assets.CheckBox_upSymbolSelected; +import spark.skins.mobile640.assets.CheckBox_down; +import spark.skins.mobile640.assets.CheckBox_downSymbol; +import spark.skins.mobile640.assets.CheckBox_downSymbolSelected; +import spark.skins.mobile640.assets.CheckBox_up; +import spark.skins.mobile640.assets.CheckBox_upSymbol; +import spark.skins.mobile640.assets.CheckBox_upSymbolSelected; + +/** + * ActionScript-based skin for CheckBox components in mobile applications. + * + * @see spark.components.CheckBox + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class CheckBoxSkin extends SelectableButtonSkinBase +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + private static const exclusions:Array = ["labelDisplay", "labelDisplayShadow"]; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function CheckBoxSkin() + { + super(); + + layoutPaddingLeft = 0; + layoutPaddingRight = 0; + layoutPaddingTop = 0; + layoutPaddingBottom = 0; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + upIconClass = spark.skins.mobile640.assets.CheckBox_up; + upSelectedIconClass = spark.skins.mobile640.assets.CheckBox_up; + downIconClass = spark.skins.mobile640.assets.CheckBox_down; + downSelectedIconClass = spark.skins.mobile640.assets.CheckBox_down; + upSymbolIconClass = spark.skins.mobile640.assets.CheckBox_upSymbol; + upSymbolIconSelectedClass = spark.skins.mobile640.assets.CheckBox_upSymbolSelected; + downSymbolIconClass = spark.skins.mobile640.assets.CheckBox_downSymbol; + downSymbolIconSelectedClass = spark.skins.mobile640.assets.CheckBox_downSymbolSelected; + + layoutGap = 40; + minWidth = 128; + minHeight = 128; + layoutBorderSize = 6; + + break; + } + case DPIClassification.DPI_480: + { + // Note provisional may need changes + upIconClass = spark.skins.mobile480.assets.CheckBox_up; + upSelectedIconClass = spark.skins.mobile480.assets.CheckBox_up; + downIconClass = spark.skins.mobile480.assets.CheckBox_down; + downSelectedIconClass = spark.skins.mobile480.assets.CheckBox_down; + upSymbolIconClass = spark.skins.mobile480.assets.CheckBox_upSymbol; + upSymbolIconSelectedClass = spark.skins.mobile480.assets.CheckBox_upSymbolSelected; + downSymbolIconClass = spark.skins.mobile480.assets.CheckBox_downSymbol; + downSymbolIconSelectedClass = spark.skins.mobile480.assets.CheckBox_downSymbolSelected; + + layoutGap = 30; + minWidth = 96; + minHeight = 96; + layoutBorderSize = 4; + + break; + } + case DPIClassification.DPI_320: + { + upIconClass = spark.skins.mobile320.assets.CheckBox_up; + upSelectedIconClass = spark.skins.mobile320.assets.CheckBox_up; + downIconClass = spark.skins.mobile320.assets.CheckBox_down; + downSelectedIconClass = spark.skins.mobile320.assets.CheckBox_down; + upSymbolIconClass = spark.skins.mobile320.assets.CheckBox_upSymbol; + upSymbolIconSelectedClass = spark.skins.mobile320.assets.CheckBox_upSymbolSelected; + downSymbolIconClass = spark.skins.mobile320.assets.CheckBox_downSymbol; + downSymbolIconSelectedClass = spark.skins.mobile320.assets.CheckBox_downSymbolSelected; + + layoutGap = 20; + minWidth = 64; + minHeight = 64; + layoutBorderSize = 3; + + break; + } + case DPIClassification.DPI_240: + { + + upIconClass = spark.skins.android4.assets.CheckBox_up; + upSelectedIconClass = spark.skins.android4.assets.CheckBox_up; + downIconClass = spark.skins.android4.assets.CheckBox_downUnselected; + downSelectedIconClass = spark.skins.android4.assets.CheckBox_down; + upSymbolIconClass = null; + upSymbolIconSelectedClass = spark.skins.android4.assets.CheckBox_upSymbolSelected; + downSymbolIconSelectedClass = spark.skins.android4.assets.CheckBox_downSymbolSelected; + downSymbolIconClass = spark.skins.android4.assets.CheckBox_downUnselected; + + layoutGap = 15; + minWidth = 48; + minHeight = 48; + layoutBorderSize = 2; + + break; + } + case DPIClassification.DPI_120: + { + upIconClass = spark.skins.mobile120.assets.CheckBox_up; + upSelectedIconClass = spark.skins.mobile120.assets.CheckBox_up; + downIconClass = spark.skins.mobile120.assets.CheckBox_down; + downSelectedIconClass = spark.skins.mobile120.assets.CheckBox_down; + upSymbolIconClass = spark.skins.mobile120.assets.CheckBox_upSymbol; + upSymbolIconSelectedClass = spark.skins.mobile120.assets.CheckBox_upSymbolSelected; + downSymbolIconClass = spark.skins.mobile120.assets.CheckBox_downSymbol; + downSymbolIconSelectedClass = spark.skins.mobile120.assets.CheckBox_downSymbolSelected; + + layoutGap = 8; + minWidth = 24; + minHeight = 24; + layoutBorderSize = 1; + + break; + } + default: + { + // default DPI_160 + upIconClass = spark.skins.mobile160.assets.CheckBox_up; + upSelectedIconClass = spark.skins.mobile160.assets.CheckBox_up; + downIconClass = spark.skins.mobile160.assets.CheckBox_down; + downSelectedIconClass = spark.skins.mobile160.assets.CheckBox_down; + upSymbolIconClass = spark.skins.mobile160.assets.CheckBox_upSymbol; + upSymbolIconSelectedClass = spark.skins.mobile160.assets.CheckBox_upSymbolSelected; + downSymbolIconClass = spark.skins.mobile160.assets.CheckBox_downSymbol; + downSymbolIconSelectedClass = spark.skins.mobile160.assets.CheckBox_downSymbolSelected; + + layoutGap = 10; + minWidth = 32; + minHeight = 32; + layoutBorderSize = 2; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * CheckBox chromeColor is drawn to match the FXG rectangle + * shape and position. + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + // super draws a transparent hit zone + super.drawBackground(unscaledWidth, unscaledHeight); + + // get the size and position of iconDisplay + var currentIcon:DisplayObject = getIconDisplay(); + var widthAdjustment:Number = layoutBorderSize * 2; + + graphics.beginFill(getStyle("chromeColor")); + graphics.drawRoundRect(currentIcon.x + layoutBorderSize, + currentIcon.y + layoutBorderSize, + currentIcon.width - widthAdjustment, + currentIcon.height - widthAdjustment, layoutBorderSize, layoutBorderSize); + graphics.endFill(); + } + + /** + * List of IDs of items that should be excluded when rendering the focus ring. + * Only items of type DisplayObject or GraphicElement should be excluded. Items + * of other types are ignored. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + override protected function get focusSkinExclusions():Array + { + return exclusions; + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentActionButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentActionButtonSkin.as new file mode 100644 index 0000000000..dadab3c2ae --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentActionButtonSkin.as @@ -0,0 +1,108 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; +import flash.display.Graphics; + +import mx.core.DPIClassification; +import mx.core.mx_internal; + +import spark.skins.mobile.assets.TransparentActionButton_down; +import spark.skins.mobile.assets.TransparentActionButton_up; +import spark.skins.mobile.supportClasses.ActionBarButtonSkinBase; +import spark.skins.mobile.supportClasses.MobileSkin; +import spark.skins.mobile320.assets.TransparentActionButton_down; +import spark.skins.mobile320.assets.TransparentActionButton_up; +import spark.skins.mobile480.assets.TransparentActionButton_down; +import spark.skins.mobile480.assets.TransparentActionButton_up; +import spark.skins.mobile640.assets.TransparentActionButton_down; +import spark.skins.mobile640.assets.TransparentActionButton_up; + +use namespace mx_internal; + +/** + * The default skin class for buttons in the action area of the Spark ActionBar component + * in mobile applications. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class TransparentActionButtonSkin extends ActionBarButtonSkinBase +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function TransparentActionButtonSkin() + { + super(); + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + upBorderSkin = spark.skins.mobile640.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile640.assets.TransparentActionButton_down; + + break; + } + case DPIClassification.DPI_480: + { + upBorderSkin = spark.skins.mobile480.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile480.assets.TransparentActionButton_down; + + break; + } + case DPIClassification.DPI_320: + { + upBorderSkin = spark.skins.mobile320.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile320.assets.TransparentActionButton_down; + + break; + } + default: + { + upBorderSkin = spark.skins.mobile.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile.assets.TransparentActionButton_down; + + break; + } + } + } + + override mx_internal function layoutBorder(unscaledWidth:Number, unscaledHeight:Number):void + { + // don't call super, don't layout twice + // leading vertical separator is outside the left bounds of the button + setElementSize(border, unscaledWidth + layoutBorderSize, unscaledHeight); + setElementPosition(border, -layoutBorderSize, 0); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentNavigationButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentNavigationButtonSkin.as new file mode 100644 index 0000000000..a19a21c950 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentNavigationButtonSkin.as @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.mobile +{ +import flash.display.DisplayObject; +import flash.display.Graphics; + +import mx.core.DPIClassification; +import mx.core.mx_internal; + +import spark.skins.mobile.assets.TransparentNavigationButton_down; +import spark.skins.mobile.assets.TransparentNavigationButton_up; +import spark.skins.mobile.supportClasses.ActionBarButtonSkinBase; +import spark.skins.mobile.supportClasses.MobileSkin; +import spark.skins.mobile320.assets.TransparentNavigationButton_down; +import spark.skins.mobile320.assets.TransparentNavigationButton_up; +import spark.skins.mobile480.assets.TransparentNavigationButton_down; +import spark.skins.mobile480.assets.TransparentNavigationButton_up; +import spark.skins.mobile640.assets.TransparentNavigationButton_down; +import spark.skins.mobile640.assets.TransparentNavigationButton_up; + +use namespace mx_internal; + +/** + * The default skin class for buttons in the navigation area of the Spark ActionBar component + * in mobile applications. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class TransparentNavigationButtonSkin extends ActionBarButtonSkinBase +{ + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function TransparentNavigationButtonSkin() + { + super(); + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + upBorderSkin = spark.skins.mobile640.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile640.assets.TransparentActionButton_down; + + break; + } + case DPIClassification.DPI_480: + { + upBorderSkin = spark.skins.mobile480.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile480.assets.TransparentActionButton_down; + + break; + } + case DPIClassification.DPI_320: + { + upBorderSkin = spark.skins.mobile320.assets.TransparentNavigationButton_up; + downBorderSkin = spark.skins.mobile320.assets.TransparentNavigationButton_down; + + break; + } + default: + { + upBorderSkin = spark.skins.mobile.assets.TransparentNavigationButton_up; + downBorderSkin = spark.skins.mobile.assets.TransparentNavigationButton_down; + + break; + } + } + } + + override mx_internal function layoutBorder(unscaledWidth:Number, unscaledHeight:Number):void + { + // trailing vertical separator is outside the right bounds of the button + setElementSize(border, unscaledWidth + layoutBorderSize, unscaledHeight); + setElementPosition(border, 0, 0); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg index e8a55a141e..3c49ba107a 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg @@ -1,40 +1,15 @@ - - - - - - + + + - - - - + - - - - - - - - + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg deleted file mode 100644 index 3882781c6b..0000000000 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbol.fxg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg index 03fdd86642..da3e71958f 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg @@ -1,30 +1,10 @@ - - - - - - + + + - + - + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downUnselected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downUnselected.fxg new file mode 100644 index 0000000000..127a4b350c --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downUnselected.fxg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg index 52d7b1dd0c..03843e7c76 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg @@ -1,30 +1,23 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg deleted file mode 100644 index b6c84feaa1..0000000000 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbol.fxg +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg index 0f613f07dd..8c8efd7665 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg @@ -1,33 +1,10 @@ - - - - - - + + + - - - - + - + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/ActionBarButtonSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/ActionBarButtonSkinBase.as index e9dfc515c3..fef56aebec 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/ActionBarButtonSkinBase.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/ActionBarButtonSkinBase.as @@ -84,7 +84,7 @@ public class ActionBarButtonSkinBase extends ButtonSkin layoutPaddingBottom = 20; layoutPaddingLeft = 40; layoutPaddingRight = 40; - measuredDefaultWidth = 2012; + measuredDefaultWidth = 212; measuredDefaultHeight = 172; break; } From 10f8840fbeac08002e73ab44bfc210bf3534cd99 Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 28 Mar 2014 14:56:11 -0700 Subject: [PATCH 007/392] Android 4.x skins for Checkbox. Modified existing skinclass to eliminate need for creating FXG assets for every supported DPI. --- .../src/spark/skins/android4/CheckBoxSkin.as | 475 +++++++++--------- .../skins/android4/assets/CheckBox_down.fxg | 21 +- .../android4/assets/CheckBox_downSelected.fxg | 35 ++ .../assets/CheckBox_downSymbolSelected.fxg | 21 +- .../assets/CheckBox_downUnselected.fxg | 15 - .../skins/android4/assets/CheckBox_up.fxg | 36 +- .../android4/assets/CheckBox_upSelected.fxg | 29 ++ .../assets/CheckBox_upSymbolSelected.fxg | 35 +- 8 files changed, 393 insertions(+), 274 deletions(-) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSelected.fxg delete mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downUnselected.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSelected.fxg diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/CheckBoxSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/CheckBoxSkin.as index 416d85fed3..f091205bc4 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/CheckBoxSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/CheckBoxSkin.as @@ -19,249 +19,250 @@ package spark.skins.android4 { -import flash.display.DisplayObject; + import flash.display.DisplayObject; + + import mx.core.DPIClassification; + + import spark.skins.android4.assets.CheckBox_up; + import spark.skins.mobile.supportClasses.SelectableButtonSkinBase; + + /** + * ActionScript-based skin for CheckBox components in mobile applications. + * + * @see spark.components.CheckBox + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class CheckBoxSkin extends SelectableButtonSkinBase + { + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + private static const exclusions:Array = ["labelDisplay", "labelDisplayShadow"]; -import mx.core.DPIClassification; + //-------------------------------------------------------------------------- + // + // Member variables + // + //-------------------------------------------------------------------------- -import spark.skins.android4.assets.CheckBox_up; -import spark.skins.mobile.supportClasses.SelectableButtonSkinBase; -import spark.skins.mobile120.assets.CheckBox_down; -import spark.skins.mobile120.assets.CheckBox_downSymbol; -import spark.skins.mobile120.assets.CheckBox_downSymbolSelected; -import spark.skins.mobile120.assets.CheckBox_up; -import spark.skins.mobile120.assets.CheckBox_upSymbol; -import spark.skins.mobile120.assets.CheckBox_upSymbolSelected; -import spark.skins.mobile160.assets.CheckBox_down; -import spark.skins.mobile160.assets.CheckBox_downSymbol; -import spark.skins.mobile160.assets.CheckBox_downSymbolSelected; -import spark.skins.mobile160.assets.CheckBox_up; -import spark.skins.mobile160.assets.CheckBox_upSymbol; -import spark.skins.mobile160.assets.CheckBox_upSymbolSelected; -import spark.skins.mobile240.assets.CheckBox_down; -import spark.skins.mobile240.assets.CheckBox_downSymbol; -import spark.skins.mobile240.assets.CheckBox_downSymbolSelected; -import spark.skins.mobile240.assets.CheckBox_up; -import spark.skins.mobile240.assets.CheckBox_upSymbol; -import spark.skins.mobile240.assets.CheckBox_upSymbolSelected; -import spark.skins.mobile320.assets.CheckBox_down; -import spark.skins.mobile320.assets.CheckBox_downSymbol; -import spark.skins.mobile320.assets.CheckBox_downSymbolSelected; -import spark.skins.mobile320.assets.CheckBox_up; -import spark.skins.mobile320.assets.CheckBox_upSymbol; -import spark.skins.mobile320.assets.CheckBox_upSymbolSelected; -import spark.skins.mobile480.assets.CheckBox_down; -import spark.skins.mobile480.assets.CheckBox_downSymbol; -import spark.skins.mobile480.assets.CheckBox_downSymbolSelected; -import spark.skins.mobile480.assets.CheckBox_up; -import spark.skins.mobile480.assets.CheckBox_upSymbol; -import spark.skins.mobile480.assets.CheckBox_upSymbolSelected; -import spark.skins.mobile640.assets.CheckBox_down; -import spark.skins.mobile640.assets.CheckBox_downSymbol; -import spark.skins.mobile640.assets.CheckBox_downSymbolSelected; -import spark.skins.mobile640.assets.CheckBox_up; -import spark.skins.mobile640.assets.CheckBox_upSymbol; -import spark.skins.mobile640.assets.CheckBox_upSymbolSelected; - -/** - * ActionScript-based skin for CheckBox components in mobile applications. - * - * @see spark.components.CheckBox - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ -public class CheckBoxSkin extends SelectableButtonSkinBase -{ - //-------------------------------------------------------------------------- - // - // Class constants - // - //-------------------------------------------------------------------------- - - private static const exclusions:Array = ["labelDisplay", "labelDisplayShadow"]; - - //-------------------------------------------------------------------------- - // - // Constructor - // - //-------------------------------------------------------------------------- - - /** - * Constructor. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - public function CheckBoxSkin() - { - super(); - - layoutPaddingLeft = 0; - layoutPaddingRight = 0; - layoutPaddingTop = 0; - layoutPaddingBottom = 0; - - switch (applicationDPI) - { - case DPIClassification.DPI_640: + protected var symbolOffsetX:Number; + protected var symbolOffsetY:Number; + protected var iconWidth:Number; + protected var iconHeight:Number; + protected var symbolWidth:Number; + protected var symbolHeight:Number; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function CheckBoxSkin() + { + super(); + + layoutPaddingLeft = 0; + layoutPaddingRight = 0; + layoutPaddingTop = 0; + layoutPaddingBottom = 0; + + upIconClass = spark.skins.android4.assets.CheckBox_up; + upSelectedIconClass = spark.skins.android4.assets.CheckBox_upSelected; + downIconClass = spark.skins.android4.assets.CheckBox_down; + downSelectedIconClass = spark.skins.android4.assets.CheckBox_downSelected; + upSymbolIconClass = null; + upSymbolIconSelectedClass = spark.skins.android4.assets.CheckBox_upSymbolSelected; + downSymbolIconSelectedClass = spark.skins.android4.assets.CheckBox_downSymbolSelected; + downSymbolIconClass = null; + + switch (applicationDPI) { - upIconClass = spark.skins.mobile640.assets.CheckBox_up; - upSelectedIconClass = spark.skins.mobile640.assets.CheckBox_up; - downIconClass = spark.skins.mobile640.assets.CheckBox_down; - downSelectedIconClass = spark.skins.mobile640.assets.CheckBox_down; - upSymbolIconClass = spark.skins.mobile640.assets.CheckBox_upSymbol; - upSymbolIconSelectedClass = spark.skins.mobile640.assets.CheckBox_upSymbolSelected; - downSymbolIconClass = spark.skins.mobile640.assets.CheckBox_downSymbol; - downSymbolIconSelectedClass = spark.skins.mobile640.assets.CheckBox_downSymbolSelected; - - layoutGap = 40; - minWidth = 128; - minHeight = 128; - layoutBorderSize = 6; - - break; + case DPIClassification.DPI_640: + { + + layoutGap = 16; + minWidth = 128; + minHeight = 128; + layoutBorderSize = 6; + iconWidth = 128; + iconHeight = 128; + symbolWidth = 64; + symbolHeight = 64; + symbolOffsetX = 32; + symbolOffsetY = 32; + + break; + } + case DPIClassification.DPI_480: + { + + layoutGap = 12; + minWidth = 96; + minHeight = 96; + layoutBorderSize = 4; + iconWidth = 96; + iconHeight = 96; + symbolWidth = 48; + symbolHeight = 48; + symbolOffsetX = 24; + symbolOffsetY = 24; + + break; + } + case DPIClassification.DPI_320: + { + + layoutGap = 8; + minWidth = 64; + minHeight = 64; + layoutBorderSize = 3; + iconWidth = 64; + iconHeight = 64; + symbolWidth = 32; + symbolHeight = 32; + symbolOffsetX = 16; + symbolOffsetY = 16; + + break; + } + case DPIClassification.DPI_240: + { + + layoutGap = 6; + minWidth = 48; + minHeight = 48; + layoutBorderSize = 2; + iconWidth = 48; + iconHeight = 48; + symbolWidth = 24; + symbolHeight = 24; + symbolOffsetX = 12; + symbolOffsetY = 12; + + break; + } + case DPIClassification.DPI_120: + { + + layoutGap = 3; + minWidth = 24; + minHeight = 24; + layoutBorderSize = 1; + iconWidth = 24; + iconHeight = 24; + symbolWidth = 12; + symbolHeight = 12; + symbolOffsetX = 6; + symbolOffsetY = 6; + + break; + } + default: + { + // default DPI_160 + + layoutGap = 4; + minWidth = 32; + minHeight = 32; + layoutBorderSize = 2; + iconWidth = 32; + iconHeight = 32; + symbolWidth = 16; + symbolHeight = 16; + symbolOffsetX = 8; + symbolOffsetY = 8; + + + break; + } } - case DPIClassification.DPI_480: + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * CheckBox chromeColor is drawn to match the FXG rectangle + * shape and position. + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + // super draws a transparent hit zone + super.drawBackground(unscaledWidth, unscaledHeight); + + // get the size and position of iconDisplay + var currentIcon:DisplayObject = getIconDisplay(); + var widthAdjustment:Number = layoutBorderSize * 2; + + graphics.beginFill(getStyle("chromeColor")); + graphics.drawRoundRect(currentIcon.x + layoutBorderSize, + currentIcon.y + layoutBorderSize, + currentIcon.width - widthAdjustment, + currentIcon.height - widthAdjustment, layoutBorderSize, layoutBorderSize); + graphics.endFill(); + } + + /** + * List of IDs of items that should be excluded when rendering the focus ring. + * Only items of type DisplayObject or GraphicElement should be excluded. Items + * of other types are ignored. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + override protected function get focusSkinExclusions():Array + { + return exclusions; + } + + override protected function commitCurrentState():void + { + super.commitCurrentState(); + if(symbolIcon != null) { - // Note provisional may need changes - upIconClass = spark.skins.mobile480.assets.CheckBox_up; - upSelectedIconClass = spark.skins.mobile480.assets.CheckBox_up; - downIconClass = spark.skins.mobile480.assets.CheckBox_down; - downSelectedIconClass = spark.skins.mobile480.assets.CheckBox_down; - upSymbolIconClass = spark.skins.mobile480.assets.CheckBox_upSymbol; - upSymbolIconSelectedClass = spark.skins.mobile480.assets.CheckBox_upSymbolSelected; - downSymbolIconClass = spark.skins.mobile480.assets.CheckBox_downSymbol; - downSymbolIconSelectedClass = spark.skins.mobile480.assets.CheckBox_downSymbolSelected; - - layoutGap = 30; - minWidth = 96; - minHeight = 96; - layoutBorderSize = 4; - - break; + symbolIcon.width = symbolWidth; + symbolIcon.height = symbolHeight; } - case DPIClassification.DPI_320: - { - upIconClass = spark.skins.mobile320.assets.CheckBox_up; - upSelectedIconClass = spark.skins.mobile320.assets.CheckBox_up; - downIconClass = spark.skins.mobile320.assets.CheckBox_down; - downSelectedIconClass = spark.skins.mobile320.assets.CheckBox_down; - upSymbolIconClass = spark.skins.mobile320.assets.CheckBox_upSymbol; - upSymbolIconSelectedClass = spark.skins.mobile320.assets.CheckBox_upSymbolSelected; - downSymbolIconClass = spark.skins.mobile320.assets.CheckBox_downSymbol; - downSymbolIconSelectedClass = spark.skins.mobile320.assets.CheckBox_downSymbolSelected; - - layoutGap = 20; - minWidth = 64; - minHeight = 64; - layoutBorderSize = 3; - - break; - } - case DPIClassification.DPI_240: - { - - upIconClass = spark.skins.android4.assets.CheckBox_up; - upSelectedIconClass = spark.skins.android4.assets.CheckBox_up; - downIconClass = spark.skins.android4.assets.CheckBox_downUnselected; - downSelectedIconClass = spark.skins.android4.assets.CheckBox_down; - upSymbolIconClass = null; - upSymbolIconSelectedClass = spark.skins.android4.assets.CheckBox_upSymbolSelected; - downSymbolIconSelectedClass = spark.skins.android4.assets.CheckBox_downSymbolSelected; - downSymbolIconClass = spark.skins.android4.assets.CheckBox_downUnselected; - - layoutGap = 15; - minWidth = 48; - minHeight = 48; - layoutBorderSize = 2; - - break; - } - case DPIClassification.DPI_120: + var iconDisplay:DisplayObject = getIconDisplay(); + if(iconDisplay != null) { - upIconClass = spark.skins.mobile120.assets.CheckBox_up; - upSelectedIconClass = spark.skins.mobile120.assets.CheckBox_up; - downIconClass = spark.skins.mobile120.assets.CheckBox_down; - downSelectedIconClass = spark.skins.mobile120.assets.CheckBox_down; - upSymbolIconClass = spark.skins.mobile120.assets.CheckBox_upSymbol; - upSymbolIconSelectedClass = spark.skins.mobile120.assets.CheckBox_upSymbolSelected; - downSymbolIconClass = spark.skins.mobile120.assets.CheckBox_downSymbol; - downSymbolIconSelectedClass = spark.skins.mobile120.assets.CheckBox_downSymbolSelected; - - layoutGap = 8; - minWidth = 24; - minHeight = 24; - layoutBorderSize = 1; - - break; + iconDisplay.width = iconWidth; + iconDisplay.height = iconHeight; } - default: - { - // default DPI_160 - upIconClass = spark.skins.mobile160.assets.CheckBox_up; - upSelectedIconClass = spark.skins.mobile160.assets.CheckBox_up; - downIconClass = spark.skins.mobile160.assets.CheckBox_down; - downSelectedIconClass = spark.skins.mobile160.assets.CheckBox_down; - upSymbolIconClass = spark.skins.mobile160.assets.CheckBox_upSymbol; - upSymbolIconSelectedClass = spark.skins.mobile160.assets.CheckBox_upSymbolSelected; - downSymbolIconClass = spark.skins.mobile160.assets.CheckBox_downSymbol; - downSymbolIconSelectedClass = spark.skins.mobile160.assets.CheckBox_downSymbolSelected; - - layoutGap = 10; - minWidth = 32; - minHeight = 32; - layoutBorderSize = 2; - - break; - } - } - } - - //-------------------------------------------------------------------------- - // - // Overridden methods - // - //-------------------------------------------------------------------------- - - /** - * @private - * CheckBox chromeColor is drawn to match the FXG rectangle - * shape and position. - */ - override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void - { - // super draws a transparent hit zone - super.drawBackground(unscaledWidth, unscaledHeight); - - // get the size and position of iconDisplay - var currentIcon:DisplayObject = getIconDisplay(); - var widthAdjustment:Number = layoutBorderSize * 2; - - graphics.beginFill(getStyle("chromeColor")); - graphics.drawRoundRect(currentIcon.x + layoutBorderSize, - currentIcon.y + layoutBorderSize, - currentIcon.width - widthAdjustment, - currentIcon.height - widthAdjustment, layoutBorderSize, layoutBorderSize); - graphics.endFill(); - } - - /** - * List of IDs of items that should be excluded when rendering the focus ring. - * Only items of type DisplayObject or GraphicElement should be excluded. Items - * of other types are ignored. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - override protected function get focusSkinExclusions():Array - { - return exclusions; - } -} + } + + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + // position the symbols to align with the background "icon" + if (symbolIcon) + { + var currentIcon:DisplayObject = getIconDisplay(); + setElementPosition(symbolIcon, symbolOffsetX, symbolOffsetY); + } + } + + } } \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg index 3c49ba107a..426df3d2f7 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_down.fxg @@ -1,3 +1,23 @@ + + + @@ -10,6 +30,5 @@ - \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSelected.fxg new file mode 100644 index 0000000000..ea7c3c43cf --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSelected.fxg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg index da3e71958f..f59d001cb8 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downSymbolSelected.fxg @@ -1,3 +1,23 @@ + + + @@ -5,6 +25,5 @@ - \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downUnselected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downUnselected.fxg deleted file mode 100644 index 127a4b350c..0000000000 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_downUnselected.fxg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg index 03843e7c76..9d652603bf 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_up.fxg @@ -1,16 +1,26 @@ - - - + + + + + - - - - - - - - - @@ -18,6 +28,4 @@ - - \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSelected.fxg new file mode 100644 index 0000000000..f51d7df976 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSelected.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg index 8c8efd7665..a728d3d9ab 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CheckBox_upSymbolSelected.fxg @@ -1,10 +1,33 @@ + + - - - - - + + + + + + + + + + - \ No newline at end of file From 074a2f44523ff1ed9857bbcbc5427a75ff45b59e Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 28 Mar 2014 19:56:55 -0700 Subject: [PATCH 008/392] New skin and FXG for RadioButton. It uses the same FXG assets for all the DPIs --- .../spark/skins/android4/RadioButtonSkin.as | 253 ++++++++++++++++++ .../android4/assets/RadioButton_down.fxg | 34 +++ .../assets/RadioButton_downSymbolSelected.fxg | 29 ++ .../skins/android4/assets/RadioButton_up.fxg | 29 ++ .../assets/RadioButton_upSymbolSelected.fxg | 36 +++ 5 files changed, 381 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/RadioButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_downSymbolSelected.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_up.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_upSymbolSelected.fxg diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/RadioButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/RadioButtonSkin.as new file mode 100644 index 0000000000..fd74645065 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/RadioButtonSkin.as @@ -0,0 +1,253 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + +import mx.core.DPIClassification; + +import spark.skins.android4.assets.RadioButton_up; +import spark.skins.mobile.supportClasses.SelectableButtonSkinBase; + +/** + * ActionScript-based skin for RadioButton controls in mobile applications. + * + * @see spark.components.RadioButton + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class RadioButtonSkin extends SelectableButtonSkinBase +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + static private const exclusions:Array = ["labelDisplay", "labelDisplayShadow"]; + + //-------------------------------------------------------------------------- + // + // Member variables + // + //-------------------------------------------------------------------------- + + protected var symbolOffsetX:Number; + protected var symbolOffsetY:Number; + protected var iconWidth:Number; + protected var iconHeight:Number; + protected var symbolWidth:Number; + protected var symbolHeight:Number; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function RadioButtonSkin() + { + super(); + + layoutPaddingLeft = 0; + layoutPaddingRight = 0; + layoutPaddingTop = 0; + layoutPaddingBottom = 0; + + upIconClass = spark.skins.android4.assets.RadioButton_up; + upSelectedIconClass = spark.skins.android4.assets.RadioButton_up; + downIconClass = spark.skins.android4.assets.RadioButton_down; + downSelectedIconClass = spark.skins.android4.assets.RadioButton_down; + upSymbolIconClass = null; + downSymbolIconClass = null; + upSymbolIconSelectedClass = spark.skins.android4.assets.RadioButton_upSymbolSelected; + downSymbolIconSelectedClass = spark.skins.android4.assets.RadioButton_downSymbolSelected; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + + layoutGap = 16; + minWidth = 128; + minHeight = 128; + iconWidth = 128; + iconHeight = 128; + symbolWidth = 44; + symbolHeight = 44; + symbolOffsetX = 44; + symbolOffsetY = 44; + + break; + } + case DPIClassification.DPI_480: + { + + layoutGap = 12; + minWidth = 96; + minHeight = 96; + iconWidth = 96; + iconHeight = 96; + symbolWidth = 33; + symbolHeight = 33; + symbolOffsetX = 33; + symbolOffsetY = 33; + + break; + } + case DPIClassification.DPI_320: + { + + layoutGap = 8; + minWidth = 64; + minHeight = 64; + iconWidth = 64; + iconHeight = 64; + symbolWidth = 22; + symbolHeight = 22; + symbolOffsetX = 22; + symbolOffsetY = 22; + + break; + } + case DPIClassification.DPI_240: + { + + layoutGap = 6; + minWidth = 48; + minHeight = 48; + iconWidth = 48; + iconHeight = 48; + symbolWidth = 16.5; + symbolHeight = 16.5; + symbolOffsetX = 16.5; + symbolOffsetY = 16.5; + + break; + } + case DPIClassification.DPI_120: + { + + layoutGap = 3; + minWidth = 24; + minHeight = 24; + iconWidth = 24; + iconHeight = 24; + symbolWidth = 8.25; + symbolHeight = 8.25; + symbolOffsetX = 8.25; + symbolOffsetY = 8.25; + + break; + } + default: + { + + layoutGap = 4; + minWidth = 32; + minHeight = 32; + iconWidth = 32; + iconHeight = 32; + symbolWidth = 11; + symbolHeight = 11; + symbolOffsetX = 11; + symbolOffsetY = 11; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * RadioButton chromeColor is drawn to match the FXG ellipse + * shape and position. + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + // super draws a transparent hit zone + super.drawBackground(unscaledWidth, unscaledHeight); + + // get the size and position of iconDisplay + var currentIcon:DisplayObject = getIconDisplay(); + + graphics.beginFill(getStyle("chromeColor")); + graphics.drawEllipse(currentIcon.x + 1, currentIcon.y + 1, currentIcon.width - 2, currentIcon.height - 2); + graphics.endFill(); + } + + /** + * @private + */ + override protected function get focusSkinExclusions():Array + { + return exclusions; + } + + override protected function commitCurrentState():void + { + super.commitCurrentState(); + if(symbolIcon != null) + { + symbolIcon.width = symbolWidth; + symbolIcon.height = symbolHeight; + } + var iconDisplay:DisplayObject = getIconDisplay(); + if(iconDisplay != null) + { + iconDisplay.width = iconWidth; + iconDisplay.height = iconHeight; + } + } + + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + // position the symbols to align with the background "icon" + if (symbolIcon) + { + var currentIcon:DisplayObject = getIconDisplay(); + setElementPosition(symbolIcon, symbolOffsetX, symbolOffsetY); + } + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_down.fxg new file mode 100644 index 0000000000..dc744148d3 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_down.fxg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_downSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_downSymbolSelected.fxg new file mode 100644 index 0000000000..374c9fc056 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_downSymbolSelected.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_up.fxg new file mode 100644 index 0000000000..4110262b43 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_up.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_upSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_upSymbolSelected.fxg new file mode 100644 index 0000000000..5b844b6e62 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/RadioButton_upSymbolSelected.fxg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file From be19b3f3f9a03b33c12199ce834a3420b052c9de Mon Sep 17 00:00:00 2001 From: Om Date: Tue, 8 Apr 2014 18:02:14 -0700 Subject: [PATCH 009/392] Bug in current ToggleSwitchSkin (needs to be merged back to develop asap) --- .../mobiletheme/src/spark/skins/mobile/ToggleSwitchSkin.as | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/mobile/ToggleSwitchSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ToggleSwitchSkin.as index fc599d59b0..6169bab00b 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/mobile/ToggleSwitchSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ToggleSwitchSkin.as @@ -86,7 +86,7 @@ public class ToggleSwitchSkin extends MobileSkin layoutTextShadowOffset = -2; layoutInnerPadding = 14; layoutOuterPadding = 22; - slidingContentOverlayClass = spark.skins.mobile320.assets.ToggleSwitch_contentShadow; + slidingContentOverlayClass = spark.skins.mobile640.assets.ToggleSwitch_contentShadow; break; } case DPIClassification.DPI_480: From 67bcdc28816e2a5eaef13cf7ef2751720d9e8515 Mon Sep 17 00:00:00 2001 From: Om Date: Tue, 8 Apr 2014 18:04:50 -0700 Subject: [PATCH 010/392] New skin ToggleSwitchSkin for Android 4.x toggle switch All visuals except label are in FXG. All DPIs now use the same FXG assets. --- .../spark/skins/android4/ToggleSwitchSkin.as | 494 ++++++++++++++++++ .../assets/ToggleSwitchBackground.fxg | 29 + .../android4/assets/ToggleSwitchThumb_off.fxg | 40 ++ .../android4/assets/ToggleSwitchThumb_on.fxg | 40 ++ 4 files changed, 603 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/ToggleSwitchSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchBackground.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchThumb_off.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchThumb_on.fxg diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ToggleSwitchSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/ToggleSwitchSkin.as new file mode 100644 index 0000000000..070481d888 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ToggleSwitchSkin.as @@ -0,0 +1,494 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.BlendMode; + import flash.events.Event; + + import mx.core.DPIClassification; + import mx.core.IVisualElement; + import mx.core.UIComponent; + + import spark.components.ToggleSwitch; + import spark.components.supportClasses.StyleableTextField; + import spark.core.SpriteVisualElement; + import spark.skins.android4.assets.ToggleSwitchBackground; + import spark.skins.android4.assets.ToggleSwitchThumb_off; + import spark.skins.mobile.supportClasses.MobileSkin; + + + /** + * ActionScript-based Android 4.x specific skin for the ToggleSwitch control. + * This class is responsible for most of the + * graphics drawing, with additional fxg assets. + * + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + * + * @see spark.components.ToggleSwitch + */ + public class ToggleSwitchSkin extends MobileSkin + { + //---------------------------------------------------------------------------------------------- + // + // Skin parts + // + //---------------------------------------------------------------------------------------------- + + /** + * The thumb skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var thumb:IVisualElement; + /** + * The track skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var track:IVisualElement; + + //---------------------------------- + // hostComponent + //---------------------------------- + + private var _hostComponent:ToggleSwitch; + public var selectedLabelDisplay:LabelDisplayComponent; + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public function get hostComponent():ToggleSwitch + { + return _hostComponent; + } + + public function set hostComponent(value:ToggleSwitch):void + { + if (_hostComponent) + _hostComponent.removeEventListener("thumbPositionChanged", thumbPositionChanged_handler); + _hostComponent = value; + if (_hostComponent) + _hostComponent.addEventListener("thumbPositionChanged", thumbPositionChanged_handler); + } + + //---------------------------------- + // selectedLabel + //---------------------------------- + + private var _selectedLabel:String; + /** + * The text of the label showing when the component is selected. + * Subclasses can set or override this property to customize the selected label. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected function get selectedLabel():String + { + return _selectedLabel; + } + + protected function set selectedLabel(value:String):void + { + _selectedLabel = value; + } + + //---------------------------------- + // unselectedLabel + //---------------------------------- + + private var _unselectedLabel:String; + /** + * The text of the label showing when the component is not selected. + * Subclasses can set or override this property to customize the unselected label. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected function get unselectedLabel():String + { + return _unselectedLabel; + } + + protected function set unselectedLabel(value:String):void + { + _unselectedLabel = value; + } + + + /** + * The contents inside the skin, not including the outline + * stroke + */ + private var contents:UIComponent; + private var switchTrack:Class; + private var switchOff:Class; + private var switchOn:Class; + protected var trackWidth:Number; + protected var trackHeight:Number; + protected var layoutThumbWidth:Number; + protected var layoutThumbHeight:Number; + private var thumbOn:IVisualElement; + private var thumbOff:IVisualElement; + + public function ToggleSwitchSkin() + { + super(); + + switchTrack = spark.skins.android4.assets.ToggleSwitchBackground; + switchOn = spark.skins.android4.assets.ToggleSwitchThumb_on; + switchOff = spark.skins.android4.assets.ToggleSwitchThumb_off; + + switch(applicationDPI) + { + case DPIClassification.DPI_640: + { + layoutThumbWidth = 188; + layoutThumbHeight = 96; + trackWidth = 388; + trackHeight = 96; + break; + } + case DPIClassification.DPI_480: + { + layoutThumbWidth = 140; + layoutThumbHeight = 72; + trackWidth = 291; + trackHeight = 72; + break; + } + case DPIClassification.DPI_320: + { + layoutThumbWidth = 94; + layoutThumbHeight = 48; + trackWidth = 194; + trackHeight = 48; + break; + } + case DPIClassification.DPI_240: + { + layoutThumbWidth = 70; + layoutThumbHeight = 36; + trackWidth = 146; + trackHeight = 36; + break; + } + case DPIClassification.DPI_120: + { + layoutThumbWidth = 35; + layoutThumbHeight = 18; + trackWidth = 73; + trackHeight = 18; + break; + } + default: + { + layoutThumbWidth = 47; + layoutThumbHeight = 24; + trackWidth = 97; + trackHeight = 24; + break; + } + } + + selectedLabel = resourceManager.getString("components","toggleSwitchSelectedLabel"); + unselectedLabel = resourceManager.getString("components","toggleSwitchUnselectedLabel"); + } + + override protected function createChildren():void + { + super.createChildren(); + contents = new UIComponent(); + contents.blendMode = BlendMode.LAYER; + addChild(contents); + drawTrack(); + drawThumbs(); + drawLabel(); + } + + override protected function commitCurrentState():void + { + toggleSelectionState(); + layoutThumbs(); + layoutLabel(); + } + + //The label is called selectedLabelDisplay because the hostComponent expects it + protected function drawLabel():void + { + selectedLabelDisplay = new LabelDisplayComponent(); + selectedLabelDisplay.id = "selectedLabelDisplay"; + selectedLabelDisplay.text = selectedLabel; + setElementSize(selectedLabelDisplay,thumb.width,thumb.height); + contents.addChild(selectedLabelDisplay); + } + + //Draw the track behind everything else + protected function drawTrack():void + { + if(track == null) + { + track = new switchTrack(); + track.width = trackWidth; + track.height = trackHeight; + contents.addChildAt(SpriteVisualElement(track),0); + } + } + + //Draw both thumbs. Set skinpart thumb to be thumbOff because default state of the switch is OFF + protected function drawThumbs():void + { + drawThumbOff(); + drawThumbOn(); + if(thumb == null) + { + thumb = thumbOff; + } + } + + //Thumb ON the right side; Thumb OFF is on the left side + protected function layoutThumbs():void + { + setElementPosition(thumbOn,trackWidth/2,0); + setElementPosition(thumbOff,0,0); + } + + //Label display sould be at the same location as the thumb + protected function layoutLabel():void + { + if(selectedLabelDisplay != null) + { + if(currentState.indexOf("AndSelected") != -1) + { + setElementPosition(selectedLabelDisplay,trackWidth/2,0); + } + else + { + setElementPosition(selectedLabelDisplay,0,0); + } + } + } + + //Depending on current state, set skinpart thumb accordingly + protected function toggleSelectionState():void + { + if(currentState.indexOf("AndSelected") != -1) + { + thumbOn.visible = true; + thumbOff.visible = false; + thumb = thumbOn; + selectedLabelDisplay.text = selectedLabel; + } + else + { + thumbOff.visible = true; + thumbOn.visible = false; + thumb = thumbOff; + selectedLabelDisplay.text = unselectedLabel; + } + } + + protected function drawThumbOn():void + { + thumbOn = new switchOn(); + thumbOn.width = layoutThumbWidth; + thumbOn.height = layoutThumbHeight; + contents.addChildAt(SpriteVisualElement(thumbOn),1); + } + + protected function drawThumbOff():void + { + thumbOff = new switchOff(); + thumbOff.width = layoutThumbWidth; + thumbOff.height = layoutThumbHeight; + contents.addChildAt(SpriteVisualElement(thumbOff),1); + } + + //Hostcomponent dispatches this event whenever the thumb position changes + protected function thumbPositionChanged_handler(event:Event):void + { + moveSlidingContent(); + } + + //Move the current thumb and label along with the animating content + protected function moveSlidingContent():void + { + if (!hostComponent) + return; + var x:Number = (track.getLayoutBoundsWidth() - thumb.getLayoutBoundsWidth()) * + hostComponent.thumbPosition + track.getLayoutBoundsX(); + var y:Number = thumb.getLayoutBoundsY(); + setElementPosition(thumb, x, y); + setElementPosition(selectedLabelDisplay, x, y); + } + } +} + + +import flash.events.Event; + +import mx.core.UIComponent; +import mx.core.mx_internal; +import mx.events.FlexEvent; + +import spark.components.supportClasses.StyleableTextField; +import spark.core.IDisplayText; + +use namespace mx_internal; + +/** + * @private + * Component combining two labels to create the effect of text and its drop + * shadow. The component can be used with advanced style selectors and the + * styles "color", "textShadowColor", and "textShadowAlpha". Based off of + * ActionBar.TitleDisplayComponent. These two should eventually be factored. + */ +class LabelDisplayComponent extends UIComponent implements IDisplayText +{ + public var shadowYOffset:Number = 0; + private var labelChanged:Boolean = false; + private var labelDisplay:StyleableTextField; + private var labelDisplayShadow:StyleableTextField; + private var _text:String; + + public function LabelDisplayComponent() + { + super(); + _text = ""; + } + + override public function get baselinePosition():Number + { + return labelDisplay.baselinePosition; + } + + override protected function createChildren():void + { + super.createChildren(); + + labelDisplay = StyleableTextField(createInFontContext(StyleableTextField)); + labelDisplay.styleName = this; + labelDisplay.editable = false; + labelDisplay.selectable = false; + labelDisplay.multiline = false; + labelDisplay.wordWrap = false; + labelDisplay.addEventListener(FlexEvent.VALUE_COMMIT, + labelDisplay_valueCommitHandler); + + labelDisplayShadow = StyleableTextField(createInFontContext(StyleableTextField)); + labelDisplayShadow.styleName = this; + labelDisplayShadow.colorName = "textShadowColor"; + labelDisplayShadow.editable = false; + labelDisplayShadow.selectable = false; + labelDisplayShadow.multiline = false; + labelDisplayShadow.wordWrap = false; + + addChild(labelDisplayShadow); + addChild(labelDisplay); + } + + override protected function commitProperties():void + { + super.commitProperties(); + + if (labelChanged) + { + labelDisplay.text = text; + invalidateSize(); + invalidateDisplayList(); + labelChanged = false; + } + } + + override protected function measure():void + { + if (labelDisplay.isTruncated) + labelDisplay.text = text; + labelDisplay.commitStyles(); + measuredWidth = labelDisplay.getPreferredBoundsWidth(); + measuredHeight = labelDisplay.getPreferredBoundsHeight(); + } + + override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void + { + if (labelDisplay.isTruncated) + labelDisplay.text = text; + labelDisplay.commitStyles(); + + var labelHeight:Number = labelDisplay.getPreferredBoundsHeight(); + var labelY:Number = (unscaledHeight - labelHeight) / 2; + + var labelWidth:Number = Math.min(unscaledWidth, labelDisplay.getPreferredBoundsWidth()); + var labelX:Number = (unscaledWidth - labelWidth) / 2; + + labelDisplay.setLayoutBoundsSize(labelWidth, labelHeight); + labelDisplay.setLayoutBoundsPosition(labelX, labelY); + + labelDisplay.truncateToFit(); + + labelDisplayShadow.commitStyles(); + labelDisplayShadow.setLayoutBoundsSize(labelWidth, labelHeight); + labelDisplayShadow.setLayoutBoundsPosition(labelX, labelY + shadowYOffset); + + labelDisplayShadow.alpha = getStyle("textShadowAlpha"); + + // unless the label was truncated, labelDisplayShadow.text was set in + // the value commit handler + if (labelDisplay.isTruncated) + labelDisplayShadow.text = labelDisplay.text; + } + + private function labelDisplay_valueCommitHandler(event:Event):void + { + labelDisplayShadow.text = labelDisplay.text; + } + + public function get text():String + { + return _text; + } + + public function set text(value:String):void + { + _text = value; + labelChanged = true; + invalidateProperties(); + } + + public function get isTruncated():Boolean + { + return labelDisplay.isTruncated; + } + + public function showShadow(value:Boolean):void + { + labelDisplayShadow.visible = value; + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchBackground.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchBackground.fxg new file mode 100644 index 0000000000..b26457326f --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchBackground.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchThumb_off.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchThumb_off.fxg new file mode 100644 index 0000000000..8f9d046b9f --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchThumb_off.fxg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchThumb_on.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchThumb_on.fxg new file mode 100644 index 0000000000..d003f72cbf --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ToggleSwitchThumb_on.fxg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 18a5b5635477aadd4f994918ae86adba956042b4 Mon Sep 17 00:00:00 2001 From: Om Date: Wed, 9 Apr 2014 12:18:52 -0700 Subject: [PATCH 011/392] Added measure() method so that the component knows its size --- .../src/spark/skins/android4/ToggleSwitchSkin.as | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ToggleSwitchSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/ToggleSwitchSkin.as index 070481d888..c5b99aa35e 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/ToggleSwitchSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ToggleSwitchSkin.as @@ -233,6 +233,18 @@ package spark.skins.android4 drawLabel(); } + override protected function measure():void + { + // The skin must be at least as large as the thumb + measuredMinWidth = layoutThumbWidth; + measuredMinHeight = layoutThumbHeight; + + // The preferred size will display all label text + var labelWidth:Number = getElementPreferredWidth(selectedLabelDisplay); + measuredWidth = layoutThumbWidth + labelWidth; + measuredHeight = layoutThumbHeight; + } + override protected function commitCurrentState():void { toggleSelectionState(); From 79f528b3e5aa9b15b01ef19706f07f05f79db3a2 Mon Sep 17 00:00:00 2001 From: Om Date: Wed, 9 Apr 2014 17:02:27 -0700 Subject: [PATCH 012/392] Android 4.x specific skins for ButtonBar component; Uses same FXG assets for all DPI --- .../android4/ButtonBarFirstButtonSkin.as | 93 ++++++++++ .../android4/ButtonBarMiddleButtonSkin.as | 95 ++++++++++ .../src/spark/skins/android4/ButtonBarSkin.as | 166 ++++++++++++++++++ .../assets/ButtonBarFirstButton_down.fxg | 34 ++++ .../ButtonBarFirstButton_selectedDown.fxg | 39 ++++ .../ButtonBarFirstButton_selectedUp.fxg | 34 ++++ .../assets/ButtonBarFirstButton_up.fxg | 29 +++ .../assets/ButtonBarMiddleButton_down.fxg | 70 ++++++++ .../ButtonBarMiddleButton_selectedDown.fxg | 75 ++++++++ .../ButtonBarMiddleButton_selectedUp.fxg | 70 ++++++++ .../assets/ButtonBarMiddleButton_up.fxg | 65 +++++++ 11 files changed, 770 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarFirstButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarMiddleButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_selectedDown.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_selectedUp.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_up.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_selectedDown.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_selectedUp.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_up.fxg diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarFirstButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarFirstButtonSkin.as new file mode 100644 index 0000000000..4b8cbba095 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarFirstButtonSkin.as @@ -0,0 +1,93 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.skins.android4.assets.ButtonBarFirstButton_selectedDown; + import spark.skins.android4.assets.ButtonBarFirstButton_selectedUp; + import spark.skins.mobile.supportClasses.ButtonBarButtonSkinBase; + + /** + * Android 4.x specific Button skin for the first Button in a ButtonBar. + * + * @see spark.components.ButtonBar#firstButton + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class ButtonBarFirstButtonSkin extends ButtonBarButtonSkinBase + { + + /** + * Class to use for the border in the selected and down state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var selectedDownBorderSkin:Class; + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ButtonBarFirstButtonSkin() + { + super(); + + upBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_up; + downBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_down; + selectedBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_selectedUp; + selectedDownBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_selectedDown; + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + //Dont draw background + } + + override protected function getBorderClassForCurrentState():Class + { + var isSelected:Boolean = currentState.indexOf("Selected") >= 0; + var isDown:Boolean = currentState.indexOf("down") >= 0; + + if (isSelected && !isDown ) + return selectedBorderSkin; + else if (isSelected && isDown) + return selectedDownBorderSkin; + else if (!isSelected && !isDown) + return upBorderSkin; + else + return downBorderSkin; + } + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarMiddleButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarMiddleButtonSkin.as new file mode 100644 index 0000000000..c6206d0f42 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarMiddleButtonSkin.as @@ -0,0 +1,95 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.skins.android4.assets.ButtonBarMiddleButton_selectedDown; + import spark.skins.android4.assets.ButtonBarMiddleButton_selectedUp; + import spark.skins.android4.assets.ButtonBarMiddleButton_up; + import spark.skins.mobile.supportClasses.ButtonBarButtonSkinBase; + + /** + * Android 4.x specific Button skin for middle Buttons in a ButtonBar. + * + * @see spark.components.ButtonBar#middleButton + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class ButtonBarMiddleButtonSkin extends ButtonBarButtonSkinBase + { + + /** + * Class to use for the border in the selected and down state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var selectedDownBorderSkin:Class; + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ButtonBarMiddleButtonSkin() + { + super(); + + upBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_up; + downBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_down; + selectedBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_selectedUp; + selectedDownBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_selectedDown; + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + //Dont draw background + } + + override protected function getBorderClassForCurrentState():Class + { + var isSelected:Boolean = currentState.indexOf("Selected") >= 0; + var isDown:Boolean = currentState.indexOf("down") >= 0; + + if (isSelected && !isDown ) + return selectedBorderSkin; + else if (isSelected && isDown) + return selectedDownBorderSkin; + else if (!isSelected && !isDown) + return upBorderSkin; + else + return downBorderSkin; + } + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarSkin.as new file mode 100644 index 0000000000..4b194fdd74 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ButtonBarSkin.as @@ -0,0 +1,166 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.ButtonBar; +import spark.components.ButtonBarButton; +import spark.components.DataGroup; +import spark.components.supportClasses.ButtonBarHorizontalLayout; +import spark.skins.android4.ButtonBarFirstButtonSkin; +import spark.skins.mobile.ButtonBarLastButtonSkin; +import spark.skins.mobile.supportClasses.ButtonBarButtonClassFactory; +import spark.skins.mobile.supportClasses.MobileSkin; + +/** + * The Android 4.x specific skin class for the Spark ButtonBar component. + * + * @see spark.components.ButtonBar + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class ButtonBarSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function ButtonBarSkin() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:ButtonBar; + + /** + * @copy spark.components.ButtonBar#firstButton + */ + public var firstButton:ButtonBarButtonClassFactory; + + /** + * @copy spark.components.ButtonBar#lastButton + */ + public var lastButton:ButtonBarButtonClassFactory; + + /** + * @copy spark.components.ButtonBar#middleButton + */ + public var middleButton:ButtonBarButtonClassFactory; + + /** + * @copy spark.components.SkinnableDataContainer#dataGroup + */ + public var dataGroup:DataGroup; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + // Set up the class factories for the buttons + 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; + } + + // create the data group to house the buttons + if (!dataGroup) + { + dataGroup = new DataGroup(); + var hLayout:ButtonBarHorizontalLayout = new ButtonBarHorizontalLayout(); + hLayout.gap = 0; + dataGroup.layout = hLayout; + addChild(dataGroup); + } + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + alpha = (currentState == "disabled") ? 0.5 : 1; + } + + /** + * @private + */ + override protected function measure():void + { + measuredWidth = dataGroup.measuredWidth; + measuredHeight = dataGroup.measuredHeight; + + measuredMinWidth = dataGroup.measuredMinWidth; + measuredMinHeight = dataGroup.measuredMinHeight; + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + setElementPosition(dataGroup, 0, 0); + setElementSize(dataGroup, unscaledWidth, unscaledHeight); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_down.fxg new file mode 100644 index 0000000000..4a7c5f7f2d --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_down.fxg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_selectedDown.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_selectedDown.fxg new file mode 100644 index 0000000000..110f940605 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_selectedDown.fxg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_selectedUp.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_selectedUp.fxg new file mode 100644 index 0000000000..52b1a90b44 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_selectedUp.fxg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_up.fxg new file mode 100644 index 0000000000..59b9cd9144 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarFirstButton_up.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_down.fxg new file mode 100644 index 0000000000..4e4f80b7af --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_down.fxg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_selectedDown.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_selectedDown.fxg new file mode 100644 index 0000000000..ec3707782d --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_selectedDown.fxg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_selectedUp.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_selectedUp.fxg new file mode 100644 index 0000000000..88a46439a8 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_selectedUp.fxg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_up.fxg new file mode 100644 index 0000000000..be1b61922d --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/ButtonBarMiddleButton_up.fxg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From f4c6c0df3ab30ea8fb21132ade698c5f1911fd74 Mon Sep 17 00:00:00 2001 From: Om Date: Thu, 10 Apr 2014 13:11:24 -0700 Subject: [PATCH 013/392] Android 4.x skin for TabbedViewNavigatorTabBar. This ensures that the TabbedViewNavigatorApplication gets the new skin as well. --- .../android4/TabbedViewNavigatorTabBarSkin.as | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/TabbedViewNavigatorTabBarSkin.as diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/TabbedViewNavigatorTabBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/TabbedViewNavigatorTabBarSkin.as new file mode 100644 index 0000000000..eea5948eea --- /dev/null +++ b/frameworks/projects/mobiletheme/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 From 3ca6855fa21850310642e2dad097aa3773079273 Mon Sep 17 00:00:00 2001 From: Om Date: Thu, 10 Apr 2014 16:18:54 -0700 Subject: [PATCH 014/392] The css file that drives all the new skins for Android 4.x theme --- frameworks/projects/mobiletheme/android4x.css | 281 ++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 frameworks/projects/mobiletheme/android4x.css diff --git a/frameworks/projects/mobiletheme/android4x.css b/frameworks/projects/mobiletheme/android4x.css new file mode 100644 index 0000000000..fee709ccaf --- /dev/null +++ b/frameworks/projects/mobiletheme/android4x.css @@ -0,0 +1,281 @@ +/* + * + * 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. + * + */ + +@namespace "library://ns.adobe.com/flex/spark"; + + +/* +Android 4.x specific Mobile Flex skins and styles. Defaults sizes are based on +application-dpi=240. +*/ + +/*Roboto Fonts*/ +@font-face { + src: url("/spark/skins/android4/assets/fonts/Roboto-Regular.ttf"); + fontFamily: RobotoRegular; + embed-as-cff: false; +} + +@font-face { + src: url("/spark/skins/android4/assets/fonts/Roboto-Bold.ttf"); + fontFamily: RobotoBold; + fontWeight: bold; + embed-as-cff: false; +} + +global +{ + fontFamily: RobotoRegular; + fontWeight: "normal"; + fontSize: 24; + color: #000000; + textShadowColor: #FFFFFF; + textShadowAlpha: 0; +} + +ActionBar +{ + chromeColor: #DEDEDD; + defaultButtonAppearance: normal; + skinClass: ClassReference("spark.skins.android4.ActionBarSkin"); + textShadowAlpha: 0; + textShadowColor: #000000; + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; +} + +ActionBar #titleDisplay +{ + color: #333333; + fontSize: 24; + fontFamily: RobotoBold; +} + +ActionBar ButtonBase +{ + color: #333333; + fontFamily: RobotoBold; + fontWeight: bold; +} + +ActionBar Group#actionGroup Button +{ + skinClass: ClassReference("spark.skins.android4.TransparentActionButtonSkin"); +} + +Button +{ + fontFamily: RobotoBold; + fontWeight: "bold"; + skinClass: ClassReference("spark.skins.android4.ButtonSkin"); +} + +Button.emphasized +{ + skinClass: ClassReference("spark.skins.mobile.DefaultButtonSkin"); +} + +ButtonBar +{ + skinClass: ClassReference("spark.skins.android4.ButtonBarSkin"); + fontFamily: RobotoBold; + fontWeight: "bold"; +} + +CheckBox +{ + skinClass: ClassReference("spark.skins.android4.CheckBoxSkin"); + chromeColor: #FFFFFF; +} + +RadioButton +{ + skinClass: ClassReference("spark.skins.android4.RadioButtonSkin"); + chromeColor: #FFFFFF; +} + +TabbedViewNavigator #tabBar +{ + fontFamily: RobotoRegular; + chromeColor: #484848; + color: #000000; + fontSize: 20; + fontWeight: normal; + iconPlacement: top; + interactionMode: mouse; + skinClass: ClassReference("spark.skins.android4.TabbedViewNavigatorTabBarSkin"); + textShadowAlpha: 0; + textShadowColor: #000000; +} + +ToggleSwitch +{ + accentColor: #3F7FBA; + color: #FFFFFF; + skinClass: ClassReference("spark.skins.android4.ToggleSwitchSkin"); + slideDuration: 125; + textShadowAlpha: .65; + textShadowColor: #000000; +} + +@media (application-dpi: 120) +{ + global + { + fontSize: 12; + focusThickness: 2; + } + + ActionBar.beveled + { + paddingLeft: 4; + paddingRight: 4; + } + + ActionBar #titleDisplay + { + fontSize: 14; + } + + TabbedViewNavigator #tabBar + { + fontSize: 11; + } +} + +@media (application-dpi: 160) +{ + global + { + fontSize: 16; + focusThickness: 2; + } + + ActionBar + { + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; + } + + ActionBar #titleDisplay + { + fontSize: 18; + } + + TabbedViewNavigator #tabBar + { + fontSize: 14; + } +} + +@media (application-dpi: 240) +{ + global + { + fontSize: 20; + } + + ActionBar #titleDisplay + { + fontSize: 24; + } +} + +@media (application-dpi: 320) +{ + global + { + fontSize: 32; + } + + ActionBar + { + paddingBottom: 2; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 2; + } + + ActionBar #titleDisplay + { + fontSize: 36; + } + + TabbedViewNavigator #tabBar + { + fontSize: 28; + } +} + +@media (application-dpi: 480) +{ + global + { + fontSize: 48; + focusThickness: 6; + } + + ActionBar + { + paddingBottom: 4; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 4; + } + + ActionBar #titleDisplay + { + fontSize: 54; + } + + TabbedViewNavigator #tabBar + { + fontSize: 42; + } +} + +@media (application-dpi: 640) +{ + global + { + fontSize: 64; + } + + ActionBar + { + paddingBottom: 4; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 4; + } + + ActionBar #titleDisplay + { + fontSize: 72; + } + + TabbedViewNavigator #tabBar + { + fontSize: 56; + } +} From b7ba72cea950839d6a31a991367b826d1d4c4b7a Mon Sep 17 00:00:00 2001 From: Om Date: Mon, 14 Apr 2014 17:49:11 -0700 Subject: [PATCH 015/392] New Android 4.x specific HSliderSkin --- frameworks/projects/mobiletheme/android4x.css | 5 + .../src/spark/skins/android4/HSliderSkin.as | 236 +++++++++++ .../spark/skins/android4/HSliderThumbSkin.as | 400 ++++++++++++++++++ .../spark/skins/android4/HSliderTrackSkin.as | 227 ++++++++++ .../android4/assets/HSliderThumb_normal.fxg | 34 ++ .../android4/assets/HSliderThumb_pressed.fxg | 39 ++ .../skins/android4/assets/HSliderTrack.fxg | 29 ++ .../android4/assets/HSliderTrack_filled.fxg | 29 ++ 8 files changed, 999 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderThumbSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderTrackSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderThumb_normal.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderThumb_pressed.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderTrack.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderTrack_filled.fxg diff --git a/frameworks/projects/mobiletheme/android4x.css b/frameworks/projects/mobiletheme/android4x.css index fee709ccaf..80a55a043a 100644 --- a/frameworks/projects/mobiletheme/android4x.css +++ b/frameworks/projects/mobiletheme/android4x.css @@ -106,6 +106,11 @@ CheckBox chromeColor: #FFFFFF; } +HSlider +{ + skinClass: ClassReference("spark.skins.android4.HSliderSkin"); +} + RadioButton { skinClass: ClassReference("spark.skins.android4.RadioButtonSkin"); diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as new file mode 100644 index 0000000000..c07aef67e6 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as @@ -0,0 +1,236 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.BlendMode; + +import mx.core.ClassFactory; +import mx.core.IFactory; + +import spark.components.Button; +import spark.components.HSlider; +import spark.skins.mobile.supportClasses.HSliderDataTip; +import spark.skins.mobile.supportClasses.MobileSkin; + +/** + * ActionScript-based skin for HSlider controls in mobile applications. + * + *

The base Flex implementation creates an HSlider with fixed height + * and variable width with a fixed-size thumb. As the height of the + * HSlider component increases, the vertical dimensions of the visible HSlider remain + * the same, and the HSlider stays vertically centered.

+ * + *

The thumb and track implementations can be customized by subclassing + * this skin class and overriding the thumbSkinClass, trackSkinClass, + * and/or dataTipClass variables as necessary.

+ * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class HSliderSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function HSliderSkin() + { + super(); + + thumbSkinClass = spark.skins.android4.HSliderThumbSkin; + trackSkinClass = spark.skins.android4.HSliderTrackSkin; + dataTipClass = spark.skins.mobile.supportClasses.HSliderDataTip; + + blendMode = BlendMode.LAYER; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:HSlider; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * HSlider track skin part + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var track:Button; + + /** + * HSlider thumb skin part + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var thumb:Button; + + /** + * HSlider dataTip class factory + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var dataTip:IFactory; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * Specifies the skin class that will be used for the HSlider thumb. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbSkinClass:Class; + + /** + * Specifies the skin class that will be used for the HSlider track. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackSkinClass:Class; + + /** + * Specifies the class that will be used for the HSlider datatip. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var dataTipClass:Class; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function commitCurrentState():void + { + if (currentState == "disabled") + alpha = 0.5; + else if (currentState == "normal") + alpha = 1; + } + + /** + * @private + */ + override protected function createChildren():void + { + // Create our skin parts: track and thumb + track = new Button(); + track.setStyle("skinClass", trackSkinClass); + addChild(track); + + thumb = new Button(); + thumb.setStyle("skinClass", thumbSkinClass); + addChild(thumb); + + // Set up the class factory for the dataTip + dataTip = new ClassFactory(); + ClassFactory(dataTip).generator = dataTipClass; + } + + /** + * @private + * The HSliderSkin width will be no less than the width of the thumb skin. + * The HSliderSkin height will be no less than the greater of the heights of + * the thumb and track skins. + */ + override protected function measure():void + { + measuredWidth = track.getPreferredBoundsWidth(); + measuredHeight = Math.max(track.getPreferredBoundsHeight(), thumb.getPreferredBoundsHeight()); + + measuredMinHeight = Math.max(track.getPreferredBoundsHeight(), thumb.getPreferredBoundsHeight()); + measuredMinWidth = thumb.getPreferredBoundsWidth(); + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + // minimum height is no smaller than the larger of the thumb or track + var calculatedSkinHeight:int = Math.max(Math.max(thumb.getPreferredBoundsHeight(), track.getPreferredBoundsHeight()), + unscaledHeight); + + // minimum width is no smaller than the thumb + var calculatedSkinWidth:int = Math.max(thumb.getPreferredBoundsWidth(), + unscaledWidth); + + // once we know the skin height, center the thumb and track + thumb.y = Math.max(Math.round((calculatedSkinHeight - thumb.getPreferredBoundsHeight()) / 2), 0); + var calculatedTrackY:int = Math.max(Math.round((calculatedSkinHeight - track.getPreferredBoundsHeight()) / 2), 0); + + // size and position + setElementSize(thumb, thumb.getPreferredBoundsWidth(), thumb.getPreferredBoundsHeight()); // thumb does NOT scale + setElementSize(track, calculatedSkinWidth, track.getPreferredBoundsHeight()); // note track is NOT scaled vertically + setElementPosition(track, 0, calculatedTrackY); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderThumbSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderThumbSkin.as new file mode 100644 index 0000000000..ae5309b3d9 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderThumbSkin.as @@ -0,0 +1,400 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + +import mx.core.DPIClassification; + +import spark.components.Button; +import spark.skins.android4.assets.HSliderThumb_normal; +import spark.skins.mobile.supportClasses.MobileSkin; + +/** + * ActionScript-based skin for the HSlider thumb skin part in mobile applications. + * + *

Note that this particular implementation defines a hit zone which is larger than + * the visible thumb for better usability on mobile screens.

+ * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class HSliderThumbSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function HSliderThumbSkin() + { + super(); + + thumbNormalClass = spark.skins.android4.assets.HSliderThumb_normal; + thumbPressedClass = spark.skins.android4.assets.HSliderThumb_pressed; + + // set the dimensions to use based on the screen density + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + thumbImageWidth = 116; + thumbImageHeight = 116; + + hitZoneOffset = 20; + hitZoneSideLength = 160; + + // chromeColor ellipse goes up to the thumb border + chromeColorEllipseWidth = chromeColorEllipseHeight = 112; + chromeColorEllipseX = 1; + chromeColorEllipseY = 1; + + break; + } + case DPIClassification.DPI_480: + { + // Note provisional may need changes + thumbImageWidth = 88; + thumbImageHeight = 88; + + hitZoneOffset = 20; + hitZoneSideLength = 130; + + // chromeColor ellipse goes up to the thumb border + chromeColorEllipseWidth = chromeColorEllipseHeight = 84; + chromeColorEllipseX = chromeColorEllipseY = 1; + + break; + } + case DPIClassification.DPI_320: + { + thumbImageWidth = 58; + thumbImageHeight = 58; + + hitZoneOffset = 10; + hitZoneSideLength = 80; + + // chromeColor ellipse goes up to the thumb border + chromeColorEllipseWidth = chromeColorEllipseHeight = 56; + chromeColorEllipseX = 1; + chromeColorEllipseY = 1; + + break; + } + case DPIClassification.DPI_240: + { + thumbImageWidth = 44; + thumbImageHeight = 44; + + hitZoneOffset = 10; + hitZoneSideLength = 65; + + // chromeColor ellipse goes up to the thumb border + chromeColorEllipseWidth = chromeColorEllipseHeight = 42; + chromeColorEllipseX = chromeColorEllipseY = 1; + + break; + } + case DPIClassification.DPI_120: + { + thumbImageWidth = 22; + thumbImageHeight = 22; + + hitZoneOffset = 5; + hitZoneSideLength = 33; + + // chromeColor ellipse goes up to the thumb border + chromeColorEllipseWidth = chromeColorEllipseHeight = 21; + chromeColorEllipseX = chromeColorEllipseY = 1; + + break; + } + default: + { + // default DPI_160 + thumbImageWidth = 29; + thumbImageHeight = 29; + + hitZoneOffset = 5; + hitZoneSideLength = 40; + + // chromeColor ellipse goes up to the thumb border + chromeColorEllipseWidth = chromeColorEllipseHeight = 29; + chromeColorEllipseX = chromeColorEllipseY = 0; + + break; + } + + } + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:Button; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + // FXG thumb classes + /** + * Specifies the FXG class to use when the thumb is in the normal state + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbNormalClass:Class; + + /** + * Specifies the FXG class to use when the thumb is in the pressed state + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbPressedClass:Class; + + /** + * Specifies the DisplayObject to use when the thumb is in the normal state + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbSkin_normal:DisplayObject; + + /** + * Specifies the DisplayObject to use when the thumb is in the pressed state + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbSkin_pressed:DisplayObject; + + /** + * Specifies the current DisplayObject that should be shown + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var currentThumbSkin:DisplayObject; + + /** + * Width of the overall thumb image + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbImageWidth:int; + + /** + * Height of the overall thumb image + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbImageHeight:int; + + /** + * Width of the chromeColor ellipse + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var chromeColorEllipseWidth:int; + + /** + * Height of the chromeColor ellipse + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var chromeColorEllipseHeight:int; + + + /** + * X position of the chromeColor ellipse + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var chromeColorEllipseX:int; + + /** + * Y position of the chromeColor ellipse + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var chromeColorEllipseY:int; + + /** + * Length of the sizes of the hitzone (assumed to be square) + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var hitZoneSideLength:int; + + /** + * Distance between the left edge of the hitzone and the left edge + * of the thumb + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var hitZoneOffset:int; + + /** + * @private + * Remember which state is currently being displayed + */ + private var displayedState:String; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function commitCurrentState():void + { + if (currentState == "up") + { + // show the normal button + if (!thumbSkin_normal) + { + thumbSkin_normal = new thumbNormalClass(); + addChild(thumbSkin_normal); + } + else + { + thumbSkin_normal.visible = true; + } + currentThumbSkin = thumbSkin_normal; + + // hide the pressed button + if (thumbSkin_pressed) + thumbSkin_pressed.visible = false; + } + else if (currentState == "down") + { + // show the pressed button + if (!thumbSkin_pressed) + { + thumbSkin_pressed = new thumbPressedClass(); + addChild(thumbSkin_pressed); + } + else + { + thumbSkin_pressed.visible = true; + } + currentThumbSkin = thumbSkin_pressed; + + // hide the normal button + if (thumbSkin_normal) + thumbSkin_normal.visible = false; + } + + displayedState = currentState; + + invalidateDisplayList(); + } + + /** + * @private + */ + override protected function measure():void + { + measuredWidth = thumbImageWidth; + measuredHeight = thumbImageHeight; + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + setElementSize(currentThumbSkin, unscaledWidth, unscaledHeight); + setElementPosition(currentThumbSkin, 0, 0) + } + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + // put in a larger hit zone than the thumb + graphics.beginFill(0xffffff, 0); + graphics.drawRect(-hitZoneOffset, -hitZoneOffset, hitZoneSideLength, hitZoneSideLength); + graphics.endFill(); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderTrackSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderTrackSkin.as new file mode 100644 index 0000000000..d1cc29c8e5 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderTrackSkin.as @@ -0,0 +1,227 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + + import mx.core.DPIClassification; + + import spark.components.Button; + import spark.skins.android4.assets.HSliderTrack; + import spark.skins.mobile.supportClasses.MobileSkin; + + /** + * ActionScript-based skin for the HSlider track skin part in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class HSliderTrackSkin extends MobileSkin + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function HSliderTrackSkin() + { + super(); + + trackClass = spark.skins.android4.assets.HSliderTrack; + + // set the right dimensions to use based on the screen density + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + trackWidth = 1200; + trackHeight = 8; + + visibleTrackOffset = 48; + + break; + } + case DPIClassification.DPI_480: + { + trackWidth = 900; + trackHeight = 6; + + visibleTrackOffset = 38; + + break; + } + case DPIClassification.DPI_320: + { + trackWidth = 600; + trackHeight = 4; + + visibleTrackOffset = 24; + + break; + } + case DPIClassification.DPI_240: + { + trackWidth = 450; + trackHeight = 3; + + visibleTrackOffset = 18; + + break; + } + case DPIClassification.DPI_120: + { + trackWidth = 225; + trackHeight = 2; + + visibleTrackOffset = 9; + + break; + } + default: + { + // default DPI_160 + trackWidth = 300; + trackHeight = 2; + + visibleTrackOffset = 12; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:Button; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * Specifies the FXG class to use for the track image + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackClass:Class; + + /** + * Specifies the DisplayObject for the track image + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackSkin:DisplayObject; + + /** + * Specifies the track image width + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackWidth:int; + + /** + * Specifies the track image height + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackHeight:int; + + /** + * Specifies the offset from the left and right edge to where + * the visible track begins. This should match the offset in the FXG assets. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var visibleTrackOffset:int; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + trackSkin = new trackClass(); + addChild(trackSkin); + } + + /** + * @private + */ + override protected function measure():void + { + measuredWidth = trackWidth; + measuredHeight = trackHeight; + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + var unscaledTrackWidth:int = unscaledWidth - (2 * visibleTrackOffset); + setElementSize(trackSkin, unscaledTrackWidth, unscaledHeight); + setElementPosition(trackSkin, visibleTrackOffset, 0); + } + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderThumb_normal.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderThumb_normal.fxg new file mode 100644 index 0000000000..c046673755 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderThumb_normal.fxg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderThumb_pressed.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderThumb_pressed.fxg new file mode 100644 index 0000000000..416f7950fc --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderThumb_pressed.fxg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderTrack.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderTrack.fxg new file mode 100644 index 0000000000..ee464e0b16 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderTrack.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderTrack_filled.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderTrack_filled.fxg new file mode 100644 index 0000000000..c4d4f74bf5 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/HSliderTrack_filled.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file From fb29d5753e5de5e1b0d7bd2c9fd34f8fd1c8f823 Mon Sep 17 00:00:00 2001 From: Om Date: Mon, 14 Apr 2014 18:06:13 -0700 Subject: [PATCH 016/392] Fix tooltip for HSlider --- frameworks/projects/mobiletheme/android4x.css | 2 ++ .../src/spark/skins/mobile/supportClasses/HSliderDataTip.as | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/frameworks/projects/mobiletheme/android4x.css b/frameworks/projects/mobiletheme/android4x.css index 80a55a043a..790a6553b1 100644 --- a/frameworks/projects/mobiletheme/android4x.css +++ b/frameworks/projects/mobiletheme/android4x.css @@ -109,6 +109,8 @@ CheckBox HSlider { skinClass: ClassReference("spark.skins.android4.HSliderSkin"); + fontFamily: RobotoBold; + fontWeight: bold; } RadioButton diff --git a/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/HSliderDataTip.as b/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/HSliderDataTip.as index 394d8d759c..2da3213b9e 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/HSliderDataTip.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/HSliderDataTip.as @@ -29,7 +29,6 @@ import mx.core.UIComponent; import mx.core.mx_internal; import spark.components.Application; -import spark.components.HSlider; import spark.components.supportClasses.StyleableTextField; use namespace mx_internal; @@ -191,9 +190,8 @@ public class HSliderDataTip extends UIComponent implements IDataRenderer switch (applicationDPI) { - case DPIClassification.DPI_320: + case DPIClassification.DPI_640: { - // Note provisional may need changes fontSize = "60"; leftTextPadding = 28; leftRightTextPadding = 56; @@ -205,7 +203,6 @@ public class HSliderDataTip extends UIComponent implements IDataRenderer } case DPIClassification.DPI_480: { - // Note provisional may need changes fontSize = "40"; leftTextPadding = 22; leftRightTextPadding = 44; @@ -239,7 +236,6 @@ public class HSliderDataTip extends UIComponent implements IDataRenderer } case DPIClassification.DPI_120: { - // Note provisional may need changes fontSize = "10"; leftTextPadding = 6; leftRightTextPadding = 11; From 323b044e1dd07c154372ff3c468f7c6f1555c803 Mon Sep 17 00:00:00 2001 From: Om Date: Mon, 14 Apr 2014 18:07:08 -0700 Subject: [PATCH 017/392] Remove unused variables and clean up comments --- .../src/spark/skins/android4/HSliderSkin.as | 2 +- .../spark/skins/android4/HSliderThumbSkin.as | 69 +------------------ 2 files changed, 2 insertions(+), 69 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as index c07aef67e6..7dd8c7421f 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as @@ -30,7 +30,7 @@ import spark.skins.mobile.supportClasses.HSliderDataTip; import spark.skins.mobile.supportClasses.MobileSkin; /** - * ActionScript-based skin for HSlider controls in mobile applications. + * Android 4.x specific ActionScript-based skin for HSlider controls in mobile applications. * *

The base Flex implementation creates an HSlider with fixed height * and variable width with a fixed-size thumb. As the height of the diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderThumbSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderThumbSkin.as index ae5309b3d9..517ff930fb 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderThumbSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderThumbSkin.as @@ -28,7 +28,7 @@ import spark.skins.android4.assets.HSliderThumb_normal; import spark.skins.mobile.supportClasses.MobileSkin; /** - * ActionScript-based skin for the HSlider thumb skin part in mobile applications. + * Android 4.x specific ActionScript-based skin for the HSlider thumb skin part in mobile applications. * *

Note that this particular implementation defines a hit zone which is larger than * the visible thumb for better usability on mobile screens.

@@ -73,11 +73,6 @@ public class HSliderThumbSkin extends MobileSkin hitZoneOffset = 20; hitZoneSideLength = 160; - // chromeColor ellipse goes up to the thumb border - chromeColorEllipseWidth = chromeColorEllipseHeight = 112; - chromeColorEllipseX = 1; - chromeColorEllipseY = 1; - break; } case DPIClassification.DPI_480: @@ -89,10 +84,6 @@ public class HSliderThumbSkin extends MobileSkin hitZoneOffset = 20; hitZoneSideLength = 130; - // chromeColor ellipse goes up to the thumb border - chromeColorEllipseWidth = chromeColorEllipseHeight = 84; - chromeColorEllipseX = chromeColorEllipseY = 1; - break; } case DPIClassification.DPI_320: @@ -103,11 +94,6 @@ public class HSliderThumbSkin extends MobileSkin hitZoneOffset = 10; hitZoneSideLength = 80; - // chromeColor ellipse goes up to the thumb border - chromeColorEllipseWidth = chromeColorEllipseHeight = 56; - chromeColorEllipseX = 1; - chromeColorEllipseY = 1; - break; } case DPIClassification.DPI_240: @@ -118,10 +104,6 @@ public class HSliderThumbSkin extends MobileSkin hitZoneOffset = 10; hitZoneSideLength = 65; - // chromeColor ellipse goes up to the thumb border - chromeColorEllipseWidth = chromeColorEllipseHeight = 42; - chromeColorEllipseX = chromeColorEllipseY = 1; - break; } case DPIClassification.DPI_120: @@ -132,10 +114,6 @@ public class HSliderThumbSkin extends MobileSkin hitZoneOffset = 5; hitZoneSideLength = 33; - // chromeColor ellipse goes up to the thumb border - chromeColorEllipseWidth = chromeColorEllipseHeight = 21; - chromeColorEllipseX = chromeColorEllipseY = 1; - break; } default: @@ -147,10 +125,6 @@ public class HSliderThumbSkin extends MobileSkin hitZoneOffset = 5; hitZoneSideLength = 40; - // chromeColor ellipse goes up to the thumb border - chromeColorEllipseWidth = chromeColorEllipseHeight = 29; - chromeColorEllipseX = chromeColorEllipseY = 0; - break; } @@ -245,47 +219,6 @@ public class HSliderThumbSkin extends MobileSkin */ protected var thumbImageHeight:int; - /** - * Width of the chromeColor ellipse - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - protected var chromeColorEllipseWidth:int; - - /** - * Height of the chromeColor ellipse - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - protected var chromeColorEllipseHeight:int; - - - /** - * X position of the chromeColor ellipse - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - protected var chromeColorEllipseX:int; - - /** - * Y position of the chromeColor ellipse - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - protected var chromeColorEllipseY:int; - /** * Length of the sizes of the hitzone (assumed to be square) * From 81da04b069ba021e13083549f446c6ddd22ff695 Mon Sep 17 00:00:00 2001 From: Om Date: Tue, 15 Apr 2014 16:35:19 -0700 Subject: [PATCH 018/392] Added a fill layer on top of track skin to indicate current value as a filled area This layer updates its size based on the current location of the thumb --- .../src/spark/skins/android4/HSliderSkin.as | 486 ++++++++++-------- .../spark/skins/android4/HSliderTrackSkin.as | 2 +- 2 files changed, 273 insertions(+), 215 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as index 7dd8c7421f..66ee390381 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as @@ -19,218 +19,276 @@ package spark.skins.android4 { -import flash.display.BlendMode; - -import mx.core.ClassFactory; -import mx.core.IFactory; - -import spark.components.Button; -import spark.components.HSlider; -import spark.skins.mobile.supportClasses.HSliderDataTip; -import spark.skins.mobile.supportClasses.MobileSkin; - -/** - * Android 4.x specific ActionScript-based skin for HSlider controls in mobile applications. - * - *

The base Flex implementation creates an HSlider with fixed height - * and variable width with a fixed-size thumb. As the height of the - * HSlider component increases, the vertical dimensions of the visible HSlider remain - * the same, and the HSlider stays vertically centered.

- * - *

The thumb and track implementations can be customized by subclassing - * this skin class and overriding the thumbSkinClass, trackSkinClass, - * and/or dataTipClass variables as necessary.

- * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ -public class HSliderSkin extends MobileSkin -{ - //-------------------------------------------------------------------------- - // - // Constructor - // - //-------------------------------------------------------------------------- - - /** - * Constructor. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - * - */ - public function HSliderSkin() - { - super(); - - thumbSkinClass = spark.skins.android4.HSliderThumbSkin; - trackSkinClass = spark.skins.android4.HSliderTrackSkin; - dataTipClass = spark.skins.mobile.supportClasses.HSliderDataTip; - - blendMode = BlendMode.LAYER; - } - - //-------------------------------------------------------------------------- - // - // Properties - // - //-------------------------------------------------------------------------- - - /** - * @copy spark.skins.spark.ApplicationSkin#hostComponent - */ - public var hostComponent:HSlider; - - //-------------------------------------------------------------------------- - // - // Skin parts - // - //-------------------------------------------------------------------------- - - /** - * HSlider track skin part - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - public var track:Button; - - /** - * HSlider thumb skin part - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - public var thumb:Button; - - /** - * HSlider dataTip class factory - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - public var dataTip:IFactory; - - //-------------------------------------------------------------------------- - // - // Variables - // - //-------------------------------------------------------------------------- - - /** - * Specifies the skin class that will be used for the HSlider thumb. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - protected var thumbSkinClass:Class; - - /** - * Specifies the skin class that will be used for the HSlider track. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - protected var trackSkinClass:Class; - - /** - * Specifies the class that will be used for the HSlider datatip. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - protected var dataTipClass:Class; - - //-------------------------------------------------------------------------- - // - // Overridden methods - // - //-------------------------------------------------------------------------- - - /** - * @private - */ - override protected function commitCurrentState():void - { - if (currentState == "disabled") - alpha = 0.5; - else if (currentState == "normal") - alpha = 1; - } - - /** - * @private - */ - override protected function createChildren():void - { - // Create our skin parts: track and thumb - track = new Button(); - track.setStyle("skinClass", trackSkinClass); - addChild(track); - - thumb = new Button(); - thumb.setStyle("skinClass", thumbSkinClass); - addChild(thumb); - - // Set up the class factory for the dataTip - dataTip = new ClassFactory(); - ClassFactory(dataTip).generator = dataTipClass; - } - - /** - * @private - * The HSliderSkin width will be no less than the width of the thumb skin. - * The HSliderSkin height will be no less than the greater of the heights of - * the thumb and track skins. - */ - override protected function measure():void - { - measuredWidth = track.getPreferredBoundsWidth(); - measuredHeight = Math.max(track.getPreferredBoundsHeight(), thumb.getPreferredBoundsHeight()); - - measuredMinHeight = Math.max(track.getPreferredBoundsHeight(), thumb.getPreferredBoundsHeight()); - measuredMinWidth = thumb.getPreferredBoundsWidth(); - } - - /** - * @private - */ - override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void - { - super.layoutContents(unscaledWidth, unscaledHeight); - - // minimum height is no smaller than the larger of the thumb or track - var calculatedSkinHeight:int = Math.max(Math.max(thumb.getPreferredBoundsHeight(), track.getPreferredBoundsHeight()), - unscaledHeight); - - // minimum width is no smaller than the thumb - var calculatedSkinWidth:int = Math.max(thumb.getPreferredBoundsWidth(), - unscaledWidth); - - // once we know the skin height, center the thumb and track - thumb.y = Math.max(Math.round((calculatedSkinHeight - thumb.getPreferredBoundsHeight()) / 2), 0); - var calculatedTrackY:int = Math.max(Math.round((calculatedSkinHeight - track.getPreferredBoundsHeight()) / 2), 0); - - // size and position - setElementSize(thumb, thumb.getPreferredBoundsWidth(), thumb.getPreferredBoundsHeight()); // thumb does NOT scale - setElementSize(track, calculatedSkinWidth, track.getPreferredBoundsHeight()); // note track is NOT scaled vertically - setElementPosition(track, 0, calculatedTrackY); - } -} + import flash.display.BlendMode; + import flash.display.DisplayObject; + import flash.events.Event; + + import mx.core.ClassFactory; + import mx.core.IFactory; + + import spark.components.Button; + import spark.components.HSlider; + import spark.components.ToggleSwitch; + import spark.skins.android4.assets.HSliderTrack_filled; + import spark.skins.mobile.supportClasses.HSliderDataTip; + import spark.skins.mobile.supportClasses.MobileSkin; + + /** + * Android 4.x specific ActionScript-based skin for HSlider controls in mobile applications. + * + *

The base Flex implementation creates an HSlider with fixed height + * and variable width with a fixed-size thumb. As the height of the + * HSlider component increases, the vertical dimensions of the visible HSlider remain + * the same, and the HSlider stays vertically centered.

+ * + *

The thumb and track implementations can be customized by subclassing + * this skin class and overriding the thumbSkinClass, trackSkinClass, + * and/or dataTipClass variables as necessary.

+ * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class HSliderSkin extends MobileSkin + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function HSliderSkin() + { + super(); + + thumbSkinClass = spark.skins.android4.HSliderThumbSkin; + trackSkinClass = spark.skins.android4.HSliderTrackSkin; + filledTrackSkinClass = spark.skins.android4.assets.HSliderTrack_filled; + dataTipClass = spark.skins.mobile.supportClasses.HSliderDataTip; + + blendMode = BlendMode.LAYER; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + private var _hostComponent:HSlider; + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public function get hostComponent():HSlider + { + return _hostComponent; + } + + public function set hostComponent(value:HSlider):void + { + if (_hostComponent) + _hostComponent.removeEventListener(Event.CHANGE, thumbPositionChanged_handler); + _hostComponent = value; + if (_hostComponent) + _hostComponent.addEventListener(Event.CHANGE, thumbPositionChanged_handler); + } + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * HSlider track skin part + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var track:Button; + + /** + * HSlider track skin part that + * depicts area that is filled + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var filledTrack:DisplayObject; + + /** + * HSlider thumb skin part + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var thumb:Button; + + /** + * HSlider dataTip class factory + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var dataTip:IFactory; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * Specifies the skin class that will be used for the HSlider thumb. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbSkinClass:Class; + + /** + * Specifies the skin class that will be used for the HSlider track. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackSkinClass:Class; + /** + * Specifies the skin class that will be used for the HSlider track's + * filled area. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var filledTrackSkinClass:Class; + + /** + * Specifies the class that will be used for the HSlider datatip. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var dataTipClass:Class; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function commitCurrentState():void + { + if (currentState == "disabled") + alpha = 0.5; + else if (currentState == "normal") + alpha = 1; + } + + /** + * @private + */ + override protected function createChildren():void + { + // Create our skin parts: track and thumb + track = new Button(); + track.setStyle("skinClass", trackSkinClass); + addChild(track); + + filledTrack = new filledTrackSkinClass(); + addChild(filledTrack); + + thumb = new Button(); + thumb.setStyle("skinClass", thumbSkinClass); + addChild(thumb); + + // Set up the class factory for the dataTip + dataTip = new ClassFactory(); + ClassFactory(dataTip).generator = dataTipClass; + } + + /** + * @private + * The HSliderSkin width will be no less than the width of the thumb skin. + * The HSliderSkin height will be no less than the greater of the heights of + * the thumb and track skins. + */ + override protected function measure():void + { + measuredWidth = track.getPreferredBoundsWidth(); + measuredHeight = Math.max(track.getPreferredBoundsHeight(), thumb.getPreferredBoundsHeight()); + + measuredMinHeight = Math.max(track.getPreferredBoundsHeight(), thumb.getPreferredBoundsHeight()); + measuredMinWidth = thumb.getPreferredBoundsWidth(); + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + // minimum height is no smaller than the larger of the thumb or track + var calculatedSkinHeight:int = Math.max(Math.max(thumb.getPreferredBoundsHeight(), track.getPreferredBoundsHeight()), + unscaledHeight); + + // minimum width is no smaller than the thumb + var calculatedSkinWidth:int = Math.max(thumb.getPreferredBoundsWidth(), + unscaledWidth); + + // once we know the skin height, center the thumb and track + thumb.y = Math.max(Math.round((calculatedSkinHeight - thumb.getPreferredBoundsHeight()) / 2), 0); + var calculatedTrackY:int = Math.max(Math.round((calculatedSkinHeight - track.getPreferredBoundsHeight()) / 2), 0); + + // size and position + setElementSize(thumb, thumb.getPreferredBoundsWidth(), thumb.getPreferredBoundsHeight()); // thumb does NOT scale + setElementSize(track, calculatedSkinWidth, track.getPreferredBoundsHeight()); // note track is NOT scaled vertically + setElementPosition(track, 0, calculatedTrackY); + + //Set size and position of filled area based on thumb's current location + var filledTrackWidth:Number = thumb.getLayoutBoundsX(); + setElementSize(filledTrack, filledTrackWidth, track.getPreferredBoundsHeight()); // note track is NOT scaled vertically + setElementPosition(filledTrack, track.x + HSliderTrackSkin(track.skin).visibleTrackOffset , calculatedTrackY); + } + + private function thumbPositionChanged_handler(event:Event):void + { + //Just trigger a redraw so that the filled area of the track updates itself + invalidateDisplayList(); + } + + } } \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderTrackSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderTrackSkin.as index d1cc29c8e5..57dc8f391c 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderTrackSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderTrackSkin.as @@ -185,7 +185,7 @@ package spark.skins.android4 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ - protected var visibleTrackOffset:int; + public var visibleTrackOffset:int; //-------------------------------------------------------------------------- // From 19312c512c20da2a2ad6ae4c2c2f5aee3b3d3b02 Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 2 May 2014 15:27:09 -0700 Subject: [PATCH 019/392] Fixed bug in current CalloutArrow --- frameworks/projects/mobiletheme/android4x.css | 11 +++++++++++ .../spark/skins/mobile/supportClasses/CalloutArrow.as | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/frameworks/projects/mobiletheme/android4x.css b/frameworks/projects/mobiletheme/android4x.css index 790a6553b1..1c5bb4d4c4 100644 --- a/frameworks/projects/mobiletheme/android4x.css +++ b/frameworks/projects/mobiletheme/android4x.css @@ -41,6 +41,7 @@ application-dpi=240. global { + primaryAccentColor: #33B5E5; fontFamily: RobotoRegular; fontWeight: "normal"; fontSize: 24; @@ -100,6 +101,16 @@ ButtonBar fontWeight: "bold"; } +Callout +{ + backgroundColor: #484848; + contentBackgroundColor: #FFFFFF; + gap: 12; + skinClass: ClassReference("spark.skins.android4.CalloutSkin"); + borderThickness: 0; + borderColor: 0; +} + CheckBox { skinClass: ClassReference("spark.skins.android4.CheckBoxSkin"); diff --git a/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/CalloutArrow.as b/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/CalloutArrow.as index ea4bcf0498..34f896b8da 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/CalloutArrow.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/CalloutArrow.as @@ -61,7 +61,7 @@ public class CalloutArrow extends UIComponent // Copy DPI-specific values from CalloutSkin switch (applicationDPI) { - case DPIClassification.DPI_320: + case DPIClassification.DPI_640: { // Note provisional may need changes gap = 32; From 5c1f4904b9d375b062df0b1791c6d9aae79f26dc Mon Sep 17 00:00:00 2001 From: Om Date: Mon, 26 May 2014 01:12:35 -0700 Subject: [PATCH 020/392] Android 4.x skin for Callout --- .../mobiletheme/{ => src}/android4x.css | 2 +- .../src/spark/skins/android4/CalloutSkin.as | 831 ++++++++++++++++++ .../assets/CalloutContentBackground.fxg | 51 ++ .../android4/supportClasses/CalloutArrow.as | 453 ++++++++++ 4 files changed, 1336 insertions(+), 1 deletion(-) rename frameworks/projects/mobiletheme/{ => src}/android4x.css (99%) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/CalloutSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CalloutContentBackground.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/CalloutArrow.as diff --git a/frameworks/projects/mobiletheme/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css similarity index 99% rename from frameworks/projects/mobiletheme/android4x.css rename to frameworks/projects/mobiletheme/src/android4x.css index 1c5bb4d4c4..f014200451 100644 --- a/frameworks/projects/mobiletheme/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -103,7 +103,7 @@ ButtonBar Callout { - backgroundColor: #484848; + backgroundColor: #33B5E5; contentBackgroundColor: #FFFFFF; gap: 12; skinClass: ClassReference("spark.skins.android4.CalloutSkin"); diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/CalloutSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/CalloutSkin.as new file mode 100644 index 0000000000..3bb0e48478 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/CalloutSkin.as @@ -0,0 +1,831 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.BlendMode; +import flash.display.GradientType; +import flash.display.Graphics; +import flash.display.Sprite; +import flash.events.Event; + +import mx.core.DPIClassification; +import mx.core.UIComponent; +import mx.core.mx_internal; +import mx.events.EffectEvent; +import mx.events.FlexEvent; +import mx.utils.ColorUtil; + +import spark.components.ArrowDirection; +import spark.components.Callout; +import spark.components.ContentBackgroundAppearance; +import spark.components.Group; +import spark.core.SpriteVisualElement; +import spark.effects.Fade; +import spark.primitives.RectangularDropShadow; +import spark.skins.android4.assets.CalloutContentBackground; +import spark.skins.android4.supportClasses.CalloutArrow; +import spark.skins.mobile.supportClasses.MobileSkin; +import spark.skins.mobile120.assets.CalloutContentBackground; +import spark.skins.mobile160.assets.CalloutContentBackground; +import spark.skins.mobile240.assets.CalloutContentBackground; +import spark.skins.mobile320.assets.CalloutContentBackground; +import spark.skins.mobile480.assets.CalloutContentBackground; +import spark.skins.mobile640.assets.CalloutContentBackground; + +use namespace mx_internal; + +/** + * The default skin class for the Spark Callout component in mobile + * applications. + * + *

The contentGroup lies above a backgroundColor fill + * which frames the contentGroup. The position and size of the frame + * adjust based on the host component arrowDirection, leaving + * space for the arrow to appear on the outside edge of the + * frame.

+ * + *

The arrow skin part is not positioned by the skin. Instead, + * the Callout component positions the arrow relative to the owner in + * updateSkinDisplayList(). This method assumes that Callout skin + * and the arrow use the same coordinate space.

+ * + * @see spark.components.Callout + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ +public class CalloutSkin extends MobileSkin +{ + mx_internal static const BACKGROUND_GRADIENT_BRIGHTNESS_TOP:int = 15; + + mx_internal static const BACKGROUND_GRADIENT_BRIGHTNESS_BOTTOM:int = -15; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public function CalloutSkin() + { + super(); + + dropShadowAlpha = 0.7; + contentBackgroundInsetClass = spark.skins.android4.assets.CalloutContentBackground; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + // Note provisional may need changes + backgroundCornerRadius = 32; + backgroundGradientHeight = 440; + frameThickness = 32; + arrowWidth = 208; + arrowHeight = 104; + contentCornerRadius = 20; + dropShadowBlurX = 64; + dropShadowBlurY = 64; + dropShadowDistance = 12; + highlightWeight = 2; + + break; + } + case DPIClassification.DPI_480: + { + // Note provisional may need changes + backgroundCornerRadius = 24; + backgroundGradientHeight = 330; + frameThickness = 24; + arrowWidth = 156; + arrowHeight = 78; + contentCornerRadius = 14; + dropShadowBlurX = 48; + dropShadowBlurY = 48; + dropShadowDistance = 8; + highlightWeight = 2; + + break; + } + case DPIClassification.DPI_320: + { + backgroundCornerRadius = 16; + backgroundGradientHeight = 220; + frameThickness = 16; + arrowWidth = 104; + arrowHeight = 52; + contentCornerRadius = 10; + dropShadowBlurX = 32; + dropShadowBlurY = 32; + dropShadowDistance = 6; + highlightWeight = 2; + + break; + } + case DPIClassification.DPI_240: + { + backgroundCornerRadius = 12; + backgroundGradientHeight = 165; + frameThickness = 12; + arrowWidth = 78; + arrowHeight = 39; + contentCornerRadius = 7; + dropShadowBlurX = 24; + dropShadowBlurY = 24; + dropShadowDistance = 4; + highlightWeight = 1; + + break; + } + case DPIClassification.DPI_120: + { + backgroundCornerRadius = 6; + backgroundGradientHeight = 83; + frameThickness = 6; + arrowWidth = 39; + arrowHeight = 19; + contentCornerRadius = 4; + dropShadowBlurX = 12; + dropShadowBlurY = 12; + dropShadowDistance = 2; + highlightWeight = 0.5; + + break; + } + default: + { + // default DPI_160 + backgroundCornerRadius = 8; + backgroundGradientHeight = 110; + frameThickness = 2; + arrowWidth = 52; + arrowHeight = 26; + contentCornerRadius = 5; + dropShadowBlurX = 16; + dropShadowBlurY = 16; + dropShadowDistance = 3; + highlightWeight = 1; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:Callout; + + /** + * Enables a RectangularDropShadow behind the backgroundColor frame. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var dropShadowVisible:Boolean = true; + + /** + * Enables a vertical linear gradient in the backgroundColor frame. This + * gradient fill is drawn across both the arrow and the frame. By default, + * the gradient brightens the background color by 15% and darkens it by 60%. + * + * @default true + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var useBackgroundGradient:Boolean = true; + + /** + * Corner radius used for the contentBackgroundColor fill. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var contentCornerRadius:uint; + + /** + * A class reference to an FXG class that is layered underneath the + * contentGroup. The instance of this class is sized to match the + * contentGroup. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var contentBackgroundInsetClass:Class; + + /** + * Corner radius of the backgroundColor "frame". + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var backgroundCornerRadius:Number; + + /** + * The thickness of the backgroundColor "frame" that surrounds the + * contentGroup. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var frameThickness:Number; + + /** + * Color of the border stroke around the backgroundColor "frame". + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var borderColor:Number = -1; // not set + + /** + * Thickness of the border stroke around the backgroundColor + * "frame". + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var borderThickness:Number = -1 ; // marker that borderThickness was not set directly + + /** + * Width of the arrow in vertical directions. This property also controls + * the height of the arrow in horizontal directions. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var arrowWidth:Number; + + /** + * Height of the arrow in vertical directions. This property also controls + * the width of the arrow in horizontal directions. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var arrowHeight:Number; + + /** + * @private + * Instance of the contentBackgroundClass + */ + mx_internal var contentBackgroundGraphic:SpriteVisualElement; + + /** + * @private + * Tracks changes to the skin state to support the fade out tranisition + * when closed; + */ + mx_internal var isOpen:Boolean; + + private var backgroundGradientHeight:Number; + + private var contentMask:Sprite; + + private var backgroundFill:SpriteVisualElement; + + private var dropShadow:RectangularDropShadow; + + private var dropShadowBlurX:Number; + + private var dropShadowBlurY:Number; + + private var dropShadowDistance:Number; + + private var dropShadowAlpha:Number; + + private var fade:Fade; + + private var highlightWeight:Number; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.components.SkinnableContainer#contentGroup + */ + public var contentGroup:Group; + + /** + * @copy spark.components.Callout#arrow + */ + public var arrow:UIComponent; + + /* helper private accessors */ + + /* returns borderThickness from style if member is -1, or borderThickness. Returns 0 if NaN */ + mx_internal function get actualBorderThickness():Number + { + var border: Number = borderThickness != -1 ? borderThickness : getStyle('borderThickness'); + return isNaN(border)? 0: border; + } + + mx_internal function get actualBorderColor():uint + { + return borderColor != -1 ? borderColor: getStyle('borderColor'); + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (dropShadowVisible) + { + dropShadow = new RectangularDropShadow(); + dropShadow.angle = 90; + dropShadow.distance = dropShadowDistance; + dropShadow.blurX = dropShadowBlurX; + dropShadow.blurY = dropShadowBlurY; + dropShadow.tlRadius = dropShadow.trRadius = dropShadow.blRadius = + dropShadow.brRadius = backgroundCornerRadius ; + dropShadow.mouseEnabled = false; + dropShadow.alpha = dropShadowAlpha; + addChild(dropShadow); + } + + // background fill placed above the drop shadow + backgroundFill = new SpriteVisualElement(); + addChild(backgroundFill); + + // arrow + if (!arrow) + { + arrow = new CalloutArrow(); + arrow.id = "arrow"; + arrow.styleName = this; + addChild(arrow); + } + + // contentGroup + if (!contentGroup) + { + contentGroup = new Group(); + contentGroup.id = "contentGroup"; + addChild(contentGroup); + } + + + } + + /** + * @private + */ + override protected function commitProperties():void + { + super.commitProperties(); + + // add or remove the contentBackgroundGraphic + var contentBackgroundAppearance:String = getStyle("contentBackgroundAppearance"); + + if (contentBackgroundAppearance == ContentBackgroundAppearance.INSET) + { + // create the contentBackgroundGraphic + if (!contentBackgroundGraphic && contentBackgroundInsetClass) + { + contentBackgroundGraphic = new contentBackgroundInsetClass() as SpriteVisualElement; + + // with the current skin structure, contentBackgroundGraphic is + // always the last child + addChild(contentBackgroundGraphic); + } + } + else if (contentBackgroundGraphic) + { + // if already created, remove the graphic for "flat" and "none" + removeChild(contentBackgroundGraphic); + contentBackgroundGraphic = null; + } + + // always invalidate to accomodate arrow direction changes + invalidateSize(); + invalidateDisplayList(); + } + + + /** + * @private + */ + override protected function measure():void + { + super.measure(); + + var borderWeight:Number =actualBorderThickness; + var frameAdjustment:Number = (frameThickness + borderWeight) * 2; + + var arrowMeasuredWidth:Number; + var arrowMeasuredHeight:Number; + + // pad the arrow so that the edges are within the background corner radius + if (isArrowHorizontal) + { + arrowMeasuredWidth = arrowHeight; + arrowMeasuredHeight = arrowWidth + (backgroundCornerRadius * 2); + } + else if (isArrowVertical) + { + arrowMeasuredWidth = arrowWidth + (backgroundCornerRadius * 2); + arrowMeasuredHeight = arrowHeight; + } + + // count the contentGroup size and frame size + measuredMinWidth = contentGroup.measuredMinWidth + frameAdjustment; + measuredMinHeight = contentGroup.measuredMinHeight + frameAdjustment; + + measuredWidth = contentGroup.getPreferredBoundsWidth() + frameAdjustment; + measuredHeight = contentGroup.getPreferredBoundsHeight() + frameAdjustment; + + // add the arrow size based on the arrowDirection + if (isArrowHorizontal) + { + measuredMinWidth += arrowMeasuredWidth; + measuredMinHeight = Math.max(measuredMinHeight, arrowMeasuredHeight); + + measuredWidth += arrowMeasuredWidth; + measuredHeight = Math.max(measuredHeight, arrowMeasuredHeight); + } + else if (isArrowVertical) + { + measuredMinWidth += Math.max(measuredMinWidth, arrowMeasuredWidth); + measuredMinHeight += arrowMeasuredHeight; + + measuredWidth = Math.max(measuredWidth, arrowMeasuredWidth); + measuredHeight += arrowMeasuredHeight; + } + } + + /** + * @private + * SkinnaablePopUpContainer skins must dispatch a + * FlexEvent.STATE_CHANGE_COMPLETE event for the component to properly + * update the skin state. + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + var isNormal:Boolean = (currentState == "normal"); + var isDisabled:Boolean = (currentState == "disabled") + + // play a fade out if the callout was previously open + if (!(isNormal || isDisabled) && isOpen) + { + if (!fade) + { + fade = new Fade(); + fade.target = this; + fade.duration = 200; + fade.alphaTo = 0; + } + + // BlendMode.LAYER while fading out + blendMode = BlendMode.LAYER; + + // play a short fade effect + fade.addEventListener(EffectEvent.EFFECT_END, stateChangeComplete); + fade.play(); + + isOpen = false; + } + else + { + isOpen = isNormal || isDisabled; + + // handle re-opening the Callout while fading out + if (fade && fade.isPlaying) + { + // Do not dispatch a state change complete. + // SkinnablePopUpContainer handles state interruptions. + fade.removeEventListener(EffectEvent.EFFECT_END, stateChangeComplete); + fade.stop(); + } + + if (isDisabled) + { + // BlendMode.LAYER to allow CalloutArrow BlendMode.ERASE + blendMode = BlendMode.LAYER; + + alpha = 0.5; + } + else + { + // BlendMode.NORMAL for non-animated state transitions + blendMode = BlendMode.NORMAL; + + if (isNormal) + alpha = 1; + else + alpha = 0; + } + + stateChangeComplete(); + } + } + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + super.drawBackground(unscaledWidth, unscaledHeight); + + var frameEllipseSize:Number = backgroundCornerRadius * 2; + + // account for borderThickness center stroke alignment + var borderWeight:Number =actualBorderThickness; + var showBorder:Boolean = borderWeight > 0 ; + + + // contentBackgroundGraphic already accounts for the arrow position + // use it's positioning instead of recalculating based on unscaledWidth + // and unscaledHeight + var frameX:Number = Math.floor(contentGroup.getLayoutBoundsX() - frameThickness) - (borderWeight / 2); + var frameY:Number = Math.floor(contentGroup.getLayoutBoundsY() - frameThickness) - (borderWeight / 2); + var frameWidth:Number = contentGroup.getLayoutBoundsWidth() + (frameThickness * 2) + borderWeight; + var frameHeight:Number = contentGroup.getLayoutBoundsHeight() + (frameThickness * 2) + borderWeight; + + var backgroundColor:Number = getStyle("primaryAccentColor"); + var backgroundAlpha:Number = getStyle("backgroundAlpha"); + + var bgFill:Graphics = backgroundFill.graphics; + bgFill.clear(); + + if (showBorder) + bgFill.lineStyle(borderWeight, actualBorderColor, 1, true); + + if (useBackgroundGradient) + { + // top color is brighter if arrowDirection == ArrowDirection.UP + var backgroundColorTop:Number = ColorUtil.adjustBrightness2(backgroundColor, + BACKGROUND_GRADIENT_BRIGHTNESS_TOP); + var backgroundColorBottom:Number = ColorUtil.adjustBrightness2(backgroundColor, + BACKGROUND_GRADIENT_BRIGHTNESS_BOTTOM); + + // max gradient height = backgroundGradientHeight + colorMatrix.createGradientBox(unscaledWidth, backgroundGradientHeight, + Math.PI / 2, 0, 0); + + bgFill.beginGradientFill(GradientType.LINEAR, + [backgroundColorTop, backgroundColorBottom], + [backgroundAlpha, backgroundAlpha], + [0, 255], + colorMatrix); + } + else + { + bgFill.beginFill(backgroundColor, backgroundAlpha); + } + + bgFill.drawRoundRect(frameX, frameY, frameWidth, + frameHeight, frameEllipseSize, frameEllipseSize); + bgFill.endFill(); + + // draw content background styles + var contentBackgroundAppearance:String = getStyle("contentBackgroundAppearance"); + + if (contentBackgroundAppearance != ContentBackgroundAppearance.NONE) + { + var contentEllipseSize:Number = contentCornerRadius * 2; + var contentBackgroundAlpha:Number = getStyle("contentBackgroundAlpha"); + var contentWidth:Number = contentGroup.getLayoutBoundsWidth(); + var contentHeight:Number = contentGroup.getLayoutBoundsHeight(); + + // all appearance values except for "none" use a mask + if (!contentMask) + contentMask = new SpriteVisualElement(); + + contentGroup.mask = contentMask; + + // draw contentMask in contentGroup coordinate space + var maskGraphics:Graphics = contentMask.graphics; + maskGraphics.clear(); + maskGraphics.beginFill(0, 1); + maskGraphics.drawRoundRect(0, 0, contentWidth, contentHeight, + contentEllipseSize, contentEllipseSize); + maskGraphics.endFill(); + + // reset line style to none + if (showBorder) + bgFill.lineStyle(NaN); + + // draw the contentBackgroundColor + bgFill.beginFill(getStyle("contentBackgroundColor"), + contentBackgroundAlpha); + bgFill.drawRoundRect(contentGroup.getLayoutBoundsX(), + contentGroup.getLayoutBoundsY(), + contentWidth, contentHeight, contentEllipseSize, contentEllipseSize); + bgFill.endFill(); + + if (contentBackgroundGraphic) + contentBackgroundGraphic.alpha = contentBackgroundAlpha; + } + else // if (contentBackgroundAppearance == CalloutContentBackgroundAppearance.NONE)) + { + // remove the mask + if (contentMask) + { + contentGroup.mask = null; + contentMask = null; + } + } + + // draw highlight in the callout when the arrow is hidden + if (useBackgroundGradient && !isArrowHorizontal && !isArrowVertical) + { + // highlight width spans the callout width minus the corner radius + var highlightWidth:Number = frameWidth - frameEllipseSize; + var highlightX:Number = frameX + backgroundCornerRadius; + var highlightOffset:Number = (highlightWeight * 1.5); + + // straight line across the top + bgFill.lineStyle(highlightWeight, 0xFFFFFF, 0.2 * backgroundAlpha); + bgFill.moveTo(highlightX, highlightOffset); + bgFill.lineTo(highlightX + highlightWidth, highlightOffset); + } + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + // pad the arrow so that the edges are within the background corner radius + if (isArrowHorizontal) + { + arrow.width = arrowHeight; + arrow.height = arrowWidth + (backgroundCornerRadius * 2); + } + else if (isArrowVertical) + { + arrow.width = arrowWidth + (backgroundCornerRadius * 2); + arrow.height = arrowHeight; + } + + setElementSize(backgroundFill, unscaledWidth, unscaledHeight); + setElementPosition(backgroundFill, 0, 0); + + var frameX:Number = 0; + var frameY:Number = 0; + var frameWidth:Number = unscaledWidth; + var frameHeight:Number = unscaledHeight; + + switch (hostComponent.arrowDirection) + { + case ArrowDirection.UP: + frameY = arrow.height; + frameHeight -= arrow.height; + break; + case ArrowDirection.DOWN: + frameHeight -= arrow.height; + break; + case ArrowDirection.LEFT: + frameX = arrow.width; + frameWidth -= arrow.width; + break; + case ArrowDirection.RIGHT: + frameWidth -= arrow.width; + break; + default: + // no arrow, content takes all available space + break; + } + + if (dropShadow) + { + setElementSize(dropShadow, frameWidth, frameHeight); + setElementPosition(dropShadow, frameX, frameY); + } + + // Show frameThickness by inset of contentGroup + var borderWeight:Number = actualBorderThickness; + var contentBackgroundAdjustment:Number = frameThickness + borderWeight; + + var contentBackgroundX:Number = frameX + contentBackgroundAdjustment; + var contentBackgroundY:Number = frameY + contentBackgroundAdjustment; + + contentBackgroundAdjustment = contentBackgroundAdjustment * 2; + var contentBackgroundWidth:Number = frameWidth - contentBackgroundAdjustment; + var contentBackgroundHeight:Number = frameHeight - contentBackgroundAdjustment; + + if (contentBackgroundGraphic) + { + setElementSize(contentBackgroundGraphic, contentBackgroundWidth, contentBackgroundHeight); + setElementPosition(contentBackgroundGraphic, contentBackgroundX, contentBackgroundY); + } + + setElementSize(contentGroup, contentBackgroundWidth, contentBackgroundHeight); + setElementPosition(contentGroup, contentBackgroundX, contentBackgroundY); + + // mask position is in the contentGroup coordinate space + if (contentMask) + setElementSize(contentMask, contentBackgroundWidth, contentBackgroundHeight); + } + + override public function styleChanged(styleProp:String):void + { + super.styleChanged(styleProp); + + var allStyles:Boolean = !styleProp || styleProp == "styleName"; + + if (allStyles || (styleProp == "contentBackgroundAppearance")) + invalidateProperties(); + + if (allStyles || (styleProp == "backgroundAlpha")) + { + var backgroundAlpha:Number = getStyle("backgroundAlpha"); + + // Use BlendMode.LAYER to allow CalloutArrow to erase the dropShadow + // when the Callout background is transparent + blendMode = (backgroundAlpha < 1) ? BlendMode.LAYER : BlendMode.NORMAL; + } + } + + /** + * @private + */ + mx_internal function get isArrowHorizontal():Boolean + { + return (hostComponent.arrowDirection == ArrowDirection.LEFT + || hostComponent.arrowDirection == ArrowDirection.RIGHT); + } + + /** + * @private + */ + mx_internal function get isArrowVertical():Boolean + { + return (hostComponent.arrowDirection == ArrowDirection.UP + || hostComponent.arrowDirection == ArrowDirection.DOWN); + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + private function stateChangeComplete(event:Event=null):void + { + if (fade && event) + fade.removeEventListener(EffectEvent.EFFECT_END, stateChangeComplete); + + // SkinnablePopUpContainer relies on state changes for open and close + dispatchEvent(new FlexEvent(FlexEvent.STATE_CHANGE_COMPLETE)); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CalloutContentBackground.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CalloutContentBackground.fxg new file mode 100644 index 0000000000..8442b969b6 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/CalloutContentBackground.fxg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/CalloutArrow.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/CalloutArrow.as new file mode 100644 index 0000000000..c18d457a88 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/CalloutArrow.as @@ -0,0 +1,453 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.supportClasses +{ +import flash.display.BlendMode; +import flash.display.GradientType; +import flash.display.Graphics; +import flash.display.GraphicsPathCommand; +import flash.display.Sprite; + +import mx.core.DPIClassification; +import mx.core.FlexGlobals; +import mx.core.IVisualElement; +import mx.core.UIComponent; +import mx.core.mx_internal; +import mx.utils.ColorUtil; + +import spark.components.Application; +import spark.components.ArrowDirection; +import spark.components.Callout; +import spark.skins.android4.CalloutSkin; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * The arrow skin part for CalloutSkin. + * + * @see spark.skin.mobile.CalloutSkin + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ +public class CalloutArrow extends UIComponent +{ + public function CalloutArrow() + { + super(); + + useBackgroundGradient = true; + + var applicationDPI:Number = Application(FlexGlobals.topLevelApplication).applicationDPI; + + // Copy DPI-specific values from CalloutSkin + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + // Note provisional may need changes + gap = 32; + backgroundGradientHeight = 440; + highlightWeight = 4; + + break; + } + case DPIClassification.DPI_480: + { + // Note provisional may need changes + gap = 24; + backgroundGradientHeight = 330; + highlightWeight = 3; + + break; + } + case DPIClassification.DPI_320: + { + gap = 16; + backgroundGradientHeight = 220; + highlightWeight = 2; + + break; + } + case DPIClassification.DPI_240: + { + gap = 12; + backgroundGradientHeight = 165; + highlightWeight = 1; + + break; + } + case DPIClassification.DPI_120: + { + // Note provisional may need changes + gap = 6; + backgroundGradientHeight = 83; + highlightWeight = 1; + + break; + } + default: + { + // default DPI_160 + gap = 8; + backgroundGradientHeight = 110; + highlightWeight = 1; + + break; + } + } + } + + /** + * A gap on the frame-adjacent side of the arrow graphic to avoid + * drawing past the CalloutSkin backgroundCornerRadius. + * + *

The default implementation matches the gap value with the + * backgroundCornerRadius value in CalloutSkin.

+ * + * @see spark.skins.mobile.CalloutSkin#backgroundCornerRadius + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var gap:Number; + + /** + * @copy spark.skins.mobile.CalloutSkin#backgroundGradientHeight + */ + protected var backgroundGradientHeight:Number; + + /** + * @copy spark.skins.mobile.CalloutSkin#highlightWeight + */ + private var highlightWeight:Number; + + /** + * @copy spark.skins.mobile.CalloutSkin#useBackgroundGradient + */ + protected var useBackgroundGradient:Boolean; + + /** + * @copy spark.skins.mobile.CalloutSkin#borderColor + */ + protected var borderColor:Number = -1; // if not set + + /** + * @copy spark.skins.mobile.CalloutSkin#borderThickness + */ + protected var borderThickness:Number = -1 ; // marker that borderThickness was not set directly + + /** + * @private + * A sibling of the arrow used to erase the drop shadow in CalloutSkin + */ + private var eraseFill:Sprite; + + /* helper private accessors */ + + /* returns borderThickness from style if member is -1, or borderThickness. Returns 0 if NaN */ + private function get actualBorderThickness():Number + { + return calloutSkin.actualBorderThickness; + } + + private function get actualBorderColor():uint + { + return calloutSkin.actualBorderColor; + } + + protected function get calloutSkin():CalloutSkin + { + return parent as CalloutSkin ; + } + + protected function get calloutHostComponent():Callout { + return calloutSkin.hostComponent; + } + + /** + * @private + */ + + override protected function createChildren():void + { + super.createChildren(); + + // eraseFill has the same position and arrow shape in order to erase + // the drop shadow under the arrow when backgroundAlpha < 1 + eraseFill = new Sprite(); + eraseFill.blendMode = BlendMode.ERASE; + + // layer eraseFill below the arrow + parent.addChildAt(eraseFill, parent.getChildIndex(this)); + } + + /** + * @private + */ + override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void + { + super.updateDisplayList(unscaledWidth, unscaledHeight); + + graphics.clear(); + eraseFill.graphics.clear(); + var hostComponent: Callout = calloutHostComponent; + var arrowDirection:String = hostComponent.arrowDirection; + + if (arrowDirection == ArrowDirection.NONE) + return; + + // when drawing the arrow, compensate for cornerRadius via padding + var arrowGraphics:Graphics = this.graphics; + var eraseGraphics:Graphics = eraseFill.graphics; + var arrowWidth:Number = unscaledWidth; + var arrowHeight:Number = unscaledHeight; + var arrowX:Number = 0; + var arrowY:Number = 0; + var arrowTipX:Number = 0; + var arrowTipY:Number = 0; + var arrowEndX:Number = 0; + var arrowEndY:Number = 0; + + var borderWeight:Number = actualBorderThickness; + var showBorder:Boolean = borderWeight > 0; + + var borderHalf:Number = borderWeight / 2; + var isHorizontal:Boolean = false; + + if ((arrowDirection == ArrowDirection.LEFT) || + (arrowDirection == ArrowDirection.RIGHT)) + { + isHorizontal = true; + + arrowX = -borderHalf; + arrowY = gap; + arrowHeight = arrowHeight - (gap * 2); + + arrowTipX = arrowWidth - borderHalf; + arrowTipY = arrowY + (arrowHeight / 2); + + arrowEndX = arrowX; + arrowEndY = arrowY + arrowHeight; + + // flip coordinates to point left + if (arrowDirection == ArrowDirection.LEFT) + { + arrowX = arrowWidth - arrowX; + arrowTipX = arrowWidth - arrowTipX; + arrowEndX = arrowWidth - arrowEndX; + } + } + else + { + arrowX = gap; + arrowY = -borderHalf; + arrowWidth = arrowWidth - (gap * 2); + + arrowTipX = arrowX + (arrowWidth / 2); + arrowTipY = arrowHeight - borderHalf; + + arrowEndX = arrowX + arrowWidth; + arrowEndY = arrowY; + + // flip coordinates to point up + if (hostComponent.arrowDirection == ArrowDirection.UP) + { + arrowY = arrowHeight - arrowY; + arrowTipY = arrowHeight - arrowTipY; + arrowEndY = arrowHeight - arrowEndY; + } + } + + var commands:Vector. = new Vector.(3, true); + commands[0] = GraphicsPathCommand.MOVE_TO; + commands[1] = GraphicsPathCommand.LINE_TO; + commands[2] = GraphicsPathCommand.LINE_TO; + + var coords:Vector. = new Vector.(6, true); + coords[0] = arrowX; + coords[1] = arrowY; + coords[2] = arrowTipX + coords[3] = arrowTipY; + coords[4] = arrowEndX + coords[5] = arrowEndY; + + var backgroundColor:Number = getStyle("backgroundColor"); + var backgroundAlpha:Number = getStyle("backgroundAlpha"); + + if (useBackgroundGradient) + { + var backgroundColorTop:Number = ColorUtil.adjustBrightness2(backgroundColor, + CalloutSkin.BACKGROUND_GRADIENT_BRIGHTNESS_TOP); + var backgroundColorBottom:Number = ColorUtil.adjustBrightness2(backgroundColor, + CalloutSkin.BACKGROUND_GRADIENT_BRIGHTNESS_BOTTOM); + + // translate the gradient based on the arrow position + MobileSkin.colorMatrix.createGradientBox(unscaledWidth, + backgroundGradientHeight, Math.PI / 2, 0, -getLayoutBoundsY()); + + arrowGraphics.beginGradientFill(GradientType.LINEAR, + [backgroundColorTop, backgroundColorBottom], + [backgroundAlpha, backgroundAlpha], + [0, 255], + MobileSkin.colorMatrix); + } + else + { + arrowGraphics.beginFill(backgroundColor, backgroundAlpha); + } + + // cover the adjacent border from the callout frame + if (showBorder) + { + var coverX:Number = 0; + var coverY:Number = 0; + var coverWidth:Number = 0; + var coverHeight:Number = 0; + + switch (arrowDirection) + { + case ArrowDirection.UP: + { + coverX = arrowX; + coverY = arrowY; + coverWidth = arrowWidth; + coverHeight = borderWeight; + break; + } + case ArrowDirection.DOWN: + { + coverX = arrowX; + coverY = -borderWeight; + coverWidth = arrowWidth; + coverHeight = borderWeight; + break; + } + case ArrowDirection.LEFT: + { + coverX = arrowX; + coverY = arrowY; + coverWidth = borderWeight; + coverHeight = arrowHeight; + break; + } + case ArrowDirection.RIGHT: + { + coverX = -borderWeight; + coverY = arrowY; + coverWidth = borderWeight; + coverHeight = arrowHeight; + break; + } + } + + arrowGraphics.drawRect(coverX, coverY, coverWidth, coverHeight); + } + + // erase the drop shadow from the CalloutSkin + if (backgroundAlpha < 1) + { + // move eraseFill to the same position as the arrow + eraseFill.x = getLayoutBoundsX() + eraseFill.y = getLayoutBoundsY(); + + // draw the arrow shape + eraseGraphics.beginFill(0, 1); + eraseGraphics.drawPath(commands, coords); + eraseGraphics.endFill(); + } + + // draw arrow path + if (showBorder) + arrowGraphics.lineStyle(borderWeight, actualBorderColor, 1, true); + + arrowGraphics.drawPath(commands, coords); + arrowGraphics.endFill(); + + // adjust the highlight position to the origin of the callout + var isArrowUp:Boolean = (arrowDirection == ArrowDirection.UP); + var offsetY:Number = (isArrowUp) ? unscaledHeight : -getLayoutBoundsY(); + + // highlight starts after the backgroundCornerRadius + var highlightX:Number = gap - getLayoutBoundsX(); + + // highlight Y position is based on the stroke weight + var highlightOffset:Number = (highlightWeight * 1.5); + var highlightY:Number = highlightOffset + offsetY; + + // highlight width spans the callout width minus the corner radius + var highlightWidth:Number = IVisualElement(calloutSkin).getLayoutBoundsWidth() - (gap * 2); + + if (isHorizontal) + { + highlightWidth -= arrowWidth; + + if (arrowDirection == ArrowDirection.LEFT) + highlightX += arrowWidth; + } + + // highlight on the top edge is drawn in the arrow only in the UP direction + if (useBackgroundGradient) + { + if (isArrowUp) + { + // highlight follows the top edge, including the arrow + var rightWidth:Number = highlightWidth - arrowWidth; + + // highlight style + arrowGraphics.lineStyle(highlightWeight, 0xFFFFFF, 0.2 * backgroundAlpha); + + // in the arrow coordinate space, the highlightX must be less than 0 + if (highlightX < 0) + { + arrowGraphics.moveTo(highlightX, highlightY); + arrowGraphics.lineTo(arrowX, highlightY); + + // compute the remaining highlight + rightWidth -= (arrowX - highlightX); + } + + // arrow highlight (adjust Y downward) + coords[1] = arrowY + highlightOffset; + coords[3] = arrowTipY + highlightOffset; + coords[5] = arrowEndY + highlightOffset; + arrowGraphics.drawPath(commands, coords); + + // right side + if (rightWidth > 0) + { + arrowGraphics.moveTo(arrowEndX, highlightY); + arrowGraphics.lineTo(arrowEndX + rightWidth, highlightY); + } + } + else + { + // straight line across the top + arrowGraphics.lineStyle(highlightWeight, 0xFFFFFF, 0.2 * backgroundAlpha); + arrowGraphics.moveTo(highlightX, highlightY); + arrowGraphics.lineTo(highlightX + highlightWidth, highlightY); + } + } + } +} +} \ No newline at end of file From 95c7ca684e44d73e6b2e25633d114f8c737779c4 Mon Sep 17 00:00:00 2001 From: Om Date: Wed, 11 Jun 2014 00:26:42 -0700 Subject: [PATCH 021/392] Android 4.x skins for SpinnerList. Contributed by Aaron Nash here: https://issues.apache.org/jira/browse/FLEX-34356 I made some tweaks to the skin to make it consistent with the color scheme I am working with. --- .../projects/mobiletheme/src/android4x.css | 28 ++ .../android4/SpinnerListContainerSkin.as | 337 ++++++++++++++++++ .../android4/SpinnerListScrollerSkin.mxml | 94 +++++ .../spark/skins/android4/SpinnerListSkin.as | 267 ++++++++++++++ .../assets/SpinnerListContainerBackground.fxg | 47 +++ ...SpinnerListContainerSelectionIndicator.fxg | 44 +++ .../assets/SpinnerListContainerShadow.fxg | 41 +++ 7 files changed, 858 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListScrollerSkin.mxml create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerBackground.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerSelectionIndicator.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerShadow.fxg diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css index f014200451..511511fd21 100644 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -111,6 +111,15 @@ Callout borderColor: 0; } +Callout ViewNavigator ActionBar +{ + skinClass: ClassReference("spark.skins.mobile.CalloutActionBarSkin"); + defaultButtonAppearance: none; + paddingLeft: 0; + paddingRight: 0; +} + + CheckBox { skinClass: ClassReference("spark.skins.android4.CheckBoxSkin"); @@ -130,6 +139,25 @@ RadioButton chromeColor: #FFFFFF; } +SpinnerList +{ + skinClass: ClassReference("spark.skins.android4.SpinnerListSkin"); +} + +SpinnerListContainer +{ + skinClass: ClassReference("spark.skins.android4.SpinnerListContainerSkin"); + backgroundAlpha: 0; +} + +SpinnerListItemRenderer +{ + paddingTop : 18; + paddingBottom : 18; + paddingLeft : 16; + paddingRight : 16; +} + TabbedViewNavigator #tabBar { fontFamily: RobotoRegular; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as new file mode 100644 index 0000000000..f8b3779072 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as @@ -0,0 +1,337 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.Graphics; + import flash.display.InteractiveObject; + import flash.display.Sprite; + + import mx.core.DPIClassification; + import mx.core.mx_internal; + import spark.components.Group; + import spark.components.SpinnerListContainer; + import spark.layouts.HorizontalLayout; + //import android skins + import spark.skins.android4.assets.SpinnerListContainerBackground; + import spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; + import spark.skins.android4.assets.SpinnerListContainerShadow + + import spark.skins.mobile.supportClasses.MobileSkin; + + use namespace mx_internal; + /** + * ActionScript-based skin for the SpinnerListContainer in mobile applications. + * + * @see spark.components.SpinnerListContainer + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public class SpinnerListContainerSkin extends MobileSkin + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + * + */ + public function SpinnerListContainerSkin() + { + super(); + + borderClass = spark.skins.android4.assets.SpinnerListContainerBackground; + selectionIndicatorClass = spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; + shadowClass = spark.skins.android4.assets.SpinnerListContainerShadow; + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + cornerRadius = 0; + borderThickness = 3; + selectionIndicatorHeight = 182; + break; + } + case DPIClassification.DPI_480: + { + cornerRadius = 0; + borderThickness = 2; + selectionIndicatorHeight = 144; + break; + } + case DPIClassification.DPI_320: + { + cornerRadius = 0; + borderThickness = 2; + selectionIndicatorHeight = 96; + break; + } + case DPIClassification.DPI_240: + { + cornerRadius = 0; + borderThickness = 1; + selectionIndicatorHeight = 72; + break; + } + case DPIClassification.DPI_120: + { + cornerRadius = 0; + borderThickness = 0; + selectionIndicatorHeight = 37; + break; + } + default: // default DPI_160 + { + cornerRadius = 0; + borderThickness = 1; + selectionIndicatorHeight = 48; + + break; + } + } + + minWidth = 30; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + /** + * Pixel thickness of the border. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var borderThickness:Number; + + /** + * Radius of the border corners. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var cornerRadius:Number; + + /** + * Height of the selection indicator. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var selectionIndicatorHeight:Number; + + /** + * Class for the border part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var borderClass:Class; + + /** + * Class for the selection indicator skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var selectionIndicatorClass:Class; + + /** + * Class for the shadow skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var shadowClass:Class; + + /** + * Border skin part which includes the background. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var border:InteractiveObject; + + /** + * Selection indicator skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var selectionIndicator:InteractiveObject; + + /** + * Shadow skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var shadow:InteractiveObject; + + /** + * Mask for the content group. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var contentGroupMask:Sprite; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * An optional skin part that defines the Group where the content + * children are pushed into and laid out. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var contentGroup:Group; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var hostComponent:SpinnerListContainer; + + //-------------------------------------------------------------------------- + // + // Overridden Methods + // + //-------------------------------------------------------------------------- + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (!border) + { + // Border and background + border = new borderClass(); + border.mouseEnabled = false; + addChild(border); + } + + if (!contentGroup) + { + // Contains the child elements + contentGroup = new Group(); + var hLayout:HorizontalLayout = new HorizontalLayout(); + hLayout.gap = 0; + hLayout.verticalAlign = "middle"; + contentGroup.layout = hLayout; + contentGroup.id = "contentGroup"; + addChild(contentGroup); + } + + if (!shadow) + { + // Shadowing sits on top of the content + shadow = new shadowClass(); + shadow.mouseEnabled = false; + addChild(shadow); + } + + + if (!contentGroupMask) + { + // Create a mask for the content + contentGroupMask = new Sprite(); + addChild(contentGroupMask); + } + } + + /** + * @private + */ + override protected function measure():void + { + super.measure(); + + var contentW:Number = contentGroup.getPreferredBoundsWidth(); + var contentH:Number = contentGroup.getPreferredBoundsHeight(); + + measuredWidth = measuredMinWidth = contentW + borderThickness * 2; + measuredHeight = contentH + borderThickness * 2; + + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + setElementSize(contentGroup, unscaledWidth - borderThickness * 2, unscaledHeight - borderThickness * 2); + setElementPosition(contentGroup, borderThickness, borderThickness); + + // Inset by the borderThickness horizontally because the selectionIndicator starts at 0 + setElementSize(border, unscaledWidth - borderThickness * 2, unscaledHeight); + setElementPosition(border, borderThickness, 0); + + setElementSize(shadow, unscaledWidth - borderThickness * 4, unscaledHeight - borderThickness * 2); + setElementPosition(shadow, borderThickness * 2, borderThickness); + + // The SpinnerLists contain a left and right border. We don't want to show the leftmost + // SpinnerLists's left border nor the rightmost one's right border. + // We inset the mask on the left and right sides to accomplish this. + var g:Graphics = contentGroupMask.graphics; + g.clear(); + g.beginFill(0x00FF00); + g.drawRoundRect(borderThickness * 2, borderThickness, unscaledWidth - borderThickness * 4, unscaledHeight - borderThickness * 2, cornerRadius, cornerRadius); + g.endFill(); + + contentGroup.mask = contentGroupMask; + } + } +} diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListScrollerSkin.mxml b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListScrollerSkin.mxml new file mode 100644 index 0000000000..270818e903 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListScrollerSkin.mxml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListSkin.as new file mode 100644 index 0000000000..69e4b75584 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListSkin.as @@ -0,0 +1,267 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.layouts.VerticalSpinnerLayout; + import spark.skins.mobile.supportClasses.MobileSkin; + + import spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; + + import spark.components.SpinnerList; + import spark.components.SpinnerListItemRenderer; + + + 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; + switch (applicationDPI) + { + case DPIClassification.DPI_640: + case DPIClassification.DPI_480: + case DPIClassification.DPI_320: + { + borderThickness = 2; + break; + } + default: + { + selectionIndicatorHeight = 48; + borderThickness = 1; + } + } + + minWidth = 16; + } + + //-------------------------------------------------------------------------- + // + // 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); + } + + } +} diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerBackground.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerBackground.fxg new file mode 100644 index 0000000000..78d8c59701 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerBackground.fxg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerSelectionIndicator.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerSelectionIndicator.fxg new file mode 100644 index 0000000000..a8d8ae77e5 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerSelectionIndicator.fxg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerShadow.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerShadow.fxg new file mode 100644 index 0000000000..2f4362262d --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerShadow.fxg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + From 56cbd2167c2a1c546d8ac69718e6afc810ecc191 Mon Sep 17 00:00:00 2001 From: Om Date: Mon, 16 Jun 2014 02:29:28 -0700 Subject: [PATCH 022/392] Make SpinnerListSkin and SpinnerListContainerSkin work with all supported DPIs --- .../projects/mobiletheme/src/android4x.css | 47 +++++++++++++++++-- .../android4/SpinnerListContainerSkin.as | 18 +++---- .../spark/skins/android4/SpinnerListSkin.as | 43 +++++++++++++---- ...SpinnerListContainerSelectionIndicator.fxg | 6 +-- .../assets/SpinnerListContainerShadow.fxg | 4 +- 5 files changed, 90 insertions(+), 28 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css index 511511fd21..424b1d951c 100644 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -147,15 +147,14 @@ SpinnerList SpinnerListContainer { skinClass: ClassReference("spark.skins.android4.SpinnerListContainerSkin"); - backgroundAlpha: 0; } SpinnerListItemRenderer { paddingTop : 18; paddingBottom : 18; - paddingLeft : 16; - paddingRight : 16; + paddingLeft : 6; + paddingRight : 6; } TabbedViewNavigator #tabBar @@ -201,6 +200,14 @@ ToggleSwitch fontSize: 14; } + SpinnerListItemRenderer + { + paddingTop : 9; + paddingBottom : 9; + paddingLeft : 3; + paddingRight : 3; + } + TabbedViewNavigator #tabBar { fontSize: 11; @@ -228,6 +235,14 @@ ToggleSwitch fontSize: 18; } + SpinnerListItemRenderer + { + paddingTop : 12; + paddingBottom : 12; + paddingLeft : 4; + paddingRight : 4; + } + TabbedViewNavigator #tabBar { fontSize: 14; @@ -245,6 +260,7 @@ ToggleSwitch { fontSize: 24; } + } @media (application-dpi: 320) @@ -267,6 +283,14 @@ ToggleSwitch fontSize: 36; } + SpinnerListItemRenderer + { + paddingTop : 24; + paddingBottom : 24; + paddingLeft : 8; + paddingRight : 8; + } + TabbedViewNavigator #tabBar { fontSize: 28; @@ -294,6 +318,15 @@ ToggleSwitch fontSize: 54; } + SpinnerListItemRenderer + { + paddingTop : 36; + paddingBottom : 36; + paddingLeft : 12; + paddingRight : 12; + } + + TabbedViewNavigator #tabBar { fontSize: 42; @@ -320,6 +353,14 @@ ToggleSwitch fontSize: 72; } + SpinnerListItemRenderer + { + paddingTop : 48; + paddingBottom : 48; + paddingLeft : 16; + paddingRight : 16; + } + TabbedViewNavigator #tabBar { fontSize: 56; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as index f8b3779072..dd5ff92d39 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as @@ -24,14 +24,13 @@ package spark.skins.android4 import mx.core.DPIClassification; import mx.core.mx_internal; + import spark.components.Group; import spark.components.SpinnerListContainer; import spark.layouts.HorizontalLayout; - //import android skins import spark.skins.android4.assets.SpinnerListContainerBackground; import spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; - import spark.skins.android4.assets.SpinnerListContainerShadow - + import spark.skins.android4.assets.SpinnerListContainerShadow; import spark.skins.mobile.supportClasses.MobileSkin; use namespace mx_internal; @@ -66,46 +65,41 @@ package spark.skins.android4 borderClass = spark.skins.android4.assets.SpinnerListContainerBackground; selectionIndicatorClass = spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; shadowClass = spark.skins.android4.assets.SpinnerListContainerShadow; + cornerRadius = 0; switch (applicationDPI) { case DPIClassification.DPI_640: { - cornerRadius = 0; borderThickness = 3; selectionIndicatorHeight = 182; break; } case DPIClassification.DPI_480: { - cornerRadius = 0; borderThickness = 2; selectionIndicatorHeight = 144; break; } case DPIClassification.DPI_320: { - cornerRadius = 0; borderThickness = 2; selectionIndicatorHeight = 96; break; } case DPIClassification.DPI_240: { - cornerRadius = 0; borderThickness = 1; selectionIndicatorHeight = 72; break; } case DPIClassification.DPI_120: { - cornerRadius = 0; borderThickness = 0; - selectionIndicatorHeight = 37; + selectionIndicatorHeight = 36; break; } default: // default DPI_160 { - cornerRadius = 0; borderThickness = 1; selectionIndicatorHeight = 48; @@ -319,8 +313,8 @@ package spark.skins.android4 setElementSize(border, unscaledWidth - borderThickness * 2, unscaledHeight); setElementPosition(border, borderThickness, 0); - setElementSize(shadow, unscaledWidth - borderThickness * 4, unscaledHeight - borderThickness * 2); - setElementPosition(shadow, borderThickness * 2, borderThickness); + setElementSize(shadow, unscaledWidth - borderThickness * 4, measuredHeight - borderThickness * 2); + setElementPosition(shadow, borderThickness * 2, unscaledHeight/2 - measuredHeight/2); // The SpinnerLists contain a left and right border. We don't want to show the leftmost // SpinnerLists's left border nor the rightmost one's right border. diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListSkin.as index 69e4b75584..b86553fd91 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListSkin.as @@ -22,17 +22,15 @@ package spark.skins.android4 import mx.core.ClassFactory; import mx.core.DPIClassification; - import mx.core.mx_internal; - + 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.mobile.supportClasses.MobileSkin; - import spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; - - import spark.components.SpinnerList; - import spark.components.SpinnerListItemRenderer; + import spark.skins.mobile.supportClasses.MobileSkin; use namespace mx_internal; @@ -59,23 +57,52 @@ package spark.skins.android4 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; } } - minWidth = 16; } //-------------------------------------------------------------------------- diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerSelectionIndicator.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerSelectionIndicator.fxg index a8d8ae77e5..067afb1e11 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerSelectionIndicator.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerSelectionIndicator.fxg @@ -22,7 +22,7 @@ --> + scaleGridLeft="3" scaleGridTop="5.5" scaleGridRight="97" scaleGridBottom="41"> @@ -30,13 +30,13 @@ - + - + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerShadow.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerShadow.fxg index 2f4362262d..41c1955e7a 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerShadow.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/SpinnerListContainerShadow.fxg @@ -22,8 +22,8 @@ --> - + scaleGridLeft="1" scaleGridTop="1" scaleGridRight="99" scaleGridBottom="99"> + From 8a6efb4043cd39100b4147b1cd9caab5da921e6b Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Wed, 25 Jun 2014 20:00:58 +0200 Subject: [PATCH 023/392] Fix swc download URLs. Signed-off-by: Erik de Bruin --- ide/checkAllPlayerGlobals.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ide/checkAllPlayerGlobals.sh b/ide/checkAllPlayerGlobals.sh index 646121ba91..cd0d624a20 100755 --- a/ide/checkAllPlayerGlobals.sh +++ b/ide/checkAllPlayerGlobals.sh @@ -121,6 +121,6 @@ downloadPlayerGlobal 11.7 12656571c57b2ad641838e5695a00e27 http://download.macro downloadPlayerGlobal 11.8 35bc69eec5091f70e221b4e63b66b60f http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_8.swc downloadPlayerGlobal 11.9 d18244c3c00c61a41f2d4d791d09fedb http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_9.swc downloadPlayerGlobal 12.0 4db4e934f39f774ba68fcd9a79654971 http://download.macromedia.com/get/flashplayer/updaters/12/playerglobal12_0.swc -downloadPlayerGlobal 13.0 7f9bfe038f00e97bc44abf52bb5b1260 http://download.macromedia.com/get/flashplayer/updaters/12/playerglobal13_0.swc -downloadPlayerGlobal 14.0 2465d2fcf0d985ed10231b43f61c3024 http://download.macromedia.com/get/flashplayer/updaters/12/playerglobal14_0.swc +downloadPlayerGlobal 13.0 7f9bfe038f00e97bc44abf52bb5b1260 http://download.macromedia.com/get/flashplayer/updaters/13/playerglobal13_0.swc +downloadPlayerGlobal 14.0 2465d2fcf0d985ed10231b43f61c3024 http://download.macromedia.com/get/flashplayer/updaters/14/playerglobal14_0.swc From 4eec88126b54474f567f5949cae7397715622756 Mon Sep 17 00:00:00 2001 From: Om Date: Wed, 2 Jul 2014 01:45:19 -0700 Subject: [PATCH 024/392] Fix visual artifacts in Callout skin --- .../projects/mobiletheme/src/android4x.css | 26 ++++++ .../src/spark/skins/android4/CalloutSkin.as | 82 +++++++++---------- 2 files changed, 64 insertions(+), 44 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css index 424b1d951c..956a78cbda 100644 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -119,6 +119,27 @@ Callout ViewNavigator ActionBar paddingRight: 0; } +Callout ViewNavigator ActionBar.beveled +{ + paddingLeft: 0; + paddingRight: 0; +} + +Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button +{ + skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); +} + +Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button.emphasized +{ + skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); +} + +Callout#viewNavigatorPopUp +{ + contentBackgroundAppearance: none; +} + CheckBox { @@ -181,6 +202,11 @@ ToggleSwitch textShadowColor: #000000; } +ViewNavigator +{ + skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); +} + @media (application-dpi: 120) { global diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/CalloutSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/CalloutSkin.as index 3bb0e48478..95fefef02c 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/CalloutSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/CalloutSkin.as @@ -42,12 +42,6 @@ import spark.primitives.RectangularDropShadow; import spark.skins.android4.assets.CalloutContentBackground; import spark.skins.android4.supportClasses.CalloutArrow; import spark.skins.mobile.supportClasses.MobileSkin; -import spark.skins.mobile120.assets.CalloutContentBackground; -import spark.skins.mobile160.assets.CalloutContentBackground; -import spark.skins.mobile240.assets.CalloutContentBackground; -import spark.skins.mobile320.assets.CalloutContentBackground; -import spark.skins.mobile480.assets.CalloutContentBackground; -import spark.skins.mobile640.assets.CalloutContentBackground; use namespace mx_internal; @@ -102,29 +96,28 @@ public class CalloutSkin extends MobileSkin { case DPIClassification.DPI_640: { - // Note provisional may need changes - backgroundCornerRadius = 32; + + backgroundCornerRadius = 24; backgroundGradientHeight = 440; - frameThickness = 32; - arrowWidth = 208; - arrowHeight = 104; - contentCornerRadius = 20; + frameThickness = 12; + arrowWidth = 160; + arrowHeight = 80; + contentCornerRadius = 40; dropShadowBlurX = 64; dropShadowBlurY = 64; dropShadowDistance = 12; - highlightWeight = 2; + highlightWeight = 4; break; } case DPIClassification.DPI_480: { - // Note provisional may need changes - backgroundCornerRadius = 24; + backgroundCornerRadius = 16; backgroundGradientHeight = 330; - frameThickness = 24; - arrowWidth = 156; - arrowHeight = 78; - contentCornerRadius = 14; + frameThickness = 8; + arrowWidth = 120; + arrowHeight = 60; + contentCornerRadius = 28; dropShadowBlurX = 48; dropShadowBlurY = 48; dropShadowDistance = 8; @@ -134,27 +127,28 @@ public class CalloutSkin extends MobileSkin } case DPIClassification.DPI_320: { - backgroundCornerRadius = 16; - backgroundGradientHeight = 220; - frameThickness = 16; - arrowWidth = 104; - arrowHeight = 52; - contentCornerRadius = 10; - dropShadowBlurX = 32; - dropShadowBlurY = 32; - dropShadowDistance = 6; - highlightWeight = 2; + + backgroundCornerRadius = 12; + backgroundGradientHeight = 220; + frameThickness = 6; + arrowWidth = 80; + arrowHeight = 40; + contentCornerRadius = 20; + dropShadowBlurX = 32; + dropShadowBlurY = 32; + dropShadowDistance = 6; + highlightWeight = 2; break; } case DPIClassification.DPI_240: { - backgroundCornerRadius = 12; + backgroundCornerRadius = 8; backgroundGradientHeight = 165; - frameThickness = 12; - arrowWidth = 78; - arrowHeight = 39; - contentCornerRadius = 7; + frameThickness = 4; + arrowWidth = 60; + arrowHeight = 30; + contentCornerRadius = 14; dropShadowBlurX = 24; dropShadowBlurY = 24; dropShadowDistance = 4; @@ -164,12 +158,12 @@ public class CalloutSkin extends MobileSkin } case DPIClassification.DPI_120: { - backgroundCornerRadius = 6; + backgroundCornerRadius = 4; backgroundGradientHeight = 83; - frameThickness = 6; - arrowWidth = 39; - arrowHeight = 19; - contentCornerRadius = 4; + frameThickness = 2; + arrowWidth = 30; + arrowHeight = 15; + contentCornerRadius = 7; dropShadowBlurX = 12; dropShadowBlurY = 12; dropShadowDistance = 2; @@ -180,12 +174,12 @@ public class CalloutSkin extends MobileSkin default: { // default DPI_160 - backgroundCornerRadius = 8; + backgroundCornerRadius = 6; backgroundGradientHeight = 110; - frameThickness = 2; - arrowWidth = 52; - arrowHeight = 26; - contentCornerRadius = 5; + frameThickness = 3; + arrowWidth = 40; + arrowHeight = 20; + contentCornerRadius = 10; dropShadowBlurX = 16; dropShadowBlurY = 16; dropShadowDistance = 3; From 3167e429fa7051e40858a45f14c6ea433f630e40 Mon Sep 17 00:00:00 2001 From: Mihai C Date: Wed, 2 Jul 2014 17:32:42 +0100 Subject: [PATCH 025/392] Spelling and logic improvements. --- README | 4 ++-- env-template.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README b/README index f5c47d6ee8..1f17d23947 100644 --- a/README +++ b/README @@ -320,7 +320,7 @@ Adobe Flash Player Version Support Change the playerglobal.version in the build.properties file to have a value other than "11.1". For this change to take effect the SDK needs to be recompiled. For example to compile against the latest version of the Adobe - Flash Player set the value of playerglobal.version to be "12.0" like so: + Flash Player set the value of playerglobal.version to be "13.0" like so: playerglobal.version = 13.0 @@ -399,7 +399,7 @@ Adobe Flash Player Version Support release. It compiles against other Adobe Flash Player versions and is expected to - work but their may be some issues, particularly with the earlier 10.2 and + work but there may be some issues, particularly with the earlier 10.2 and 10.3 versions of the Adobe Flash Player. Software Dependencies diff --git a/env-template.properties b/env-template.properties index d1465ca123..ab7cb89d94 100644 --- a/env-template.properties +++ b/env-template.properties @@ -17,7 +17,7 @@ ## ################################################################################ -# If you chose to use this file rename it to env.properties and uncomment the +# If you choose to use this file, rename it to env.properties and uncomment the # properties you wish to set. # For each of From 16d30c9c00c4689aa60a73968550d5af2b7b0373 Mon Sep 17 00:00:00 2001 From: Mihai C Date: Wed, 2 Jul 2014 17:46:34 +0100 Subject: [PATCH 026/392] FLEX-34385 documenting unusual try-catch behaviour after event dispatch. --- frameworks/projects/framework/src/mx/core/UIComponent.as | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frameworks/projects/framework/src/mx/core/UIComponent.as b/frameworks/projects/framework/src/mx/core/UIComponent.as index 06ae5864a0..7432f5238f 100644 --- a/frameworks/projects/framework/src/mx/core/UIComponent.as +++ b/frameworks/projects/framework/src/mx/core/UIComponent.as @@ -13659,6 +13659,12 @@ public class UIComponent extends FlexSprite * Dispatches an event into the event flow. * The event target is the EventDispatcher object upon which * the dispatchEvent() method is called. + * + * Note that when dispatchEvent() is called by code inside a + * try block, any error thrown thereafter can be caught by + * listening to LoaderInfo.uncaughtErrorEvents. It will NOT reach the + * catch block. + * * * @param event The Event object that is dispatched into the event flow. * If the event is being redispatched, a clone of the event is created automatically. From 75236aa867f3962452d1a4497923b5b653372cb5 Mon Sep 17 00:00:00 2001 From: Om Date: Wed, 2 Jul 2014 16:52:14 -0700 Subject: [PATCH 027/392] StageTextInput and StageTextArea skins. Contributed by Aaron Nash. Details here: https://issues.apache.org/jira/browse/FLEX-34395 --- .../projects/mobiletheme/src/android4x.css | 28 + .../spark/skins/android4/StageTextAreaSkin.as | 190 ++++ .../skins/android4/StageTextInputSkin.as | 134 +++ .../src/spark/skins/android4/TextAreaSkin.as | 892 ++++++++++++++++++ .../src/spark/skins/android4/TextInputSkin.as | 376 ++++++++ .../src/spark/skins/android4/TextSkinBase.as | 212 +++++ .../supportClasses/StageTextSkinBase.as | 402 ++++++++ 7 files changed, 2234 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/StageTextAreaSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/StageTextInputSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/TextAreaSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/TextInputSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/TextSkinBase.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/StageTextSkinBase.as diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css index 956a78cbda..02511c108f 100644 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -192,6 +192,34 @@ TabbedViewNavigator #tabBar textShadowColor: #000000; } +TextInput +{ + skinClass: ClassReference("spark.skins.android4.StageTextInputSkin"); + contentBackgroundAlpha: 0; + focusColor: #33B5E5; + borderColor: #4e4e4e; + contentBackgroundColor: #DEDEDD; + contentBackgroundBorder: "flat"; + fontFamily: RobotoRegular; + selectionHighlighting: "never"; + focusEnabled:"false"; + focusThickness: 0; +} + +TextArea +{ + skinClass: ClassReference("spark.skins.android4.StageTextAreaSkin"); + contentBackgroundAlpha: 1; + contentBackgroundBorder: "rectangle"; + contentBackgroundColor: #DEDEDD; + focusColor: #33B5E5; + fontFamily: RobotoRegular; + borderColor: #4e4e4e; + selectionHighlighting: "never"; + focusEnabled:"false"; + focusThickness: 0; +} + ToggleSwitch { accentColor: #3F7FBA; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/StageTextAreaSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/StageTextAreaSkin.as new file mode 100644 index 0000000000..d48377d3f3 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/StageTextAreaSkin.as @@ -0,0 +1,190 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 spark.components.TextArea; + import spark.components.supportClasses.IStyleableEditableText; + import spark.components.supportClasses.ScrollableStageText; + 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 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); + + 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); + } + } + + override protected function createTextDisplay():IStyleableEditableText + { + return new ScrollableStageText(multiline); + } + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/StageTextInputSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/StageTextInputSkin.as new file mode 100644 index 0000000000..7f92647f1d --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/StageTextInputSkin.as @@ -0,0 +1,134 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.IStyleableEditableText; + import spark.components.supportClasses.ScrollableStageText; + 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); + } + } + + override protected function createTextDisplay():IStyleableEditableText + { + return new ScrollableStageText(multiline); + } + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/TextAreaSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/TextAreaSkin.as new file mode 100644 index 0000000000..0c231a6f2d --- /dev/null +++ b/frameworks/projects/mobiletheme/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(); + } + +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/TextInputSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/TextInputSkin.as new file mode 100644 index 0000000000..5a7a35e524 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/TextInputSkin.as @@ -0,0 +1,376 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.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.android4.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 = 4; + flatheight = 9; + break; + } + case DPIClassification.DPI_480: + { + + measuredDefaultWidth = 880; + measuredDefaultHeight = 100; + layoutBorderSize = 3; + flatheight = 7; + break; + } + case DPIClassification.DPI_320: + { + measuredDefaultWidth = 600; + measuredDefaultHeight = 66; + layoutBorderSize = 2; + flatheight = 6; + break; + } + case DPIClassification.DPI_240: + { + measuredDefaultWidth = 440; + measuredDefaultHeight = 50; + layoutBorderSize = 2; + flatheight = 5; + break; + } + case DPIClassification.DPI_120: + { + measuredDefaultWidth = 220; + measuredDefaultHeight = 25; + layoutBorderSize = 1; + flatheight = 2; + break; + } + default: + { + measuredDefaultWidth = 300; + measuredDefaultHeight = 33; + layoutBorderSize = 1; + flatheight = 3; + break; + } + } + addEventListener(FocusEvent.FOCUS_IN, focusChangeHandler); + addEventListener(FocusEvent.FOCUS_OUT, focusChangeHandler); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + protected var isFocused:Boolean = false; + + protected var flatheight: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 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 + */ + 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(); + } + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/TextSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/TextSkinBase.as new file mode 100644 index 0000000000..79ce852e94 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/TextSkinBase.as @@ -0,0 +1,212 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.supportClasses +{ + + import flash.display.DisplayObject; + + import mx.core.mx_internal; + + import spark.components.supportClasses.StyleableTextField; + + use namespace mx_internal; + + /** + * ActionScript-based skin for text input controls in mobile applications that + * uses a StyleableTextField class for the text display. + * + * @see spark.components.supportClasses.StyleableTextField + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class TextSkinBase extends MobileSkin + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function TextSkinBase() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Graphics variables + // + //-------------------------------------------------------------------------- + + /** + * Defines the border. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + + //-------------------------------------------------------------------------- + // + // Layout variables + // + //-------------------------------------------------------------------------- + + /** + * Defines the corner radius. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + + protected var layoutBorderSize:uint; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * + * Instance of the border graphics. + */ + protected var border:DisplayObject; + + private var borderVisibleChanged:Boolean = false; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * textDisplay skin part. + */ + public var textDisplay:StyleableTextField; + + [Bindable] + /** + * Bindable promptDisplay skin part. Bindings fire when promptDisplay is + * removed and added for proper updating by the SkinnableTextBase. + */ + public var promptDisplay:StyleableTextField; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (!textDisplay) + { + textDisplay = StyleableTextField(createInFontContext(StyleableTextField)); + textDisplay.styleName = this; + textDisplay.editable = true; + textDisplay.useTightTextBounds = false; + addChild(textDisplay); + } + } + + /** + * @private + */ + protected function createPromptDisplay():StyleableTextField + { + var prompt:StyleableTextField = StyleableTextField(createInFontContext(StyleableTextField)); + prompt.styleName = this; + prompt.editable = false; + prompt.mouseEnabled = false; + prompt.useTightTextBounds = false; + prompt.focusEnabled = false; + return prompt; + } + + /** + * @private + */ + + override public function styleChanged(styleProp:String):void + { + var allStyles:Boolean = !styleProp || styleProp == "styleName"; + + if (allStyles || styleProp == "borderVisible") + { + borderVisibleChanged = true; + invalidateProperties(); + } + + if (allStyles || styleProp.indexOf("padding") == 0) + { + invalidateDisplayList(); + } + + super.styleChanged(styleProp); + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + alpha = currentState.indexOf("disabled") == -1 ? 1 : 0.5; + + var showPrompt:Boolean = currentState.indexOf("WithPrompt") >= 0; + + if (showPrompt && !promptDisplay) + { + promptDisplay = createPromptDisplay(); + addChild(promptDisplay); + } + else if (!showPrompt && promptDisplay) + { + removeChild(promptDisplay); + promptDisplay = null; + } + + invalidateDisplayList(); + } + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/StageTextSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/StageTextSkinBase.as new file mode 100644 index 0000000000..3dc1966264 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/StageTextSkinBase.as @@ -0,0 +1,402 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.supportClasses +{ +import flash.display.DisplayObject; +import flash.events.FocusEvent; + +import mx.core.DPIClassification; +import mx.core.mx_internal; + +import spark.components.supportClasses.IStyleableEditableText; +import spark.components.supportClasses.SkinnableTextBase; +import spark.components.supportClasses.StyleableStageText; +import spark.components.supportClasses.StyleableTextField; +import spark.core.IDisplayText; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * ActionScript-based skin for text input controls in mobile applications. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ +public class StageTextSkinBase extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + * + */ + public function StageTextSkinBase() + { + super(); + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + measuredDefaultWidth = 1200; + measuredDefaultHeight = 132; + layoutBorderSize = 4; + flatheight = 9; + break; + } + case DPIClassification.DPI_480: + { + measuredDefaultWidth = 880; + measuredDefaultHeight = 100; + layoutBorderSize = 3; + flatheight = 7; + break; + } + case DPIClassification.DPI_320: + { + measuredDefaultWidth = 600; + measuredDefaultHeight = 66; + layoutBorderSize = 2; + flatheight = 6; + break; + } + case DPIClassification.DPI_240: + { + measuredDefaultWidth = 440; + measuredDefaultHeight = 50; + layoutBorderSize = 2; + flatheight = 5; + break; + } + case DPIClassification.DPI_120: + { + measuredDefaultWidth = 220; + measuredDefaultHeight = 25; + layoutBorderSize = 1; + flatheight = 2; + break; + } + default: + { + measuredDefaultWidth = 300; + measuredDefaultHeight = 33; + layoutBorderSize = 1; + flatheight = 3; + break; + } + + } + addEventListener(FocusEvent.FOCUS_IN, focusChangeHandler); + addEventListener(FocusEvent.FOCUS_OUT, focusChangeHandler); + } + + //-------------------------------------------------------------------------- + // + // Graphics variables + // + //-------------------------------------------------------------------------- + + + //-------------------------------------------------------------------------- + // + // Layout variables + // + //-------------------------------------------------------------------------- + + /** + * Defines the border's thickness. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + protected var layoutBorderSize:uint; + + protected var flatheight:uint; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + protected var isFocused:Boolean = false; + + /** + * @private + * + * Instance of the border graphics. + */ + protected var border:DisplayObject; + + private var borderVisibleChanged:Boolean = false; + + /** + * @private + * + * Multiline flag. + */ + protected var multiline:Boolean = false; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * textDisplay skin part. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + public var textDisplay:IStyleableEditableText; + + [Bindable] + /** + * Bindable promptDisplay skin part. Bindings fire when promptDisplay is + * removed and added for proper updating by the SkinnableTextBase. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + public var promptDisplay:IDisplayText; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (!textDisplay) + { + textDisplay = createTextDisplay(); + textDisplay.editable = true; + textDisplay.styleName = this; + this.addChild(DisplayObject(textDisplay)); + } + } + + /** Could be overridden by subclasses + * + * @return instance of IStyleableEditableText + */ + protected function createTextDisplay():IStyleableEditableText + { + return new StyleableStageText(multiline); + } + + /** + * @private + */ + 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; + } + var halfGap:int = flatheight * 2; + // change the border type + if (getStyle("contentBackgroundBorder") == "flat") + { + //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 + */ + override public function styleChanged(styleProp:String):void + { + var allStyles:Boolean = !styleProp || styleProp == "styleName"; + + if (allStyles || styleProp == "borderVisible") + { + borderVisibleChanged = true; + invalidateProperties(); + } + + if (allStyles || styleProp.indexOf("padding") == 0) + { + invalidateDisplayList(); + } + super.styleChanged(styleProp); + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + alpha = currentState.indexOf("disabled") == -1 ? 1 : 0.5; + + var showPrompt:Boolean = currentState.indexOf("WithPrompt") != -1; + + if (showPrompt && !promptDisplay) + { + promptDisplay = createPromptDisplay(); + promptDisplay.addEventListener(FocusEvent.FOCUS_IN, promptDisplay_focusInHandler); + } + else if (!showPrompt && promptDisplay) + { + promptDisplay.removeEventListener(FocusEvent.FOCUS_IN, promptDisplay_focusInHandler); + removeChild(promptDisplay as DisplayObject); + promptDisplay = null; + } + super.commitCurrentState(); + + invalidateDisplayList(); + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Create a control appropriate for displaying the prompt text in a mobile + * input field. + */ + protected function createPromptDisplay():IDisplayText + { + var prompt:StyleableTextField = StyleableTextField(createInFontContext(StyleableTextField)); + prompt.styleName = this; + prompt.editable = false; + prompt.mouseEnabled = false; + prompt.useTightTextBounds = false; + // StageText objects appear in their own layer on top of the display + // list. So, even though this prompt may be created after the StageText + // for textDisplay, textDisplay will still be on top. + addChild(prompt); + + return prompt; + } + + /** + * @private + * Utility function used by subclasses' measure functions to measure their + * text host components. + */ + protected function measureTextComponent(hostComponent:SkinnableTextBase):void + { + 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"); + + if (textDisplay) + { + textHeight = getElementPreferredHeight(textDisplay); + } + // 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, (textHeight - 2)); + measuredWidth = (characterWidth * hostComponent.maxChars) + paddingLeft + paddingRight; + } + + measuredHeight = paddingTop + textHeight + paddingBottom; + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + /** + * Listen to see if the component gains focus then change the style to selected + */ + private function focusChangeHandler(event:FocusEvent):void + { + isFocused = event.type == FocusEvent.FOCUS_IN; + invalidateDisplayList(); + } + + /** + * If the prompt is focused, we need to move focus to the textDisplay + * StageText. This needs to happen outside of the process of setting focus + * to the prompt, so we use callLater to do that. + */ + private function focusTextDisplay():void + { + textDisplay.setFocus(); + } + + private function promptDisplay_focusInHandler(event:FocusEvent):void + { + callLater(focusTextDisplay); + } +} +} \ No newline at end of file From 0d2342a0111474a161ace47aa8b190e00c2943c1 Mon Sep 17 00:00:00 2001 From: Justin Mclean Date: Sun, 6 Jul 2014 12:17:41 +1000 Subject: [PATCH 028/392] FLEX-34378 count multiple separators as an invalid date format --- .../framework/src/mx/validators/DateValidator.as | 15 ++++++++++++++- .../Properties/Validators_Date_Properties.mxml | 15 +++++++++++++++ .../Validators_Date_Properties_spark.mxml | 15 +++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/frameworks/projects/framework/src/mx/validators/DateValidator.as b/frameworks/projects/framework/src/mx/validators/DateValidator.as index 89138cc14a..f833ee293a 100644 --- a/frameworks/projects/framework/src/mx/validators/DateValidator.as +++ b/frameworks/projects/framework/src/mx/validators/DateValidator.as @@ -211,7 +211,7 @@ public class DateValidator extends Validator else { noSeperators = false; - formatParts[part] = formatChar; + formatParts[part] += formatChar; } lastFormatChar = formatChar; @@ -388,6 +388,19 @@ public class DateValidator extends Validator dateObj.year = dateParts[part]; yearPart = formatParts[part]; } + else if (!noSeperators) + { + // separator part, we have valid separator characters just validate against + // repeating separator values, validate now as we could have multiple separators + if (dateParts[part].length != formatParts[part].length) + { + results.push(new ValidationResult( + true, baseField, "wrongLength", + validator.wrongLengthError + + (DateValidator._includeFormatInError?" " + inputFormat:""))); + return results; + } + } } // DD or D format diff --git a/mustella/tests/Validators/DateValidator/Properties/Validators_Date_Properties.mxml b/mustella/tests/Validators/DateValidator/Properties/Validators_Date_Properties.mxml index 1432fb67ce..0cdbfbdb39 100644 --- a/mustella/tests/Validators/DateValidator/Properties/Validators_Date_Properties.mxml +++ b/mustella/tests/Validators/DateValidator/Properties/Validators_Date_Properties.mxml @@ -184,6 +184,21 @@ + + + + + + + + + + + + + + + diff --git a/mustella/tests/Validators/DateValidator/Properties/Validators_Date_Properties_spark.mxml b/mustella/tests/Validators/DateValidator/Properties/Validators_Date_Properties_spark.mxml index 997f4927e0..784d6f5501 100644 --- a/mustella/tests/Validators/DateValidator/Properties/Validators_Date_Properties_spark.mxml +++ b/mustella/tests/Validators/DateValidator/Properties/Validators_Date_Properties_spark.mxml @@ -259,6 +259,21 @@ + + + + + + + + + + + + + + + From d478f6852014a6c8c955580c737e6fe7d40a5f93 Mon Sep 17 00:00:00 2001 From: Justin Mclean Date: Mon, 7 Jul 2014 18:56:51 +1000 Subject: [PATCH 029/392] FLEX-34285 fix issue with remove --- .../projects/framework/src/mx/collections/XMLListAdapter.as | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/projects/framework/src/mx/collections/XMLListAdapter.as b/frameworks/projects/framework/src/mx/collections/XMLListAdapter.as index 17d2173b17..0a8b5e6e02 100644 --- a/frameworks/projects/framework/src/mx/collections/XMLListAdapter.as +++ b/frameworks/projects/framework/src/mx/collections/XMLListAdapter.as @@ -420,7 +420,7 @@ public class XMLListAdapter extends EventDispatcher implements IList, IXMLNotifi */ public function removeItem( item:Object ):Boolean { - var _item:Object = removeItemAt[getItemIndex(item)]; + var _item:Object = removeItemAt(getItemIndex(item)); return _item != null; } From b019135c8e4d8193f8c7d21ce4bc745bb7e82b3c Mon Sep 17 00:00:00 2001 From: Om Date: Mon, 7 Jul 2014 18:07:23 -0700 Subject: [PATCH 030/392] Added new component MobileBusyIndicator. It has only an Android 4.x specific skin for now. The current BusyIndicator's visual will be moved into an iOS 7 specific skin later. --- .../projects/mobilecomponents/manifest.xml | 1 + .../spark/components/MobileBusyIndicator.as | 181 ++++++++++++++++++ .../projects/mobiletheme/src/android4x.css | 6 +- .../spark/skins/android4/BusyIndicatorSkin.as | 180 +++++++++++++++++ .../src/spark/skins/android4/HSliderSkin.as | 1 - .../skins/android4/assets/BusyIndicator.fxg | 64 +++++++ 6 files changed, 431 insertions(+), 2 deletions(-) create mode 100644 frameworks/projects/mobilecomponents/src/spark/components/MobileBusyIndicator.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/BusyIndicatorSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/BusyIndicator.fxg diff --git a/frameworks/projects/mobilecomponents/manifest.xml b/frameworks/projects/mobilecomponents/manifest.xml index 810bfeafb2..4e85f42fbf 100644 --- a/frameworks/projects/mobilecomponents/manifest.xml +++ b/frameworks/projects/mobilecomponents/manifest.xml @@ -25,6 +25,7 @@ --> + diff --git a/frameworks/projects/mobilecomponents/src/spark/components/MobileBusyIndicator.as b/frameworks/projects/mobilecomponents/src/spark/components/MobileBusyIndicator.as new file mode 100644 index 0000000000..a25755797e --- /dev/null +++ b/frameworks/projects/mobilecomponents/src/spark/components/MobileBusyIndicator.as @@ -0,0 +1,181 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 components +{ + import flash.events.Event; + import mx.core.IUIComponent; + import mx.core.IVisualElement; + import mx.events.FlexEvent; + import mx.states.State; + import spark.components.supportClasses.SkinnableComponent; + + [SkinState("rotatingState")] + [SkinState("notRotatingState")] + + public class MobileBusyIndicator extends SkinnableComponent + { + private var effectiveVisibility:Boolean = false; + private var effectiveVisibilityChanged:Boolean = true; + + public function BusyIndicator() + { + super(); + // Listen to added to stage and removed from stage. + // Start rotating when we are on the stage and stop + // when we are removed from the stage. + addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler); + addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler); + states = [ + new State({name:"notRotatingState"}), + new State({name:"rotatingState"}) + ]; + } + + override protected function getCurrentSkinState():String + { + return currentState; + } + + private function addedToStageHandler(event:Event):void + { + // Check our visibility here since we haven't added + // visibility listeners yet. + computeEffectiveVisibility(); + + if (canRotate()) + currentState = "rotatingState"; + + addVisibilityListeners(); + invalidateSkinState(); + } + + private function removedFromStageHandler(event:Event):void + { + currentState = "notRotatingState"; + + removeVisibilityListeners(); + invalidateSkinState(); + } + + private function computeEffectiveVisibility():void + { + + // Check our design layer first. + if (designLayer && !designLayer.effectiveVisibility) + { + effectiveVisibility = false; + return; + } + + // Start out with true visibility and enablement + // then loop up parent-chain to see if any of them are false. + effectiveVisibility = true; + var current:IVisualElement = this; + + while (current) + { + if (!current.visible) + { + if (!(current is IUIComponent) || !IUIComponent(current).isPopUp) + { + // Treat all pop ups as if they were visible. This is to + // fix a bug where the BusyIndicator does not spin when it + // is inside modal popup. The problem is in we do not get + // an event when the modal window is made visible in + // PopUpManagerImpl.fadeInEffectEndHandler(). When the modal + // window is made visible, setVisible() is passed "true" so + // as to not send an event. When do get events when the + // non-modal windows are popped up. Only modal windows are + // a problem. + // The downside of this fix is BusyIndicator components that are + // inside of hidden, non-modal, popup windows will paint themselves + // on a timer. + effectiveVisibility = false; + break; + } + } + + current = current.parent as IVisualElement; + } + } + + /** + * The BusyIndicator can be rotated if it is both on the display list and + * visible. + * + * @returns true if the BusyIndicator can be rotated, false otherwise. + */ + private function canRotate():Boolean + { + if (effectiveVisibility && stage != null) + return true; + + return false; + } + + + /** + * @private + * Add event listeners for SHOW and HIDE on all the ancestors up the parent chain. + * Adding weak event listeners just to be safe. + */ + private function addVisibilityListeners():void + { + var current:IVisualElement = this.parent as IVisualElement; + while (current) + { + // add visibility listeners to the parent + current.addEventListener(FlexEvent.HIDE, visibilityChangedHandler, false, 0, true); + current.addEventListener(FlexEvent.SHOW, visibilityChangedHandler, false, 0, true); + + current = current.parent as IVisualElement; + } + } + + /** + * @private + * Remove event listeners for SHOW and HIDE on all the ancestors up the parent chain. + */ + private function removeVisibilityListeners():void + { + var current:IVisualElement = this; + while (current) + { + current.removeEventListener(FlexEvent.HIDE, visibilityChangedHandler, false); + current.removeEventListener(FlexEvent.SHOW, visibilityChangedHandler, false); + + current = current.parent as IVisualElement; + } + } + + /** + * @private + * Event call back whenever the visibility of us or one of our ancestors + * changes + */ + private function visibilityChangedHandler(event:FlexEvent):void + { + effectiveVisibilityChanged = true; + invalidateProperties(); + } + + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css index 02511c108f..b04cb20035 100644 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -19,7 +19,6 @@ @namespace "library://ns.adobe.com/flex/spark"; - /* Android 4.x specific Mobile Flex skins and styles. Defaults sizes are based on application-dpi=240. @@ -82,6 +81,11 @@ ActionBar Group#actionGroup Button skinClass: ClassReference("spark.skins.android4.TransparentActionButtonSkin"); } +MobileBusyIndicator +{ + skinClass: ClassReference("spark.skins.android4.BusyIndicatorSkin"); +} + Button { fontFamily: RobotoBold; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/BusyIndicatorSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/BusyIndicatorSkin.as new file mode 100644 index 0000000000..dd3aa44908 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/BusyIndicatorSkin.as @@ -0,0 +1,180 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + import flash.events.TimerEvent; + import flash.geom.Matrix; + import flash.utils.Timer; + import mx.core.DPIClassification; + import spark.skins.android4.assets.BusyIndicator; + import spark.skins.mobile.supportClasses.MobileSkin; + + import spark.components.MobileBusyIndicator; + + public class BusyIndicatorSkin extends MobileSkin + { + static private const DEFAULT_ROTATION_INTERVAL:Number = 50; + private var busyIndicatorClass:Class; + private var busyIndicator:DisplayObject; + private var busyIndicatorDiameter:Number; + private var rotationTimer:Timer; + private var rotationInterval:Number; + /** + * @private + * + * Current rotation of this component in degrees. + */ + private var currentRotation:Number = 0; + + public function BusyIndicatorSkin() + { + super(); + + busyIndicatorClass = spark.skins.android4.assets.BusyIndicator; + rotationInterval = getStyle("rotationInterval"); + if (isNaN(rotationInterval)) + rotationInterval = DEFAULT_ROTATION_INTERVAL; + if (rotationInterval < 16.6) + rotationInterval = 16.6; + + switch(applicationDPI) + { + case DPIClassification.DPI_640: + { + busyIndicatorDiameter = 144; + break; + } + case DPIClassification.DPI_480: + { + busyIndicatorDiameter = 108; + break; + } + case DPIClassification.DPI_320: + { + busyIndicatorDiameter = 72; + break; + } + case DPIClassification.DPI_240: + { + busyIndicatorDiameter = 54; + break; + } + case DPIClassification.DPI_120: + { + busyIndicatorDiameter = 27; + break; + } + default://160 DPI + { + busyIndicatorDiameter = 36; + break; + } + } + } + + private var _hostComponent:spark.components.MobileBusyIndicator; + + public function get hostComponent():spark.components.MobileBusyIndicator + { + return _hostComponent; + } + + public function set hostComponent(value:spark.components.MobileBusyIndicator):void + { + _hostComponent = value; + } + + override protected function createChildren():void + { + busyIndicator = new busyIndicatorClass(); + busyIndicator.width = busyIndicator.height = busyIndicatorDiameter; + addChild(busyIndicator); + } + + override protected function measure():void + { + measuredWidth = busyIndicatorDiameter; + measuredHeight = busyIndicatorDiameter; + + measuredMinHeight = busyIndicatorDiameter; + measuredMinWidth = busyIndicatorDiameter + } + + override protected function commitCurrentState():void + { + super.commitCurrentState(); + if(currentState == "rotatingState") + { + startRotation(); + } + else + { + stopRotation(); + } + } + + private function startRotation():void + { + rotationTimer = new Timer(rotationInterval); + if (!rotationTimer.hasEventListener(TimerEvent.TIMER)) + { + rotationTimer.addEventListener(TimerEvent.TIMER, timerHandler); + rotationTimer.start(); + } + } + + private function stopRotation():void + { + if (rotationTimer) + { + rotationTimer.removeEventListener(TimerEvent.TIMER, timerHandler); + rotationTimer.stop(); + rotationTimer = null; + } + } + + /** + * @private + * + * Rotate the spinner once for each timer event. + */ + private function timerHandler(event:TimerEvent):void + { + currentRotation += rotationInterval; + if (currentRotation >= 360) + currentRotation = 0; + + rotate(busyIndicator,currentRotation,measuredWidth/2,measuredHeight/2); + event.updateAfterEvent(); + } + + private var rotationMatrix:Matrix; + private function rotate(obj:DisplayObject, angle:Number, aroundX:Number, aroundY:Number):void + { + rotationMatrix = new Matrix(); + rotationMatrix.translate(-aroundX,-aroundY); + rotationMatrix.rotate(Math.PI*angle/180); + rotationMatrix.translate(aroundX,aroundY); + obj.transform.matrix = rotationMatrix; + } + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as index 66ee390381..6f970f850e 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/HSliderSkin.as @@ -28,7 +28,6 @@ package spark.skins.android4 import spark.components.Button; import spark.components.HSlider; - import spark.components.ToggleSwitch; import spark.skins.android4.assets.HSliderTrack_filled; import spark.skins.mobile.supportClasses.HSliderDataTip; import spark.skins.mobile.supportClasses.MobileSkin; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/BusyIndicator.fxg b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/BusyIndicator.fxg new file mode 100644 index 0000000000..e04248f34b --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/BusyIndicator.fxg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From b2fa2be67d17b20f164d37c71200cae731084670 Mon Sep 17 00:00:00 2001 From: Om Date: Tue, 8 Jul 2014 17:05:36 -0700 Subject: [PATCH 031/392] Cleaned up SpinnerListContainerSkin while working on DateSpinnerSkin Fixed ActionBarSkin (removed unnecessary bottom padding) --- .../projects/mobiletheme/src/android4x.css | 15 --------------- .../src/spark/skins/android4/ActionBarSkin.as | 18 +----------------- .../skins/android4/SpinnerListContainerSkin.as | 7 +------ 3 files changed, 2 insertions(+), 38 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css index b04cb20035..280ca6ec88 100644 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -144,7 +144,6 @@ Callout#viewNavigatorPopUp contentBackgroundAppearance: none; } - CheckBox { skinClass: ClassReference("spark.skins.android4.CheckBoxSkin"); @@ -307,20 +306,6 @@ ViewNavigator } } -@media (application-dpi: 240) -{ - global - { - fontSize: 20; - } - - ActionBar #titleDisplay - { - fontSize: 24; - } - -} - @media (application-dpi: 320) { global diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as index d8fbd8fcb8..8ffbe4d2a1 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ActionBarSkin.as @@ -91,7 +91,6 @@ public class ActionBarSkin extends MobileSkin case DPIClassification.DPI_640: { borderSize = 2; - layoutShadowHeight = 12; layoutContentGroupHeight = 172; layoutTitleGroupHorizontalPadding = 52; @@ -99,9 +98,7 @@ public class ActionBarSkin extends MobileSkin } case DPIClassification.DPI_480: { - // Note provisional may need changes borderSize = 2; - layoutShadowHeight = 9; layoutContentGroupHeight = 130; layoutTitleGroupHorizontalPadding = 40; @@ -111,7 +108,6 @@ public class ActionBarSkin extends MobileSkin case DPIClassification.DPI_320: { borderSize = 2; - layoutShadowHeight = 6; layoutContentGroupHeight = 86; layoutTitleGroupHorizontalPadding = 26; @@ -120,7 +116,6 @@ public class ActionBarSkin extends MobileSkin case DPIClassification.DPI_240: { borderSize = 1; - layoutShadowHeight = 3; layoutContentGroupHeight = 65; layoutTitleGroupHorizontalPadding = 20; @@ -129,7 +124,6 @@ public class ActionBarSkin extends MobileSkin case DPIClassification.DPI_120: { borderSize = 1; - layoutShadowHeight = 1.5; layoutContentGroupHeight = 32; layoutTitleGroupHorizontalPadding = 10; @@ -139,7 +133,6 @@ public class ActionBarSkin extends MobileSkin { // default DPI_160 borderSize = 1; - layoutShadowHeight = 3; layoutContentGroupHeight = 43; layoutTitleGroupHorizontalPadding = 13; @@ -174,15 +167,6 @@ public class ActionBarSkin extends MobileSkin */ private var borderSize:uint; - /** - * Height of shadow embedded in borderClass graphic. - * - * @langversion 3.0 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - protected var layoutShadowHeight:uint; - /** * Default height for navigationGroup, titleGroup and actionGroup. * @@ -434,7 +418,7 @@ public class ActionBarSkin extends MobileSkin if (border) { // FXG uses scale-9, drop shadow is drawn outside the bounds - setElementSize(border, unscaledWidth, unscaledHeight + layoutShadowHeight); + setElementSize(border, unscaledWidth, unscaledHeight); } // position groups, overlap of navigation and action groups is allowed diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as index dd5ff92d39..e076a6b347 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/SpinnerListContainerSkin.as @@ -66,41 +66,36 @@ package spark.skins.android4 selectionIndicatorClass = spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; shadowClass = spark.skins.android4.assets.SpinnerListContainerShadow; cornerRadius = 0; + borderThickness = 0; switch (applicationDPI) { case DPIClassification.DPI_640: { - borderThickness = 3; selectionIndicatorHeight = 182; break; } case DPIClassification.DPI_480: { - borderThickness = 2; selectionIndicatorHeight = 144; break; } case DPIClassification.DPI_320: { - borderThickness = 2; selectionIndicatorHeight = 96; break; } case DPIClassification.DPI_240: { - borderThickness = 1; selectionIndicatorHeight = 72; break; } case DPIClassification.DPI_120: { - borderThickness = 0; selectionIndicatorHeight = 36; break; } default: // default DPI_160 { - borderThickness = 1; selectionIndicatorHeight = 48; break; From 9f566280ac9650d945598aa217a9f822099dcf92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Fernandes?= Date: Fri, 11 Jul 2014 00:26:43 +0100 Subject: [PATCH 032/392] testing commit --- SVN-TEST.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SVN-TEST.txt b/SVN-TEST.txt index 772e129b10..842a9f5380 100644 --- a/SVN-TEST.txt +++ b/SVN-TEST.txt @@ -73,3 +73,5 @@ Me too. Woot. mamsellem git commit first try (2013/09/29) Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. (Brian Kernighan) + +Testing, testing (jfernandes) \ No newline at end of file From facd8cec7de339f24350853e5d27902dda386849 Mon Sep 17 00:00:00 2001 From: jfernandes Date: Mon, 14 Jul 2014 12:13:22 +0100 Subject: [PATCH 033/392] renamed SVN-TEST to GIT-TEST --- SVN-TEST.txt => GIT-TEST.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename SVN-TEST.txt => GIT-TEST.txt (100%) diff --git a/SVN-TEST.txt b/GIT-TEST.txt similarity index 100% rename from SVN-TEST.txt rename to GIT-TEST.txt From 9288662392b1f97598ed596cfc308a0da007182a Mon Sep 17 00:00:00 2001 From: piotrz Date: Mon, 14 Jul 2014 22:23:25 +0200 Subject: [PATCH 034/392] Added with Polish language properties file to flex sdk --- installer.properties/pl_PL.properties | 125 ++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 installer.properties/pl_PL.properties diff --git a/installer.properties/pl_PL.properties b/installer.properties/pl_PL.properties new file mode 100644 index 0000000000..927ec2177c --- /dev/null +++ b/installer.properties/pl_PL.properties @@ -0,0 +1,125 @@ +################################################################################ +## +## 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. +## +################################################################################ + + +yes.no.prompts=y,n +yes=y +no=n + +flash.prompt.text=\ +Apache Flex SDK używa pliku Adobe Flash Player playerglobal.swc do budowania apkilacji Adobe Flash.\n\ +\n\ +Plik playerglobal.swc podlega Umowie Licencyjnej \n\ +Adobe Flex SDK dostÄ™pnej tutaj:\n\ +http://www.adobe.com/products/eulas/pdfs/adobe_flex_software_development_kit-combined-20110916_0930.pdf,\n\ +Poprzez pobieranie, modyfikowanie, rozpowszechnianie, używanie i/lub uzyskiwanie dostÄ™pu do pliku playerglobal.swc,\n\ +zgadzasz siÄ™ na postanowienia i warunki umowy licencyjnej koÅ„cowego użytkownika oprogramowania.\n\ +\n\ +\w dodatku do postanowieÅ„ licencyjnych licencji Adobe, zgadzasz siÄ™ również na zobowiÄ…zania wynikajÄ…ce z warunków strony trzeciej wymienionych tutaj:\n\ +http://www.adobe.com/products/eula/third_party/.\n\ +Adobe zaleca zapoznanie siÄ™ z warunkami strony trzeciej.\n\ +\n\ +Ta licencja nie jest kompatybilna z licencjÄ… Apache v2.\n\ +Czy chcesz pobrać i zainstalować playerglobal.swc? + +air.prompt.text=\ +Apache Flex SDK używa Adobe AIR SDK do tworzenia aplikacji Adobe AIR.\n\ +Adobe AIR SDK podlega Umowie Licencyjnej \n\ +Adobe AIR SDK dostÄ™pnej tutaj:\n\ +http://www.adobe.com/products/air/sdk-eula.html.\n\ +Ta licencja nie jest kompatybilna z licencjÄ… Apache v2.\n\ +Czy chcesz pobrać i zainstalować Adobe AIR SDK? + +osmf.prompt.text=\ +Open Source Media Framework (OSMF) używany przez komponenty video\n\ +podlegaja licencji Mozilla Public License Version 1.1.\n\ +Mozilla Public License Version 1.1:\n\ +\n\ +Zawartość pliku (plików) podlega licencji Mozilla Public License Version 1.1.\n\ +Nie możesz używać plików poza postanowieniami tej licencji. \n\ +Możesz pobrać kopiÄ™ licencji tutaj:\n\ +http://www.mozilla.org/MPL/. \n\ +Przez Å›ciÄ…ganie, modyfikowanie, rozpowszechnianie, używanie i/lub uzyskiwanie dostÄ™pu do pliku (plików), \n\ +zgadzasz siÄ™ z postanowieniami i warunkami umowy licencynjej.\n\ +\n\ +Czy przeczytaÅ‚eÅ› warunki umowy licencyjnej MPL1.1 powyżej? + +swfobject.prompt.text=\ +Apache Flex SDK używa SWFObject podczas budowania aplikacji Adobe Flash.\n\ +SWFObject podlega Umowie Licencyjnej \n\ +MIT dostÄ™pnej tutaj:\n\ +http://opensource.org/licenses/mit-license.php.\n\ +Ta licencja nie jest kompatybilna z licencjÄ… Apache v2.\n\ +Czy chcesz pobrać i zainstalować SWFObject? + +blazeds.prompt.text=\ +Apache Flex opcjonalnie może integrować siÄ™ z Adobe BlazeDS.\n\ +Ta opcja wymaga pliku flex-messaging-common.jar z Adobe Flex SDK.\n\ +Umowa licencyjna Adobe SDK na Adobe Flex 4.6 obejmuje ten plik.\n\ +Licencja ta nie jest kompatybilna z licencjÄ… Apache V2.\n\ +\n\ +Umowa Licencyjna Adobe Flex SDK:\n\ +\n\ +Wszystkie pliki zawarte w pobranym pliku Adobe Flex SDK podlegajÄ… \n\ +umowie licencyjnej Adobe Flex SDK dostÄ™pnej tutaj: \n\ + http://www.adobe.com/products/eulas/pdfs/adobe_flex_software_development_kit-combined-20110916_0930.pdf, \n\ +Poprzez pobieranie, modyfikowanie, rozpowszechnianie, używanie i/lub uzyskiwanie dostÄ™pu do plików w Adobe Flex SDK, \n\ +zgadzasz siÄ™ na postanowienia i warunki umowy licencyjnej koÅ„cowego użytkownika oprogramowania.\n\ +\n\ +W dodatku do postanowieÅ„ licencyjnych licencji Adobe, zgadzasz siÄ™ również na zobowiÄ…zania wynikajÄ…ce z warunków strony trzeciej wymienionych tutaj: \n\ + http://www.adobe.com/products/eula/third_party/. \n\ +Adobe zaleca zapoznanie siÄ™ z warunkami strony trzeciej.\n\ +\n\ +Czy chcesz pobrać i zainstalować wybrany plik z Adobe Flex SDK? + +fontswf.prompt.text=\ +Apache Flex opcjonalnie może integrować siÄ™ z wbudowanym wsparciem czcionek Adobe.\n\ +Ta opcja wymaga kilku paczek czcionek z Adobe Flex SDK.\n\ +Umowa licencyjna Adobe SDK na Adobe Flex 4.6 obejmuje te paczki.\n\ +Licencja ta nie jest kompatybilna z licencjÄ… Apache V2.\n\ +\n\ +Umowa Licencyjna Adobe Flex SDK:\n\ +\n\ +Wszystkie pliki zawarte w pobranym pliku Adobe Flex SDK podlegajÄ… \n\ +umowie licencyjnej Adobe Flex SDK dostÄ™pnej tutaj: \n\ + http://www.adobe.com/products/eulas/pdfs/adobe_flex_software_development_kit-combined-20110916_0930.pdf, \n\ +Poprzez pobieranie, modyfikowanie, rozpowszechnianie, używanie i/lub uzyskiwanie dostÄ™pu do plików w Adobe Flex SDK, \n\ +zgadzasz siÄ™ na postanowienia i warunki umowy licencyjnej koÅ„cowego użytkownika oprogramowania.\n\ +\n\ +W dodatku do postanowieÅ„ licencyjnych licencji Adobe, zgadzasz siÄ™ również na zobowiÄ…zania wynikajÄ…ce z warunków strony trzeciej wymienionych tutaj: \n\ + http://www.adobe.com/products/eula/third_party/. \n\ +Adobe zaleca zapoznanie siÄ™ z warunkami strony trzeciej.\n\ +\n\ +Czy chcesz pobrać i zainstalować wybrane archiwum z Adobe Flex SDK? + +unable.to.find.java=Nie można znaleźć pliku wykonawczego Java. Ustaw zmiennÄ… Å›rodowiskowÄ… JAVA_HOME + +install.complete.echo=${basedir} jest teraz kompatybilnym folderem IDE +find.java.echo.pattern=Java jest $${java.executable} +mac.copy.echo.pattern=Kopiowanie plików z $${download.dir}/airsdk/$${srcdir} do $${destdir} + +INFO_DOWNLOADING_AIR_RUNTIME_KIT_MAC=ÅšciÄ…ganie Adobe Air Runtime Kit dla systemu Mac z: +INFO_DOWNLOADING_AIR_RUNTIME_KIT_WINDOWS=ÅšciÄ…ganie Adobe AIR Runtime Kit dla systemu Windows z: +INFO_FINISHED_UNTARING=ZakoÅ„czono rozpakowywanie: +INFO_FINISHED_UNZIPPING=ZakoÅ„czono rozpakowywanie: +INFO_INSTALLING_PLAYERGLOBAL_SWC=Instalowanie Adobe Flash Player playerglobal.swc z: +INFO_INSTALLING_CONFIG_FILES=Instalowanie plików konfiguracyjnych frameworka skonfigurowanych do użytku z IDE +INFO_DOWNLOADING_FILE_FROM=ÅšciÄ…ganie {0} z: {1} +INFO_DOWNLOADED=ÅšciÄ…ganie zakoÅ„czone +INFO_VALIDATING_FILE=Potwierdzanie pobierania: From 129366babfbd2e66da1f9ce913556670ec8ee82c Mon Sep 17 00:00:00 2001 From: piotrz Date: Tue, 15 Jul 2014 07:37:16 +0200 Subject: [PATCH 035/392] Added Polish language properties file to flex sdk --- installer.properties/pl_PL.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer.properties/pl_PL.properties b/installer.properties/pl_PL.properties index 927ec2177c..61112ba9dd 100644 --- a/installer.properties/pl_PL.properties +++ b/installer.properties/pl_PL.properties @@ -29,7 +29,7 @@ Plik playerglobal.swc podlega Umowie Licencyjnej \n\ Adobe Flex SDK dostÄ™pnej tutaj:\n\ http://www.adobe.com/products/eulas/pdfs/adobe_flex_software_development_kit-combined-20110916_0930.pdf,\n\ Poprzez pobieranie, modyfikowanie, rozpowszechnianie, używanie i/lub uzyskiwanie dostÄ™pu do pliku playerglobal.swc,\n\ -zgadzasz siÄ™ na postanowienia i warunki umowy licencyjnej koÅ„cowego użytkownika oprogramowania.\n\ +zgadzasz siÄ™ na postanowienia i warunki umowy licencyjnej koÅ„cowego użytkownika oprogramowania. \n\ \n\ \w dodatku do postanowieÅ„ licencyjnych licencji Adobe, zgadzasz siÄ™ również na zobowiÄ…zania wynikajÄ…ce z warunków strony trzeciej wymienionych tutaj:\n\ http://www.adobe.com/products/eula/third_party/.\n\ From 0a2c3bd8396fa9c559a0d1ce7a38b39d498b784d Mon Sep 17 00:00:00 2001 From: Mihai C Date: Mon, 21 Jul 2014 17:41:33 +0100 Subject: [PATCH 036/392] FLEX-34119 renaming variables for increased readability. --- .../HierarchicalCollectionViewCursor.as | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as index c5765af590..b829dfe35e 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as @@ -142,7 +142,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher * @private * A stack of the currentChildBookmark in all parents of the currentNode. */ - private var childIndexStack:Array = []; + private var parentBookmarkStack:Array = []; /** * @private @@ -215,7 +215,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher */ public function get current():Object { - if (childIndexStack.length == 0) + if (parentBookmarkStack.length == 0) { return modelCursor.current; } @@ -431,7 +431,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher childNodes = collection.getChildren(currentNode); if (childNodes.length > 0) { - childIndexStack.push(currentChildBookmark); + parentBookmarkStack.push(currentChildBookmark); parentNodes.push(currentNode); currentChildBookmark = CursorBookmark.FIRST; childCursor = childNodes.createCursor(); @@ -481,7 +481,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher (childCursor.bookmark == CursorBookmark.LAST || childCursor.afterLast)) { //check for the end of the tree here. - if (childIndexStack.length < 1 && !more) + if (parentBookmarkStack.length < 1 && !more) { currentNode = null; currentIndex++; @@ -507,7 +507,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher childNodes = null; } //get new current nodes index - currentChildBookmark = childIndexStack.pop(); + currentChildBookmark = parentBookmarkStack.pop(); //pop the level up one _currentDepth--; } @@ -515,7 +515,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher else { //if no childnodes then we're probably at the top level - if (childIndexStack.length == 0) + if (parentBookmarkStack.length == 0) { //check for more top level siblings //and if we're here the depth should be 1 @@ -568,11 +568,11 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher childNodes = null; } //get new current nodes index - currentChildBookmark = childIndexStack.pop(); + currentChildBookmark = parentBookmarkStack.pop(); //pop the level up one _currentDepth--; - if (childIndexStack.length == 0) + if (parentBookmarkStack.length == 0) { //check for more top level siblings //and if we're here the depth should be 1 @@ -628,7 +628,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher { //at the first node in this branch so move to parent currentNode = parentNodes.pop(); - currentChildBookmark = childIndexStack.pop(); + currentChildBookmark = parentBookmarkStack.pop(); var grandParent:Object = parentNodes[parentNodes.length-1]; //we could probably assume that a non-null grandparent has descendants //but the analogy only goes so far... @@ -709,7 +709,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher childNodes = collection.getChildren(currentNode); if (childNodes.length > 0) { - childIndexStack.push(currentChildBookmark); + parentBookmarkStack.push(currentChildBookmark); parentNodes.push(currentNode); // if the child collection has only one item then set the // bookmark to first @@ -1066,7 +1066,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher more = false; currentChildBookmark = CursorBookmark.FIRST; parentNodes = []; - childIndexStack = []; + parentBookmarkStack = []; currentIndex = 0; _currentDepth = 1; } @@ -1077,7 +1077,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher public function moveToLast():void { childNodes = null; - childIndexStack = []; + parentBookmarkStack = []; _currentDepth = 1; parentNodes = []; var emptyBranch:Boolean = false; @@ -1109,7 +1109,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher break; } parentNodes.push(currentNode); - childIndexStack.push(currentChildBookmark); + parentBookmarkStack.push(currentChildBookmark); currentNode = childCursor.current; currentChildBookmark = CursorBookmark.LAST; try @@ -1149,9 +1149,9 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher { var i:int; var n:int; - var node:Object; - var nodeParent:Object - var parent:Object; + var changingNode:Object; + var parentOfChangingNode:Object; + var parentOfCurrentNode:Object; var parentStack:Array; var parentTable:Dictionary; var isBefore:Boolean = false; @@ -1168,10 +1168,10 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher parentTable[parentStack[i]] = i + 1; } // remember the current parent - parent = parentStack[parentStack.length - 1]; + parentOfCurrentNode = parentStack[parentStack.length - 1]; - var tmpChildnodes:ICollectionView; - var childCursor:IViewCursor; + var changingNodeAndSiblings:ICollectionView; + var changingCollectionCursor:IViewCursor; if (event.kind == CollectionEventKind.ADD) { @@ -1184,25 +1184,25 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher for (i = 0; i < n; i++) { - node = event.items[i]; + changingNode = event.items[i]; if (isBefore) { // if the added node is before the current // and they share parent's then we have to // adjust the currentChildIndex or // the stack of child indexes. - nodeParent = collection.getParentItem(node); - tmpChildnodes = collection.getChildren(nodeParent); - if (nodeParent == parent) + parentOfChangingNode = collection.getParentItem(changingNode); + changingNodeAndSiblings = collection.getChildren(parentOfChangingNode); + if (parentOfChangingNode == parentOfCurrentNode) { - if (tmpChildnodes != null) + if (changingNodeAndSiblings != null) { - childCursor = tmpChildnodes.createCursor(); + changingCollectionCursor = changingNodeAndSiblings.createCursor(); try { - childCursor.seek(currentChildBookmark); - childCursor.moveNext(); - currentChildBookmark = childCursor.bookmark; + changingCollectionCursor.seek(currentChildBookmark); + changingCollectionCursor.moveNext(); + currentChildBookmark = changingCollectionCursor.bookmark; } catch (e:ItemPendingError) { @@ -1210,21 +1210,21 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } } } - else if (parentTable[nodeParent] != null) + else if (parentTable[parentOfChangingNode] != null) { - if (tmpChildnodes != null) + if (changingNodeAndSiblings != null) { - childCursor = tmpChildnodes.createCursor(); + changingCollectionCursor = changingNodeAndSiblings.createCursor(); try { - childCursor.seek(currentChildBookmark); - childCursor.moveNext(); - currentChildBookmark = childCursor.bookmark; + changingCollectionCursor.seek(currentChildBookmark); + changingCollectionCursor.moveNext(); + currentChildBookmark = changingCollectionCursor.bookmark; } catch (e:ItemPendingError) { } - childIndexStack[parentTable[nodeParent]] = currentChildBookmark; + parentBookmarkStack[parentTable[parentOfChangingNode]] = currentChildBookmark; } } } @@ -1247,8 +1247,8 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher seek(CursorBookmark.FIRST, newIndex); for (i = 0; i < n; i++) { - node = event.items[i]; - delete collection.parentMap[UIDUtil.getUID(node)]; + changingNode = event.items[i]; + delete collection.parentMap[UIDUtil.getUID(changingNode)]; } return; } @@ -1259,25 +1259,25 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher for (i = 0; i < n; i++) { - node = event.items[i]; + changingNode = event.items[i]; if (isBefore) { // if the removed node is before the current // and they share parent's then we have to // adjust the currentChildIndex or // the stack of child indexes. - nodeParent = collection.getParentItem(node); - tmpChildnodes = collection.getChildren(nodeParent); - if (nodeParent == parent) + parentOfChangingNode = collection.getParentItem(changingNode); + changingNodeAndSiblings = collection.getChildren(parentOfChangingNode); + if (parentOfChangingNode == parentOfCurrentNode) { - if (tmpChildnodes != null) + if (changingNodeAndSiblings != null) { - childCursor = tmpChildnodes.createCursor(); + changingCollectionCursor = changingNodeAndSiblings.createCursor(); try { - childCursor.seek(currentChildBookmark); - childCursor.movePrevious(); - currentChildBookmark = childCursor.bookmark; + changingCollectionCursor.seek(currentChildBookmark); + changingCollectionCursor.movePrevious(); + currentChildBookmark = changingCollectionCursor.bookmark; } catch (e:ItemPendingError) { @@ -1285,25 +1285,25 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } } } - else if (parentTable[nodeParent] != null) + else if (parentTable[parentOfChangingNode] != null) { - if (tmpChildnodes != null) + if (changingNodeAndSiblings != null) { - childCursor = tmpChildnodes.createCursor(); + changingCollectionCursor = changingNodeAndSiblings.createCursor(); try { - childCursor.seek(currentChildBookmark); - childCursor.movePrevious(); - currentChildBookmark = childCursor.bookmark; + changingCollectionCursor.seek(currentChildBookmark); + changingCollectionCursor.movePrevious(); + currentChildBookmark = changingCollectionCursor.bookmark; } catch (e:ItemPendingError) { } - childIndexStack[parentTable[nodeParent]] = currentChildBookmark; + parentBookmarkStack[parentTable[parentOfChangingNode]] = currentChildBookmark; } } } - delete collection.parentMap[UIDUtil.getUID(node)]; + delete collection.parentMap[UIDUtil.getUID(changingNode)]; } } From 77ffb23f38b11b0edb97395ed44ea53e21cde725 Mon Sep 17 00:00:00 2001 From: Om Date: Tue, 22 Jul 2014 00:14:56 -0700 Subject: [PATCH 037/392] Skins for VScrollBar and HScrollBar --- .../projects/mobiletheme/src/android4x.css | 12 + .../spark/skins/android4/HScrollBarSkin.as | 205 +++++++++++++++++ .../skins/android4/HScrollBarThumbSkin.as | 169 ++++++++++++++ .../spark/skins/android4/VScrollBarSkin.as | 207 ++++++++++++++++++ .../skins/android4/VScrollBarThumbSkin.as | 178 +++++++++++++++ 5 files changed, 771 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/HScrollBarSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/HScrollBarThumbSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/VScrollBarSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/VScrollBarThumbSkin.as diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css index 280ca6ec88..d8740517a9 100644 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -150,6 +150,12 @@ CheckBox chromeColor: #FFFFFF; } +HScrollBar +{ + skinClass: ClassReference("spark.skins.android4.HScrollBarSkin"); + thumbColor: #333333; +} + HSlider { skinClass: ClassReference("spark.skins.android4.HSliderSkin"); @@ -233,6 +239,12 @@ ToggleSwitch textShadowColor: #000000; } +VScrollBar +{ + skinClass: ClassReference("spark.skins.android4.VScrollBarSkin"); + thumbColor: #333333; +} + ViewNavigator { skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/HScrollBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/HScrollBarSkin.as new file mode 100644 index 0000000000..1e712793fd --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/HScrollBarSkin.as @@ -0,0 +1,205 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 spark.components.Button; +import spark.components.HScrollBar; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * ActionScript-based skin for HScrollBar components in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class HScrollBarSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function HScrollBarSkin() + { + super(); + + minWidth = 20; + thumbSkinClass = HScrollBarThumbSkin; + var paddingBottom:int; + var paddingHorizontal:int; + + // Depending on density set our measured height + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + minHeight = 24; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_640DPI; + paddingHorizontal = HScrollBarThumbSkin.PADDING_HORIZONTAL_640DPI; + break; + } + case DPIClassification.DPI_480: + { + minHeight = 18; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_480DPI; + paddingHorizontal = HScrollBarThumbSkin.PADDING_HORIZONTAL_480DPI; + break; + } + case DPIClassification.DPI_320: + { + minHeight = 12; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_320DPI; + paddingHorizontal = HScrollBarThumbSkin.PADDING_HORIZONTAL_320DPI; + break; + } + case DPIClassification.DPI_240: + { + minHeight = 9; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_240DPI; + paddingHorizontal = HScrollBarThumbSkin.PADDING_HORIZONTAL_240DPI; + break; + } + case DPIClassification.DPI_120: + { + minHeight = 5; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_120DPI; + paddingHorizontal = HScrollBarThumbSkin.PADDING_HORIZONTAL_120DPI; + break; + } + default: + { + // default DPI_160 + minHeight = 6; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_DEFAULTDPI; + paddingHorizontal = HScrollBarThumbSkin.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); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:HScrollBar; + + /** + * Minimum width for the thumb + */ + protected var minThumbWidth:Number; + + /** + * Skin to use for the thumb Button skin part + */ + protected var thumbSkinClass:Class; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + /** + * HScrollbar track skin part. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var track:Button; + + /** + * HScrollbar thumb skin part. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var thumb:Button; + + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + /** + * @private + */ + override protected function createChildren():void + { + // Create our skin parts if necessary: track and thumb. + if (!track) + { + // We don't want a visible track so we set the skin to MobileSkin + track = new Button(); + track.setStyle("skinClass", spark.skins.mobile.supportClasses.MobileSkin); + track.width = minWidth; + track.height = minHeight; + addChild(track); + } + + if (!thumb) + { + thumb = new Button(); + thumb.minWidth = minThumbWidth; + thumb.setStyle("skinClass", thumbSkinClass); + thumb.width = minHeight; + thumb.height = minHeight; + addChild(thumb); + } + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + setElementSize(track, unscaledWidth, unscaledHeight); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/HScrollBarThumbSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/HScrollBarThumbSkin.as new file mode 100644 index 0000000000..ed3a6b9c2b --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/HScrollBarThumbSkin.as @@ -0,0 +1,169 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.CapsStyle; +import flash.display.JointStyle; +import flash.display.LineScaleMode; + +import mx.core.DPIClassification; +import mx.core.mx_internal; + +import spark.components.Button; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * ActionScript-based skin for the HScrollBar thumb skin part in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class HScrollBarThumbSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + // These constants are also accessed from HScrollBarSkin + mx_internal static const PADDING_BOTTOM_640DPI:int = 10; + mx_internal static const PADDING_HORIZONTAL_640DPI:int = 8; + mx_internal static const PADDING_BOTTOM_480DPI:int = 8; + mx_internal static const PADDING_HORIZONTAL_480DPI:int = 6; + mx_internal static const PADDING_BOTTOM_320DPI:int = 5; + mx_internal static const PADDING_HORIZONTAL_320DPI:int = 4; + mx_internal static const PADDING_BOTTOM_240DPI:int = 4; + mx_internal static const PADDING_HORIZONTAL_240DPI:int = 3; + mx_internal static const PADDING_BOTTOM_120DPI:int = 2; + mx_internal static const PADDING_HORIZONTAL_120DPI:int = 2; + mx_internal static const PADDING_BOTTOM_DEFAULTDPI:int = 3; + mx_internal static const PADDING_HORIZONTAL_DEFAULTDPI:int = 2; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function HScrollBarThumbSkin() + { + super(); + + // Depending on density set padding + switch (applicationDPI) + { + case DPIClassification.DPI_480: + { + paddingBottom = PADDING_BOTTOM_480DPI; + paddingHorizontal = 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; + } + default: + { + paddingBottom = PADDING_BOTTOM_DEFAULTDPI; + paddingHorizontal = PADDING_HORIZONTAL_DEFAULTDPI; + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:Button; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + /** + * Padding from bottom. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var paddingBottom:int; + + /** + * Horizontal padding from left and right. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var paddingHorizontal:int; + + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + super.drawBackground(unscaledWidth, unscaledHeight); + + var thumbHeight:Number = unscaledHeight - paddingBottom; + + graphics.beginFill(getStyle("thumbColor"), 1); + graphics.drawRect(paddingHorizontal + .5, 0.5, unscaledWidth - 2 * paddingHorizontal, thumbHeight); + + graphics.endFill(); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/VScrollBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/VScrollBarSkin.as new file mode 100644 index 0000000000..1a2a0df78e --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/VScrollBarSkin.as @@ -0,0 +1,207 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 spark.components.Button; +import spark.components.VScrollBar; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * ActionScript-based skin for VScrollBar components in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class VScrollBarSkin extends MobileSkin +{ + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function VScrollBarSkin() + { + super(); + + minHeight = 20; + thumbSkinClass = VScrollBarThumbSkin; + var paddingRight:int; + var paddingVertical:int; + + // Depending on density set our measured width + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + minWidth = 24; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_640DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_640DPI; + break; + } + case DPIClassification.DPI_480: + { + minWidth = 18; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_480DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_480DPI; + break; + } + case DPIClassification.DPI_320: + { + minWidth = 12; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_320DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_320DPI; + break; + } + case DPIClassification.DPI_240: + { + minWidth = 9; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_240DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_240DPI; + break; + } + case DPIClassification.DPI_120: + { + minWidth = 9; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_120DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_120DPI; + break; + } + default: + { + // default DPI_160 + minWidth = 6; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_DEFAULTDPI; + paddingVertical = VScrollBarThumbSkin.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); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:VScrollBar; + + /** + * Minimum height for the thumb + */ + protected var minThumbHeight:Number; + + /** + * Skin to use for the thumb Button skin part + */ + protected var thumbSkinClass:Class; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + /** + * VScrollbar track skin part + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var track:Button; + + /** + * VScrollbar thumb skin part + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var thumb:Button; + + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + /** + * @private + */ + override protected function createChildren():void + { + // Create our skin parts if necessary: track and thumb. + if (!track) + { + // We don't want a visible track so we set the skin to MobileSkin + track = new Button(); + track.setStyle("skinClass", spark.skins.mobile.supportClasses.MobileSkin); + track.width = minWidth; + track.height = minHeight; + addChild(track); + } + if (!thumb) + { + thumb = new Button(); + thumb.minHeight = minThumbHeight; + thumb.setStyle("skinClass", thumbSkinClass); + thumb.width = minWidth; + thumb.height = minWidth; + addChild(thumb); + } + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + setElementSize(track, unscaledWidth, unscaledHeight); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/VScrollBarThumbSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/VScrollBarThumbSkin.as new file mode 100644 index 0000000000..9d17a9752b --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/VScrollBarThumbSkin.as @@ -0,0 +1,178 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.CapsStyle; +import flash.display.JointStyle; +import flash.display.LineScaleMode; + +import mx.core.DPIClassification; +import mx.core.mx_internal; +use namespace mx_internal; + +import spark.components.Button; +import spark.skins.mobile.supportClasses.MobileSkin; + +/** + * ActionScript-based skin for the VScrollBar thumb skin part in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class VScrollBarThumbSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + // These constants are also accessed from VScrollBarSkin + mx_internal static const PADDING_RIGHT_640DPI:int = 10; + mx_internal static const PADDING_VERTICAL_640DPI:int = 8; + mx_internal static const PADDING_RIGHT_480DPI:int = 8; + mx_internal static const PADDING_VERTICAL_480DPI:int = 6; + mx_internal static const PADDING_RIGHT_320DPI:int = 5; + mx_internal static const PADDING_VERTICAL_320DPI:int = 4; + mx_internal static const PADDING_RIGHT_240DPI:int = 4; + mx_internal static const PADDING_VERTICAL_240DPI:int = 3; + mx_internal static const PADDING_RIGHT_120DPI:int = 2; + mx_internal static const PADDING_VERTICAL_120DPI:int = 1; + mx_internal static const PADDING_RIGHT_DEFAULTDPI:int = 3; + mx_internal static const PADDING_VERTICAL_DEFAULTDPI:int = 2; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function VScrollBarThumbSkin() + { + super(); + + // Depending on density set padding + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + minWidth = 19; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_640DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_640DPI; + break; + } + case DPIClassification.DPI_480: + { + minWidth = 19; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_480DPI; + paddingVertical = VScrollBarThumbSkin.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; + } + default: + { + paddingRight = PADDING_RIGHT_DEFAULTDPI; + paddingVertical = PADDING_VERTICAL_DEFAULTDPI; + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:Button; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + /** + * Padding from the right + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var paddingRight:int; + + /** + * Vertical padding from top and bottom + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var paddingVertical:int; + + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @protected + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + super.drawBackground(unscaledWidth, unscaledHeight); + + var thumbWidth:Number = unscaledWidth - paddingRight; + + graphics.beginFill(getStyle("thumbColor"), 1); + graphics.drawRect(0.5, paddingVertical + 0.5,thumbWidth, unscaledHeight - 2 * paddingVertical); + + graphics.endFill(); + } + +} +} \ No newline at end of file From 0c4c9c00d09eab81e6c43340df245f4c9ca239b0 Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Tue, 22 Jul 2014 09:18:18 +0100 Subject: [PATCH 038/392] FLEX-34119 Adding two unit tests for HierarchicalCollectionViewCursor (HCVC). HierarchicalCollectionViewCursor_Basics_Test is meant to ensure that HCVC works as expected in non-corner cases (it's definitely incomplete, and could use more tests. I created it mainly to test the few functions I needed when working with FLEX-34119), while HierarchicalCollectionViewCursor_FLEX_34119_Test reproduces the FLEX-34119 bug (and, incidentally, also uncovered FLEX-34424). FYI The latter test lasts just under 3 minutes on my machine. --- .../unitTests/mx/collections/DataNode.as | 62 ++++ ...rchicalCollectionViewCursor_Basics_Test.as | 164 +++++++++ ...calCollectionViewCursor_FLEX_34119_Test.as | 325 ++++++++++++++++++ .../HierarchicalCollectionViewTestUtils.as | 172 +++++++++ 4 files changed, 723 insertions(+) create mode 100644 frameworks/tests/unitTests/mx/collections/DataNode.as create mode 100644 frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as create mode 100644 frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as create mode 100644 frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as diff --git a/frameworks/tests/unitTests/mx/collections/DataNode.as b/frameworks/tests/unitTests/mx/collections/DataNode.as new file mode 100644 index 0000000000..da4f7fd213 --- /dev/null +++ b/frameworks/tests/unitTests/mx/collections/DataNode.as @@ -0,0 +1,62 @@ +package { +import mx.collections.ArrayCollection; + +public class DataNode { + private var _label:String; + private var _children:ArrayCollection; + private var _isSelected:Boolean = false; + private var _isPreviousSiblingRemoved:Boolean = false; + + public function DataNode(label:String) + { + _label = label; + } + + public function get children():ArrayCollection { + return _children; + } + + public function set children(value:ArrayCollection):void { + _children = value; + } + + public function get label():String { + return _label + (_isSelected ? " [SEL]" : "") + (_isPreviousSiblingRemoved ? " [PREV ITEM REMOVED]" : ""); + } + + public function toString():String + { + return label; + } + + public function addChild(node:DataNode):void { + if(!_children) + _children = new ArrayCollection(); + + _children.addItem(node); + } + + public function set isSelected(value:Boolean):void { + _isSelected = value; + } + + public function get isSelected():Boolean { + return _isSelected; + } + + public function clone():DataNode + { + var newNode:DataNode = new DataNode(_label); + for each(var childNode:DataNode in children) + { + newNode.addChild(childNode.clone()); + } + + return newNode; + } + + public function set isPreviousSiblingRemoved(value:Boolean):void { + _isPreviousSiblingRemoved = value; + } +} +} diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as new file mode 100644 index 0000000000..65e2cb98c4 --- /dev/null +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as @@ -0,0 +1,164 @@ +package +{ + import flash.events.UncaughtErrorEvent; + + import mx.collections.ArrayCollection; + import mx.collections.CursorBookmark; + import mx.collections.HierarchicalCollectionView; + import mx.collections.HierarchicalCollectionViewCursor; + import mx.core.FlexGlobals; + + import spark.components.WindowedApplication; + + import org.flexunit.asserts.assertEquals; + + public class HierarchicalCollectionViewCursor_Basics_Test + { + private static var _utils:HierarchicalCollectionViewTestUtils = new HierarchicalCollectionViewTestUtils(); + private static var _currentHierarchy:HierarchicalCollectionView; + private static var _noErrorsThrown:Boolean = true; + private var _level0:ArrayCollection; + + private var _sut:HierarchicalCollectionViewCursor; + + [BeforeClass] + public static function setUpBeforeClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, handleUncaughtClientError); + } + + [AfterClass] + public static function tearDownAfterClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.removeEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, handleUncaughtClientError); + } + + [Before] + public function setUp():void + { + _currentHierarchy = generateHierarchyViewWithOpenNodes(); + _level0 = _utils.getRoot(_currentHierarchy) as ArrayCollection; + _sut = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + + _noErrorsThrown = true; + } + + [After] + public function tearDown():void + { + _sut = null; + _currentHierarchy = null; + _level0 = null; + } + + + + [Test] + public function testMovingAround():void + { + //given + var lastCompany:DataNode = _level0.getItemAt(_level0.length - 1) as DataNode; + var firstCompany:DataNode = _level0.getItemAt(0) as DataNode; + var firstLocation:DataNode = firstCompany.children.getItemAt(0) as DataNode; + var secondLocation:DataNode = firstCompany.children.getItemAt(1) as DataNode; + var firstDepartment:DataNode = firstLocation.children.getItemAt(0) as DataNode; + var secondDepartment:DataNode = firstLocation.children.getItemAt(1) as DataNode; + + //when + _sut.moveNext(); + + //then + assertEquals(firstLocation, _sut.current); + + //when + _sut.moveNext(); + + //then + assertEquals(firstDepartment, _sut.current); + + //when + _sut.moveNext(); + + //then + assertEquals(secondDepartment, _sut.current); + + //when + _sut.movePrevious(); + + //then + assertEquals(firstDepartment, _sut.current); + + //when + _sut.moveToLast(); + + //then + assertEquals(lastCompany, _sut.current); + + //when + _sut.seek(new CursorBookmark(4)); + + //then + assertEquals(secondLocation, _sut.current); + } + + [Test] + public function testCollectionChangeInRootDoesNotChangeCurrent():void + { + //given + var lastCompany:DataNode = _level0.getItemAt(_level0.length - 1) as DataNode; + + //when + _sut.moveToLast(); + + var newFirstCompany:DataNode = _utils.createSimpleNode("[INS] Company"); + _level0.addItemAt(newFirstCompany, 0); + + var newLastCompany:DataNode = _utils.createSimpleNode("[INS] Company"); + _level0.addItemAt(newLastCompany, _level0.length); + + //then + assertEquals(lastCompany, _sut.current); + + //when + _sut.moveToLast(); + + //then + assertEquals(newLastCompany, _sut.current); + } + + + private static function handleUncaughtClientError(event:UncaughtErrorEvent):void + { + event.preventDefault(); + event.stopImmediatePropagation(); + _noErrorsThrown = false; + + trace("\n" + event.error); + _utils.printHCollectionView(_currentHierarchy); + } + + + private static function generateHierarchyViewWithOpenNodes():HierarchicalCollectionView + { + return _utils.generateOpenHierarchyFromRootList(_utils.generateHierarchySourceFromString(HIERARCHY_STRING)); + } + + private static const HIERARCHY_STRING:String = (Location(1) + Company(1)->Location(1)->Department(1) + Company(1)->Location(1)->Department(2) + Company(1)->Location(2) + Company(1)->Location(2)->Department(1) + Company(1)->Location(2)->Department(2) + Company(1)->Location(2)->Department(3) + Company(1)->Location(3) + Company(2) + Company(2)->Location(1) + Company(2)->Location(2) + Company(2)->Location(2)->Department(1) + Company(2)->Location(3) + Company(3) + ]]>).toString(); + } +} \ No newline at end of file diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as new file mode 100644 index 0000000000..6f52139e9f --- /dev/null +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as @@ -0,0 +1,325 @@ +package +{ + import flash.events.UncaughtErrorEvent; + + import mx.collections.ArrayCollection; + import mx.collections.CursorBookmark; + import mx.collections.HierarchicalCollectionView; + import mx.collections.HierarchicalCollectionViewCursor; + import mx.collections.IViewCursor; + import mx.core.FlexGlobals; + + import spark.components.WindowedApplication; + + import flexunit.framework.AssertionFailedError; + + import org.flexunit.assertThat; + import org.flexunit.asserts.assertEquals; + import org.flexunit.asserts.assertNotNull; + import org.flexunit.asserts.assertTrue; + import org.flexunit.runners.Parameterized; + + [RunWith("org.flexunit.runners.Parameterized")] + public class HierarchicalCollectionViewCursor_FLEX_34119_Test + { + private static const OP_ADD:int = 0; + private static const OP_REMOVE:int = 1; + private static const OP_SET:int = 2; + private static const OPERATIONS:Array = [OP_ADD, OP_REMOVE, OP_SET]; + private static var _generatedHierarchy:HierarchicalCollectionView; + private static var _utils:HierarchicalCollectionViewTestUtils = new HierarchicalCollectionViewTestUtils(); + public static var positionAndOperation:Array = []; + + { + _generatedHierarchy = _utils.generateOpenHierarchyFromRootList(_utils.generateHierarchySourceFromString(HIERARCHY_STRING)); + NO_ITEMS_IN_HIERARCHY = _generatedHierarchy.length; + + private static var SELECTED_INDEX:int = 0; + private static var OPERATION_LOCATION:int = 0; + private static var OPERATION_INDEX:int = 0; + for(SELECTED_INDEX = 0; SELECTED_INDEX < NO_ITEMS_IN_HIERARCHY; SELECTED_INDEX++) + for(OPERATION_LOCATION = SELECTED_INDEX -1; OPERATION_LOCATION >= 0; OPERATION_LOCATION--) + for(OPERATION_INDEX = 0; OPERATION_INDEX < OPERATIONS.length; OPERATION_INDEX++) + positionAndOperation.push([SELECTED_INDEX, OPERATION_LOCATION, OPERATIONS[OPERATION_INDEX]]); + } + + private static var NO_ITEMS_IN_HIERARCHY:int = NaN; + private static var _noErrorsThrown:Boolean = true; + private static var _operationPerformedInLastStep:Boolean = false; + private static var _currentHierarchy:HierarchicalCollectionView; + private static var _sut:HierarchicalCollectionViewCursor; + private static var _operationCursor:HierarchicalCollectionViewCursor; + private static var _mirrorCursor:IViewCursor; + + private static var foo:Parameterized; + + [BeforeClass] + public static function setUpBeforeClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtClientError); + } + + [AfterClass] + public static function tearDownAfterClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.removeEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtClientError); + } + + [Before] + public function setUp():void + { + if(_operationPerformedInLastStep || !_currentHierarchy) + { + _operationPerformedInLastStep = false; + + _currentHierarchy = _utils.clone(_generatedHierarchy); + _utils.openAllNodes(_currentHierarchy); + _sut = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + } + } + + [After] + public function tearDown():void + { + if(_operationPerformedInLastStep) + { + _sut = null; + _currentHierarchy = null; + } + + _operationCursor = null; + _mirrorCursor = null; + } + + [Test] + public function reproduce_FLEX_34119_WithADDSimple():void + { + //given + var _level0:ArrayCollection = _utils.getRoot(_currentHierarchy) as ArrayCollection; + + var secondRegion:DataNode = _level0.getItemAt(1) as DataNode; + var firstCity:DataNode = secondRegion.children.getItemAt(0) as DataNode; + var secondCompany:DataNode = firstCity.children.getItemAt(1) as DataNode; + + //when + _sut.seek(new CursorBookmark(4)); //Region(2)->City(1)->Company(2) + secondRegion.children.addItemAt(_utils.createSimpleNode("City [INS]"), 0); //RTE should be thrown here + + //then + assertEquals(secondCompany, _sut.current); + assertTrue(_noErrorsThrown); + } + + [Test(dataProvider="positionAndOperation")] + public function testReproduce_FLEX_34119_Comprehensive(selectedItemIndex:int, operationIndex:int, operation:int):void + { + assertThat(operationIndex < selectedItemIndex); + + try { + //WHEN + //1. Select a random node + _sut.seek(new CursorBookmark(selectedItemIndex)); + + var selectedNode:DataNode = DataNode(_sut.current); + assertNotNull(selectedNode); + + //2. Make sure FLEX-34119 can be reproduced with the current indexes + if(!isFLEX_34119_Reproducible(operationIndex, selectedNode, operation)) + { + //trace("can't reproduce " + operation + "; " + operationIndex + "; " + selectedNode); + return; + } + + selectedNode.isSelected = true; + + //3. Perform operation + if (operation == OP_ADD) + testAddition(_operationCursor); + else if (operation == OP_REMOVE) + testRemoval(_operationCursor); + + //THEN 1 + assertTrue(_noErrorsThrown); + + + + //4. Create mirror HierarchicalCollectionView from the changed root, as the source of truth + _mirrorCursor = _utils.navigateToItem(_currentHierarchy.createCursor() as HierarchicalCollectionViewCursor, selectedNode); + + //5. Navigate somewhere in both HierarchicalCollectionViews and make sure they do the same thing + _sut.moveNext(); + _mirrorCursor.moveNext(); + + //THEN 2 + assertEquals(_mirrorCursor.current, _sut.current); + } + catch(error:AssertionFailedError) + { + trace("FAIL ("+selectedItemIndex + "," + operationIndex + "," + operation + "): " + error.message); + _utils.printHCollectionView(_currentHierarchy); + throw(error); + } + } + + private function isFLEX_34119_Reproducible(where:int, selectedNode:DataNode, operation:int):Boolean + { + var hasParent:Boolean = _currentHierarchy.getParentItem(selectedNode) != null; + if(!hasParent) + return false; + + _operationCursor = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + _operationCursor.seek(new CursorBookmark(where)); + var itemToPerformOperationOn:DataNode = _operationCursor.current as DataNode; + + switch(operation) + { + case OP_ADD: + return _utils.nodesHaveCommonAncestor(itemToPerformOperationOn, selectedNode, _currentHierarchy); + case OP_REMOVE: + return !_utils.isAncestor(itemToPerformOperationOn, selectedNode, _currentHierarchy); + case OP_SET: + return false; + default: + return false; + } + } + + private function testRemoval(where:HierarchicalCollectionViewCursor):void + { + var itemToDelete:DataNode = where.current as DataNode; + assertNotNull(itemToDelete); + + //mark the next item, so we know which item disappeared + where.moveNext(); + var nextItem:DataNode = where.current as DataNode; + if (nextItem) + nextItem.isPreviousSiblingRemoved = true; + + //remove the item + var parentOfItemToRemove:DataNode = _currentHierarchy.getParentItem(itemToDelete) as DataNode; + var collectionToChange:ArrayCollection = parentOfItemToRemove ? parentOfItemToRemove.children : _utils.getRoot(_currentHierarchy) as ArrayCollection; + //trace("REM: sel=" + selectedNode + "; before=" + itemToDelete); + _operationPerformedInLastStep = true; + collectionToChange.removeItem(itemToDelete); + } + + + /** + * @return true when the where parameter designates an item on the root collection (last ancestor of modified node) + */ + private function testAddition(where:HierarchicalCollectionViewCursor):void + { + var itemBeforeWhichWereAdding:DataNode = where.current as DataNode; + assertNotNull(itemBeforeWhichWereAdding); + + var parentOfAdditionLocation:DataNode = _currentHierarchy.getParentItem(itemBeforeWhichWereAdding) as DataNode; + var collectionToChange:ArrayCollection = parentOfAdditionLocation ? parentOfAdditionLocation.children : _utils.getRoot(_currentHierarchy) as ArrayCollection; + var positionOfItemBeforeWhichWereAdding:int = collectionToChange.getItemIndex(itemBeforeWhichWereAdding); + + _operationPerformedInLastStep = true; + collectionToChange.addItemAt(_utils.createSimpleNode(itemBeforeWhichWereAdding.label + " [INSERTED NODE]"), positionOfItemBeforeWhichWereAdding); + //trace("ADD: sel=" + selectedNode + "; before=" + itemBeforeWhichWereAdding); + } + + + + + + private static function onUncaughtClientError(event:UncaughtErrorEvent):void + { + event.preventDefault(); + event.stopImmediatePropagation(); + _noErrorsThrown = false; + + trace("\n" + event.error); + _utils.printHCollectionView(_currentHierarchy); + } + + + private static const HIERARCHY_STRING:String = (City(1) + Region(2)->City(1)->Company(1) + Region(2)->City(1)->Company(2) + Region(2)->City(1)->Company(2)->Department(1) + Region(2)->City(1)->Company(2)->Department(1)->Employee(1) + Region(2)->City(1)->Company(2)->Department(1)->Employee(2) + Region(2)->City(1)->Company(2)->Department(2) + Region(2)->City(1)->Company(2)->Department(2)->Employee(1) + Region(2)->City(1)->Company(2)->Department(2)->Employee(2) + Region(2)->City(1)->Company(2)->Department(2)->Employee(3) + Region(2)->City(1)->Company(2)->Department(3) + Region(2)->City(1)->Company(2)->Department(3)->Employee(1) + Region(2)->City(1)->Company(2)->Department(3)->Employee(2) + Region(2)->City(1)->Company(2)->Department(3)->Employee(3) + Region(2)->City(1)->Company(2)->Department(3)->Employee(4) + Region(2)->City(1)->Company(3) + Region(2)->City(1)->Company(3)->Department(1) + Region(2)->City(1)->Company(3)->Department(1)->Employee(1) + Region(2)->City(1)->Company(3)->Department(2) + Region(2)->City(1)->Company(3)->Department(2)->Employee(1) + Region(2)->City(1)->Company(3)->Department(2)->Employee(2) + Region(2)->City(1)->Company(3)->Department(3) + Region(2)->City(1)->Company(3)->Department(3)->Employee(1) + Region(2)->City(1)->Company(3)->Department(3)->Employee(2) + Region(2)->City(1)->Company(3)->Department(3)->Employee(3) + Region(2)->City(1)->Company(3)->Department(3)->Employee(4) + Region(2)->City(1)->Company(3)->Department(3)->Employee(5) + Region(2)->City(1)->Company(3)->Department(4) + Region(2)->City(1)->Company(3)->Department(4)->Employee(1) + Region(2)->City(1)->Company(3)->Department(4)->Employee(2) + Region(2)->City(1)->Company(3)->Department(4)->Employee(3) + Region(2)->City(1)->Company(3)->Department(4)->Employee(4) + Region(2)->City(1)->Company(4) + Region(2)->City(1)->Company(4)->Department(1) + Region(2)->City(1)->Company(4)->Department(1)->Employee(1) + Region(2)->City(1)->Company(4)->Department(1)->Employee(2) + Region(2)->City(1)->Company(4)->Department(1)->Employee(3) + Region(3) + Region(3)->City(1) + Region(3)->City(1)->Company(1) + Region(3)->City(1)->Company(1)->Department(1) + Region(3)->City(1)->Company(1)->Department(1)->Employee(1) + Region(3)->City(1)->Company(1)->Department(1)->Employee(2) + Region(3)->City(1)->Company(1)->Department(1)->Employee(3) + Region(3)->City(1)->Company(1)->Department(1)->Employee(4) + Region(3)->City(1)->Company(1)->Department(2) + Region(3)->City(1)->Company(1)->Department(2)->Employee(1) + Region(3)->City(1)->Company(1)->Department(2)->Employee(2) + Region(3)->City(1)->Company(1)->Department(2)->Employee(3) + Region(3)->City(1)->Company(1)->Department(3) + Region(3)->City(1)->Company(1)->Department(3)->Employee(1) + Region(3)->City(1)->Company(1)->Department(3)->Employee(2) + Region(3)->City(1)->Company(1)->Department(3)->Employee(3) + Region(3)->City(1)->Company(2) + Region(3)->City(1)->Company(2)->Department(1) + Region(3)->City(1)->Company(2)->Department(1)->Employee(1) + Region(3)->City(1)->Company(2)->Department(2) + Region(3)->City(1)->Company(2)->Department(2)->Employee(1) + Region(3)->City(1)->Company(2)->Department(2)->Employee(2) + Region(3)->City(1)->Company(2)->Department(3) + Region(3)->City(1)->Company(2)->Department(4) + Region(3)->City(1)->Company(3) + Region(3)->City(1)->Company(4) + Region(3)->City(1)->Company(4)->Department(1) + Region(3)->City(1)->Company(4)->Department(1)->Employee(1) + Region(3)->City(1)->Company(4)->Department(1)->Employee(2) + Region(3)->City(1)->Company(4)->Department(1)->Employee(3) + Region(3)->City(1)->Company(4)->Department(1)->Employee(4) + Region(3)->City(1)->Company(4)->Department(2) + Region(3)->City(1)->Company(4)->Department(2)->Employee(1) + Region(3)->City(1)->Company(4)->Department(2)->Employee(2) + Region(3)->City(1)->Company(4)->Department(2)->Employee(3) + Region(3)->City(1)->Company(4)->Department(3) + Region(3)->City(1)->Company(5) + Region(3)->City(2) + Region(3)->City(3) + Region(3)->City(4) + Region(3)->City(4)->Company(1) + Region(4) + Region(4)->City(1) + Region(4)->City(1)->Company(1) + ]]>).toString(); + } +} \ No newline at end of file diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as new file mode 100644 index 0000000000..82d00c7ffb --- /dev/null +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as @@ -0,0 +1,172 @@ +package +{ + import com.adobe.utils.StringUtil; + + import mx.collections.ArrayCollection; + import mx.collections.HierarchicalCollectionView; + import mx.collections.HierarchicalCollectionViewCursor; + import mx.collections.HierarchicalData; + import mx.collections.IViewCursor; + + public class HierarchicalCollectionViewTestUtils + { + //assumes the root is an ArrayCollection of DataNodes + public function clone(hcv:HierarchicalCollectionView):HierarchicalCollectionView + { + var oldRoot:ArrayCollection = ArrayCollection(getRoot(hcv)); + var newRoot:ArrayCollection = new ArrayCollection(); + + for each(var rootNode:DataNode in oldRoot) + { + newRoot.addItem(rootNode.clone()); + } + + return generateHCV(newRoot); + } + + public function createNodes(level:String, no:int):ArrayCollection + { + var nodes:ArrayCollection = new ArrayCollection(); + for(var i:int = 0; i < no; i++) + { + nodes.addItem(createSimpleNode(level)); + } + + return nodes; + } + + public function generateOpenHierarchyFromRootList(root:ArrayCollection):HierarchicalCollectionView + { + var hcv:HierarchicalCollectionView = generateHCV(root); + openAllNodes(hcv); + return hcv; + } + + public function generateHCV(rootCollection:ArrayCollection):HierarchicalCollectionView + { + return new HierarchicalCollectionView(new HierarchicalData(rootCollection)); + } + + public function openAllNodes(hcv:HierarchicalCollectionView):void + { + var cursor:HierarchicalCollectionViewCursor = hcv.createCursor() as HierarchicalCollectionViewCursor; + while(!cursor.afterLast) + { + hcv.openNode(cursor.current); + cursor.moveNext(); + } + } + + public function getRoot(hcv:HierarchicalCollectionView):Object + { + return hcv.source.getRoot(); + } + + public function printHCollectionView(hcv:HierarchicalCollectionView):void + { + trace(""); + var cursor:HierarchicalCollectionViewCursor = hcv.createCursor() as HierarchicalCollectionViewCursor; + while(!cursor.afterLast) + { + trace(DataNode(cursor.current).label); + cursor.moveNext(); + } + } + + public function createSimpleNode(label:String):DataNode + { + return new DataNode(label); + } + + public function isAncestor(node:DataNode, forNode:DataNode, hcv:HierarchicalCollectionView):Boolean + { + do + { + forNode = hcv.getParentItem(forNode) as DataNode; + } while(forNode && forNode != node) + + return forNode == node; + } + + public function nodesHaveCommonAncestor(node:DataNode, withNode:DataNode, hcv:HierarchicalCollectionView):Boolean + { + var nodeAndAncestors:Array = [node].concat(getNodeAncestors(node, hcv)); + var otherNodeAndAncestors:Array = [withNode].concat(getNodeAncestors(withNode, hcv)); + for each(var ancestor:DataNode in nodeAndAncestors) + if(otherNodeAndAncestors.indexOf(ancestor) != -1) + return true; + + return false; + } + + public function getNodeAncestors(node:DataNode, hcv:HierarchicalCollectionView):Array + { + var nodeParents:Array = []; + + // Make a list of parents of the node. + var parent:Object = hcv.getParentItem(node); + while (parent) + { + nodeParents.push(parent); + parent = hcv.getParentItem(parent); + } + + return nodeParents; + } + + public function navigateToItem(cursor:IViewCursor, item:DataNode):IViewCursor + { + while(!cursor.afterLast && cursor.current != item) + { + cursor.moveNext(); + } + + return cursor; + } + + public function generateHierarchySourceFromString(source:String):ArrayCollection + { + var rootCollection:ArrayCollection = new ArrayCollection(); + var alreadyCreatedNodes:Array = []; + var node:DataNode; + + var lines:Array = source.split("\n"); + for each(var line:String in lines) + { + if(!line) + continue; + + var currentLabel:String = ""; + var previousNode:DataNode = null; + var nodesOnThisLine:Array = StringUtil.trim(line).split("->"); + for each(var nodeName:String in nodesOnThisLine) + { + if(!nodeName) + continue; + + currentLabel += currentLabel ? "->" + nodeName : nodeName; + + var nodeAlreadyCreated:Boolean = alreadyCreatedNodes[currentLabel] != undefined; + + if(nodeAlreadyCreated) + node = alreadyCreatedNodes[currentLabel]; + else { + node = createSimpleNode(currentLabel); + alreadyCreatedNodes[currentLabel] = node; + } + + if(!nodeAlreadyCreated) { + if (previousNode) + previousNode.addChild(node); + else + rootCollection.addItem(node); + } + + previousNode = node; + } + } + + return rootCollection; + } + } +} \ No newline at end of file From 4cb80bfbb37225b86b9e69cc4ec2fc21efe0a668 Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Tue, 22 Jul 2014 11:22:50 +0100 Subject: [PATCH 039/392] FLEX-34119 CAUSE: HierarchicalCollectionViewCursor.collectionChangeHandler dealt with CollectionEventKind.ADD and CollectionEventKind.REMOVE events in a defective way: when the event happened before the currently-selected node, but in a different collection from the selected node, it assumed that it could use the currentChildBookmark to correct the position of the bookmark in that collection. However, currentChildBookmark can only be used on the collection it was created in, which is the collection of the currently-selected node and its siblings. This made ListCollectionView.getBookmarkIndex throw the 'Bookmark no longer valid' error. SOLUTION: In the case detailed above instead of changing the currentChildBookmark we need to adjust the bookmark associated with the collection that the changing (i.e. added or removed) node is in. NOTES: -Another problem in HierarchicalCollectionViewCursor.collectionChangeHandler was that currentChildBookmark was (attempted to be) updated with the corrected position of the parent of the changing node's sibling collection. However, currentChildBookmark represents the position of the currently-selected item, and should only be updated when the selected item's siblings collection changes, not when other collections change. Instead, we need to update the parentBookmarkStack collection, which contains the positions of the selected node's parents' positions in their collections. -After these changes HierarchicalCollectionViewCursor_FLEX_34119_Test doesn't show any more 'Bookmark no longer valid' errors. (But it still fails due to FLEX-34424). --- .../HierarchicalCollectionViewCursor.as | 77 +++++++++---------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as index b829dfe35e..db52010ca4 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as @@ -111,6 +111,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher /** * @private + * The bookmark representing the position of the current node in its siblings collection */ private var currentChildBookmark:CursorBookmark = CursorBookmark.FIRST; @@ -1152,21 +1153,9 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher var changingNode:Object; var parentOfChangingNode:Object; var parentOfCurrentNode:Object; - var parentStack:Array; - var parentTable:Dictionary; + var parentStack:Array = getParentStack(current); var isBefore:Boolean = false; - - // get the parent of the current item - parentStack = getParentStack(current); - // hash it by parent to map to depth - parentTable = new Dictionary(); - n = parentStack.length; - // don't insert the immediate parent - for (i = 0; i < n - 1; i++) - { - // 0 is null parent (the model) - parentTable[parentStack[i]] = i + 1; - } + // remember the current parent parentOfCurrentNode = parentStack[parentStack.length - 1]; @@ -1206,25 +1195,29 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } catch (e:ItemPendingError) { - } } } - else if (parentTable[parentOfChangingNode] != null) - { - if (changingNodeAndSiblings != null) + else { + var parentOfChangingNodeIndex:int = parentStack.indexOf(parentOfChangingNode); + var isOurAncestorChanging:Boolean = parentOfChangingNodeIndex != -1; + if (isOurAncestorChanging) { - changingCollectionCursor = changingNodeAndSiblings.createCursor(); - try - { - changingCollectionCursor.seek(currentChildBookmark); - changingCollectionCursor.moveNext(); - currentChildBookmark = changingCollectionCursor.bookmark; - } - catch (e:ItemPendingError) + if (changingNodeAndSiblings != null) { + var changingNodeCollectionIndex:int = parentOfChangingNodeIndex + 1; + changingCollectionCursor = changingNodeAndSiblings.createCursor(); + var bookmarkInChangingCollection:CursorBookmark = parentBookmarkStack[changingNodeCollectionIndex]; + try + { + changingCollectionCursor.seek(bookmarkInChangingCollection); + changingCollectionCursor.moveNext(); + } + catch (e:ItemPendingError) + { + } + parentBookmarkStack[changingNodeCollectionIndex] = changingCollectionCursor.bookmark; } - parentBookmarkStack[parentTable[parentOfChangingNode]] = currentChildBookmark; } } } @@ -1281,25 +1274,29 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } catch (e:ItemPendingError) { - } } } - else if (parentTable[parentOfChangingNode] != null) - { - if (changingNodeAndSiblings != null) + else { + var parentOfChangingNodeIndex:int = parentStack.indexOf(parentOfChangingNode); + var isOurAncestorChanging:Boolean = parentOfChangingNodeIndex != -1; + if (isOurAncestorChanging) { - changingCollectionCursor = changingNodeAndSiblings.createCursor(); - try - { - changingCollectionCursor.seek(currentChildBookmark); - changingCollectionCursor.movePrevious(); - currentChildBookmark = changingCollectionCursor.bookmark; - } - catch (e:ItemPendingError) + if (changingNodeAndSiblings != null) { + var changingNodeCollectionBookmarkIndex:int = parentOfChangingNodeIndex + 1; + changingCollectionCursor = changingNodeAndSiblings.createCursor(); + var bookmarkInChangingCollection:CursorBookmark = parentBookmarkStack[changingNodeCollectionBookmarkIndex]; + try + { + changingCollectionCursor.seek(bookmarkInChangingCollection); + changingCollectionCursor.movePrevious(); + } + catch (e:ItemPendingError) + { + } + parentBookmarkStack[changingNodeCollectionBookmarkIndex] = changingCollectionCursor.bookmark; } - parentBookmarkStack[parentTable[parentOfChangingNode]] = currentChildBookmark; } } } From 75e8db42470094f75c04a68254a7165c28bb065f Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Tue, 22 Jul 2014 11:24:48 +0100 Subject: [PATCH 040/392] FLEX-34119 Adding some explanations on the main unit test. --- ...ierarchicalCollectionViewCursor_FLEX_34119_Test.as | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as index 6f52139e9f..8bc72d79d4 100644 --- a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as @@ -19,6 +19,17 @@ package import org.flexunit.asserts.assertTrue; import org.flexunit.runners.Parameterized; + /** + * README + * + * -This unit test was initially created to test FLEX-34119, and ended up uncovering FLEX-34424 as well. + * -This test builds a hierarchical collection from a string (see HIERARCHY_STRING), so that it's easy + * to edit and change it for specific scenarios. + * -This test uses utility functions from HierarchicalCollectionViewTestUtils, and data objects from DataNode. + * -Running the test takes about 3min on my machine. It will vary on yours, of course, but it shouldn't + * be wildly different. + * -To speed it up you can decrease the size of the hierarchical collection by editing HIERARCHY_STRING. + */ [RunWith("org.flexunit.runners.Parameterized")] public class HierarchicalCollectionViewCursor_FLEX_34119_Test { From b8cc55f31da9e8c0fad497a9f7925fcc038c3d5f Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Tue, 22 Jul 2014 16:42:41 +0100 Subject: [PATCH 041/392] FLEX-34119 Simplified two if statements, removed an unused variable, and removed Duplicate variable definition warnings in HierarchicalCollectionViewCursor. --- .../HierarchicalCollectionViewCursor.as | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as index db52010ca4..af7c2c0320 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as @@ -91,11 +91,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher modelCursor = model.createCursor(); //check to see if the model has more than one top level items - if (model.length > 1) - more = true; - else - more = false; - + more = model.length > 1; } //-------------------------------------------------------------------------- @@ -1061,10 +1057,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher // let it throw an IPE, the classes using this cursor will handle it modelCursor.seek(CursorBookmark.FIRST, 0); - if (model.length > 1) - more = true; - else - more = false; + more = model.length > 1; currentChildBookmark = CursorBookmark.FIRST; parentNodes = []; parentBookmarkStack = []; @@ -1081,8 +1074,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher parentBookmarkStack = []; _currentDepth = 1; parentNodes = []; - var emptyBranch:Boolean = false; - + //first move to the end of the top level collection // let it throw an IPE, the classes using this cursor will handle it modelCursor.seek(CursorBookmark.LAST, 0); @@ -1155,6 +1147,9 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher var parentOfCurrentNode:Object; var parentStack:Array = getParentStack(current); var isBefore:Boolean = false; + var parentOfChangingNodeIndex:int; + var isOurAncestorChanging:Boolean; + var bookmarkInChangingCollection:CursorBookmark; // remember the current parent parentOfCurrentNode = parentStack[parentStack.length - 1]; @@ -1199,15 +1194,15 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } } else { - var parentOfChangingNodeIndex:int = parentStack.indexOf(parentOfChangingNode); - var isOurAncestorChanging:Boolean = parentOfChangingNodeIndex != -1; + parentOfChangingNodeIndex = parentStack.indexOf(parentOfChangingNode); + isOurAncestorChanging = parentOfChangingNodeIndex != -1; if (isOurAncestorChanging) { if (changingNodeAndSiblings != null) { var changingNodeCollectionIndex:int = parentOfChangingNodeIndex + 1; changingCollectionCursor = changingNodeAndSiblings.createCursor(); - var bookmarkInChangingCollection:CursorBookmark = parentBookmarkStack[changingNodeCollectionIndex]; + bookmarkInChangingCollection = parentBookmarkStack[changingNodeCollectionIndex]; try { changingCollectionCursor.seek(bookmarkInChangingCollection); @@ -1278,15 +1273,15 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } } else { - var parentOfChangingNodeIndex:int = parentStack.indexOf(parentOfChangingNode); - var isOurAncestorChanging:Boolean = parentOfChangingNodeIndex != -1; + parentOfChangingNodeIndex = parentStack.indexOf(parentOfChangingNode); + isOurAncestorChanging = parentOfChangingNodeIndex != -1; if (isOurAncestorChanging) { if (changingNodeAndSiblings != null) { var changingNodeCollectionBookmarkIndex:int = parentOfChangingNodeIndex + 1; changingCollectionCursor = changingNodeAndSiblings.createCursor(); - var bookmarkInChangingCollection:CursorBookmark = parentBookmarkStack[changingNodeCollectionBookmarkIndex]; + bookmarkInChangingCollection = parentBookmarkStack[changingNodeCollectionBookmarkIndex]; try { changingCollectionCursor.seek(bookmarkInChangingCollection); From 0e15df32a70f77916978b3008e62d218ebde56b5 Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Tue, 22 Jul 2014 17:54:16 +0100 Subject: [PATCH 042/392] FLEX-34119 Renamed a Boolean for clarity in HierarchicalCollectionViewCursor.collectionChangeHandler. --- .../HierarchicalCollectionViewCursor.as | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as index af7c2c0320..1452676c8d 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as @@ -1148,7 +1148,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher var parentStack:Array = getParentStack(current); var isBefore:Boolean = false; var parentOfChangingNodeIndex:int; - var isOurAncestorChanging:Boolean; + var selectedAndChangingNodesHaveCommonAncestor:Boolean; var bookmarkInChangingCollection:CursorBookmark; // remember the current parent @@ -1172,11 +1172,12 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher if (isBefore) { // if the added node is before the current - // and they share parent's then we have to + // and they share parents then we have to // adjust the currentChildIndex or // the stack of child indexes. parentOfChangingNode = collection.getParentItem(changingNode); changingNodeAndSiblings = collection.getChildren(parentOfChangingNode); + if (parentOfChangingNode == parentOfCurrentNode) { if (changingNodeAndSiblings != null) @@ -1195,8 +1196,8 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } else { parentOfChangingNodeIndex = parentStack.indexOf(parentOfChangingNode); - isOurAncestorChanging = parentOfChangingNodeIndex != -1; - if (isOurAncestorChanging) + selectedAndChangingNodesHaveCommonAncestor = parentOfChangingNodeIndex != -1; + if (selectedAndChangingNodesHaveCommonAncestor) { if (changingNodeAndSiblings != null) { @@ -1251,11 +1252,12 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher if (isBefore) { // if the removed node is before the current - // and they share parent's then we have to + // and they share parents then we have to // adjust the currentChildIndex or // the stack of child indexes. parentOfChangingNode = collection.getParentItem(changingNode); changingNodeAndSiblings = collection.getChildren(parentOfChangingNode); + if (parentOfChangingNode == parentOfCurrentNode) { if (changingNodeAndSiblings != null) @@ -1274,8 +1276,8 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } else { parentOfChangingNodeIndex = parentStack.indexOf(parentOfChangingNode); - isOurAncestorChanging = parentOfChangingNodeIndex != -1; - if (isOurAncestorChanging) + selectedAndChangingNodesHaveCommonAncestor = parentOfChangingNodeIndex != -1; + if (selectedAndChangingNodesHaveCommonAncestor) { if (changingNodeAndSiblings != null) { From 6b1330f006e04966687e310d37d36c6e799590af Mon Sep 17 00:00:00 2001 From: Justin Mclean Date: Sun, 27 Jul 2014 16:34:01 +1000 Subject: [PATCH 043/392] fixed MD5s for FP 13 and 14 --- ide/checkAllPlayerGlobals.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ide/checkAllPlayerGlobals.sh b/ide/checkAllPlayerGlobals.sh index 6ee9624b81..c290bbe3e8 100755 --- a/ide/checkAllPlayerGlobals.sh +++ b/ide/checkAllPlayerGlobals.sh @@ -121,6 +121,6 @@ downloadPlayerGlobal 11.7 12656571c57b2ad641838e5695a00e27 http://download.macro downloadPlayerGlobal 11.8 35bc69eec5091f70e221b4e63b66b60f http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_8.swc downloadPlayerGlobal 11.9 d18244c3c00c61a41f2d4d791d09fedb http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_9.swc downloadPlayerGlobal 12.0 4db4e934f39f774ba68fcd9a79654971 http://download.macromedia.com/get/flashplayer/updaters/12/playerglobal12_0.swc -downloadPlayerGlobal 13.0 7f9bfe038f00e97bc44abf52bb5b1260 http://download.macromedia.com/get/flashplayer/updaters/12/playerglobal13_0.swc -downloadPlayerGlobal 14.0 21fe2130f44ba535a6d250e65e6f260e http://download.macromedia.com/get/flashplayer/updaters/12/playerglobal14_0.swc +downloadPlayerGlobal 13.0 d40ea369a7fc2bcacc4cfcffff1339d4 http://download.macromedia.com/get/flashplayer/updaters/13/playerglobal13_0.swc +downloadPlayerGlobal 14.0 0bae2abfe34eed7d718eda123ec9e786 http://download.macromedia.com/get/flashplayer/updaters/14/playerglobal14_0.swc From ca262c672862788727c78284e5384a7cfea5f6ba Mon Sep 17 00:00:00 2001 From: Om Date: Mon, 28 Jul 2014 02:17:50 -0700 Subject: [PATCH 044/392] New Android4.x skin for ViewMenu and ViewMenuItems that matches --- .../projects/mobiletheme/src/android4x.css | 17 ++ .../spark/skins/android4/ViewMenuItemSkin.as | 238 ++++++++++++++++++ .../spark/skins/android4/ViewMenuSkin.mxml | 183 ++++++++++++++ 3 files changed, 438 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/ViewMenuItemSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/ViewMenuSkin.mxml diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css index d8740517a9..5fee06f1bb 100644 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -245,6 +245,23 @@ VScrollBar thumbColor: #333333; } +ViewMenu +{ + skinClass: ClassReference("spark.skins.android4.ViewMenuSkin"); +} + +ViewMenuItem +{ + iconPlacement : "left"; + chromeColor : #FFFFFF; + focusColor: #DEDEDD; + paddingBottom: 8; + paddingLeft: 8; + paddingRight: 8; + paddingTop: 8; + skinClass : ClassReference("spark.skins.android4.ViewMenuItemSkin"); +} + ViewNavigator { skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ViewMenuItemSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/ViewMenuItemSkin.as new file mode 100644 index 0000000000..6a75c65caa --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ViewMenuItemSkin.as @@ -0,0 +1,238 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.GradientType; +import flash.display.Graphics; + +import mx.core.DPIClassification; +import mx.core.mx_internal; + +import spark.components.IconPlacement; +import spark.skins.mobile.assets.ViewMenuItem_down; +import spark.skins.mobile.assets.ViewMenuItem_showsCaret; +import spark.skins.mobile.assets.ViewMenuItem_up; +import spark.skins.mobile.supportClasses.ButtonSkinBase; +import spark.skins.mobile120.assets.ViewMenuItem_down; +import spark.skins.mobile120.assets.ViewMenuItem_showsCaret; +import spark.skins.mobile120.assets.ViewMenuItem_up; +import spark.skins.mobile320.assets.ViewMenuItem_down; +import spark.skins.mobile320.assets.ViewMenuItem_showsCaret; +import spark.skins.mobile320.assets.ViewMenuItem_up; +import spark.skins.mobile480.assets.ViewMenuItem_down; +import spark.skins.mobile480.assets.ViewMenuItem_showsCaret; +import spark.skins.mobile480.assets.ViewMenuItem_up; +import spark.skins.mobile640.assets.ViewMenuItem_down; +import spark.skins.mobile640.assets.ViewMenuItem_showsCaret; +import spark.skins.mobile640.assets.ViewMenuItem_up; + + +use namespace mx_internal; + +/** + * Default skin for ViewMenuItem. Supports a label, icon and iconPlacement and draws a background. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class ViewMenuItemSkin extends ButtonSkin +{ + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ViewMenuItemSkin() + { + super(); + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + + upBorderSkin = spark.skins.mobile640.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile640.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile640.assets.ViewMenuItem_showsCaret; + + layoutGap = 24; + layoutPaddingLeft = 24; + layoutPaddingRight = 24; + layoutPaddingTop = 24; + layoutPaddingBottom = 24; + layoutBorderSize = 3; + + break; + } + case DPIClassification.DPI_480: + { + // Note provisional may need changes + upBorderSkin = spark.skins.mobile.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile.assets.ViewMenuItem_showsCaret; + + layoutGap = 16; + layoutPaddingLeft = 16; + layoutPaddingRight = 16; + layoutPaddingTop = 16; + layoutPaddingBottom = 16; + layoutBorderSize = 2; + + break; + + } + case DPIClassification.DPI_320: + { + + upBorderSkin = spark.skins.mobile320.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile320.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile320.assets.ViewMenuItem_showsCaret; + + layoutGap = 12; + layoutPaddingLeft = 12; + layoutPaddingRight = 12; + layoutPaddingTop = 12; + layoutPaddingBottom = 12; + layoutBorderSize = 2; + + + break; + } + case DPIClassification.DPI_240: + { + upBorderSkin = spark.skins.mobile.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile.assets.ViewMenuItem_showsCaret; + + layoutGap = 8; + layoutPaddingLeft = 8; + layoutPaddingRight = 8; + layoutPaddingTop = 8; + layoutPaddingBottom = 8; + layoutBorderSize = 1; + + break; + + } + case DPIClassification.DPI_120: + { + upBorderSkin = spark.skins.mobile120.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile120.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile120.assets.ViewMenuItem_showsCaret; + + layoutGap = 4; + layoutPaddingLeft = 4; + layoutPaddingRight = 4; + layoutPaddingTop = 4; + layoutPaddingBottom = 4; + layoutBorderSize = 1; + + break; + + } + default: + { + upBorderSkin = spark.skins.mobile.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile.assets.ViewMenuItem_showsCaret; + + layoutGap = 6; + layoutPaddingLeft = 6; + layoutPaddingRight = 6; + layoutPaddingTop = 6; + layoutPaddingBottom = 6; + layoutBorderSize = 1; + } + } + + } + + /** + * Class to use for the border in the showsCaret state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + * @default Button_down + */ + protected var showsCaretBorderSkin:Class; + + /** + * @private + */ + override protected function getBorderClassForCurrentState():Class + { + var borderClass:Class = super.getBorderClassForCurrentState(); + + if (currentState == "showsCaret") + borderClass = showsCaretBorderSkin; + + return borderClass; + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + var iconPlacement:String = getStyle("iconPlacement"); + useCenterAlignment = (iconPlacement == IconPlacement.LEFT) + || (iconPlacement == IconPlacement.RIGHT); + + super.layoutContents(unscaledWidth, unscaledHeight); + } + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + // omit call to super.drawBackground(), drawRect instead + + if (currentState == "showsCaret" || currentState == "down") + { + graphics.beginFill(getStyle("focusColor")); + } + else + { + colorMatrix.createGradientBox(unscaledWidth, + unscaledHeight, + Math.PI / 2, 0, 0); + var chromeColor:uint = getStyle("chromeColor"); + + graphics.beginGradientFill(GradientType.LINEAR, + [chromeColor, chromeColor], + [1.0, 1.0], + [0, 255], + colorMatrix); + } + graphics.drawRect(0,0,unscaledWidth,unscaledHeight); + graphics.lineStyle(0.5,0,0.2); + graphics.drawRect(0,unscaledHeight,unscaledWidth,0.5); + graphics.endFill(); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/ViewMenuSkin.mxml b/frameworks/projects/mobiletheme/src/spark/skins/android4/ViewMenuSkin.mxml new file mode 100644 index 0000000000..67fd754ff5 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/ViewMenuSkin.mxml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 90c9136299d993a0d8a8d97a861f663ea5593e16 Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Mon, 28 Jul 2014 11:48:21 +0100 Subject: [PATCH 045/392] FLEX-34119 FLEX-34440 Adding the setItemAt() operation to HierarchicalCollectionViewCursor_FLEX_34119_Test, which uncovers FLEX-34440. Separate unit test for FLEX-34440 will follow. --- ...calCollectionViewCursor_FLEX_34119_Test.as | 66 +++++++++++-------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as index 8bc72d79d4..c32e406f16 100644 --- a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as @@ -8,7 +8,7 @@ package import mx.collections.HierarchicalCollectionViewCursor; import mx.collections.IViewCursor; import mx.core.FlexGlobals; - + import spark.components.WindowedApplication; import flexunit.framework.AssertionFailedError; @@ -49,7 +49,7 @@ package private static var OPERATION_LOCATION:int = 0; private static var OPERATION_INDEX:int = 0; for(SELECTED_INDEX = 0; SELECTED_INDEX < NO_ITEMS_IN_HIERARCHY; SELECTED_INDEX++) - for(OPERATION_LOCATION = SELECTED_INDEX -1; OPERATION_LOCATION >= 0; OPERATION_LOCATION--) + for(OPERATION_LOCATION = SELECTED_INDEX; OPERATION_LOCATION >= 0; OPERATION_LOCATION--) for(OPERATION_INDEX = 0; OPERATION_INDEX < OPERATIONS.length; OPERATION_INDEX++) positionAndOperation.push([SELECTED_INDEX, OPERATION_LOCATION, OPERATIONS[OPERATION_INDEX]]); } @@ -124,7 +124,7 @@ package [Test(dataProvider="positionAndOperation")] public function testReproduce_FLEX_34119_Comprehensive(selectedItemIndex:int, operationIndex:int, operation:int):void { - assertThat(operationIndex < selectedItemIndex); + assertThat(operationIndex <= selectedItemIndex); try { //WHEN @@ -143,17 +143,21 @@ package selectedNode.isSelected = true; + var selectedNodeOrDirectAncestorWasRemoved:Boolean; //3. Perform operation if (operation == OP_ADD) - testAddition(_operationCursor); + selectedNodeOrDirectAncestorWasRemoved = testAddition(_operationCursor); else if (operation == OP_REMOVE) - testRemoval(_operationCursor); + selectedNodeOrDirectAncestorWasRemoved = testRemoval(_operationCursor, selectedNode); + else if(operation == OP_SET) + selectedNodeOrDirectAncestorWasRemoved = testReplacement(_operationCursor, selectedNode); //THEN 1 assertTrue(_noErrorsThrown); - - + if(selectedNodeOrDirectAncestorWasRemoved) + return; //it means that _sut.current is now (correctly) null + //4. Create mirror HierarchicalCollectionView from the changed root, as the source of truth _mirrorCursor = _utils.navigateToItem(_currentHierarchy.createCursor() as HierarchicalCollectionViewCursor, selectedNode); @@ -181,25 +185,16 @@ package _operationCursor = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; _operationCursor.seek(new CursorBookmark(where)); var itemToPerformOperationOn:DataNode = _operationCursor.current as DataNode; - - switch(operation) - { - case OP_ADD: - return _utils.nodesHaveCommonAncestor(itemToPerformOperationOn, selectedNode, _currentHierarchy); - case OP_REMOVE: - return !_utils.isAncestor(itemToPerformOperationOn, selectedNode, _currentHierarchy); - case OP_SET: - return false; - default: - return false; - } + return _utils.nodesHaveCommonAncestor(itemToPerformOperationOn, selectedNode, _currentHierarchy); } - private function testRemoval(where:HierarchicalCollectionViewCursor):void + private function testRemoval(where:HierarchicalCollectionViewCursor, selectedNode:DataNode):Boolean { var itemToDelete:DataNode = where.current as DataNode; assertNotNull(itemToDelete); - + + var currentWillBeNulled:Boolean = itemToDelete == selectedNode || _utils.isAncestor(itemToDelete, selectedNode, _currentHierarchy); + //mark the next item, so we know which item disappeared where.moveNext(); var nextItem:DataNode = where.current as DataNode; @@ -209,16 +204,15 @@ package //remove the item var parentOfItemToRemove:DataNode = _currentHierarchy.getParentItem(itemToDelete) as DataNode; var collectionToChange:ArrayCollection = parentOfItemToRemove ? parentOfItemToRemove.children : _utils.getRoot(_currentHierarchy) as ArrayCollection; - //trace("REM: sel=" + selectedNode + "; before=" + itemToDelete); + //trace("REM: sel=" + selectedNode + "; before=" + nextItem); _operationPerformedInLastStep = true; collectionToChange.removeItem(itemToDelete); + + return currentWillBeNulled; } - /** - * @return true when the where parameter designates an item on the root collection (last ancestor of modified node) - */ - private function testAddition(where:HierarchicalCollectionViewCursor):void + private function testAddition(where:HierarchicalCollectionViewCursor):Boolean { var itemBeforeWhichWereAdding:DataNode = where.current as DataNode; assertNotNull(itemBeforeWhichWereAdding); @@ -229,7 +223,25 @@ package _operationPerformedInLastStep = true; collectionToChange.addItemAt(_utils.createSimpleNode(itemBeforeWhichWereAdding.label + " [INSERTED NODE]"), positionOfItemBeforeWhichWereAdding); - //trace("ADD: sel=" + selectedNode + "; before=" + itemBeforeWhichWereAdding); + //trace("ADD: sel=" + selectedNode + "); + return false; + } + + private function testReplacement(where:HierarchicalCollectionViewCursor, selectedNode:DataNode):Boolean + { + var itemToBeReplaced:DataNode = where.current as DataNode; + assertNotNull(itemToBeReplaced); + + var currentWillBeNulled:Boolean = itemToBeReplaced == selectedNode || _utils.isAncestor(itemToBeReplaced, selectedNode, _currentHierarchy); + + var parentOfReplacementLocation:DataNode = _currentHierarchy.getParentItem(itemToBeReplaced) as DataNode; + var collectionToChange:ArrayCollection = parentOfReplacementLocation ? parentOfReplacementLocation.children : _utils.getRoot(_currentHierarchy) as ArrayCollection; + var replacedItemIndex:int = collectionToChange.getItemIndex(itemToBeReplaced); + + _operationPerformedInLastStep = true; + collectionToChange.setItemAt(_utils.createSimpleNode(itemToBeReplaced.label + " [REPLACED NODE]"), replacedItemIndex); + //trace("REPLACE: sel=" + selectedNode + "); + return currentWillBeNulled; } From 3336f2ecb058b9184b070c1fc910e15ecfaeb2e6 Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Mon, 28 Jul 2014 11:51:10 +0100 Subject: [PATCH 046/392] FLEX-34440 Added unit test to reproduce FLEX-34440. This also required a few changes in HierarchicalCollectionViewTestUtils. --- ...calCollectionViewCursor_FLEX_34440_Test.as | 125 ++++++++++++++++++ .../HierarchicalCollectionViewTestUtils.as | 20 ++- 2 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34440_Test.as diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34440_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34440_Test.as new file mode 100644 index 0000000000..c6fb4cae22 --- /dev/null +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34440_Test.as @@ -0,0 +1,125 @@ +package +{ +import flash.events.UncaughtErrorEvent; + +import mx.collections.ArrayCollection; +import mx.collections.CursorBookmark; +import mx.collections.HierarchicalCollectionView; +import mx.collections.HierarchicalCollectionViewCursor; +import mx.core.FlexGlobals; + +import org.flexunit.asserts.assertNotNull; +import org.flexunit.asserts.assertTrue; +import org.flexunit.runners.Parameterized; + +import spark.components.WindowedApplication; + +public class HierarchicalCollectionViewCursor_FLEX_34440_Test + { + private static var _utils:HierarchicalCollectionViewTestUtils = new HierarchicalCollectionViewTestUtils(); + private static var _noErrorsThrown:Boolean = true; + private static var _currentHierarchy:HierarchicalCollectionView; + private static var _sut:HierarchicalCollectionViewCursor; + private static var _operationCursor:HierarchicalCollectionViewCursor; + + [BeforeClass] + public static function setUpBeforeClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtClientError); + } + + [AfterClass] + public static function tearDownAfterClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.removeEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtClientError); + } + + [Before] + public function setUp():void + { + _currentHierarchy = _utils.generateOpenHierarchyFromRootListWithAllNodesMethod(_utils.generateHierarchySourceFromString(HIERARCHY_STRING)); + _sut = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + } + + [After] + public function tearDown():void + { + _sut = null; + _currentHierarchy = null; + _operationCursor = null; + } + + [Test] + public function testReproduce_FLEX_34440():void + { + //GIVEN + var selectedItemIndex:int = 10, operationIndex:int = 5; + + //WHEN + //1. Select a specific node + _sut.seek(new CursorBookmark(selectedItemIndex)); + + var selectedNode:DataNode = DataNode(_sut.current); + assertNotNull(selectedNode); + selectedNode.isSelected = true; + + //2. Perform setItemAt operation + _operationCursor = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + _operationCursor.seek(new CursorBookmark(operationIndex)); + performReplacement(_operationCursor); + + //THEN + assertTrue(_noErrorsThrown); + } + + private static function performReplacement(where:HierarchicalCollectionViewCursor):void + { + var itemToBeReplaced:DataNode = where.current as DataNode; + assertNotNull(itemToBeReplaced); + + var parentOfReplacementLocation:DataNode = _currentHierarchy.getParentItem(itemToBeReplaced) as DataNode; + var collectionToChange:ArrayCollection = parentOfReplacementLocation ? parentOfReplacementLocation.children : _utils.getRoot(_currentHierarchy) as ArrayCollection; + var replacedItemIndex:int = collectionToChange.getItemIndex(itemToBeReplaced); + + collectionToChange.setItemAt(_utils.createSimpleNode(itemToBeReplaced.label + " [REPLACED NODE]"), replacedItemIndex); + } + + + + + + private static function onUncaughtClientError(event:UncaughtErrorEvent):void + { + event.preventDefault(); + event.stopImmediatePropagation(); + _noErrorsThrown = false; + + trace("\n FAIL: " + event.error); + _utils.printHCollectionView(_currentHierarchy); + } + + + private static const HIERARCHY_STRING:String = (City(0) + Region(2)->City(1) + Region(2)->City(1)->Company(1) + Region(2)->City(1)->Company(2)TBR + Region(2)->City(1)->Company(2)TBR->Department(1) + Region(2)->City(1)->Company(2)TBR->Department(2) + Region(2)->City(1)->Company(2)TBR->Department(2)->Employee(1) + Region(2)->City(1)->Company(2)TBR->Department(2)->Employee(2) + Region(2)->City(1)->Company(2)TBR->Department(2)->Employee(3)SEL + Region(2)->City(1)->Company(2)TBR->Department(3) + Region(2)->City(1)->Company(2)TBR->Department(3)->Employee(1) + Region(2)->City(1)->Company(2)TBR->Department(3)->Employee(2) + Region(2)->City(1)->Company(2)TBR->Department(3)->Employee(3) + Region(2)->City(1)->Company(2)TBR->Department(3)->Employee(4) + Region(2)->City(1)->Company(3) + Region(2)->City(1)->Company(3)->Department(1) + Region(2)->City(1)->Company(3)->Department(1)->Employee(1) + Region(2)->City(1)->Company(3)->Department(2) + ]]>).toString(); + } +} \ No newline at end of file diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as index 82d00c7ffb..f89e10c4ef 100644 --- a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as @@ -3,14 +3,18 @@ package import com.adobe.utils.StringUtil; import mx.collections.ArrayCollection; + import mx.collections.CursorBookmark; import mx.collections.HierarchicalCollectionView; import mx.collections.HierarchicalCollectionViewCursor; import mx.collections.HierarchicalData; import mx.collections.IViewCursor; + import mx.utils.UIDUtil; public class HierarchicalCollectionViewTestUtils { //assumes the root is an ArrayCollection of DataNodes + private var _allNodes:Object = {}; + public function clone(hcv:HierarchicalCollectionView):HierarchicalCollectionView { var oldRoot:ArrayCollection = ArrayCollection(getRoot(hcv)); @@ -37,14 +41,20 @@ package public function generateOpenHierarchyFromRootList(root:ArrayCollection):HierarchicalCollectionView { - var hcv:HierarchicalCollectionView = generateHCV(root); + var hcv:HierarchicalCollectionView = generateHCV(root, false); openAllNodes(hcv); return hcv; } + + public function generateOpenHierarchyFromRootListWithAllNodesMethod(root:ArrayCollection):HierarchicalCollectionView + { + var hcv:HierarchicalCollectionView = generateHCV(root, true); + return hcv; + } - public function generateHCV(rootCollection:ArrayCollection):HierarchicalCollectionView + public function generateHCV(rootCollection:ArrayCollection, useAllNodes:Boolean = false):HierarchicalCollectionView { - return new HierarchicalCollectionView(new HierarchicalData(rootCollection)); + return new HierarchicalCollectionView(new HierarchicalData(rootCollection), useAllNodes ? _allNodes : null); } public function openAllNodes(hcv:HierarchicalCollectionView):void @@ -75,7 +85,9 @@ package public function createSimpleNode(label:String):DataNode { - return new DataNode(label); + var node:DataNode = new DataNode(label); + _allNodes[UIDUtil.getUID(node)] = node; + return node; } public function isAncestor(node:DataNode, forNode:DataNode, hcv:HierarchicalCollectionView):Boolean From 7b55b014b8957d66dbce877843755ea3cc6a3fab Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Mon, 28 Jul 2014 12:11:05 +0100 Subject: [PATCH 047/392] FLEX-34119 FLEX-34440 changing the package of the unit tests (and helper classes) to mx.collections. --- frameworks/tests/unitTests/mx/collections/DataNode.as | 2 +- .../collections/HierarchicalCollectionViewCursor_Basics_Test.as | 2 +- .../HierarchicalCollectionViewCursor_FLEX_34119_Test.as | 2 +- .../HierarchicalCollectionViewCursor_FLEX_34440_Test.as | 2 +- .../mx/collections/HierarchicalCollectionViewTestUtils.as | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frameworks/tests/unitTests/mx/collections/DataNode.as b/frameworks/tests/unitTests/mx/collections/DataNode.as index da4f7fd213..db0d35ff98 100644 --- a/frameworks/tests/unitTests/mx/collections/DataNode.as +++ b/frameworks/tests/unitTests/mx/collections/DataNode.as @@ -1,4 +1,4 @@ -package { +package mx.collections { import mx.collections.ArrayCollection; public class DataNode { diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as index 65e2cb98c4..2990a80458 100644 --- a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as @@ -1,4 +1,4 @@ -package +package mx.collections { import flash.events.UncaughtErrorEvent; diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as index c32e406f16..8da88410a3 100644 --- a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34119_Test.as @@ -1,4 +1,4 @@ -package +package mx.collections { import flash.events.UncaughtErrorEvent; diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34440_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34440_Test.as index c6fb4cae22..1b5e81d56d 100644 --- a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34440_Test.as +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34440_Test.as @@ -1,4 +1,4 @@ -package +package mx.collections { import flash.events.UncaughtErrorEvent; diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as index f89e10c4ef..6eb443be8a 100644 --- a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewTestUtils.as @@ -1,4 +1,4 @@ -package +package mx.collections { import com.adobe.utils.StringUtil; From 3ce553b3db3b0d2e95f55b367317f91e99869e8f Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Mon, 28 Jul 2014 12:19:13 +0100 Subject: [PATCH 048/392] FLEX-34119 FLEX-34440 Renaming variables to make code more readable. --- .../collections/HierarchicalCollectionView.as | 54 +++++++++---------- .../HierarchicalCollectionViewCursor.as | 20 +++---- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as index f5dee09a74..6b41c621e4 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as @@ -1542,8 +1542,8 @@ public class HierarchicalCollectionView extends EventDispatcher var n:int; var location:int; var uid:String; - var parent:Object; - var node:Object; + var parentOfChangingNode:Object; + var changingNode:Object; var items:Array; var convertedEvent:CollectionEvent; @@ -1570,14 +1570,14 @@ public class HierarchicalCollectionView extends EventDispatcher ce.kind); for (i = 0; i < n; i++) { - node = ce.items[i]; - if (node is XML) - startTrackUpdates(node); - parent = getParentItem(node); - if (parent != null) - getVisibleNodes(node, parent, convertedEvent.items); + changingNode = ce.items[i]; + if (changingNode is XML) + startTrackUpdates(changingNode); + parentOfChangingNode = getParentItem(changingNode); + if (parentOfChangingNode != null) + getVisibleNodes(changingNode, parentOfChangingNode, convertedEvent.items); } - convertedEvent.location = getVisibleLocationInSubCollection(parent, ce.location); + convertedEvent.location = getVisibleLocationInSubCollection(parentOfChangingNode, ce.location); dispatchEvent(convertedEvent); } else if (ce.kind == CollectionEventKind.REMOVE) @@ -1590,14 +1590,14 @@ public class HierarchicalCollectionView extends EventDispatcher ce.kind); for (i = 0; i < n; i++) { - node = ce.items[i]; - if (node is XML) - stopTrackUpdates(node); - parent = getParentItem(node); - if (parent != null) - getVisibleNodes(node, parent, convertedEvent.items); + changingNode = ce.items[i]; + if (changingNode is XML) + stopTrackUpdates(changingNode); + parentOfChangingNode = getParentItem(changingNode); + if (parentOfChangingNode != null) + getVisibleNodes(changingNode, parentOfChangingNode, convertedEvent.items); } - convertedEvent.location = getVisibleLocationInSubCollection(parent, ce.location); + convertedEvent.location = getVisibleLocationInSubCollection(parentOfChangingNode, ce.location); currentLength -= convertedEvent.items.length; dispatchEvent(convertedEvent); } @@ -1622,22 +1622,22 @@ public class HierarchicalCollectionView extends EventDispatcher for (i = 0; i < n; i++) { - node = ce.items[i].oldValue; - parent = getParentItem(node); + changingNode = ce.items[i].oldValue; + parentOfChangingNode = getParentItem(changingNode); - if (parent != null) + if (parentOfChangingNode != null) { - getVisibleNodes(node, parent, convertedEvent.items); + getVisibleNodes(changingNode, parentOfChangingNode, convertedEvent.items); // update the parent of the new item only // if the parent node is opened - if (_openNodes[UIDUtil.getUID(parent)] != null) + if (_openNodes[UIDUtil.getUID(parentOfChangingNode)] != null) { var newNode:Object = ce.items[i].newValue; uid = UIDUtil.getUID(newNode); - parentMap[uid] = parent; + parentMap[uid] = parentOfChangingNode; // delete the parent map for the old node - delete parentMap[UIDUtil.getUID(node)]; + delete parentMap[UIDUtil.getUID(changingNode)]; } } } @@ -1646,10 +1646,10 @@ public class HierarchicalCollectionView extends EventDispatcher var j:int = 0; for (i = 0; i < n; i++) { - node = ce.items[i].oldValue; - if (node is XML) - stopTrackUpdates(node); - while (convertedEvent.items[j] != node) + changingNode = ce.items[i].oldValue; + if (changingNode is XML) + stopTrackUpdates(changingNode); + while (convertedEvent.items[j] != changingNode) j++; convertedEvent.items.splice(j, 1); } diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as index 1452676c8d..df6ee81c83 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as @@ -1148,8 +1148,9 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher var parentStack:Array = getParentStack(current); var isBefore:Boolean = false; var parentOfChangingNodeIndex:int; - var selectedAndChangingNodesHaveCommonAncestor:Boolean; + var isChangingNodeParentAncestorOfSelectedNode:Boolean; var bookmarkInChangingCollection:CursorBookmark; + var changingNodeCollectionBookmarkIndex:int; // remember the current parent parentOfCurrentNode = parentStack[parentStack.length - 1]; @@ -1196,14 +1197,14 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } else { parentOfChangingNodeIndex = parentStack.indexOf(parentOfChangingNode); - selectedAndChangingNodesHaveCommonAncestor = parentOfChangingNodeIndex != -1; - if (selectedAndChangingNodesHaveCommonAncestor) + isChangingNodeParentAncestorOfSelectedNode = parentOfChangingNodeIndex != -1; + if (isChangingNodeParentAncestorOfSelectedNode) { if (changingNodeAndSiblings != null) { - var changingNodeCollectionIndex:int = parentOfChangingNodeIndex + 1; + changingNodeCollectionBookmarkIndex = parentOfChangingNodeIndex + 1; changingCollectionCursor = changingNodeAndSiblings.createCursor(); - bookmarkInChangingCollection = parentBookmarkStack[changingNodeCollectionIndex]; + bookmarkInChangingCollection = parentBookmarkStack[changingNodeCollectionBookmarkIndex]; try { changingCollectionCursor.seek(bookmarkInChangingCollection); @@ -1212,7 +1213,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher catch (e:ItemPendingError) { } - parentBookmarkStack[changingNodeCollectionIndex] = changingCollectionCursor.bookmark; + parentBookmarkStack[changingNodeCollectionBookmarkIndex] = changingCollectionCursor.bookmark; } } } @@ -1276,12 +1277,12 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } else { parentOfChangingNodeIndex = parentStack.indexOf(parentOfChangingNode); - selectedAndChangingNodesHaveCommonAncestor = parentOfChangingNodeIndex != -1; - if (selectedAndChangingNodesHaveCommonAncestor) + isChangingNodeParentAncestorOfSelectedNode = parentOfChangingNodeIndex != -1; + if (isChangingNodeParentAncestorOfSelectedNode) { if (changingNodeAndSiblings != null) { - var changingNodeCollectionBookmarkIndex:int = parentOfChangingNodeIndex + 1; + changingNodeCollectionBookmarkIndex = parentOfChangingNodeIndex + 1; changingCollectionCursor = changingNodeAndSiblings.createCursor(); bookmarkInChangingCollection = parentBookmarkStack[changingNodeCollectionBookmarkIndex]; try @@ -1297,6 +1298,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } } } + delete collection.parentMap[UIDUtil.getUID(changingNode)]; } From c2e68dc6d130094816c9407c5fa6f2782f1023d6 Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Mon, 28 Jul 2014 13:09:23 +0100 Subject: [PATCH 049/392] FLEX-34440 CAUSE: When HierarchicalCollectionView converts a CollectionEventKind.REPLACE CollectionEvent into a CollectionEventKind.REMOVE one, it also deletes the parent(s) of the replaced node root(s). However, if the HierarchicalCollectionViewCursor which then immediately responds to this converted event (in collectionChangeHandler()) happens to have its 'current' node inside the replaced branch, it will fail to retrieve all the parents of the current node, because the parent of the root of the branch has been deleted. This is problematic, because HierarchicalCollectionViewCursor.collectionChangeHandler assumes that parentStack and parentBookmarkStack have the same number of items, and that any item in the latter array represents the position (CursorBookmark) of an item in the former array in its siblings collection. So it will try to use a parentBookmarkStack item on a different collection to which it was created on, thus throwing a 'Bookmark no longer valid' CollectionViewError. SOLUTION: HierarchicalCollectionView.nestedCollectionChangeHandler now deletes the parent(s) of the replaced node root(s) *after* dispatching the converted CollectionEvent, as opposed to *before*. NOTES: -This now makes HierarchicalCollectionViewCursor_FLEX_34440_Test pass. -Also removed three unused local variables from HierarchicalCollectionView.nestedCollectionChangeHandler. --- .../collections/HierarchicalCollectionView.as | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as index 6b41c621e4..40ba8c39b7 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as @@ -1540,11 +1540,8 @@ public class HierarchicalCollectionView extends EventDispatcher { var i:int; var n:int; - var location:int; - var uid:String; var parentOfChangingNode:Object; var changingNode:Object; - var items:Array; var convertedEvent:CollectionEvent; if (event is CollectionEvent) @@ -1632,12 +1629,7 @@ public class HierarchicalCollectionView extends EventDispatcher // if the parent node is opened if (_openNodes[UIDUtil.getUID(parentOfChangingNode)] != null) { - var newNode:Object = ce.items[i].newValue; - uid = UIDUtil.getUID(newNode); - parentMap[uid] = parentOfChangingNode; - - // delete the parent map for the old node - delete parentMap[UIDUtil.getUID(changingNode)]; + parentMap[UIDUtil.getUID(ce.items[i].newValue)] = parentOfChangingNode; } } } @@ -1660,6 +1652,15 @@ public class HierarchicalCollectionView extends EventDispatcher dispatchEvent(convertedEvent); } dispatchEvent(event); + + for (i = 0; i < n; i++) + { + changingNode = ce.items[i].oldValue; + parentOfChangingNode = getParentItem(changingNode); + + if (parentOfChangingNode != null && _openNodes[UIDUtil.getUID(parentOfChangingNode)] != null) + delete parentMap[UIDUtil.getUID(changingNode)]; + } } else if (ce.kind == CollectionEventKind.RESET) { From 4970df88bea72d134a53c4a5ceddf236db7dde84 Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Mon, 28 Jul 2014 15:57:26 +0100 Subject: [PATCH 050/392] FLEX-34424 Added a unit test to reproduce FLEX-34424. As expected, it is currently failing. Also added a few more tests to HierarchicalCollectionViewCursor_Basics_Test to do with the deletion of nodes. These are currently passing. --- ...rchicalCollectionViewCursor_Basics_Test.as | 81 ++++++++++- ...calCollectionViewCursor_FLEX_34424_Test.as | 126 ++++++++++++++++++ 2 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34424_Test.as diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as index 2990a80458..6e3e34ad59 100644 --- a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_Basics_Test.as @@ -1,8 +1,10 @@ package mx.collections { import flash.events.UncaughtErrorEvent; - - import mx.collections.ArrayCollection; + +import flashx.textLayout.debug.assert; + +import mx.collections.ArrayCollection; import mx.collections.CursorBookmark; import mx.collections.HierarchicalCollectionView; import mx.collections.HierarchicalCollectionViewCursor; @@ -125,6 +127,81 @@ package mx.collections //then assertEquals(newLastCompany, _sut.current); } + + [Test] + public function testRemovingCurrentMiddleItemChangesCurrentToNextItem():void + { + //given + var firstCompany:DataNode = _level0.getItemAt(0) as DataNode; + var secondLocation:DataNode = firstCompany.children.getItemAt(1) as DataNode; + var thirdDepartmentOfSecondLocation:DataNode = secondLocation.children.getItemAt(2) as DataNode; + + _sut.seek(new CursorBookmark(6)); //Company(1)->Location(2)->Department(2) + + //when + secondLocation.children.removeItemAt(1); + + //then + assertEquals(thirdDepartmentOfSecondLocation, _sut.current); + } + + [Test] + public function testRemovingPreviousSiblingOfCurrentMiddleItemDoesNotChangeCurrent():void + { + //given + var firstCompany:DataNode = _level0.getItemAt(0) as DataNode; + var secondLocation:DataNode = firstCompany.children.getItemAt(1) as DataNode; + var secondDepartmentOfSecondLocation:DataNode = secondLocation.children.getItemAt(1) as DataNode; + + //when + _sut.seek(new CursorBookmark(6)); //Company(1)->Location(2)->Department(2) + + //then + assertEquals(secondDepartmentOfSecondLocation, _sut.current); + + //when + secondLocation.children.removeItemAt(0); + + //then + assertEquals(secondDepartmentOfSecondLocation, _sut.current); + } + + [Test] + public function testRemovingCurrentFirstItemChangesCurrentToNextItem():void + { + //given + var firstCompany:DataNode = _level0.getItemAt(0) as DataNode; + var secondCompany:DataNode = _level0.getItemAt(1) as DataNode; + + //initial assumption + assertEquals(firstCompany, _sut.current); + + //when + _level0.removeItemAt(0); + + //then + assertEquals(secondCompany, _sut.current); + } + + [Test] + public function testRemovingSiblingOfCurrentFirstItemDoesNotChangeCurrent():void + { + //given + var firstCompany:DataNode = _level0.getItemAt(0) as DataNode; + var firstLocation:DataNode = firstCompany.children.getItemAt(0) as DataNode; + + //when + _sut.seek(new CursorBookmark(1)); //Company(1)->Location(1) + + //then + assertEquals(firstLocation, _sut.current); + + //when + firstCompany.children.removeItemAt(1); + + //then + assertEquals(firstLocation, _sut.current); + } private static function handleUncaughtClientError(event:UncaughtErrorEvent):void diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34424_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34424_Test.as new file mode 100644 index 0000000000..2927c1636a --- /dev/null +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34424_Test.as @@ -0,0 +1,126 @@ +package mx.collections +{ +import flash.events.UncaughtErrorEvent; + +import mx.collections.ArrayCollection; +import mx.collections.CursorBookmark; +import mx.collections.HierarchicalCollectionView; +import mx.collections.HierarchicalCollectionViewCursor; +import mx.core.FlexGlobals; + +import org.flexunit.asserts.assertNotNull; +import org.flexunit.asserts.assertTrue; +import org.flexunit.runners.Parameterized; + +import spark.components.WindowedApplication; + +public class HierarchicalCollectionViewCursor_FLEX_34424_Test + { + private static var _utils:HierarchicalCollectionViewTestUtils = new HierarchicalCollectionViewTestUtils(); + private static var _noErrorsThrown:Boolean = true; + private static var _currentHierarchy:HierarchicalCollectionView; + private static var _sut:HierarchicalCollectionViewCursor; + private static var _operationCursor:HierarchicalCollectionViewCursor; + + [BeforeClass] + public static function setUpBeforeClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtClientError); + } + + [AfterClass] + public static function tearDownAfterClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.removeEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtClientError); + } + + [Before] + public function setUp():void + { + _currentHierarchy = _utils.generateOpenHierarchyFromRootListWithAllNodesMethod(_utils.generateHierarchySourceFromString(HIERARCHY_STRING)); + _sut = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + } + + [After] + public function tearDown():void + { + _sut = null; + _currentHierarchy = null; + _operationCursor = null; + } + + [Test] + public function testReproduce_FLEX_34424():void + { + //GIVEN + var selectedItemIndex:int = 10, operationIndex:int = 8; + + //WHEN + //1. Select a specific node + _sut.seek(new CursorBookmark(selectedItemIndex)); + + var selectedNode:DataNode = DataNode(_sut.current); + assertNotNull(selectedNode); + selectedNode.isSelected = true; + + //2. Perform setItemAt operation + _operationCursor = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + _operationCursor.seek(new CursorBookmark(operationIndex)); + performRemoval(_operationCursor); + + //THEN + assertTrue(_noErrorsThrown); + assertNotNull(_sut.current); + } + + private static function performRemoval(where:HierarchicalCollectionViewCursor):void + { + var itemToBeRemoved:DataNode = where.current as DataNode; + assertNotNull(itemToBeRemoved); + + var parentOfReplacementLocation:DataNode = _currentHierarchy.getParentItem(itemToBeRemoved) as DataNode; + var collectionToChange:ArrayCollection = parentOfReplacementLocation ? parentOfReplacementLocation.children : _utils.getRoot(_currentHierarchy) as ArrayCollection; + var removedItemIndex:int = collectionToChange.getItemIndex(itemToBeRemoved); + + collectionToChange.removeItemAt(removedItemIndex); + } + + + + + + private static function onUncaughtClientError(event:UncaughtErrorEvent):void + { + event.preventDefault(); + event.stopImmediatePropagation(); + _noErrorsThrown = false; + + trace("\n FAIL: " + event.error); + _utils.printHCollectionView(_currentHierarchy); + } + + + private static const HIERARCHY_STRING:String = (City(0) + Region(2)->City(1) + Region(2)->City(1)->Company(1) + Region(2)->City(1)->Company(2) + Region(2)->City(1)->Company(2)->Department(1) + Region(2)->City(1)->Company(2)->Department(2) + Region(2)->City(1)->Company(2)->Department(2)->Employee(1)TBR + Region(2)->City(1)->Company(2)->Department(2)->Employee(2) + Region(2)->City(1)->Company(2)->Department(2)->Employee(3)SEL + Region(2)->City(1)->Company(2)->Department(3) + Region(2)->City(1)->Company(2)->Department(3)->Employee(1) + Region(2)->City(1)->Company(2)->Department(3)->Employee(2) + Region(2)->City(1)->Company(2)->Department(3)->Employee(3) + Region(2)->City(1)->Company(2)->Department(3)->Employee(4) + Region(2)->City(1)->Company(3) + Region(2)->City(1)->Company(3)->Department(1) + Region(2)->City(1)->Company(3)->Department(1)->Employee(1) + Region(2)->City(1)->Company(3)->Department(2) + ]]>).toString(); + } +} \ No newline at end of file From cc5f71cd2161d0264ef1a178961eafe8d3ffc1ed Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Mon, 28 Jul 2014 17:23:22 +0100 Subject: [PATCH 051/392] FLEX-34424 Improved the unit test by making it parameterized and adding a few more cases. --- ...calCollectionViewCursor_FLEX_34424_Test.as | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34424_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34424_Test.as index 2927c1636a..8c2469c82d 100644 --- a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34424_Test.as +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34424_Test.as @@ -7,6 +7,11 @@ import mx.collections.CursorBookmark; import mx.collections.HierarchicalCollectionView; import mx.collections.HierarchicalCollectionViewCursor; import mx.core.FlexGlobals; +import mx.events.CollectionEvent; +import mx.events.CollectionEventKind; +import mx.events.PropertyChangeEvent; + +import org.flexunit.asserts.assertEquals; import org.flexunit.asserts.assertNotNull; import org.flexunit.asserts.assertTrue; @@ -14,6 +19,7 @@ import org.flexunit.runners.Parameterized; import spark.components.WindowedApplication; +[RunWith("org.flexunit.runners.Parameterized")] public class HierarchicalCollectionViewCursor_FLEX_34424_Test { private static var _utils:HierarchicalCollectionViewTestUtils = new HierarchicalCollectionViewTestUtils(); @@ -21,6 +27,9 @@ public class HierarchicalCollectionViewCursor_FLEX_34424_Test private static var _currentHierarchy:HierarchicalCollectionView; private static var _sut:HierarchicalCollectionViewCursor; private static var _operationCursor:HierarchicalCollectionViewCursor; + public static var selectionAndRemovalLocations:Array = [[10, 8, 1], [11, 7, 1], [11, 6, 2], [19, 17, 1]]; + + private static var foo:Parameterized; [BeforeClass] public static function setUpBeforeClass():void @@ -49,12 +58,9 @@ public class HierarchicalCollectionViewCursor_FLEX_34424_Test _operationCursor = null; } - [Test] - public function testReproduce_FLEX_34424():void + [Test(dataProvider="selectionAndRemovalLocations")] + public function testReproduce_FLEX_34424(selectedItemIndex:int, removalIndex:int, noItemsToRemove:int):void { - //GIVEN - var selectedItemIndex:int = 10, operationIndex:int = 8; - //WHEN //1. Select a specific node _sut.seek(new CursorBookmark(selectedItemIndex)); @@ -63,17 +69,17 @@ public class HierarchicalCollectionViewCursor_FLEX_34424_Test assertNotNull(selectedNode); selectedNode.isSelected = true; - //2. Perform setItemAt operation + //2. Perform removal _operationCursor = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; - _operationCursor.seek(new CursorBookmark(operationIndex)); - performRemoval(_operationCursor); + _operationCursor.seek(new CursorBookmark(removalIndex)); + performRemoval(_operationCursor, noItemsToRemove); //THEN assertTrue(_noErrorsThrown); - assertNotNull(_sut.current); + assertEquals(selectedNode, _sut.current); } - private static function performRemoval(where:HierarchicalCollectionViewCursor):void + private static function performRemoval(where:HierarchicalCollectionViewCursor, noItemsToRemove:int):void { var itemToBeRemoved:DataNode = where.current as DataNode; assertNotNull(itemToBeRemoved); @@ -82,7 +88,13 @@ public class HierarchicalCollectionViewCursor_FLEX_34424_Test var collectionToChange:ArrayCollection = parentOfReplacementLocation ? parentOfReplacementLocation.children : _utils.getRoot(_currentHierarchy) as ArrayCollection; var removedItemIndex:int = collectionToChange.getItemIndex(itemToBeRemoved); - collectionToChange.removeItemAt(removedItemIndex); + if(noItemsToRemove == 1) + collectionToChange.removeItemAt(removedItemIndex); + else {//note that this assumes the collection is not filtered or sorted; for this test the assumption holds. + var removedItems:Array = collectionToChange.source.splice(removedItemIndex, noItemsToRemove); + //fake a CollectionEventKind.REMOVE event with more than one item removed + collectionToChange.dispatchEvent(new CollectionEvent(CollectionEvent.COLLECTION_CHANGE, false, false, CollectionEventKind.REMOVE, removedItemIndex, -1, removedItems)); + } } @@ -109,9 +121,9 @@ public class HierarchicalCollectionViewCursor_FLEX_34424_Test Region(2)->City(1)->Company(2) Region(2)->City(1)->Company(2)->Department(1) Region(2)->City(1)->Company(2)->Department(2) - Region(2)->City(1)->Company(2)->Department(2)->Employee(1)TBR + Region(2)->City(1)->Company(2)->Department(2)->Employee(1) Region(2)->City(1)->Company(2)->Department(2)->Employee(2) - Region(2)->City(1)->Company(2)->Department(2)->Employee(3)SEL + Region(2)->City(1)->Company(2)->Department(2)->Employee(3) Region(2)->City(1)->Company(2)->Department(3) Region(2)->City(1)->Company(2)->Department(3)->Employee(1) Region(2)->City(1)->Company(2)->Department(3)->Employee(2) From 82d6b51694e846f0fb4e5505bf5a1b0601c21e7b Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Mon, 28 Jul 2014 17:44:51 +0100 Subject: [PATCH 052/392] FLEX-34424 CAUSE: HierarchicalCollectionViewCursor deals with deletions of nodes before the current node in two distinct ways: 1) relatively: if it's possible, it calls movePrevious() as many times as the number of nodes removed; if not, 2) absolutely: it seeks to the location of the first deleted node from the beginning of the hierarchy. The problem was in the conditions used to determine whether to use method 1) or 2): indeed, method 1) should be used when the node that used to be at 'currentIndex' has been directly affected by the deletion (i.e. that node is among the ones deleted). However, it should also be used when it has been indirectly (but irretrievably) affected, because enough nodes have been removed from its siblings collection that there is no item anymore on the value of the CursorBookmark we have stored for it (i.e. if it was the 8th node in a collection of 9 nodes, and two nodes before it have been deleted, there's no 8th node to retrieve anymore). The way we know this is that HierarchicalCollectionViewCursor.current now returns null. (So when 'current' was null, it was impossible to retrieve the parent stack of the items up to the root, and everything would fail from there onward.) SOLUTION: Choose deletion strategy 1) when 'current == null'. NOTES: -This now makes HierarchicalCollectionViewCursor_FLEX_34424_Test pass. -This implies that the computation of the parent nodes (in the 'parentStack' array) now happens after this check, whereas it used to happen before. -It also implies that the index HierarchicalCollectionViewCursor seeks to in strategy 1) is different when 'current == null' (namely, it's 'currentIndex - n'). -Renamed some variables in these conditional statements, to make it easier to decypher what is happening. --- .../HierarchicalCollectionViewCursor.as | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as index df6ee81c83..954d94877b 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as @@ -1145,16 +1145,12 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher var changingNode:Object; var parentOfChangingNode:Object; var parentOfCurrentNode:Object; - var parentStack:Array = getParentStack(current); + var parentStack:Array; var isBefore:Boolean = false; var parentOfChangingNodeIndex:int; var isChangingNodeParentAncestorOfSelectedNode:Boolean; var bookmarkInChangingCollection:CursorBookmark; var changingNodeCollectionBookmarkIndex:int; - - // remember the current parent - parentOfCurrentNode = parentStack[parentStack.length - 1]; - var changingNodeAndSiblings:ICollectionView; var changingCollectionCursor:IViewCursor; @@ -1167,6 +1163,9 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher isBefore = true; } + parentStack = getParentStack(current); + parentOfCurrentNode = parentStack[parentStack.length - 1]; + for (i = 0; i < n; i++) { changingNode = event.items[i]; @@ -1226,20 +1225,23 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher n = event.items.length; if (event.location <= currentIndex) { - if (event.location + n >= currentIndex) + var lastIndexAffectedByDeletion:int = event.location + n; + var isCurrentIndexAmongRemovedNodes:Boolean = lastIndexAffectedByDeletion >= currentIndex; + var currentItemNotFoundAmongItsSiblings:Boolean = isCurrentIndexAmongRemovedNodes ? false : current == null; + + if (isCurrentIndexAmongRemovedNodes || currentItemNotFoundAmongItsSiblings) { - // the current node was removed // the list classes expect that we // leave the cursor on whatever falls // into that slot - var newIndex:int = event.location; + var indexToReturnTo:int = isCurrentIndexAmongRemovedNodes ? event.location : currentIndex - n; moveToFirst(); - seek(CursorBookmark.FIRST, newIndex); + seek(CursorBookmark.FIRST, indexToReturnTo); for (i = 0; i < n; i++) { - changingNode = event.items[i]; - delete collection.parentMap[UIDUtil.getUID(changingNode)]; + delete collection.parentMap[UIDUtil.getUID(event.items[i])]; } + return; } @@ -1247,6 +1249,9 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher isBefore = true; } + parentStack = getParentStack(current); + parentOfCurrentNode = parentStack[parentStack.length - 1]; + for (i = 0; i < n; i++) { changingNode = event.items[i]; From ed6da554e931c317717f8e8673e1f698140b0be1 Mon Sep 17 00:00:00 2001 From: Tom Chiverton Date: Fri, 1 Aug 2014 18:41:05 +0100 Subject: [PATCH 053/392] Fix FLEX-34450 by removing the length checks for the last part of an email domain Update Mustella tests to mathc new behaviour i.e. the tests should now return valid --- .../src/mx/validators/EmailValidator.as | 10 -- .../Properties/Validators_Email_IPV6.mxml | 82 ++++++++--------- .../Validators_Email_IPV6_spark.mxml | 82 ++++++++--------- .../Validators_Email_Properties.mxml | 86 +++++++++--------- .../Validators_Email_Properties_spark.mxml | 74 +++++++-------- .../baselines/invalidDomainError.png | Bin 4918 -> 2746 bytes .../baselines/ipv6_invalidDomainError.png | Bin 4444 -> 2641 bytes .../ipv6_invalidDomainError_spark.png | Bin 4667 -> 2796 bytes 8 files changed, 162 insertions(+), 172 deletions(-) diff --git a/frameworks/projects/framework/src/mx/validators/EmailValidator.as b/frameworks/projects/framework/src/mx/validators/EmailValidator.as index f928371b08..9776db360c 100644 --- a/frameworks/projects/framework/src/mx/validators/EmailValidator.as +++ b/frameworks/projects/framework/src/mx/validators/EmailValidator.as @@ -217,16 +217,6 @@ public class EmailValidator extends Validator if (nextPeriodPos == -1) { lastDomain = domain.substring(periodPos + 1); - if (lastDomain.length != 3 && - lastDomain.length != 2 && - lastDomain.length != 4 && - lastDomain.length != 6) - { - results.push(new ValidationResult( - true, baseField, "invalidDomain", - validator.invalidDomainError)); - return results; - } break; } else if (nextPeriodPos == periodPos + 1) diff --git a/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_IPV6.mxml b/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_IPV6.mxml index b6c153e9e1..f782ac1ac9 100644 --- a/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_IPV6.mxml +++ b/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_IPV6.mxml @@ -48,7 +48,7 @@ - + @@ -58,7 +58,7 @@ - + @@ -69,12 +69,12 @@ - + - + @@ -84,7 +84,7 @@ - + @@ -95,12 +95,12 @@ - + - + @@ -110,7 +110,7 @@ - + @@ -121,12 +121,12 @@ - + - + - + @@ -136,23 +136,23 @@ - + - - - + + + - + - + - + @@ -162,7 +162,7 @@ - + @@ -173,12 +173,12 @@ - - + + - + @@ -188,7 +188,7 @@ - + @@ -199,13 +199,13 @@ - + - - + + - + @@ -215,7 +215,7 @@ - + @@ -226,12 +226,12 @@ - + - + @@ -241,7 +241,7 @@ - + @@ -252,12 +252,12 @@ - + - + - + @@ -267,7 +267,7 @@ - + @@ -278,12 +278,12 @@ - + - + - + @@ -293,7 +293,7 @@ - + @@ -304,7 +304,7 @@ - - + + diff --git a/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_IPV6_spark.mxml b/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_IPV6_spark.mxml index b5b6765498..a06925f1af 100644 --- a/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_IPV6_spark.mxml +++ b/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_IPV6_spark.mxml @@ -48,7 +48,7 @@ - + @@ -58,7 +58,7 @@ - + @@ -69,12 +69,12 @@ - + - + @@ -84,7 +84,7 @@ - + @@ -95,12 +95,12 @@ - + - + @@ -110,7 +110,7 @@ - + @@ -121,12 +121,12 @@ - + - + - + @@ -136,23 +136,23 @@ - + - - - + + + - + - + - + @@ -162,7 +162,7 @@ - + @@ -173,12 +173,12 @@ - - + + - + @@ -188,7 +188,7 @@ - + @@ -199,13 +199,13 @@ - + - - + + - + @@ -215,7 +215,7 @@ - + @@ -226,12 +226,12 @@ - + - + @@ -241,7 +241,7 @@ - + @@ -252,12 +252,12 @@ - + - + - + @@ -267,7 +267,7 @@ - + @@ -278,12 +278,12 @@ - + - + - + @@ -293,7 +293,7 @@ - + @@ -304,7 +304,7 @@ - - + + diff --git a/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_Properties.mxml b/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_Properties.mxml index 9eda33d744..ae32f1a831 100644 --- a/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_Properties.mxml +++ b/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_Properties.mxml @@ -48,16 +48,16 @@ - + - + - + @@ -71,11 +71,11 @@ - + - + @@ -87,12 +87,12 @@ - - + + - + @@ -100,16 +100,16 @@ - - - + + + - - + + - + @@ -121,12 +121,12 @@ - - - + + + - + @@ -138,12 +138,12 @@ - - - + + + - + @@ -155,12 +155,12 @@ - - - + + + - + @@ -172,12 +172,12 @@ - - + + - + @@ -189,12 +189,12 @@ - - + + - + @@ -206,8 +206,8 @@ - - + + @@ -220,11 +220,11 @@ - + - + @@ -236,23 +236,23 @@ - - - + + + - + - + - + - + diff --git a/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_Properties_spark.mxml b/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_Properties_spark.mxml index e05d554496..e488b9144f 100644 --- a/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_Properties_spark.mxml +++ b/mustella/tests/Validators/EmailValidator/Properties/Validators_Email_Properties_spark.mxml @@ -48,16 +48,16 @@ - + - + - + @@ -71,11 +71,11 @@ - + - + @@ -87,12 +87,12 @@ - - + + - + @@ -100,16 +100,16 @@ - - - + + + - - + + - + @@ -121,12 +121,12 @@ - - - + + + - + @@ -138,12 +138,12 @@ - - - + + + - + @@ -155,12 +155,12 @@ - - - + + + - + @@ -172,12 +172,12 @@ - - + + - + @@ -189,23 +189,23 @@ - - + + - + - + - + - - - + + + diff --git a/mustella/tests/Validators/EmailValidator/Properties/baselines/invalidDomainError.png b/mustella/tests/Validators/EmailValidator/Properties/baselines/invalidDomainError.png index a500e73c300b2468f876d18e7f120b785374d655..66731245198d2084c45eff07dbb7977f42fe4340 100644 GIT binary patch literal 2746 zcmc&$3s6&M7QQS|0YRj$x>iH5RIp&hDM}#=gb3<70@~X048)3n1tpeO5KOK@ZPq%L zQ63WIaa)zCB8f4ZP@Y#|5DGM8M-xIKL{T7;7r4p2hLB{#ZfkJ5+u2<^+nN8)o%79q z&UerG{(ruEW?%F!7pFI!003~={ilcz0l-dYeW(tLt;qi8&2s?YbZU15@uLE%`l0+R zDIME0Git=Wz!j*Y{4kWF$Jaa-o%cyt>b3Jy!r7ILQt>yUHxJ)EDZbWtY9leyP3d~% zjMu&vaYn=M_ekH`*qcQ^`bL-6+S=LS@wK(?gRJyxQG(m-)xn2y@&yCbddBfVCR10x z7*G|wf7}5G6WRg9B>)BsEYF=^w)mgWkMYYb*4VaXK?);p5WXcrG{)-GB(x&M6@wkU z3zf{j_uhCU2^Zci^Z6=ddG7SpFXyq~>thmoVo#Y*ATp2PC;sM&I}8@;DHK^H?Ajzk zQqKBi83S(1%XWcg)(AsQLM5h=3=N6iY79ccgl;;erc#E^#a?9)_iiC%Pll*Zl@Q{{ zhU0&9`$3-$2VrO`bJjNMW_ZN3uE;~7>V#lJDac}s{S}8zE=?XQuZjsa_KH)p4N`V> ze0=jNehVpvAFF$wb9B($wGT@}Bs}i4w9=Gi>9bcF#fa+iQ9}^SR^Cwd%8f>${35+* zLUpB@mRZC~3CuJPjDKMi=M5b(J=wZSXP&vfdb3F_%A>5(UHaNw}S7_Db1&(VD{UTgmGV z!G($VtPVXt6BN^K&=@idMc~~7=vJKIF6opY*zlUgVH&O*w`NnWWuES;*USjlsu@5LS(*arZtqB zHK^a_jCyl`@z36ZZTrExDY|nLI&^+xEW}73>gstLy+TOQl#IgPDNtq8xsYZ0?LNLJ zFYOPDX$5PwNRMOjS5Vh7r$SsU2lBq@_(_dhY#2A>BD?8MrX>RIOr*TyO0d_2SF8q1 z>KMrsV)SE-YaOY$+o5#*2X`c(F@TLN>#u`<=}_P zMLXJsz4WfIp-7WT&oq=nlX!;qP^3#Cf;9^6Om}xq`=cDlm0YgVUU0DL)f%WU4O13< zV@YZcAyMonQB&$0-Ct&PH*{9^i_BeM{x)QgGqtHDw3IDJ$zctCZ7%vcbk}L{#W+VL zqu`3x?anX4`KDTEVC-Fw^}@U7U9UaPUyv>Sh|wzn|DjO`<$5@<_lKE+hMRf*Wrhy*q+410!;)S%&$8C{_dN-9ts)X~UwEtgDtx&l zW|Yv5$Z#ZrW7osg&&T^0ASWK5;kUHl`zwYq*yXuZ%XKAkuq~-2_tQ#Wt|Q(3IZkX_s?Lyb!*|^qYk2`G$Z0bFgZ=N$)`MnfY_Rk@Lu8|?@>+7@a z(OT|>s~b+rr`PHD^8X*9YAkxWIfbF%4rK?Xi0L7y~)g%fIuzpR+DZnYY%3@xQdrUoP>hrYtb*|0C-wBK-EEyfV@M oP1jdSyzVjo?L|46qXaE2J literal 4918 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 Le^Ptm%dGzZw3=Aa diff --git a/mustella/tests/Validators/EmailValidator/Properties/baselines/ipv6_invalidDomainError.png b/mustella/tests/Validators/EmailValidator/Properties/baselines/ipv6_invalidDomainError.png index 98ad05b8535db73cc39c234e383fd8df25dfa229..a4f302bbf30e60b2dbc78088fe111ee16c31f618 100644 GIT binary patch literal 2641 zcmd5;eNa+a6n~j5=9ap(Wwj}0nUm=BwS8Gaq_x?WmR3llW+s+~njfGNQdn78GU=#m zrPAs;TPif7;3rhpl0@QWVnV5<2^$in0>u;9XJ6XP&g^XKAI;o1Z_e-DbKbe-kB&>Nloct33h@bR5eP0 zdL>W!D~%CsxB^eLadZ)ylb*!fUr| zo^}AwzyFdWdg%(M+<%p0og1gNKSYurq{EK>(y>-|8ALWdU#GiV)BYpSJzqVCZwA=J z1M6ER0B9IsZZ|SU?f1}odvpn8k1iSG64IK!*o~WC$2)|@P@XryXjZvP72VJ~Bg-XG zJx|eHd{$>~;lBFnvynNUYl-UPjGki4PVxw*?KFzls#*r_scuW9x8xKh&jWM#RJ}2P zO6e)Qq>N4M@Akh`%HvA~ZVCs*>Z?Zav^$F*$CB%wf+TL84p~2&c@!m1Sk+&>pFQ`e zvBk;cdBllU(9^u<`yoWqW11DMS@5cO4@P(f=hzBcNXHy?!6&(Pwf78i?(5jHzQ_&P zr}1@COaa*xt&K)Hqe@i+i6`CSBt6)-qy`_9uf3QT!PzLtBn%F39(ysnUtCMR$ zVai~AN0MIkBJ$zjfTp#bifuu+gbaJHxbU_&oF$v$$Mao}NIc^QueIF1-=BM)P4HlG zw%ygsd(W6gEeBRO_xXl zBWeRG2&TtZx^A}Y(l{eEdDeJQWM86QrZCJPYW-O|1gq;<7g>ExI!!ermC>kRIMXQA zFSb-L_k!0Ygn^D2jZ89#*jr_?l=dq%p!Z$9tUEV(T`;0pBlToYwfh-9l*CU>LbI?D zg>Ol!RrEIX?=Lqk&}-&+qSf?JA`uNkr|m$)&&D#R#bxJHb(jZ~b2BG3FiSvO8)dWd zT4hvK`>x^s>_hOOD*}WtorY8g=o!{_f?pXrK0moWV@W_qB4RzMi3DErPg= z!O{v4BBM~(Xx>2Y+kQeRPdabjU}V~#34$x6B~hjAyVPlu_4T2%rw`R?kP8)Sm?G@c zX1}0OF|8ClkCjxHs$Y}US^z`OzB2oay8yV9oAG0_f#6^y#5%5n$9!B zzz>!>G&lQmm2A3ElCU2OmYINFxgqW4Df}gpYn7_L3k{bTtHC#79v&|`l4EQghBgmg z^x$nQ9JY=z_ z%)siH5D1&>?x0xWsGu8@gV$1x!wWF@>G6=SR|_1QTv^Uy`NoE=M8hD`%qeP5dS#rv zdD|z%a2%%Nu*1bZpAd@JU|qR9ym{*<#1PJZ)ap}+^k~ZNFt@XbpY`1knVKAo!{ILV z3J=q)EZomo=mSwaZoYd$#ArSw<0~d{pts4JDO#vF#%Mgvpz6pZ&U9y=F`>2U$A4zP zQOMEIPq-9v1zP+uE)z%Oh6*yqWL%QJird!K_A#v4hZ07C&;7{Hy0fyx{i?gu9!>qQHrX5TZNMuo`IMbViQ+2 zd{ZpTPI? zm5X|Xb4L&D|8}mwlZNqhfu!W$)nweiI=cP$8uyv~KV)w_5*-oW@vQI{?CmQc&-;Dv z|9;PVS%1O)u;4W-*R2GBKx;zw1RVr{R?GqKSu;~$=Flu-5d^Yy4he!sWQ@wjd8Fug zyQeX7y~U!%%DLVv8?GVFz(ZO;e3Tm$a^6Yt5o7JXd}GG!{?A-KTmRQfsa0zac$>lV zBTpBGzPs>E7WLx6KV53N*c2Mh3GL?I&@a zYzH&gz;VvA<WM>tj!p|78J|fX4UFN!l zTL-&A0Hi)$3NfJY@Y1Orbl{1j$u z=@ENt?fW%6uxf$PrpBaS@@}%`T@j5ctGWI34df!a;IZ5k4uDc^`(gqvr2JcoNevj3}=^AAwpWOm1LxJHNK9-MNLNa(|s!;?UpO3la-6T^uz% zp2}-jUk4sZCscR~TBfksv8c4;Jo={LIzUM7QF~8N)Gy{gbjYL(-{PJK9)|X`Mb>mA)}f75|}H zPNLiWlr5(*(%MR3k~TG*P8CO`QWQ9pm%JZ$$PEIfZe2ZRtxX zu}v6J;BYR{Jyl4KGO2|b5uNuX`#+Tj&y{yIvqPoSv*Q&9u{V78>k(T@jiEIxytOCp}Q|6vF_Wi5%h9X6PovShAzfn77 zzsVn{t!M-j%*|`&M~vV-aO>q5z}$sIpc!4;PvXsB1apuX30!h?!8->F6b#{zc_RMn#^VP( zCep{aiQ0F>vfzQuQ>IRm?Uy=w_GV&_ZEhdi-&f~+U8q~~#bnP6T^BupLN-I8%$C~Q z?NxRuKJucTmf>6S-ptOj6DYS}5IoO6VJdv)Pa;;@{l&PrTHo0Pu&5fJ>T|YBO`)@papi)<R|iVsc&%HvDVmjThc8Wd_66N(%_)ja&E?2B98*%CKwTHUmRh%XOX@Gen$g zr*aRlAF@0189Ojh55MhhI%V6j&Qks2iao^wyHT5VOyNQ(v6R=_T0mFY49qvJiFFA!Y}BdOc-^=cOoAP97qlYPaj1qx@2znbte^VqR5j(rVJUH^cq4KWMw`;CXio`(?alG}<*b znB#n#fU@=YM3qQS&WRx+%Pg_BHSc}>Yy*bdu+oPfm-9aCs`Cm{P1P|J}9zzIaE zGaxOGn71alxKDEptSeq6Dl(F#*Uzx_472a3dGxyWhpc|*KNSs<1)hq5-U@%cu4 zx`L~@R8I;(ITQt)f(>wCl3B&fCzwKf#W?}7ii$ZNiTU{jGe{Zo)xGNg$Xa0tllb$f5oNsW? zk#LT(J`<+np4N#=67H*H>MRDzPfSaGg{kHyLU{%zAFN%F;%t#Zg?dE67!%8==qIwK zsq|@GHZf~_EI?bvKr3t8%Y)kx@`|jmXx_KT5VL#zWQ$h(Iazz_cp^fJcDR=_PH>f+ zL73uX1d~DJ@uIz>&KOsDaTbK!3W@3WxF6|auFl*~)Cogj=09fcs%2NTus(HOSAQka zv%NdYoQ4=LyG6h4L$B4&g6zS{o%7wEn8}gYww$WwTWm*6eAUVG3y@#MQ+&A5ST1lgF1!7-}&bu;$pY012u}mLwKlkvAt~AiEGRt1o zo~U1SEMEs6Z&w-m@szT&2AzgN25vuUEgWq)vJk{^F5UM^sDJN`VY+)F@wmpY>_MJA zQ!ksUZ(VwsP`+7*%)H;4DY`j`Q|#c5r}sdv$CTTuV2nB-SrsLB%En24o{Z_vsEt!@ zvwEekWH_ZD7oBf}F%2Ns1y(%kGs@_8LUZBlpzRSyCE(m_W`7~eG`Kv>wNg1_U zBzo5_F8Zt04S1>kD7y`3{evHUEC=^hAjbs4l2X0!P~67S%6t5E6!iS;`JIX#XmPAe z7cf8clgm>@WR#1Ak7LHe?1gDe<;=|J5#J`AB7RU7=}#V{il4c&VM3>46{?|x-b@fm zz&b9vb*x;LX5z<96bOijwj{15EizRP=X=OkX3;er%ECaLsXZqD@jmOh#jYO&=OkKO z5>%ae(QWd+I-8kgFXJ)pjO}ixinDS}aW(p5ctM&E-upCrFTi`(UTV*UZ&j_&!>={> zlt4S|k;-)c5>1aR*6MDtf*%wyp@Xy|Mzfb*7+qHkddO(WT{>ZO|1g;$O!^Xf^<;!Q zCz2 z9O&(dBk3GrqWnQ|g1o~vTKDiMr*hO0P=~8$oZvH~VNBMdh%Oa`EPJ${s?mkto;8Y; zSsWg69O$zM2Ylj60gn`iahp!uX-(o8&VPe)K5-bo=ZgyHZUm7il3LUbHS>7IDhx#? zo>s*$vYBOPmwl(@r?T4h8y+s6N9eYGh>|N|7gf?^ysg^|9hxlm9Br0bw27F#nrfb7 zVZUmW{zR&>0IqrF35tt;_DWf!K@U{_-7MECs{Q4+*L;99W`7yrw3FoW28kKWtR)7> zbIWtf(sG)UAO8o7HhVaNb2DuXY9E0mwxjedmEI<$HDJW7)H%rkusuBQ?ZTpx-A{!0 z^u4#<(pNLQB>pkR_9aQ%j%ul{MYc1iM;|>{?~^|8Y&R$7@x-pi1a^5tim!r8QfCix>6XjF%4A0bKxYxw$d-B$#)kO>`EdzIl3K z3ddYy?|FlZI9J!Y@cu!(c2;ybCN#D-vi|$x;^G^F6fds?aWHZH=Fh{+u@8Z4Pdii@ zcC)^|-W>)TU7tN+l!|WGr;jwm7XgOarW|47bn2--?%lw=vOB9_1E^O*s?|)Tqc1>z z^+-bel0m<`eJ8ZXa(3P+Dr!a15)LRz(iqa-T(|C?o;4k%-Mvd|K4wsjK)-ZpK%)ja z|Ev6SI-i3a`q?xoCUX+==ss_0`qRYSmE6Hv%kVL`M%A1C|` DS{_Iv diff --git a/mustella/tests/Validators/EmailValidator/Properties/baselines/ipv6_invalidDomainError_spark.png b/mustella/tests/Validators/EmailValidator/Properties/baselines/ipv6_invalidDomainError_spark.png index 8a31cdda9d87e9e4aa6f82473fd4f6775af323ed..1b4bb89ff94afa7735125aaf0974ad0e7f6c416c 100644 GIT binary patch literal 2796 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 Date: Sun, 3 Aug 2014 00:20:41 -0700 Subject: [PATCH 054/392] Adding fonts. Edit the LICENSE file to mention the font files --- LICENSE | 4 ++++ .../android4/assets/fonts/Roboto-Bold.ttf | Bin 0 -> 135820 bytes .../android4/assets/fonts/Roboto-Regular.ttf | Bin 0 -> 145348 bytes 3 files changed, 4 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/fonts/Roboto-Bold.ttf create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/assets/fonts/Roboto-Regular.ttf diff --git a/LICENSE b/LICENSE index fd4c4a606d..4c16ea067e 100644 --- a/LICENSE +++ b/LICENSE @@ -715,3 +715,7 @@ EXHIBIT A -Mozilla Public License. ------------------------------------------------------------------------------------------ +The Roboto-Regular and Roboto-Bold fonts are available under Apache License 2.0. +For details see frameworks\projects\mobiletheme\src\spark\skins\android4\assets\fonts + +------------------------------------------------------------------------------------------ diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/fonts/Roboto-Bold.ttf b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/fonts/Roboto-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..aaf374d2cc000c058f434f43c3b53bcaf315e2a0 GIT binary patch literal 135820 zcmdSCd3Y2>*Ed|%-P4nVER#JXkj!K!`yL2efDrZ&k^mtjBy0h~ju2J>0TGZ*L_kEs zCL)`Yh=|Iq2#6pEsECN55D^g+5fM?6p7&SP)5%QK=efV@yWT(E30*ziT~l@H>~-o? zLntA{9fd>;-E#Zp-&*v=dP3ieB&4rTxBUK{Tl)tsAc9LKA-bNu3-Y^XcMJZK5Dz1M z;MO-kKIxs`=mA6Ml1e)X*m;5eMRl{#xL<^Qdt%%Ts^f@){u#R|uUDIeO&K5r3E?t5BCcPaBOJ zE`Cm3ab1Ax(9z?k&RBQEH4fL02r)#Cn>c)^u3Om+^mm*P2fy({XH1e?i+|zy4AeJH z7&?Arx30SLgszhSf9a%&PfXo^Kjk?>H)0GS2PaJ#Imy^_C!V$3h2LBy3^zD#ojkpv z)UDm`q`3ob5OVyUJNhK#tk;>~$G_Kq;kd+M4(d4)f!)Oizi}8={{JfrR2!&NX^K4;zLS_&idSypa~KGa4kx&2#4{6=r-z-(PWT%7C$BBtQ)N>Laq_v zmt0~TjGIQ*c(;iYN8y``rCx=>VVDqYB($n8>oG%T#xo-ksKV-^U7Xn=#FE9rYLX-! zBMH)aQmp%tlOcn~rYO#&wKI)g^*n^`R zj!rn*;248rIF5EW2II&RvdL;8iA<3^Nt*OBSt13K>0&);sc%jabwR{g7fhDuT9Oi7 z2Rz$=V>7M?k>R@CWQj0{Y?PiO8M5qCXbo0mz zoiEYreTg5=K{{umlMa$TLI(OQB?8Va;(QV#t|3&|N%}~=NxHO%Op-!L2b{Y|?NIj$ z#>>7hB4dOQGEWGyY?gYEN$k7)6Y4G^W3A8G_b$RGxMw68!fE2jek1u1gYG-h5@$~w zIvl-(9V9`}lYKY`>*C26j{7XK68PvY9l;oul3eL~;v?wDJe{5lkvfx2ayA(tu0Y$A z43ox?xeWL0o+OdAVhb`?>O+deVp1*^l0m|kWSn%5JS(M<_QF_FC4`ZoLVJ=UzD6qX z`*PhE2=$|GcqXnk}qlNVl23byk78`Irg5z~vF>tHVfziU| zkTOZ8ZUOEwTG$?p7T=OZI#==x$2-G)lSBT1#Bo}H4vZGsAtYI9z(b30-izZU=_vTM zkq(SjjmK)z(Rw|G(}U4OrHf8lMrJaauyM1#|8{0$2QP3O{L>jcs`DpZ*!ZPw=#$}x z!TGiu7 znapD{B6POwOam=;lc$6Vk}qC{o*7402ph;|Lh63R@du7438`;M z$m8E}mW8*-U1=nFk@dx7RjRX8%C43w;XjsLx@RrF>Lyt_=%!k33a?pKu{umYO4Go1 z7l{w&GmMAJ9$;hok+?p!wlC(ui6xn4X?TI;zxOG06!aOx0e|iUd@{Lvn~dc37@y@pmYD8g^?Hy|tgbGR zY5~2v7PcXUB;$Ir?oX16I=;GxBul^}PqJF|PbsbrQ^WBEe*^`A_nK&VdZjuY$a(He;SR5O`p+uj-+((j4I8N#Z7Z zKwJ&kdS*ktogdQ9#%_gJdm&jOsM>1A^mAoKL$L}IZM|m!J8ox=_bRXpK zSKe17=*W1G*(B&dW|!nUq`7>H_tBE~&-!D1;H(oNznzJPGz<1{9@!_Clb!M-(BLYW z;MkXJbZ|vKEl}4HV`cmRns6HWSUz=JL5dwVf{s@Hm+B!0-C%njld9lS`o{ygcyaI5tzO@SQL0G4T%2jW1>XAIvAK3zAmNZNvPN6S&E#PW%zbM)VV z9y?JVc$a`Ds~j5|&Y%Tg^VBDjXx(ziC1}F%r<;QF9GqVSzs(_~@@mUn>6GO#mkZFB z(L?K_;cWdaqdlV)uiL~PX!5V=bzLpS-Uhyr^{l@~JmkxyIpZm*CCP>@C}2ke4m4b7 z`VzduXltcE^9gj?7?^A``Z6Bld~lBR63a+$#(!+g4QG+`27bFh-%gP)kRrJ+>7)xK zvCSzqW9E2(U!&>J zre{qj{{3v(CH1zn{`>uZv8_#Tto=^I@xPxfQ>Av6jDLI1^#N=l&PyR16PPSBJpKJ_ z`5dw|=a{yr;83%imD;1H&Xsf083Q2=+IRO5+2Yl2*`36~m z-*=IUNjKd!GD3Vn{2h+LUv(vA;I*}kC-vhFOc@qb>byiNPzx7IKCzUY>W;uWS~PTY+Z!q zH8Gn^5f_kBaR}tDHyJ7o1uw^f{{!H+zX)6N0`3(%z%X;^*+Up$o*rc(w{h5d4t{=&2}t{||kX0(+);++k@eS z9k%e}yTdn}x3eV1KN=5VT%3pSTvtZ|k=gQE%mt=fZt6qHQ?e78s#^e_T?AboLR{FP zUxGR_$vFM9jdkIBEP+2ZNqQ3J_V5|3_DA|jwLkJE!1WINp%U1%pJ7WzlA*d7z+j!v z$nL6L)P4tlagnqJxYPR9&KMK_LzF^K^?DlVL-7e?1$E(NL*W`EYu{bs*;ALGV1FZ} zj?|Y%$FWB(DK)ml9V3ot8eqf+ca8BhJ{~`yE%ZUItQuu^BXP8#{*-#KJCrt8PbhH@ zAN(X9J+KeDz$cpDW(B&jepnCagO}*VdbcHNu)jd98b6~N`*T6(tg8CQL5U8=SlAy1 z?1Bm|tjQq$$6#fD_8&)M;~&Nz9~~FZ{s4FqM;*SfpCk(QKcmek)v;T_I6^6nbKxMn z_y)NIqZ#%jKAOhz{^E_X)aZ6++aWT_Z+G0SNNbg{&7KD>Nh~Z z{uzU5BEwHhUtZl^0yfo0yqdEy*ci`%b>x?f@bU3U_#Fq7-(yeFJeqX}dT`;35d(cP z`yT9%`-~enVre3$fl5LCV^(l%8dWAhLx6x|pZ(mY6^+HofP;UbG{6_^xCB-~ns3Jf#{O1=0ECs!Jl zfNw7GEvX-8UJ=+n;L|)(*HEM32^Sa2e!!RmAD!qevNK0z2>XT)`ZYRWJoqr|^G|GW z6waDVsmxCB5v@lHLiyzx++p8Z5J@!SyqC-;GKnEBWGM4~;P3 zAl=t_2GYQeflmki8Pvzv%;;?l1ebT6pN+aVgb}H#7MRP+AZ{TK>L^A$Dus| zXfFX;(g5uuK)X`hE`ADViR3GVNUfzTsh3nHl}l5k`O*?;g|t_CPx?^0CH)pi0y_lG z58MK1iP6*O3vmkrv`NM`4bW}}w0rEJ9c+cx-3Hnb99jx!>ns+_@0RZ@M=fT{cFQ`; zKueA#&C*9HV%qgD<9G?jIvgwO zSD>7OqY}r&8w+oAyP^N?=kGSw-m1M`d#(1{+N-q}YCo?%Tl-1viQ4tGD{3ohN7s(5 zb*UxSmFvGu>v4MA$l1P$BGD#t+B#oq#)}#%|AZgpd&q8Ru6^WnvY(hqHF<-)Ne+;M>C| zDBhXEUxFe^WD!|T7LpZY8CgYEljq1v@&bWOleJ_Md6~RKexrG`kW8ipv_Bm{X3;@( zAk8bxf9-mc^Qd7&DDB#hS$!W42ig z%g!qd8Dv~$T-Ia6GGn%J^w1GzDU6@-VC1qv@kTSrFC2ripwMK_92C&-b>yHy8L?)G zHG^NE_Od}}pu*Y!&iqzy zlr+7OX?p`0=z56G&qY%>g_r`^jSy3a2@np-iZwgN^vW;n3J^^I(J98 zR<|Ts0dHgaeJcz1oukc=mm2CQM3c8@a#X0lfjC6JC3H--eiOT-#|C+@ds22A>o;AW zj)@`O?4I1CU3{6u%e>@l_#>|eGk@=2kup|0+&;3+&u6A_fUA@X(=mSXN83o zeZ5fS94o3=5#+_1Vnvk?p8mp$jy`x$%8GNWu&^S?2UYN3T1{V8Ok_o=A1cpd#VS_3 z&x#;FRPM_PH$POtC4R;V+`}kYfyw~QAC)_@Vje3dTEAfk{;W$BO!-EU>(Ds6p_A4^ zen&&TcjK3giEl?BA5HFfv&}_%U!Ry8Qluq*0>#VJMZ?u;A4n1 z3@}`AZ|Ab-4wb%^mypSu)wgnVXMQog&hbx5q3H3Uii`Q zv*Fjm??*@xei2a-?ILm_Mns&AxE66gQi}9}lieDN&1})G*9}^yv z6q6M*AZB#TteE97n`6u|M`JF<+>WV>b&Z`Hdo%V?oKswjxVX5?xV*R_aTRfM;x@$X zh%?6>jk^$cJD$Y%jvo>~DSmPMrudrplku10ZzUuoOh}lYuqI(w!g~ql6KWG4B^D&s zBpy$^n0PDkuOvfKXwu%~oaD0PDalKcHz%8uk0xJ8zM1?e#VN%nB{(G|B|D`!WlGAT zl(i`rQqxkqrxvA-OP!m#I(1v>q13afH&P#^Noj_(;Ix#q?6ktP32F1w)}-x7JD7Gd z?Q*(P`l9sO)~#DFXnnEutu~~MUz?~l?b_tDDQh#O&7w93+nj9kAR{ThsRAwzX}4Y8Toruid0}i`yM(_gTBk?S5*fv^TU5ZlBUVyZw;%R)^jl3OX$6u)M?C4wpOBcJ%1z-?6;ogpQRRYdhY~%*x!6`A+8f%-YNc zo#ak_o#Hxe>+IDzxAVa+#x8Ta9M6i%D$W{_bv)}#)`hO!yXJP?()CexLH4ff3*CIW z&F}W0dtUcL-OqNf?f#&L+{3TOoF3PD=JYJfdyVKdtJi{FcY7E09@l$r z@72A}_P*FByiZ)8v_2jB?C6w$4Qd6_m%sW^*x%~ zIyWzS9Aw;*p>-o<<=-#OnaKPP`p{$~Y&1(gMd`iJ&k z+y7?&M+2M(tQ@dqpwmE)f&K#%2DTo!rBEmwS2(wDb>X(c+CiCvRt~yYlvK2`sBZAE z!8eOX6xWsPDA`+bprmey$B@7wQA0MCI+PZaZZ5r0T341(*1fE3Xu;6qLoW}#H!Nq^ zjA6Tm-5VY^JY#tF@J++-jVKs#V8oFTXGZ#uTr_g^$W0@6jNCi&L3z9K?&SsLo6B#O z-!E52<&D}o>ei^b(G{a_j=nqkuQBqNvN7+BIWgw5G2e{2J?3GBP~lY3q9U#$tD>-? ztYUP}{dnrdskKw@P9xJiriD&Ro7R0=(X>g^7EfC85 zP5Y@*sPw6fsm!b#P+3tqzj9sW-pV7D7b|a1C)2&AM@{cAy#1g->r z3pMME>o%?1zwXw0uk{_)k6!=IhNunWHk^4e<;98@Z*J_paplI_FXg?o|E0T|CTx1| zWuKQbUtaR^?aiY%Z`^!l^M%cis#;VfRCTT@s4B0TQMJ5kTh%*NC#o)0-L9(JV%QSA zC1p$Ymf|fFw#?tMX3LH(2e*8@<;s@3Tgg_ht>If+Z|%8tz}C@QXKh`+b@Nv9)}vc5 zY`wYl{x)fw&$h^I8QXHV4cj(#+p=w&w(Z*X&bBk#YPUVu?zG*wJz;z2?S}D#JA@ssJB&Nhc68rSv}4?kxjWYG*s`N$$MGE(cii4l_loN){;$k` z<-*SJo$GeqdUe37D_=dcD{$AaT?b!_du`5Zw|0-%eSc5qJsbBt+&f_Jm3_1J-P~Wc zfA9XIuUDGm%zLY&suxvXsVS)0UGv}#@`m#pes6@nQTfKn1Ca;jylHrI%bR=OJoM)A zH_yLW`{sj#IR`f#+;MR4!J`K+9=vr>dCLpl!D7spcf)rOF=tD}yryfRS%?p)77QH* z@e2tTk`pfUW~b=FO95AKTlBmnENG3I_2<#9KEyp)E&R&o-=x_nbPj(C&dSdySld|Ll9mJR z0L&fd0it-b9M5}jL8C4j^aflQvbfOph}6@rTpjHJdt#L?ZOjoIH&Ez(Bt0kt!Oz1qv> zW{AOTJZ}y$R7W@7#5hvIBf`T|Q_|8?lf8X>^m%Bu#!^6|l zOeXo{PJLx`II0k-wiDoP%vov)h{j{s(SZQKZCN-aO>p3{AZXh8sG{^^+6 zR}ypUcyoqIRZcr?#NilmIHMi-#!2IwYImt|(4wK*&*m~5l+)^&!wuE3HqU|#)hUga z35lL5X~{`G-WYC3aCmAmA8Kl`TFqwbUWd-pum zXTk10PqpdSt7D%w{ra_e)JvMu3)0!%a!1z!_%R_O(graB&ct?n5Cp6QHD&Lb7D1dT zS{Tgk=gk;va3jHM64Yl226H&)ko3l991rQ785lM|dp%7{13F~Auig|P1396(q_lLZ zml=PEV3Bk@=V)rb;OVR`m)@-UXjuOe>J=J!IV=0Tpd9Yg|G=oribC(CRwODHJd?vF zxI{+}XdTg`ZKrIyq;gM5@rJ(p&V9Ii*tYhamG{~$&slb>=z(rfdiwXefB;(Tk}x=K z&=cZQ(Sy@lhxU$5${EOIBT2kR{V>l#cCx*O0XvIKi7@@4Yo>fJMDqG`ln%nHa&JVV z9En+sH@n1Rs0Nar>?;fI-Ubg}eMmTQpF_y74qjK6)2M*3llaJE& zLWc5G{pY!>h9P>G?@x2-+rwAp)_u3e_5@YI=VRUmHUB<@W zQTdE+C4ceApIHJz!=MjSO>}G3v25DHMfs3s-C*@5Q$=`1_!is@S^@WVh9uP_yi$Kg zNTJGgR(Cau127{ZqJ(_#1nf;1dpB5$ghUenqX)RzL3PW0%R(^}I1>?PG!qqPnlVdA z)S0M>x+*sWAKKzcITkjg)N)5^ZPht#)n1&?i<72j8Vm+3f_tNEK{p&PZ`oqzG(sM{@CU}DL)??&KnLh_8gv>$Y9t+DdS8mS)%z-PQp1El zlqgP)x(?SJJ++uMk+P9H;GG7eB0 zZyNjpz^{qAXE6LBZ4|=Zyl1mhI*-bFdaEoZt{Zafs`ATMKPcbOo`?HQ33_sL(fA3v z4}$_P`ucuk)+J{scfP!$)Y3N88;J~oG_O8BC3fh(cV3w`6CrwvH4P$l0zI6u?!mdd z8K7^b4W)q{n%N5{Ve}USQEy83@JKhMnxsy*_k_7BS6qXy9jo`k`p!A!t`e?PuU$(! z3C*uV%|cTS2JYGccfMGAiO1Rn?=Kep#cCqvjSFwBbIzCykFj=$t8oj6W%>eQCvsQ9 zWP?<11ESmwz8QdoZqVe4Ih$55LxTIMdYjC-wD~UiKQVIZV&(4DZo3ZIe(54 zMVT_Vc;cjz!IMlSeRGQj_v<@YiW|QxKJnnRkH7lrs6kyn>=VdV<&WayK%!3+j>;6d{82(%sUcNk;e>c z0HXjuG9ul_#}n?7Rl#`q2o6s?m-tN1=^v@r7vEAJ<=DzfHTI}L2J9kvhc@ZSU+BCP7$dQBN0`}3f zQXGd>I82EW67hTr?DGXYkH8Zuj-xVh!-gR|7w{bXmPWdYYhgw7%ootbS4)l5szD+m z2n(Wl>)dH7@;LH^PIcSFAwsx(TDeCjen%%L_n^m?(B6`Rcnut-2OKp-FJh6vPFt9P zI-8c!BzV-F@u@@Id~rTIgva04kNX~jqGUQ?`c{JprcGx96V+f!4RePGxeFiE9~4K| zZ4g4`v(!ad^PTb>bp=ewqZiL{8V6$K@9);GVGh``!R={qNou5jw1Dnh+e*w1VgO8~ zS&asq;&2!Z?kdA2_QbdlW$!n$TmPX&Jx5U(KO{7qzh-%1Qu}a0xs2S6&C3^*WGv1v z9$na9K62)}WAiTch%e1ohG91vU{6%q%Ae_$!SZ(@6NuR@-kcb3j#jy}K?s=z)SMK% z2~VP-ID{IcJJr%)-JI>%E%){MytN1)?rl9~Jt{)?{aRqGF8cU_n=?XMRXzE^jL9fli7N3D2?%rYd&$RKrbuD z3;X8f-94_*q?9zN{#a@Dl`dihm`ER^e^+Q*BUYU`N7@-$KlJFQ zDPGONGy55;-MH7wLliX*ZhlPHLb?o%LcvUPP~&AnVzRr*$lWki)lzqmjH#b6tDh6H zg#Lu=9pl*Yg$tFR>XqN9%hXw;Cn;Ayn>Kge+><)<$&W@>MYcFJ^V2VdMN09=;bSL% zTvj}?46<1Q-T5`-swHOW|FeDaY=p=x8LIsoFTrNMK8%@|hQz5M*r55%p9_-mmGYPJ zr*ep9QbJv6f+Q$6maSR1aoe(`3+XTN9pz^%VmpBVjmrz@1YgXbiK>8ZT8;5=;(>Npxq}V`yALA0R2ONZG0Ue*S}( zYieHBnUy`t@4qS!lzq~dxnBY_2Bh>t7K*s0$FktM4QX7UnBu*L3FV${L0b6_%+SA6DO=D-sQs zf0hI2iYBngO*>b+mt5;ND_6vy>RSuLgp&I0tlx=hzsx_o34SqZIQX|&77MgtuI16# z1Dk8vOg3tQL?_1kLGjgrjg??_WeKXt%1Q`Oa2@2WvQj#?F*iFYKYynDuI#5>!Awpx zKopdp9Tv@7ykqz3@;OFD5yC$ju$cA+FC`#vua8pyxjglmbK8}(JI_T0QP+=E{OL@b zmR4(Ghd(=7+KsS*&zOd3pfWIPLsoUZrbbiUk?qC2b~dpU@@R;P&ia+Y6cwKvLCp66 zhX@JeJf%%?`GwY-+9G|AwSvlkKLd1d!B>OkSMsVF`|-&o({_&M%{oK1i!FY^WA@V^ z;WiBcLNT$0uij!pNx#WDbDi$;ih#Gk0dj!XV=_@a<5LR3 z;(U5Q=8W1~MjZS*S{DdT2&Ce5h93d3>pQvt$Uckf{{M`zm>ML!NsL7ofRNm;%5RF9 z_C+$b8?3>V-7mbbW9Qm+yM+GG$lK@;>P%%Q>{4Z`^1HD6@>hG5NBb^c-p65NI{F7- z$_aL&12Cle1$MOKc7v14&NeXhWTUM>EryvS!h!~2R)cGzdzC9f)?;c^u02++QRA$+ zPtI1Z35NQ6In3wDH;sb zzKsF`Sa`5DjC*NT)%Z8W4aoiVedVvTsU7|edyB5;|FlW*^!|4YayzT@0RH?jhs^um z@CV-afS2Q8MW)T{yT*wGuyFvVHc@H~rHGq zkMx^9PPtt-d*;ka#Uf}#*Z8*N;dEd7nGM!hT==xtHcHsiU^bmMw=`6T*%1Y6ETN&PDJ;O$ z;PY4)SZo7L;>PJOD0PS%CjNYn#_0s*`)6iNo4<6|uaA_k?%!25Gqqc_a{634r=UEx z*V#AT_|EdKJo3rW{d$(BcDeG#yO(~yDleEhjE}7a_`4GH(QA6&&QvvMTPRpNQ_p5N zO!KfQTBR}qal~?|NILg8Q98$j8ySv}Z3ndLjv3_Nb)TI|*Q{O>v*PZ>e7{t5qDE5O zbq>m5IDLESaAAXJW-didezG3#1p)4Y#Y-V(09YiqMZ)6>zj|3 zu^VM0@=Z=>E<0>RrHAZJ+tU=qUAdrm&=k6`ZyzcCuUxcBk)KNB$N1)^>2*6BYo{}L zbWGEx+CVuzI2f#F%oq@0cjW;)2R4NSAjzH#j1UHjr!P&`ImqW}OJy$<+3zwOm0y)T zH1JcIx_?PO7H|HQD}GSd5!eWz;!TG8AdFuR_?jW(mG@=m+1kNp2a+C~tY@4o>$xWd z)y@M)07LsHEX6dxK*^=2lq%&1{OzId;wn$vD@gTm^}&Km{cplI0?WImu%Rj(j>yC1 zaMWcl*`_<%I*YrJgB$e79V?ht;k4I((|^xS`7hCP*qBE0E-;pF2lg#-0{B;RY- zHc08$t$y=##VoYK{%bw71B(<%@hp~MCo6XP0K~=06Z6M~Rx4I0IkZN1?9UG`>F)r3 z0c#WsK{vieA*dQ&bHflR1v3cvSEhWcGynNH>btKAj930GPO)X*KDtr^s${WaVmZI6JN~%d*RX0Uk`SsU~ANoj#g&w*SSdYs9 zxD4c)QbJ2=US~xRp_6Oov2Pt&;fBJ@!xSv9;GmlnB*jZ&2}2)EHwis{R(=&|s&tr2 zGI|wAI&`!f_NE$<=end}RjfHUy16TajJZ_}vd%6vPx!z*msKu8Nlm;9oqV9LOR0-6 zxyIrG0N!UsDJ$@(#N6C&nWx6fr8#3$s72doCL(@ejgxV>L*dp?2|}64$<_d0SVTBq zMTT1QMMy9`RSj$kcMQI|*yqc4ANu{y_PpF~eUzJ2U-!w;`@YTVKkYNadwWS)_prR^ zzN24XU-kNcoB>_iQ+eq}A0AXX^cmy&rOzu4P?XIQ+=-qu-C@59RymV&B zibS8kXh{6R{%0>KS4$>k4G9P+?K-&#YjMgqeS0sK{pw%ul&zdn>NmgLCRIsGOsTTU zR0qo)@jhsY6lRqlxeu%Xtifm}WKf(4E+fS3$x3@)hs9k@zCH-N5;z8kyf$Vpz+n*5 za)wYxQL_B!zVa8<-9Py|LJ1Fx`%Nt^Jw3R$yyGfOx)=BhO}X)oTrly#%E0HUIgYyk z#~$`L=GsQXF-NNga>E*lop=Ag;Mmi^!)w5B2#>c3scLRTy1P1q?Q->0zXe$5ee9R~ zY|%$oX-L_)jzaikPsK!IW=L4=>Mg6m6W zHmq0K(ZUld!L#Ww8H3wSAn#%Es^*9T(@6MsAl{*)19U_JyRn5mA`Wzl8LL@tHZp>Q zALRe|zJwE;U53v!KjiwP5!`f%kuwPbg$41VJiD_U$POCAn*DOE7Q?u9H`Ts`= z;=^WxV-mz?J?sKwakd=`g6=HL4w2zYMCw#@Me^FB*FI3L3@U6_9LQ#)Vs+2_>v!ts z3XA5BiH@lI0T2kt6C-pxzOglmbQ>$EaSAsc8fcnyK>wR{+WLSRQ(+!4-OjpUhm=7@ zm_Atc%}Ou22kpCIwAKilomR6OI`_&zSKCmd*#l`hT3>TFm8|U9PlLjeY(=aLH@#go=IXHeR%lQMOVHDfQIsPXB|3d(ieGJCWie(Oj zHe#-HHC$+(6eq@i8-s!S0x#}HE<;QA$23pbNmnV=^h2y=_|-j2*DF5t%(rf&gKhju z14Bc+ifa|m7yy2UvB+~EH(}T%#o^-MYY=mvSGx#1aV^K$2 zd19;9l&|>usAVI+dD-(Go#&kL@~F{k!;`j*+w!(@CAV*f!2yDDH8;2Ipq9#2DPvVm z|KgH?!>-oWPZn0sE=y1J3~;S)Bdne=JhoNc9qino1WS^VKgQJ{BX;AmQ)ZfB=w7wY=UJ!(*X{pFtW zD-9Vvb@G^sDU-(t5%+1Ta^^Sh2g)Zj`N7&Z4m|I@`run|H{t6jWgI3OU&qe9;f{|+ zY_3H#eaS&*82VrAu+`TAnoJyZ9)I(7zWJ#CqIl&hLSG5b6rK7;xl%r+b4frzNmj*h zYPqLe>Da#hPu=DDaOqB1$cyi#B`M)?2|HIZy#Re*#{E_XKZ}om@$7JFr|h*OIf&}Rghs$$EIz)fN?2WI*%^%Y9Y2+7!l(`#HE zTf%ui(9AW!6%P?>cW?AiYJzzb=YSOK6^w^0MAP^X0BdQmwqB$)L<0wtCBSlBEWBMZ z_sFq3AD;Ui;hCHHx!wBpUvTp1U!PohEDN-mIBsCS{xm8fJ0Rl4Maz#JT`PCW>zb03 z**x;4C2QXPdbvC>Clh0JwA>Na=(@m{>8FmB&*L?e^4tsUq9ICbpM}GGVVeaZ7)<6M zdoc}$Noa_55ri$)BA=CcE$;O6!GkF=z9ygMF$Ib1w{PC8>!SSeN&QzH({%FZZf-jd z2$7#qN7a6?+#o+_h6dea$E@5+Yw$G)8)I;;wV32Jwcjir$pEp2d=Nx-4-s`q8%ON< zfQIDeWREb?&_6fzFaG)mVMYDq%2BZ~qUU2Z=3{`K)B_g#n2*-I9b-1|q89Vv!4?z! z!&COEp^3_BTUbyi5h!Mi8k58ac&;t1ax}*4NAM?@Lx3e#H`iHA)HdZ$91`TZ+ z^U-4YEarn`aS>4;Po#-UE#(!|L;3l^Df!fISk3fUC4YR3E)?E+d_q`MKS#=_?V7Ot^^jc|ir-bs5Z@6dkA zTh-OdL7d;F-Gy3OSpQ@FRXR#}Q3w%yRXB9;UEKkPx8~2;X)>NNqrt&30Zmrp>EOk> z_JmqE-^8N^lln4HI8#`CoOBkTd+02m!}@uTxXqixI>rXO1Uh%m@eWqz(+ql|onU?( zIGBadK`OY zU{TS)SB|yrpFOCkXi#>4WkdSCJ*S6HI5i%rK)9Sdw+sf{zs8F!af?pG(YU<-Yu$T0a=@PPKtIFy8DFU+rrHuin>(!wVw_d(Bg` zFkfSIVQ?mU!+tZcgZpO}4;fON-M{tGodb%C2kbohF^6wUZ0y!C48E7sf7x?-Vp6(&yl-j#`re7XVv>3a54V2ZF*_!*yqo{FFS~S(jTqen_IIVSjMjiRnBR{G zo)uGEMKA_wh(MWn7L3(uu|OIQ#L=vNzOqbw`ys`on{>A{6BS)J-dA9aU2w-<<2B7$ zw&gWUkDyWit2Kwk%5rqRu>ejohhW-W?=y(7>Y^RccGjCT&0jcOEtOQ{wz)ih{_;uJJ*R07kc;4S>5K^HRqj$!>(Wa?B2I;$z_A5 zjk|aqw9K`9uj?%R%Ci;Yh}l~mk!E_TBO006jJ(6x#!HqYz${OMK0?hnOpi!c-DKZ% zU!IT1wl+{LqZ0u;DWT7y5hLI3_0)=I5|dk}u3oXSXZ6ssnqI3{u1#*89RKXfx|w(P z?*8SM-Fxqj?a--H`$eml_I_j7$aiv{U;SKqyY{WuKDR#Str0`3d#!j*nEke5IrNr- z_h^HEd_a@4kXv88`GG5GUra-MHG*go%%AXtM3|NuPlFg;LtK0zo;CA4K`CfcFKw#G z;^?^6pJyB9OPKV4QYhUTD)4+I7Gg(OTK!7}d$T{N|A{i*p_QpDc%U4nSy+F$qU@vj zIGQO7XeTijOXz7QC!D->3x)78XfO|Y=V`8Y^q7k=dBRd9RVl&Q2}7yD3xi4yxFO$4 zz$BI}TQe7M9+eS*la>2oK;8G^Nr5fM3uK6rENudhCga^5h*h!O2-@6?wdCsO6s=J_&0hiKv*WB_vb~~9PS=nUIld6TS*$oY8$<}>PEH(A- zD|KN!XRQ`mT5(CewQx&k}}`jJ7`%< z(u!ewKmF#UT=>MmF1`9BWWH-~@@eMNGa)xW#mS3(=~e{%ZIW(?8?E#}1}By$U}xce zG;Y}wm^TPw^Jx7>ww{LN6_nDAGiT0#1|k`NwY*y0ari*3@P-xeC0aqy(4f+SCj@H@ znhb7D22UPf0eghZ42Tjk1?SnoZRp#-;R1XJ>?dP2wBc5)!5pDo05Bf6Wed72vZL3B zsJXw);J{plsk7hxA`IdBzh<^UI!A9x3bT z807uvnYW|-N!wC0UXb*ma-6N+@Hwb(Ky(3*vb6`~V`8(RbyE&seqd}mPe#zNqm1z3 z>+GA+T1;U9ByyfDI85d%3_O^mX7bsrFf`s}ImKa(m%z_ItuRFV%L+rWgOg~Qm@|FB zYulE#ZPU5?D^%XSeDTZ9uF5mXPm5iUp(5?!`N`IOKY~o)!DD4eE-I}8?1b`3y4Cds9XTg5SOmj3hn4`~|vF)R+_JoB*8(9h8 zS*kW?G3t&nsA;@b=gd3A>bylTb_8#R3~0(#vut^e9$rtIuiu~1IwMu@^pd00rT6A% zx(&}6xxRCTzD)d0os~GB;yjP~iBUty^{SHhA}oO6!)jxLxb+EKamm=PXXt zRYO{Vdji}yrueGjEl#Wt6Ta3tzXCKvFU%Ep2rq$W8J-Io^eVTNjA4&B0%^n%!Tx#d z+a&hw9{ZNb3IhtJTiqI2kvWZ=RgmEjul(XHN!Sk>v~Vt}YPp@#q392%Kn4Zf)oB*h0)mD4)jz zX4Np+wGRiwoH5J7eKnNAtF)W)wuLBf&_3|hT$b4EEir9 zuIWBPzDi$iy_mJpwn5e0V=I$Jwor*sWl7C^R;at7svY_EICY;ecu?I6<==Rd6Cq5? z2=@-LiYP1$);)x4^=oKqyO8+A^wy^Yxq9vD&10V&FVG_4rI+fGy0nT&Z5JJQU?qe-A3U7Kb`>V3SC!U~QwcOaR+0_8Qtmbup5U|uA@CLDO^H|ZB z6~FjnG~cpsM)vI|lS5EK<31^sS`!m_DbR8W*K zrRLixT5^!x?-(UcHn-B`>7cpQkyhth@wHo3NH9~d89&QG=1h`q-B8QKD3xyL(5_8- za+{NaY+k&fx?B6~UT50U+@Z^{vdF1Tr6VeAa_bbhsH0R05 za#VamOj6p@3`gUz-dw@gn^U2Vm`bYYC;+P%b&|L<6 z?P0W$*od4pYYd%(q$y3C7`i(P!6F`uyums7KPvT9{ExWNNwxe#y=5K?f^7{Si#gv+fu)*%s`DI(J8 z;AWWv-rA*4ZkL%8B)4gnmpgX;)>cuiOuCh5J4bktZh^ki@mvy>-+3^DX)7ZW$@xqg zxrB1zwAuOC$iTpE2*O-pbj#=SXrAz*P+7%e7Xqmi-VmeFK8tN*&kx=iz=+hK4_|je z&^6=)(|2h0})qh?u?4MUSD8FE! zRJr!uch^pP;qA9y=s#*y|7rQ7N9V)dmXr13TJa$4tplb9c%K_Z;FgQIUt@M0V zqo=IlnD5oFsZ&XdHF`Fw!|2J|0X;`*^lVax(Ms*Bf^<@8e&8$8rR9w&@vMyd9cnNmt==aM0SZypB5i^b9Vw zuL=tS35+jz9Yki$x(2>Lh*7eDFPN?@Q*{idw#F283a5c7un%{}ig%=kbNOzmchc~#)=5V@YMn~F z-E}gPOJmbI1GPHfhvRe%ujA09odpeb3dtvUbp+Z8YFZ~)s{{GoLZ>j=m{=XNiA|!p ze8Wy=l5^r8(hB&^m{U+F)V<1i z>Y;iX7jM+U> z&uvn+%~5~@FGVo%^f4A~V^ek{%A_YVe*iHyUtdgWSxy{6oDmUJjp?ds{C!uq&s{Zr z^kkOGKLS}Cs}}U_UpFEsP^K56CS|>_Lzv;(OjbIY^5V7Req+$mGx_x!)cpR`gq7ds zUwcaVtLMBJyZ|IN>8m}l1wpi~-pBXyKJW(5S>|+aZKZoNmF}K;M~&`H>r~PsY8{`Z zb(p;Ic0l)2s=WC)G--#)o7z_ec}0~sUseYJEwiUeQ^3XZXgOTiDhPX1NtdgAx?1~$ zu3iM0tb$BB!z;zR0uU#&2Hm*)X;!zv>BLORCM$r=AN}`AM9+q|LEkYM=U25xze5Hm6k35zfkfF)&@_@VTh?)vt77S(DFDM zvJ)5=HSC{&CAU=o&Q=~OA7p04kP#mFPyqcg>Mhs9ZO?w ze$}rkB0XYCr``jU3*(D2dY4LlY9GFD9(OFOwetP){~QkV|JKLUyHhvnva;x8udJeH zrU885oadA3E`vT<7>-Q3jP+tt8SBuTbHYE=VRSL#9mc2?Z>^=ZBNMG1n%ByM)kqMV zwj)P3)G16#;;lF~t)n}w)gcwS0Hy&FSRHeM!5quDrJ=t*)_QupzxOouOQU8ggUOtV zK!?g#km)$f9eD-T(&O-^iO%F3f|M66a>ZX5q|Ipl`z+eN2xR?NnX@(ThromG8-nUV zA((uwV&5#dcc3(6A~vVV;k&LWu zTV|oeCb_sg)^DGXXp@;swcJGMo2O27X?ObBBcCe2y_wguUom^fhvh=~uD0#B55I7~ zzEY6p%qgCe)2GA5x|cHMhP^Rte7+c5Iz%tfivHgHdS?xK^X00&ZTk-H)!IW|eK;LE zqLoLro8?JQN47eY(Bo|TD05-Qwq1n-v&ZOK3_(64=-EL@VY#rNXEqp<`79l{&%$#! zb=PpeyUL&A{Ta`g9Q2L+XM%B zD%25m%%KS0s=Q}GFqH1+F#U#a@QW=Y)&6u1eg@N1!UE36td2}7{dBB9%@bi`w(Lcn zmAaqNA8!Z#N(dljwZsO0Mca5jAaz$OGD-loV5u5CtZbdd&lv= zlw-8{`C91PSSf!+`jW{NvZ00ZWzR10<0n5J^})Kzh?LDwO+xYwurU_;-kpfeARsm?Wj%f@>6GVKS|mG!_TTOM@$uas0iXze zeFC{;@7w!0+^x|xp_X}C-NeViw7;p3!}HuUw}!I~m?eZcJjkiF_i;qk$6?uTNhLAa zTSt|aBLyy@FtXd$Q6tA7V&NfE$+MO3F5A1y*j7H9wn*_=CQBRF4OeQNK zji8ZBV?03hOR7;v^-Crj@gGIQFR}F%csu&hT08JPPt!xJmQ7rNw+pW z6Pmm_D}}lYomp*%=;=T)jmvSmQYf|JHVH7W`$8DM&)@x^XLBOVuQ@%ps&k@XeKSqp z@Vy;eC&8ZaI-aV&33hPP>Zt9oc@u93{_}2i-W2R$-wxMFtR3)S3$BxbMYRy8_~|K|2>8n(Lbc;nt1>GYf{KtTAB|PW_uzRdtJPHsCby!gwz0Y8+@XDb{4Y zAr7JXZ%o!zA8&Fnmvs~KyEGYPJT;!nI&TNEzCn|9`*!3AtsPo0WIE$I?!Vc$lY}~I zJN*$E;2Ob&#idXS`bmZ7S8F?Z$ja}?2S7_2M5wa_x&iMrQQKjQQLX4p`8Nq4gG}o*0dd_JJogyMq72KjjyA&!*q`d zPk%a1)jd3BqmGNi!*$R9;qJZTt0=zy@jJVF_XZM3LP$a&5JEx%1Pl;D01-m(y+i1T zbO<7%fHYAMsUl4UMMM;k5COZ`#U6VDr72*+-bn7``#xuO_wI(^^Lf6{^Zott`}pF1 zvNx02Gc#w-oH^xvs2`foQYCvDSy~h3XS@t2d=$GMAbEZuZ^vnRsBv0u8tzH=r%@sZ z#4y^b@!e<970?HWroL@Ndy|kZhxvy6i)<6H?&F>PeP1fuM3n1{$C^|NbU>RPR+~uB z7UnCg7NaZ1(;$(2{_U_g#K3>N(y`ZbOtNhTWLJZ2dEhWO`@xBWrOnKS0SaSUgq)Vb zO!@%!(?~Lf`ijW99~TNEpvWGCZnBT0P%txle>t`#X%Qe82{mQz zT!oubvP$b8e9Snox448LR19OUK!GeFf@@yGWQtNCZ5hoWpK7314@DplrHsI2OD87V z)(ahP1~ucM(tvg#=%6sO21zISsV&7I09w*5onYf|8D4PVB}oO9APy=4B+}*iOqNX2 zwqEq6HLYGPQZE`W%AdWtO~#9dckTplls;Z z&qrvpg5{Q?XJF|RCAaLGAZUI8ZRVKKc0XcZ>0sYj>L<+U$5&qt6<@HtK|iQTHRKbu z7b@fXqh>YyZBO<6GwS;dSlpKaB9t?+-NrQtW+rnF}1U(}{*NMBkZ;|WU#{ai(DAR~6myO8~Rq;Zwp zDt)WykP=l`N`S^$vKr-lp>1!g?O?gFytbH!CD;QSF|>cl53?u5*qV#*-0;5P zG;auBQ?h!7tVa2Pc7J+0Q25lmRmiQ-w*s|AP>gEJ^!W=z{b~)2owg3gCRhkU655~v z`EJ@@%o|K6XgC_u_jpzb44qex#ho{Guv5pV% ziu6FOBR>++K=?!I?bf@W0KUTUTW`giS7pAO3GA`!GG5L=8Ec+*bqc+j2+ivA9%dqod5&)`&>wQ2nDBr zaAfGyZMRI~YPN$K<6|T*mT?3VsMYSk(%InbmPi_S``Zp%s37b zhpmqsBB2~(q)%pVAG_;GB;JDbxTZB)wdvO{cU*1CqukPO#D`~t?TGl3ra8F3_WYc& znGH%VLaZ_$DnDWX>(i8ZEZ_GGZ1#|4HC_59&WT+AL9|hJv;1~^52)$3t*L%UZ{>cV z)BUCN*4kzI;X6O~BL*(ylaK9h%mR=xs-H1VKf`?gC|_e5^@9Ql2o1B_(j9K5S;3}D z3|qwZyfo4_LoGGm*wXIgGrYp7yt7kY{UXuKs;8UeaU@!OFGD3fw5Y1&ZG7nAis7OM@B4br$-ouun9b_sv{{6tj=MUO?T#&}J6SLzCq z!B(3vHO`EXzghJ|_haq{_r4A4e%v6`hWp|BG4+G{aWvnL8|cbwt>}@B2n$X1!~Up) zrGI0tTggV0my7T5xBPuA{$9P8Y-j4d1ALDt+gZc1@1=QA?;TK&=b>Q**LxFjb^@mi zZE|RgSf{Dh+(+a_^o^jokFe*yf~-t=vuHa+we=OrJNV9=n43s<=0tAE<4MH%IG5Ln z`^7p7B7^U@Vt)}Y0?ZIs>K`e2F`lq3I>C`Y zOsFO*6@vkv5n;zRGrD?H9`Wn1yE2In9kOzX?>qdllT7@{;rl3?82qE+DyC!2I35CV zIUlI|X>3@Sy;s?RBYI}rE7_fzi2m+yN)-A`l6^uza4<)6sU<=biO zW&QB|RM9igSEBBxab^19`>CR5-~zs#R=d2Pwa&N(i>WH!u0{akqN_f8Pnj3h_*$SX z^+&$@?~!W(&q>Vyc}}o+DbCre=8Nt(ioRh!3BEhuWxqzC;`D2e!1Yp{rsJ!Y!{E-lh8K@ANakcas=J zZ6eF|bIIxl=S4%4TR8j%U3d=eRJ(wG6zM}YtD$6(tvwWGQlz_AFQ))Il{~9zS;R)q z0Y*`e#tGFM)a=sJC?tHMPyJ@UZpE+{K-Rnqd&GJ4Pc^G;y@i!ITh?%tul>MAGE%qe zK=8SA4w${XL7;Ruh%PE@Oxnx+q}D7V6(%#jEmWAopXrMi=vO|dIHaw39e63aS1~6W znP*I~S=!$B1mogF#y76fvhDr%e{hrr8J}6ROSkv*=_OO0+l79&8EKhBLMk#X z367gy>;WbkjefVjeW3#c2+kRzMt6q z&B?j;gA-gw=fAC_)9LO)4+4$IXbf1cCJ)pF)9#5m-wr@9yO!tCG0*yk^JHLm1r z8T5+#wLGGbF|hymlfv>KKiI#l=ZofI6XFFQ zufeh3IEy0L_-w`Vs2I3Fb+jj6b3o7cz2+N8?XM0{EZNmj1(`+k*^Z{P?s^Ir_DCBO++$L(O(AY;2kA#%nIxWqVWVAIWPFqAEpu%^E;eTv<7n<$>VJ!= zW;NZ^{=vFg{$B3dvj0K0uS72IRK(zh!Om+ZGZkpwblxM8fuulYKp$D)1}s3nRRC!( zS^CuTcdj}ksy|{B$1h)A{b#%i{fx45F%epRZuq*0 z`y&b>UWh1)@Q*BTvN?0ZcSIhDJR134dn63f?I8^!^Iza)!LJmRfiNX{=Y`fK(4 zpTHg&4{s!5Xbam?qN6jBhuZ3WbVdxKjRNB*saRLab6hfi_|Thg8a90XoEG)#XJ^-M zkmIj1AU}V=z-iM4X5{2#G;Gn5=o=u1Y6kS47^>qIQ&NXrGi_Dgxa{stYGg!&ZHr0BzqZ#Z zT*K(SHyZEi6a4?+y~tSBF;X2Pbz-R~O*uceLR+(sIoh0Q8u>I@*jSN|QORDr!Z8T; z%&l;LrGiQ?R4S@uV4`5+vww?U;OT{^q9|iz;g~4oHzUT5LYjd*o>GU61`7Lu4iJ69 zjUzfAY?j*i-W!LvcmdVK{cn%CeMq}T4MwdRk_#I@)@C($^k?wCANv=YjlDJw#YZB| zBs0?lW5RF^%x!~SMfrQ_dUCv;qJ1jjHF@ScZKH_Me*WHn5y^ah;rGj8^u1l)tW_+1 zk=Gq{Zjov1EP03*LlI@X^7c3zH;KPOqU-i4y?aiY)U(gz+386&>(;HAlzz6$s8L-y z=jC;-nVwdwb~*)tlw2_O%OU=6!Jn}>UtDi4jKqpy45$(-ha7zFoUva3%ckTnqfk!p z|Df7aMvhGgBj%!gmYx#4<=dl=`F}tlQfbLTG{45_Zmt#j@ zv`Er(c6UK2SI>!`BVT~CA<=(&^XBQ<&C^nHR3db2ut`++wtj#24uPj$8JuhUyi%`K z%S=gHK50a5*9tIH`n2LdjI*Uft+fLt1gPc|IVDek0+H#*vLUF?EGqDmHy3XS5Xvvc zo|7C5Y8T`gO~h$Ee)w4vF~$WyFn|f~MRqdzG%md?c~H8(O*0%=AHKAo@fI zpzHR`B05<_Y{{#Uwh1 zyseTrR!nRXXMA+s#xsO8p}g2@h5n(V`tdJ@>H|F^W$?>jaJHyku#r(0Q1bDDeO{YG zamMj^EykAK47%aNY70bSy2zl7m7l4Zz&j;9U@q(#@0-J6tEe}D?qp--3UIjRZ%TJS z4_JYI&F#={?!wXq#5nmUc5GE&Vbw@f z6N7UBC2aAjlGp6H!(G;-xx+Iw5~{C+=ep=y)w{uI0eR9;>2-Y8+H2`}FmYLO-CKw+ z$XjV#6P$^9HirrD9(|B}io-Zx#m~eaNHr$zU@f6+kM3OG0ywT1VH*i;b;0f0NYPMh zeW{{EYjn`aS!hOp5CO6>P_c%^m6K*cn!R zAv-}lWR$W^@J@IY?kuhW7nH$9M1-`{M}f~7|eZj%zXg%bRJsyYw7G*vV=jYVQhQG*ayzd zHd7vy?~044(RES2XUr;IN7L`jy@@(5cwb}Z{p9mA$v1}?9tYmavDQ8u8Zy)1$+>2N z?76m?%QMknGy9Ek1xb>wl1p)s~ujIY*$>Z9O&S^U1Pn2UaR zUj*zlm(PQ32rV>q0zV_sh~pYO=Z*~U3!Q%FodRIg*)<8cLINd&W(%mEvB^SjAvbEv zi~m9?f=>EHWP44~K9ZGAZP7Yutv8>Nal|v0jTUsm&WmTZ6*1kgIbjX21`j<6x}$OX zOo1Jm?6?-#kSEK{30;CU38X{Qz^R4wDR-GuSb5zZs9HdUjw+~4$66fHmV5zF4tGN{ zUk>z7#DeEUBks_TWVb*xuCMy)zeSdO8>M+K$+tzbOCmM+_S*TTIe)F5yZR>6ylJ&@ zE=qp&%_(^%^5vH+iNw?AL~7+Xg0&WIFg2~27Pe){lFbz~luHiVfN>^aT&M+sabtww z2UrXJYcS3P_!4cOJ6maNL1lwo@Zzz9*!~S4aI|+27vN$}B&?E-z!6Jy{RXE*`3Te# zB`5L815DQEnb*%l$o zFJh}!LFt(6tTceEDaaR;qYVHNOZz%+%I))RTt0QI4EufF2GO>(RJ6Zq?j;#Ghhj%R zjyihmjps-GB#V>Jof#o&-Jd6-MV{Oxf699Rm}O^(*Z%X|TAMF950`DBZW|`$1$u0l z-Qa&+V>ZB)vtC4$en5BNCV^`9J$ZEL55`qyeY9Dt+W5XJeV^J~6{v2vnJUKu zhnxCYZ?!qC`iT$4)2zh#yTYt$oxi3S`}VWcOH9BOZ=DZ%TADl11{wzZN8@V0L??80 zA5Lgsio}y&L7BTXAJYyWXd+^thN@L%X0weciHHPFv>zPKqa}872jR+f0cX&fq8MU> zkd+bp%>!`ijqc5{!g|x1Wm|q;!=I8`sY=C!l-vd*mn1h?G2+;XuihB4yvbFv{pe{| z53C^)`?s7r&S)8o$j#D?527M^_U_RKu(%xO+qe7j;A?9iYSb_g87|&z)Z{1*Ez%3t z$mzxk@NQMaflg54Y=EIPunz-|lE9M2j!xjn9Aag7aNWi^z`MJwiUkd#%48`y07c3G zfi2i9HAW?Hg3ql24UDm(UH@t6#9=or|LMK%8?HglWWupgLq`r8JS}~Chih+~HF1pp z)eoK;_h93UrM?%0yzT1hvJnzra&$do8Knz2{I?K&?W z`v@O6_Db>rfDsmII+bW6tsm|teDdAis`3(pIy~AZKw{klxVg=u$q@jv!esI(@U{*c zX~AI=CaAfk_=8-?vN-5?ICZPItlgK%EKH*NjKP}G>k&E-&sRtV+yjpCT9#3w061=T z))&wh+iG>Q;q1mYw&hd*P)^f+pSxArJ3o6QOkgHAV?>2 zqFOCs{&+C>+!JeKEyp&EMWEh1_NmbeTw>`D^}!|eZ7g=SecE%#S{xX|qcFf@=jMv~ z*a;!y0d@jtRwOGd3{;t71kbVvc3D}mTGufXuNygPib&A>^1_S97HZm+;?E}@WsigH+9|g(BtdP=DFRQ=Cpl|#?(U&(%;AW zBr}#$S&f+Gw$1Du_-`@0D?X!8B=I`Mc`zg}oLJy{(10i%7)T$ulrkg$VSH4{$w^Pn z){n_8)APnoY<}0HTH)Pe+x2PFt;vgT+`8%=Em-0&xL5XFa`cvo<2T&4@Qns9CU(xQ zmvMaOR#ENIU;QW5daz7{d~&djS%tHgz{B9I64(QAq!Sc6SB5o6<{*kP0P!3+^w1OX zOLx3b(K(dp%bqhHxa&W{>Ztp!1)sNp45M9^yCu*U5Vs1Q=MwL&h|*t;8)1J)hDXm% zaOM?ch67*P#hwebO3ZGEkZ~*Hh}o_hI#x(pQ4CaavOwl1qF;#nC><0~1@UoBa{xO- z8KFArC3TUnih0ee_06u-Al$6quI~07jl|R{SJ#h7h>WS-A$if=4cC~FM#{9D&W84W zc*OQOB@44V1~l;{Hd=)zmWdU(fM7fp-W0}jBR@xBe89TKGvbS}&#sfXMsw`5t9(G? zfc47KTRle$Cv;mI%q0j(b#?1Nrh<@xuhM`yM{%aVlHAWlOsI;N~DzCfsSNp zo|BTTe=N^WpE`B+=C$`dc*~5Nwmzmka$o$+xNDCg z=a($V%fEBw>KB^qtKO}3qpS->%b(1wb7TKb*Cn6FFfk7-?-65SQXCw{-5dMj6n02F z;vZQRWGQ0r(a`yc6?P!UAxYf#TO6jY!e4YDha-~l(Juo>$3dEc1k%@EbTQxO^28S7 zCVfKiM2H2GO77Jsd_reG)h1{I8A6C+77Bs~6uwK@L!pkN^wh+`*7X)!qQle?*Xg8dH}h#tkNrv| zmxc_OHD>Ah=k{xb8^^YTX7$hSB4G6X`lvZ*#*sT~t=Je_Yw>Nj9Bc4m&5q6MHL9E{ z!oI5Yqi9a^+zx!O5PT2?>?mMgkdo-}(z)(zp2f!v)4i{;_+&m3$?g*$;Aq}ZH)Elc zeJ9Ajzx@RvZjnD}ZA#8hnlpLa_$m35^`Y$#kB>bezouBW??nCJXMDup{^;J@m|t;2 zWL2MNN*>Zje9ir`-i`6c+I~~dy756_ zZwmW(Dp$Z}r)lZQNOF%taj#T}*^So@oIFNvc9n>0ntP`78(9j#xqH_v{BX-HLc4Xc z3~qtp=EdLt(Z*p7vSl6r6QH3Qq%1TTpuf>FcnI;ViIXvN`SrO>#3Js_wP*@z)7{yR zc}OL!j~vLM0N*Wv786i6=jDjKtcMuQ<2am7$UnQZ5|K??ikt2*wI-k1 zVEOm-xYJk1c zom1xU=B*iR%vUhTZBJHhRWCdtDmG6^?a6p-m@&8 z8W|y(126PFCfNvl*4;iS6ep|h?Xgy0)aK99=je8y5U}%*)I6Ki=0Vku7Ny!W zwAw%h+zfv}HR!2?5d{oIE2`OJjCB+|x8Q}U?9R-E+Um{BRlz*51QbODVe4AA`m|J3 zV<9aBnreKGR_D`wdymQI9WXmg!{ADPgPcCvP%VK5U&*M zX*w#LJwX~@G6s~cWHW6Y*k^eUnOhcBOFVXZ&i*D@`)8kdy+*=ZbGh80*Q{3ZeDy@F zL+?IgMAqouy*lanUGE$0-W`MQ>-t8pr04d2eYW@B@%GA1@4SP31pZ%vHJZ+`CiRFP zfx&ns%sz?-RBiqRYmfW8hWZmTxWBn#yoz11bIKrVX5fzgga0+0aF#!Xx!Gm~&mD}J zo(^-E6x^3C@OFZ09>MuugF}2A@wioyI}os|Jiy9rUB`^Gnjt?&8RUQDlfZEEiOL9H zKzW2aHZ0pF4$B@p?^wA^{A_-YiZT6h93~?bVW9l}Cv)A_j~_}pUFYGC?}CjFW8t`L z?A8C@)wBk0l<^n+x9qQ!lUa%F<&7{;8f9;XUClNoj;<~bRLzkABq|)e2Isr`Vg(Qq z*TsrV=x!GQZegw@6*@O@2d5xAXniF1;3;m}JUW`bWCz==0iRt8o|dZM`~@ly;jcz}{Z^&2kDzxCGndN=L5;4f0X^R6t>CIz?n3xZKH^^S?-SMs@tars-a zT!3$oV$j5JwU47iw(MKbCZ6~D3rco~IMt>S#bm2bDg52+2}nm-hbq2gfE z2;Y3U#a|V37*4uJm`iW6XV7!>vGT}QCa{SeE9k~EhYm<{V*NKC?veh0d+rad2+q#p z#zxVlzH*D0gyjQ|b1Wb8Dg4`5Pgn*F@;?*)UoS6Kt4)o=npg_=9T=FaZp9S5=&~B| zLWP#eSt6rhaDlx*AM1zlFn{^-`QW%y@)GeLmL*9>gWpywda*7c|GOo>;eXdmlDD=z z=ke6T9|(%MHBLi5HSmp6XRyb2?cqkJY9pN0ZnF(`pbI^&uxdTF?RX8P7@lJqPfkxu z&8lqxSdfvajpRD1DCeeBKxHq#XXULMR!v_gzkze3)i)5Y&6mzza)&%5|M*rGnnEOQ zoww$pyVkv`51cl3;LOSW&BLcYJ2qi=X2!mSZ=C()9dpo}8%A6|^-CHb^C0sM(2gC2 z+g>VGxa|kqU(C%yT*Q`h(~6fNrb*{56|etF&{k&fFiZd`97LeMB}$MtT+&>qwNU&I z-WD$?-UfeL_&^T-^FHGBz&--Kkb{WSp*TLbZNMhcfpDaeIM;0~fbKf%ybfK=JhOU~ zW=l6e3Qx*$rSh+gdMihf_nqa}aJl;A@1m2oSzOWlE@967#^Yu5Ks~oViC~j5;fpyN8E^~Y+-X6XbZ2q4aJ{6BVW+!sl6U}pE!H4WVq{}L6G3jYQu4QEn8KjP=$cCN9GMSIk z{Gui>MF>VWD1-7W)&IHa)+Kk!cH-!rOP0+8EW~-#+j&g>0qgniNG=r@%{8~b|47EE z_&p!3Tm!kj4LW==XtSqkC?lWx0jGc||0&&f|~XefO^28*M0FF-k-Pg1fLrt80mH zFk+3YL`>xWs!L0vtO3pSg}_>rBDej0DPs6CZ;VQw+R?bK#;Y zs1t?hTY@&rLErxyJP|>*Hq4gAt1zV@tSfdFQ9SX2$d^0B5;@-mC!z&k!JGkug6GV8 zuGw?u$Wof7e>vA&KnF_BCezJ0B|QhqMYNHC8+^$tKW6Gl6I#>U=|jJlFUSwS6}wI& zN1t(4{3|dok#;>1ULBhT4-A@fuk;cG>Ku0mpxP0bgt9Lc)j5L#CafJG(3DH`?mLuPBG-|Ml1(xwzZ<28uEyHIZy z&m$C{$Zf`9=94lyk-ZGe3-*J36uH-+Je>%TF=C=fy;q(n`5flp-TLs7-NC2DM7$E? zg=)mLW@E_XQ`x19*st*xTrGD1e}2`2Mc*}xi+?2FG@C|imSFIH{v% z)$NuBYKDXb-u?gw+rFv&fl@_+k7c?^@K52sZso{aK7(zFSRt|>?8}%tDuqH}OtzY{ z#Ao$btQBdZF8-xn>SE7=K3r8L=J(4jFyxZ#fKCRy|I5Vwl}WJ8f0vU$sUC15Lx5C7 zhPz-wh$Mxm79#0u1S#GbqN-$X@NqE-#3+5ye+EC6t1sGq=(De345W!B;e6w!ud1cIffY;i5tK+5%_bB4kUlZxZBfO-tT- z&TYrog3FY9BYR~`VH5)J3;WcB%w0r3<!N)7%roYonX6{{5BR4nr@gBAmP3bag>eyvucgC!Bm(bFJJiXuZ$sAxoN%`9lC(FM zg@R11|E8vktmdd1U=1%doz(-cOIJK{FQT&E|5-#Ki0d=?SHf04eV(v^O0>#f3aPo$!L~Ly84d#V9Ct-#G&}_kckS9nb=+YTGrIz?ip2^em%ftjVXnyck;<<7SK)zM~E1XWY>k7tbV|Mffo9!G`#c zaQV@8JMwDx0#nbVY!>XJ1lSm!#y+AbHz1j~ZSD3x`hR3=x8!w-(%R{^7qYb*)!+J} zxcCbheg31Lpu!IysyBXX_|T!)Yx2Q2ahQ-c|B8I?%nNUdcOcGB56o@d8^ghPpqrXU zFdnk8PjqbTEDFk%c8+j|_Z%5xi1kLfbKFhc*4Z*vbp#8QXH|F3AJ3V%mK17r4n->s zTU(({qpjirxsy&t@sxy`;S57S&jl7N_IeH9&Hqa{sE0CZw5@@?{@+1DO}-)$YO?mM zeBTR&+OsD%ai;N%{Id)e>NAJ;>>(cJd#L|S?A=enz2qZk1p$t|8y&*+<9h|~Z@SNt ze{r?i-<8T>k}&=Y^kTa6cHuj3^$Ip73F(K>jn$nT`eAudF&_QQIT`2xDOW}0YR!&9 z2LJI|Qj+B}xME`Kkd|Ehn%pKPg6)V0Ik%4d0>p#k*)L#)Ni(zAwmSP&g>3;8T9||n z3XOJ)V!%6sE0O8}Z-MJ{qWD2C0|FGtc3mlfo2A=Ikhc7UW5wIP9TkL5D5pXD4a$UP zv_JgRQ^C(Hw}A$leg3v=YmFrAe@h;)K4cT_Tx@8HR5b-~3k$;Rg2OXTr6i(!GCvE+IzM1=6mI}q8n z=uJ6IPYwIxJGn;;T{bazRBJbJ8U6h0=bE-ExETPs_@4$u0H1#*)+7Pa|2pT~@+fuo zg_=j?(bAo<8q7gpc9(oW5DeI#Mnt+?XeEI{L@|9JpJFfBd~Odc7VDQX@7G5fgyjo8n$` ztaA=a5xTrzVdb?+8EVKK_Zz={!a3Usjy@p1WFpuv8Xt3Mu?k0_{Y!XK@fSxNkThPEfgbFOfl6b%yhY`0IjVbsBlkb|T!GNU2g*bp+6;{LWN&d;V%1&!={e|pHa zAC5$3CuL`51M=W1l1|F~jJtNzkmostb~J+85GO_;8J70}MX`|vtngND&7Xe#Y*`|V zUq$>)Gj3ZWfBd%9)y)UC?!RM?cy3Sbl=*kwwpMfqfwJhMCJyO0^4ts0&E1`mv46pv zXR;c#?p}Rw>I-+SyKCy;8FS9-+ilbq&6g;9>s!qfp05MAxirMh#hNh>!!X96eY3m5 zj1{8PB1{ntacB97K915ylJzmWFbcmdph*q!MfsEZKbcSSO7j`x%;(0uVb@(t^ZBi) zFlzAhIl&K`bZFC~dH-d*#r|zAuAMtCe@Ag>HnrJZ+hpgyvw7pd`6TQq08Qu z&VIw82fLHY3<6d{@L-H5z>pQ2)6r&BUgJGWIHa&nrL=1z&dUpO7f$TEh7J{TcRm;v zenRM1X9UT7%y+o=q1)6obG0-rjC5ZyT0aiE2I5ewQC%ju)C#P6qZnJB>`{U88yR;P zT2Y=Kktp~cwJ5^w#+W$nl)g7^WqF)eI?rFjtPdY@CdG)-Xjw0@MX+OJgSrHL1jjfc zr(Djtf{djI9uS3D|@4{Fx)8~_OY&L&J!n|$3+}$?6`m`A}Zrld@ z*0!4xwAveoH;S7wH7;}bU9sy%WW)~}72jk8%v|8pJh{Qxi?vQDYaz84&lwy}19!9= z#^()hLvpy5O_^r3eNK=gMfGOs-Q|go{t{WG0-ka>KEAnW+0r}jT>7lsAjT$(jz8g? z2p9cj@JDm?iVyb46OVidx(3Vjh7Ml4j4pfE{P*a(+$RIN!sPLz?;&;b_20-HGosJT zv14XGC)bPdwME*8wJwTm_+tjlLZZsr$9b;k{G83m5^6Hn5eNOXZxI)# ziR0)F^EuHUua7ZfakFW}`2Y@FS3#&V>`kD;;xpPl*C>U7%d=v;k^8afW2hRLVtpoV@}4sI?4x4I7BzP%FYVJ2NpgrTJ`fE zU%g{G0s?TCM*DGgdpA}R+M^6(R_wF=S^+2|q2u%&h&l`N<;Yh8Mg9YjTuG2NZ4u2q z+BXwX1vmH#24)sa$SjzXS2=0p`V zKYz@a*@iZ`aaPl-`tcL}5>J`(1J7e{i04m(gYer>Pg3<{|LrxNevRME zKG$8>$INKeDnov6_MU{F8@9yH6+%A;{|Wsp&zgNFPV9>|EgSl)yZ-hH?P;C6$?<`e zi1Q&EPG#)31muCGAm$r5JiwYlxzF@WURO`ob7Dk%4E$|9!*3qbxm5%{v|5!!FS)yTP9^!c7_Q%| ziFO_9%F>eGH2Fd&k(Lp?vE6I>nCQFPzuLO8+%Lu&J1awe-7ljf7L4n1HGPQ3qPBL7 zKUlIRdTXa++F4C>?j%1hIityEx`+%uOK*{T&18y2rFcDGx)@0Dday}F8jDLmC%aWt z$R;&a?)9hGaTvZdio>9m`ckW9C?4bg$~IKSEL0e!L4W)I-ZljMI>^^90URIDQu&m1 zb}${ej=;zD)8!_^CbVu>MwcM2$J#ywlQD*qm__mh28kQ}H(*A~^P$_)o5BZNeHK6y z(@6tKq0#~LpMGO~3e^!8fL}IDP_&M?n&fKf@1U75rz*j3&NO?j9DL)lFC%=#v>%+& zz}oA!$w5VerRK7*O1?Q$^(#US*X}wek8j+F2;G_Iv@O9&+LpuG_)`)gpaK{lDjum4 z;g(xl!=vCkFGl)DnHK}i4{?nD_HKag=AV|ww{F$W$abg2>$0VI{jhlcG!7Z)hb!bg ze$-0RxMqqq@O<1cd@e!@?Ef8@WA0d_lE0G|!#= z;0%G-!m$Yg&D?!kWqfm}V~v|s(162*158~@v*7X068;>y3}GmD%8Sh(9zfUc0T0&` z2JY9BOU`L|-+aR{ZI1{PZ_|b_tz=)`3uW4&m0e)A$=~A_WLhs%Q#`c73Fd}}$BHAi zr;4IgchL&XHY>)s2Z5Z~a@9lCH(yPpUcEVC8!pF)brN^)+owt?9X%@UWqy1KbJ~mN zbfIILX1kj`tsegLsOg?*b&MkfZ|}MXFA6|7;Fe;9E_Zk&gNQ+{jlwFv$WX|ZWh{U5v@EPv~$a~^>V>o|56?@@b-V0x-@Aba4Zj`$bxQpe8m6ysa zWiJ{pjm`d^z2LFwIC}xgxqC0bx$a^&B{6Nu20P6@t-pSEaE&Uahop=)+U!%sO{jz( z2vRcC4|5D;+&0Hz&lK5n@8S4zDLS+WP7Q73Ap)A!@(b4rvK`RJXhD_Y=_83gj^evR zeZ**Lgr7&!@8MI%2VJP#V}kMD74&-9nBW)FZbT5P6?UfGDvq2gd00iB(fTA}9h}+| zv_2XlpzwWTe2+^rWEJIM<9oq)Pta#=%&pek&cesReIIHm=E(ZJFN*Lep!Yf_mc44_ z`etAQnTNGMKo01!@q40)p7J~yo)AP{O}6?(kxv8s67AaGI22Hek#P3wTIY~RrmRNn ziY$HnYx4Nv`SN`Q=~D=VpQ1Y}$id>#!}u`wa&yYLJKfr!5l=%`}&U}WfCQ+VXC-@BD=5hsZlB)wgSH7DQn@}t_ zQt#Bk&e2Z@oP1uihob-)y_BDG5h45c8tgUFv1zN_Rme}C+QNXmwf zaM5J%1J6Or6HMktFs@62()_Z#_t+pAAQhRG5S2ujA}j0*?d4YmT!9=frw==IDtNmS zF^4%_g*iP+bBc&lC1cw0oL+}HJ;po?-sQRO&U3m7JWM$UiGo|AEMTBcIdsD6 zg4k4w3QP|K9^N@_32K4IBt0A!sGA0E7!qcP4V$X;o~Ysj>ql1CiyvN4w2xsV;o*WKZY?%0vrGy`>$d=xsE>bvP7fF-H7q8|Nj^KNhV=nwE zuNJSao*q*(?W_}E7vCzorb7fUagVg6UJ_I1gZ zXzeIZa)(1LHapy#sM6|2pTY?bvEYV8gaYYG@y1oFwhyodfa;Hnui}eGP63Cfz|c*F zWOTcl6mWj`%asxLJp4-g_Q_(*ADHMCB?GkzMIX+2x@VJSU;n!Lu^3HTz9}BXbz4?D zeqivdSl|N;6mp|{X_NWCxA@0Rp9kPd`{U3 z(4IXjzYCkc?1}pI>y#Yz7Sg)UuAU`ooJf6bW7|N3k3u+tU zjALC8>v9LL3*GCJd0ki+#El+zf3zpL&NX{%xvstG$n|KqT&FlS)qh|C_m7BiT6h1K z+|LrccLTTo+~S8{`1`Va!+ED%7bz7SMR~Dr*|~#;ojc2UJX~jx%yx*2$>TbMjVqvP zgpFUJ`k~x>?g!aDb5yOKD2l;Rp$K+c?vLy6aDTojey%gf{S`EZpAB-G#s-&fpd0fz zG-Nz;tqG1*+oQ$~8T>!4l@1&K;L`8>LtqD|I5lV-?x<^XOrv` zzuoy*RV_GRkN;JfFAq16FUji!N=>!dWIlT?y~e4;oZ|R5AM__}dkXV18Zxk|(MKTX z#TaX}!Z`pNH7TJE)pQbq4bYayYJLJ=5Kb^ylBW1pi!|~DrBaRJg48Hi{VML){}$|S zR={5-gaQ``FI9&%gXj}y6dzR&tK>pL;jvJB;p>E%0)LijI|xnAN1oCPo|$hTa5Xy>fEw|r={=> z#JrDXoeK6Jm?ddOJi?OiMx+KJFxYhqEwm7k5nE6rbjTE%H5g#zvJgND+$J!(*s2C_ zoa}gR#z)N7e$s=x^g03X%N%(94a3$hI}D!X{AQ{FNA^C9Qt)F0^x z+!B3qelp_|G`0BZe#KW@r?sHA+qbIlO%Y=Z?<@0F8p}Pd*Xp$7J&HV6s%0kLv-+GQ zt|tymV_BN!t{cZW1{~K2dmHg;P}OW@I_;L|Q`K9e8t!@AE-C*Fx|blPCdR+}lm z@|bH-8>(!_Qp9T4lIzcLUt(A{ZdaSV$rv$3X_2hqI|t7z_n)++%@f3 z?`QlY+*@cIN!(`w)r=p-v4 z13*ttpD#`Ce4s7fJ{luXeAE-Q;tS9Z=z`Xc-xsV$Rie>81f5aU1fNM25iqVK=ws{n z99MVb(YhK}4EoNlMnULCn_Q*hOuhU3?j}ADo*V2`eXB z4f=i=d$5nAy};~Zt ztqac5Dg}Sj)*aTS21_&}h{YhtL{KYR z0oeLW6zd3@V2WcXZZJz9I}d^JPaHfbkBC|`uLb;R*PZ5W(Ov#IL%a`6vg_nl@%~|P zh{EV3R+M4@@PEc?tZ_w9PW9rHr1#wRSnNa6dyEgz6}*W=qy{5N5IKS|9OMTNxjq?y z@TZF9=OVqhxOo1uW%Km{&4>8Pun&ra8Ehf29N)Pz)U^rK?k<*}iF(Dw zBBj%a@rFpMC2A(80=($_n{rF>f@QbPS0rn`;wED9sGhrfkKfego#Q8+0cgJAxQE^j zyKWt1*jOGD)=+Glu19J2grzxXr%2&;l?zJ9~!x5ln{@T!a| z=_rH(T+8$Ri&(FQHUxs47edI)I9_k1~e*N*DC&R=o z<8L}MY5LG{XFq;x&h92DyBBn9+(wQ4w#SFl*!2ew(w-5%My03yvDo7c38^eW?U*_k zR2}KJt<&B0m1Ef8U8Cf(@I96*u?X)o0>{E}x#$ zXD{v1w8h7*TeR+a`q8a@@>3J$4pL)lFN?T7NF93}*_2|(nX>=c-hZ}SD1gDlwQwjP z;f9whtQk>>))DONLS=)=75?iKFz021vqKAGvy7Y_FSom{Ui^*Ik<|Cw{CPLsAd5`V z{BKcx?(D_+yTXB3+_?6}PLrnHeCoQ16Xu>e^H%=OrfCP4O~o3`kk1VWR1$ zazNja10JdX;yXrG&%I~x5FPNdfBHFbWJ56yK zi^kI*9Q~l8@wRLxXUjEbf}*xoQ2dIL7H?g5^K;0<#EyBM6cb*20q97{Udz zHIV@dW68ss&>hJc3-1SeO*{sF<}ce4Dmjhshg73vzmk}?Hz2(tC&bst~?ob39M>1#X^Ps8LNMrd0*GAH<50&=DbZ#i&LN-%Cz8xl8{2@JVs=^uCXc+C8<$*FVsH z=nQ%R7Y%wXTn10rtdr{Q-plX-xaw*^dP0#w6&@%uz^D#^GuV`q`DH?<9KB)%kyxp+ z@$FM3sAE&@mr-jnugbisQ~sQV?~1FhX*Rt{y9tf<9uzIlHaIO>r6e7%k@U>M28*92 z8f)V`9}iwnAj|?pE@jI`Z~hoU1G+W@3H+20uD@aGbJc&z%Q*vmVrjdNh$VC0q+%hhXEX|nJboFzXUaO!_SIVej(4UpSpV8(mP&#b^V52>*n4g zY9Q^V@GM}R4vQ@d_Ge`7o^b55Q-{s|lV=VbJMGJ<*N?bi&LH!hPtLNgZq@&Qj~KDG z(A^uz9%cu=x3Ya+A8l;m?;~;VyN2(rY@gRh>rnR5J_6}!z-Pm~FBOMf0H}u@`1>5W z!iaN|u02;c>@YUiCwEW&18ZQ{CTV*@h5{fK|t4qv#_0g`#VU?_+gukF9`r7I@l5$nBo_ z9b>5bc8=WVncp!+GoIfFj5#UrI{LAAPWIljuRVohsB1U06spQ#V7SAkkKrQRriqJ^@vP;E!h zcG~H4h;IwH1udhvPXzT+VOx-b9PJj^cai4(PM<^S5oXfkjk!vZzzfZ>bdc=Ih>2qkxxhwY>W;%Ti^W7_AJbik`4mw|R`W%-0p{Gx4?ARw*$3T14 zr!QUf_ub9$l)Roa_w0XA_9$Lk)2I4`MW%oKDp*NgVQSe}zs#TUvqS4}*c?ZBXr-DV zJf{lh%zV_Lb$IV;9$MRa<}}nN`C7QoSY9XaCzZl0zt4fAxzB7~r|c+YttL4^Yah12 zp>u@qjWTp5dixACcjz3^$4h5VH=SX~7g4;f=lR|#(`VVSUvm1)8|v-T89T2NVqRIE zA?B6*Og#J8cjc~An(7nl1b>rfo!ow>6>^U;o#kXb^h2xpxUVASrI*}pXuwiSV=OiL z9>ymnC*d=qeT??#b(Ded4|;z@m}$#N#$hvWQ-8~CZpCRz91<3=+d$r;pXp9NLwz&E z9BLC;wx5PpKc*PkEQ?yCR8YOx^!U57JYaet4<^|10Q7j}0qF8q=6%)`v_l>=s7QR4 zN>q{OhCLYA<8zock_W2KA?VYR2h^uW9uPjVWY2bKru(?{~)T1y^)FKG6oT)mOJKg=!rv=@#BtT0c-LQ33?Uj4Lua$9 zAwcI8{+9XsK{evUFx?&t_49C`)b0m-`LY;8GhUm{Z}L`94io;2XhGXXd`?k+;LC1& zPSvK~oR;9r57Dkh;4i1oA?R}`^;wJitQA#I*Y2})82B=m&neaCkZycV)#5(wmUuVO z*)brUK8O0A7p=?hlkKN(^8QhM4(-V2RJncD2&>`rIn4L0sORa^Gj`BP=alMmSbILF zoIbfF(MjtV7^3=QzI;VYCT^qkr1h!gS|3_lKBv$p@nv^)PGN<)rLI92(z9s2FOuFTST8}F_O{;Vthef?j?+&E8$&z&%KZ?A2J4UMA-+-l zJZJX<{%a&!5IxOZ<0E=Z@SmawGI*EbzvgB88SnHn)OSKm;c=DiC&TIo@^wH{ZsD09 zx!tU8_EY`PKJkYup2ZwrG~QEx%ll_5@1J>CCyu|MEKhD9&)-jmtfBa{1? z2mT9Vxq$Y(-|8s2P=)+UO7EDA@m6EGfWPJawo}Q4s-Zj&^kXh&|8ys79p6#zJv=RU^zx z4~=btc&uAl0R1YL^}-TS%?pd z4ZYVd_i(-4H01IsIeUiXvOo9>;T2kcOMEr3bO3G}sFK3|JoSD$ulW6;`$EMKU-0{x zFX;WGi%eI15ob|=^;G?ko}u~~>f5g7&*l48{b2soIKtY9UXT{J%}< zU3z2rp6@ds;ssMP5A{hMor=gel?04;ZqbGrAL-ZlI_tH*@D0eyB^kd>8|E)k-xc^K zPQ2oGnoQT%VW!f3?GeeZ!4KMA_+WD?ETHJ_`$d7!X6IJ$i6~K9G#19lBkBizw*5nX zZul&*SgaG&)(__>I)nDrRR2qihwq4U3(3~P?l*XjaKlG}S8PhtooieFVLTzm%KKkL z`B#p?QhYIzXvVav;6%( z{QWWjiZxoOQbTR8gYPiK8|#U z;jlf9*87({ruRQD=E^(h{o_y%kMMkAe7C6gd+hI?S6(6F9P8+q9%)f+hl3|R8a^*q zv_H9bQPU5=BPONI6vSZ40Qiajh+;)G3~9+mTTuR`d{_*le}LQsKsEl25%-_e=AEV? zoDQx~^LV@XUYlT4M2-XY1JM2|hLSvCJtFi+f`3FU(%Y9#ow0Pu^yy3WvyGdJG0ykt>U;V<{Qoes_=c7K5SR`dSORuSHY8L( zpdxMX74*-fbXJ-U24t{pVvfCzeq(#js1kW7i5F9Y;Dl70{DT<={itf;0x7bo>k;bBx_`=^rlHQMWI#I=j|w`kI&#r{RT7Vpbx+B9e1Vtw(V$M!GowP@eIMXmbu zY1OM$zkaPs7PsuzqiwI2{ra}ThxOCnJ&f5sc+dVUfQN=S+KOrq8-W&r6sr+h(?DTp~m16$EKEKq zA>{;N{$zTfbQ2`YC8r}!B062yewZ=igv>C+N2gDiXY^Yiee}@<+RWflQD2iEW2}R< z)wifIhyak45y6L?1`#w?9+6+W3iL zPQcA@DY{U;AnGkH1oo2^ET0lxT01qr`=S*e=eBLqA^H>lWAk&nY`PDa*w0=&v_V?% zQ$3mIvI=D79L#YvqUL#SJhIAm4;Dt55i~1NnVtYRO?``5b#m@0TOJdMxr0a175j`y zD|8Ih)Bw<=}SMj*I2-xpVa&f-SUh+Q_Gmefi}vqtYoklh#lG?bl~4#u_#N z@+8ye0i1H^ORJCC>+mGG@Q#U+`cVZ9ob?3^1)d=z5&i`+iO;&u@2WEPRPB%xt*~a@ z+88m&CZ{Y&&ql;VHqoA*lAc9qH7+=7LY>4vtzO+!?SYcSijA+@yUVy=Y}0>v^V1)x zYE@pB&V6+;K|W|eHO@037ub(i-jJRRwR6JqKnr}69-WFIB zLZ(^dL}k-HCR^_J%?3;yLVSTbR~s1SVOwy`6Z z@c!>0A2xQ9et^7PXij=(ah=v0I%|91#?qz8eJ|Wmhagq!w5_9I7vbl^MfCAAKA;{6 zUl-t#{s`X5zHI z!gP;P1n(mHDG5JOz-~R~Z^2V|jxQnvNKXKOU2~dpK-m8Vt|HD-fEceTg<&(?2`k$mNDb+1-^+SChPvv}X$CB2qDx_60n^7U)UCm-*>>ZL#G2QXJPpnpIrTJ;@0 zdBKi$7Ej56o*)qThF$Jfg7tBUl>+$cT-y!7AOSNqI~#nEjo9~STdUFnG#$F7GEi|* z?Sj8l2AfRCYJP2_Hx6t%`sL9vb8_p~s$0+O)UC3nU;Ic93wx$Q1+n@4i_hHGV4XZR zzS;bn^nMMSv>q}LuqrKg;XGZ0(8}xmFCng_2{h#t-vWP1X%MGql>gn*ILMtb`dtS3 zZ6lFM0)%@yDOtH9O)JJ(UIY^&6AJnj}374Il0>mbIFby&3W6F$cV-3ZV_vY{Q|Y78VLERR#R>gQ-Ws@XxLj6PR4S8u8pW~ zvlc$@SnPR3Wm^$Be2CksyAw}T#i0UccbxrDI4chOv?vZS^_76tW`B)Kh1!8sodC$$ z$*Q(=4$#6WyEHA$s50c)(O<}SMTa-u6SafBoYlQXKRraQ>%O{`me_pNsGO@sxAUh( zANl6FO!;JX%hql52JKqP??tU<*MM})`S8*|%rX8#NUzzx2<5*&3~#rUt|(LafYz2Eu)fL|=dTUqBx-p$l!~zBpV~w0lEz61U6lsV9rM9s}c3psi>e)R6#$xuil_* zG52*N#;E#gZjEtjs4tIZ;lzS8Eh?xURnUg}!dfG4+nDA55jpP3@N7~{sT<3LUJe-y z$%=psR|E!yk$EHi3?!JKt{{bPa?m4U8Pkzol8qw0q=i?F6W<|N_geYd_vhvD(o+NO z%G9f0*!|+v4Jo${yMFFs|BtQZ6#3clKja%Cq3Dc=YMNo*^Gd_wqUYDlZ;-Qn$*iq7 zUv`y-89!ma1mI5~?UFVOYb3OgNd3q03zfuEg2T_r!nlR~^lfnIIittl{r(b?G5rk;xz6JDLQm)Ew;W{N7VjI zj{5Gouf#24+kpc{97=V?;WeOSOzHOl!Wvh1L&Qltw?11Gu*k9>9J%1A{X_yTd9Ex? znoXBXrd@~IVpDLWAP!QYv33a)R{u_3Y5sR7q_13-t*MvpCHz1 z{J8a0&+ezU{U8$M@f&6?-W648&%#-AFYTK%d%>Pckq<4JHAfTQ$&U)|Sam=&{I2;0 zan-)tZrdk6xNyh5^8;!P`u&mBs~`DeV6FZa_QAS+9@<52x}X_4_w6I$ke@O<4KjT5RjsM)1wy`zAJKVb%j z_cy*c@@ja+PyJ>;m~TifU+eqM$TD=SMHVpb3gc=*2glU9YvJLpf&&7_1P+L(BIFd% z)TZ!f8hsvY98xw58Wa^|Vb)`Cqi!BY#lcmYEG+?QpF<`GuK1gonG4!=9yz#sUWeQT zjVm@@klP`z`{0qC+bzhfdd1)9(>wMSwHu@*_3HF-r(Q{E4Qh+t9X~JUJL2yZzTJq; z{|v z%K;PLxz4H`(-{V$F=jFR#W(lho2%lRaO^4Y{>EQ?gZb`svC~4-C-zPVy%|4?yNhgD zUei{0V-lS|Dwgl|k$WD(89X^N?g4>3fMb{D>l@&FaRdGM&8C)SZ|ueotOp|-y3nu5 zC+X>WAH8pSI_G7rzjGDoMfmyOhHlZ?5l#(BuN8~?zgP0}pO1piu<68<1AcD-j(^B? z8liy3Xy>BKG;T!SV%)e!YFq-i3@Fj$JvW|RX#~Q(--wUpakVSD(ZEKSQpE5F+xaW4 z5k?LWuO1Uxt|15kUxNY|-tfUYAI`c4?|jz8)o*V;_0+Z6k(+b74p}%NFYm+&x|W+O zvU!Jx9y-&$-TCG%r%W7rxc(urC$)I zHiSRt-w@{mh9b=WyQLcoh*hEY(VbbkL=DhYkexRJ?Z5*>}@c#m%i`Z4O z;PL{{uw((5zkB+Ek_F2bu&E9w?i>LNu-9?vK(s_m)O_8}hL?d&65 z35N&TXII;2R@SLsJJ1WqYU089lksLbDU&qLQvv&95>?eOmI* zJYuUdUm}Sm^;4F`+2W(k)uegDWD?Rag*2m$-Pil3_UYHV|J3@c^Tj`FhZ6CRd^z*> z+h>-}SORz0duZoj!y*R+G|B8SJc|rY&&oeRa^hok#~)}^U;+n zE%6BhZE+*oCQX_!`Q?V;rpD_`upM{_9q}sjPVC9WYy!HG`OXl)h*d(fvcG`*EHjnR z1DMb9^^|r}u6vE^4U=bZSJU_q*0FO^JRF0N2WKBYKUP2caM9@3P4|=DA^Hc8?LBl4 zu*Tehg|P0B#~rZ;8oNb%NI@Z7BOBX2#Pke}?GcIn$BY?}G_2Kt?r~}9aozjVQGF)2X*+9R>C&a81822uGr3Pe zMY(S4)~i>yZt?MCS?6wDBO;=)cuH|r^Plt&;N2Q}fw8WAWnP-l@6a-W_0V zZ;i&5Og?Gx#1@m54(?=_jPm)3)Ug?g0FEhl4S}gklF|lX>aww4cG&2y4?Mkl!$VaE zAGMXob?lZsqQ`*1;k|4FlQ)d$-?Lr2hnJRZdE>t0E1wMx+#KXj{Mxn*&73+oE-?dm z>Jg~1{xe*f`CHzAQnQ!2M*_PKv5Ue?QDmP$S?ycGK=mAv=;Cr)e@Y&xK z<2R+_Nr<3x$E;3?PMnuE`>xp4S$huPT#V24KS<=;sX47m#ta=l%Jf95mM5AuU0RgU zE#~8wcAjy5PTC&3*07{oL}2ckse`hh78YmLn_8RJfvW@g*%VMiD+fMeiOoR8SUZDS z+OyB+T53x*@5M1X_2Ehk@D~~=mw%Cvec0j>X=NMNu7wM*!@)FzbZq#Lw6O$`SI!M z?27I>uq#?fNmn$P8vb-S|JlZG_v6RjHE?Ga_kJTsvo2l5_x`PUFZo}d_-%-z|FB)Y z$*@7gGcvOEbJX(Zo@2kw$exj}UqAMi{x7!8x1K&?$fPl257Av2skV^=6D^x}zq)WB{a&U)%C`%9L@VPpG@&Qxo~BQQzZEqH6Nlr&-ki$MruFy>&`F9DD-LGhRf@?IP2 zQm~xIS-pG}(?$%*st$1zJEd^g6Wd+?5_u^lt$*s!G1y>e`Tg*xC)Yju6eTCWBNgd$ zWBQJnI@t33H}Ac&bnnVXx!MK$K{Xl(_{^BI2u>H8mz&@-nZ+UMrfAREj95_ZXzNS^ z_0v1Z7wnPqd<{jmr=G~O3C5>Z$*6@>~6P;`{9>(nLR9IB8Nr!IX&wWJ}dgKUIhlO{9?5L6y&Z-ZuJGEAAOAc(crJ=k!NWgFOSbza)b=vr_+G)qGvOT@TvxvAmGUaDR}n; z$Ksq5EnKg{J3zjbo9~TA!2=GE(fSi)uD*fuu)kp!xkq0}htm`e z11u`dR{=u+RyhR>UO0Qf;3XEk01~9($_a=g%q20z6-Q*MLtVIsd#*?|^|GvlJGo!= zAKtXs^&j+KN$d*Cn^(vO^w9Nk`clJq)4GN@ik)*Dt^m+xqo|7>5-w4oK^w2SFc#9F zodFT}EDi;N1R0G$A1g|ninG6_Ku6OnA{<9Al_OVzvBoc^Rk#Y$5@r5gU5Hoy2C6sg z?Fv#Hug+<=1({aqKV14n|ABN`P+D36WC9QUcj3UM{M&82kfOAcYc0S-5$Kn>4 z|3IbhrSkA@YBLd+fV`y2ItiL})&!K7HxABiD|f{>x1D^JAw? z96f2>*I(#=oxZ65W`R(q^JnEwC#i{p+jigf)I%SB@}gz%$kg~g>Fv5dy=CjWpS)_x z&P;)9!b+NH1ZdS7V>O&?ruOIyiDV!pS`6nrj;^75cC6RX^OR`ld7S(wK;vuC{HjL1 znCcjaD=w`)Mg)$=n(%%C(dIoc;D#u!sS3scEkeT823})xa!2wUrp8CUcIMA2%kvA+ zkNaAtPI}|zaZ{#^eVIJ1PcyB1@0scMcMIK6IJQ%MQ~#y~aid3Ne?KZCX}MW`h#`nIqNa}K5X(+q*E1~I16 zeo5=!c|4{d>AZf1gkLz%PYz#NShVQ&qB%>*;c43kY`Fiiz#!s#;`c9!kN($jGH=8B z;y+3se6W@$pc_WyOC5eOBf*nEf;j2lj@H;vZ+Ja?Y=8RyTT&p0zD zFRyUU?A$_{q}S*tNH?_b6cR>y=*RW%Lw9W7zC&Ma-@e_BhCsE+Xa`0AD}5S=4~u*E^x-Y|Xw$KrlBqaX zz%_jV+n{e`eH(J$;}D=_jF)DSDrT=qa%dn)9xNG3)cST^Jc@?H?VQPW~x zJLCU>dhrn)|9UTZPfsn=We{Mb8>Q%O7^f0f2nK^PHn$Tj_7fwbu~^^*O(Xb zx`t;sj10VhI#TUo*zu1n3nDwQ{OE8QOJk4I2I1Yh$uUk9*Cu?wM_4S9WN%e&#bTkjwhzfhmKB>>b!I zgBv6eZK-J#S#M#G`+?FT(oj4b_To#+w{^U1a>60OZ_j~u=9u=e_LhyXAZo^aDWO%l z4#KfFGrqOE4WPJaliT)L>6iUe z1`aTvc*_Dik=3gUEnE-B$0K|$a}4!lMQg(NPK{%4*}6sjxh6Cqv!{vy%ksjQ94m2h ziUTv1F?zqD`a$v$=}$h?59ukS(+11Yqo#G&%gO6}n>sIllIcg&kCtulP3FgKP)yN| zfhJU~U{H?k?f}C5xI@Y3Hibl4&@kcGeb|o6gnD;Cenc{gZO`S$b6x}vVOgVys2Lmus8!vKeEkkSTGV9h>?iBkx?DH0~)n& z6x|50xa$C8226nP>tm#{`x;lf1AN>2M*9j_BqW{V%^g`d+YCT9Zw4Tw%N0H4&#T5cU{zZr|o)r1QV0nhda+Oappbw>~$-28bVf%+j!=g0*aA32jp{ene7# zE36d&|9I2SY`x`oz~5GzcnkQGeI+3WHp*&bzL}U9jJYc|fG_!SyLR9ug_#B$8YG|s(tC`=VZop z2+f$6p4bsE8|^Uw?Xg7g;d1byBY^)*=RbliU2r+P5& zO`ow2)=;$LFn)Rk+RuLk`6xj40CG8iRPcovkh?j>F_2qDyEw#y_OO(0Z=}Bk>^JdG z8)A89+f)0jZZ`n*tA)!5X*wg0TdwCfXS|>^|DArrqUs z42MLtLu*F@7daf4!%)FT!)n4Vhq3w6#NuX@o8F55ni0$1C!c%=g12At!2T~z&**hu zWV?vlrw;12scV}^j`*E-=32VhY~4uU(7EYxox+FAON;G<_CvHu`WCWGyNF&CYxk5J z^kXPOmho|=zJ+FqG~xPm9Je+Q^YU1i)E2TCwkpX1-Y1q>r+Qnpcjh_~V+BO6Kt__N z$rCrs8#uXB#}+->+d9X@4NSOiSl;|rO&@RM*R4(Tpm^BFJgE;OuV{axKH6*c0FU_y zw~z4}(R9AS`8Ih44y;o<`!|nk-Dkkx`dRL``g^nWwlUcKM4Vfl-?Qdy6Y>p_t3T16 z1_3d#EF2Eynp5t{7!GzihUb!=*cz2g-+yz-tbDTdKK%=}>H4KL`qke1y06Yzwu;jb zI~wRr*iCusqYW*9F*0-fXMJ=y^wBdhoWD$MrX5ETc%CECR}P16p*8k|i_TE!;BU(F zf1-oOY+m?UBQ3&B!|OxI*RyY%H94;kI(Rc_^5a=T9(+M>pfqYJ*7+ZUs zQ45|_OV<#);gJzSvo5`?-Rw1(tbcZHe*VlcrF!C3{}!jB;-92$_J}o;W)vKR2}PhaR4zkT2ApEK{l%4M7T!+4a_ki(xN_&AdLANO3RG%)xduT!=V z27N%lP@{RTQ@%Ck-pu&a{EXF4Sy=2N(?0r6zkKRE{9z4!XLMQnr3I7b6`Mb9ANo~` z7N6S9@qP54zdo(klHS#`&{MikMMShF)TZb`tZZ5xm){TpyFZ& z%!52ftYTS$Qf3WL3J&I0W9;yWtziNBY5%Z8I~yjm_e@8z9?)5LJp3?8p{>rI1??F0 zcR$0H`(w36pC`)`2E0bI607w_;f8KhEXaE_D%Pe48kHlUhtTTs<$3o@J71o6-=e8` zG%P5%x%*JDl%>p4Zd#rv;re0tgnLo{Td!^(dc0K&axt;z z>g~@zyI|>N$^R=LLuO&l!~N@G?)6{m?!D&fTn*!)l|j{TT?H4}ChT6qxDzc@>f8VR z4w~WLzdL?>>fE_gp`+FxGOauNy8Zj_XAd4S2RzHywl4#Q_S`1fQ-&DR_x~V69P7Cf z%3EXzx#FG(D+yxd5=3_NaN_PIK}rVEt9o#&h`fQjk6yTRK)=F;aNgMQ6Q+*J7!ySP zp_{(@Eu<}>2X;BWc;RVFVBgG~v7@GUX$wBchmA^*vekh0#Zz_|7AgiG@R_*PZHM6q z^p}vg5i>bqnBv&b2BR}>?ch!aqTX!nAS_eB`(9Fn)F#b}>kv6QW@t|5+(^@tQ8Z^0IoDJLCx$MXR zKEBwqcatnPu<@z~Hc#Z`Pe{RYm#knA*rm9JaB+L2K~Na zBeC`C7#*9`>vL+^`S6A(=PsQ`$)qjyacNy5ZT+Gxa~@f_V9e6|sj$Z2{nQ3iHvQ1@ z3C6!c&<{eh5)i#RAfSCfbO6jn_w)JbHa`DzZQ1eAU$~VUZuJu;v95hcAa!^PGxP=0 zm`cnfl03MYyiYW$U)xC+Ro<3eUY>nh<^1@_&attbBjYWfSPq+i9XV&t$WgQNvm#?- zBO~MEMLWdUnrsI>y~pb}(bHqR?nSzeFt_YXwjWYz{3v>mFYGWOg3=v${p!B1xTh)`>KKgLmlOG=+k(N4QczW7!62I}) zS2yl`aL0}Z$*>6%hP^X#+&G|zebbQnhoGl#|%-hb7ncsCz?~l(C%aIQV(SQ4V*z-eU`Y*^&%5FV-#GrBG50~AR(V^op`}1d= z+GqL)dw*%&@>tVgvbIzEA=8)588kW{d^H^F7(T$^mNtq87RIPY+9pUEZW_cl448B%oIEd`v{hcKHGboX&~(q zx3?N+S}1nGVat>lXXd`LK|lTN9SvWy1h(T_y|(`Cx37^^i(n~%aRC_RG01iAG7OIO zD#Oj9SiA(6!Xg$jT$Vu}TVD9Z*715N9Zy$pet*aE<*yOnkz0WV_Aksa-OEDZ6AULS z@p~r42dBrr&a1k(Bh8r-E+eip@;lnSP%P(Ua)TgW~k=EMENXzD9 zPsdp3N#1B9d^qBi6lh5C8_vG~zSDJ^32Q<~KN=ZWwZ#Nx9C(f~FFPSS^F$p>Z;d}?rvuvENp_G>d zOwz_W%{fEXpV{j2akm`pVX2wJ#M4-`1x-cmHBu=!Zq65w<{mSLN#sMuI789vp>~W^ zNP~$YeOT0uENhPbFXS+2F4AB$XXi#tlIDDVuu0T)2B!mj#;t>vP1LrF4#JK@zz%Cb z!Fx|h*xR}Nm&r6ZM~KNZ3s4u`c|O9YovbgB#uBX3fcK_%@=PJ>PNwPaN;9D#ou_H9 zJd?T8n9mq#v^jlyaO_;(^Mtb{)Ss{|6L7;~Q^37P!rj!0lZGTrGEFV=l4%x@dC=se z#O61ou~3x;a4%@)nL@xM^Eqs!(dNL<7GM&&a20G*#k0b;QPi>Yxx0XLk$mUxE-wIe za!lCc%eFOtjTA$Nq_G63G{8AK+%ttoKAaatK4csTg#sWD6M^Dm>q{vXUFyE8n9a+u8i~iY7O-wz;A`%(EKWD*fn9P%z$x<|J5azW+Pcx6gzbZ%+X#Wnm_k9f z&Z?!j=vHB|A%C8Rp9{EETg_oGQgPYFZ74)Lmube4cuCt1?lw1F_v3aJMm{VO2WPJZ z*X@ewoZK0`*Y?it{)5watH=*NFj3|h%w@c!8RW*3kQ?_2Ss-Q01ZSj_1y;8oPR{3B zEWRjHv==f_dj(@|aC%$Lzi|Cwcl$$zrzTn=>ORz4B>V~fEWAW(BDUr zO#MMYqu}IVPNQHqjlg|ut(mvs!Msdu!H-s6D~(kTD|tX~&S3MT((wFz^^GWXcZ_ec z?8Q{DQI?8Xu zRsMisa1Z`%50$(4HU>Dk$AO%$Yzn6)$xTKz;W#xh>Q0Oq1x{kjNXFU(PKE>%`HB9> z(gS7kb_&0b%M*B+A<8SJJB`_4q|xTj6P}ox-jv1?rPA>FeMwuybGoU2ggft9?q%p(C zH{)p_y9w0KRJ&{(;akbWw##8|A(mC(JPk83k26o~INQq85DGgY_{o7-_~Bcs zpu^QVM@C{lm|SJyid~q^J)n2WxV_guq=WvCe!)y0n?qJsrY-CTPk#^M4D6%&y1?MN z)1T^2Mh~wjn0uF>Z^Kg4(X&7WxOB$;g}uO~1^ny@Tm<-ry4-cByi4+N#9;L9g-ftF zbHW^b!!apv)nP#Hb(XgTdYRs(@4^^yqW75<>tYu70=UnZCIZ}deS9zeruQRN>xzAS z8pd%@sn!N(&3_L#{V}hAUcw|-cD>w+xQt6-bpuBe&?tHmwrMptoW<6B?^Boo42<~V z6N9m$1-F(KYQ?>|O8=++F8%iwr6VgFN3l0vtKaH#TPE$Tuht(Yv-QVmW*QdbTGH(6 zG4zfz`hh}rnbQ)!Q>=A>F+{sd(o%qDthtDe!yv5KIB48>Ez!D-m%vyq?GLBFShV5v z2NIqq01-H@^D}YHF6>V*)51J~k6MNKtU8`hoM8u$Nw|67%lere`dg!L7ER$Bl4?`eURSjf|WRvq-1Uz$T0?2(pd?c`=Vzu3aaLIdn=b*zP)+RPfIpIE z(GIOyV@@C~+Tqu@nIKg(X9p|TNLcO!M|-Ys^39r_i(PH2`B|`-4YaR@T`b^@$C*EY zS~%Y@*il}_W7i-wZcs4_T1!1IJI!$J;{^Q${R-1hTd-935>Ar~f`PgI)#G;izk*X#-r2X0q@zs)l9BuM(feP2c{e@M5cl>=ufNObzZ(423H0|hY$=5b z*Py@PFR+KFEj7muZYi+Q(Jc1Gi``Y6l_;(Zv;pOR1 zTX}s4@bYjTF0XR0A_W)oB0bkEp|_{P$Q}iImlw}?x$ZI#G}EtMgYYrRy&i*YmfVpH z+IwrrrC)IAhnmC+HZR(1`P#tNfz9H)KC#G-g=`N4j~m-R^rs%DIAPTT&0Q{v0VwQ_ zmo?U!?*6v{Hm%=WZ>ZIaakFIgDjX%=aPIFT$DF344d?Xk_Y~$X*3D_L{(PHd>=}%#&_w* z!wS`E!4{vy#1?KHIhHi&+#dX4?CG>FTDYq=ypc7ZjAwFPy`4 z{X$L1d3aR|JO3aYBd_cpXzOSBa9PKu_bxHV4mi7YNyzG_o?0EUWGnb19A^n$!dX+i z&l==vL1G_*;i1G}V^8yPNN*@hxiE)i9>xP+9TqT1;;bv0QaFRqM2FRJd2WW;%tE)K zK{c$l2cM&stc@17ifOe{0{Mc>p*nS>+JTl7+5&m~;N0Wq=L!*u6z{mims*yS-z z-$hDK&LJJ=o+72Vo{DFi^#UH~Fqk-tE8GPM$5q_62`h@?Z19~S6PENEu_{Ou34!ON!*zzfxcf9(v zrPt7ry)#qx@_sK}pJCnvf0^wuL+fjdON9V3T9+707}Y48;lLzLkVp()`M?5ee%OHu zT>v-`3rxK{DAbWZ-x=%iq$|c4?o(y?@H7~1*I)}25s7-_ojJA1JzrMoTF<6jWx=-| zmmlfJ_IFMRB^^W4Blmyt#k-N2q57H7^iJAy;4t(?p*{p7m>j!(~Gr-)~|Ka~XU7f%VT(MjvHt|Kn(f z9nhDw2CsYcB|-`s#PE>QzEbY+QF&tzTwx{PIhKUv_L7c}+`@!+|k3aMFYcljz^aUVZTkod4EvM*nPlTKXg|7m2n6e$C8~-(iN$o|iqGAtLOi zYPfX^-~gg8^PD7L>VvKGFbc&u1wJXm_~1{iukmVt3kKI7Ti1ovtY0}*va5HWH;YeU zmtd#-DKqAhh&j`y7Mkt#L1+IQl{IqopJ&o=8Y~YSyP$N#|jM$4nrd zM~xaX!qlhX_S2U#@$b@UdUwO^3(CqC@Oa^T9&6MIEvhNQ(hB4BHbx5(n?#KkV!-DJ z3Kacy5Ii2c*EBKG2;{^UBMrAu2w$?~=N6X<-^ z{Gtn}+r{YRMZaR)MBq&XZ_-=@ib}wZBREVPjm(c1=RJ$PvFKksIU7fadtwvj<)wh1 zThm4`8b6bU#M>|g4%1p#v71+{HuJ4WVvRj1yoH6o3bKhUeB`-3B$;T$hlDnTLu9`d z>+gCTI~s;PeeasZexwa;qMz4w{hGdK4>uoX69bVlTfh3J<>5^yo(n!3^TLVsn=F^n zD~}a>gWX)icfon=F2=3Bbu~Z2s)Hb5B8+L98~d4HL;(+RV*X^|z z-Hed$KKm2?r2m>SdRC5=lD_)K)SNSWLOwM;}biLUcPCmNm7%~62*OFbbW{!E6 zxp>Xw;g)C55c8LMKQvkVW3VUs*&GHtIX+%&kNt9Yz|Cz-!6~8z7(8T5J{=l-x;c*W zA3OHEJCfDLcaMEdytnY=8?*usBU12+;1qF8D!9U|*u>Tu%gusy%whU#`c?gR-A=M_ z4r&0Z|8&FJ=O2FL#fG(X(aW1QJxj-7dh|4z28Zz$OptJJf&S;W-CjF&>b06KFP}R3 z3dalQd6))(CnR3WfLE}>%d77*nmGsBLySAVhf3M8od0A61K0M!*!6x{UCrNhXYht# zTILAk@2}t$7s8$ui9M%2cQ+e)lD*|!Wk!3aGPqD2S>Gtx`CM{fnbZi>@XI|;!EiwCZ~izksuk?|qsCKl|b z(tkfyWbf10K8JU|ekUwb@W2*-I;{RDQh%Y|L{F|bC}Q(w#H&MWUveke9>KDdNR#Y6WtSGO1 z>7~l@ips+k735Dh!Dl4RTV0NErx@2+t2UMkACap_&rT?i8z z!Jt-y743rMg;UQ8oh)&bsNFk0n^coRc|Dl`l(fHNQ8@yV1I9G z%BhdUp7mchb#vd*h4-LsmExQwob`xxi!R&?As^<7vAn@W!bs7$5qe5vw(Bzd^Yh`F zU`J3hp$GFmDV;08JT?J1B0KO+5J1RDh&;Q`@wen%=)_MZuKj#Cc_-iIq;7A2YCj!!`4%WtqeEPn_Cg z?4mdn`6ALw!a31r+zW|IL zIkI`r-iD`m-w#;l>b2||2Ad&Th6`4&p2gL~(Vuv(B++2ePta&jrUKEy)~?-kT)Ydw z*a$r(9xySCo74iBRTFWN9&$rqA*7w~WtFX;Z$E;9{M}qY&ji&H3M%fpD5e^wG z--+j~^et9@er6pzEuWh=i09+=NK-Aw$Pr|}d`|9m-j8ujrXI!hOGtl4J@4w|{;;r) zX5sbY&;3vE`tTZhd5x=G>r_0M(aGZMA)(G}O@5yy0-R;=Gls{f1E3n*d!4D@ipD8Mori2&HR`Co-m z`c<1ys-Mv->5lw5DDt+E75W`ylRg*95IvV{)oDW&=*jtfXQL3%v89&l+QH*>?2l^| zj8o5GB!?C({VHHp=Y}0r-kuiVj=^J|0z%%3<+;!@ci8OB^qVmw0pptLce!=tmbVYlTE09k+%oPPs{-=W^)zvq`kF; zl4FIMz>xP|3zTl9Z?%J2_}NI@z}wRyHk^}q><>gYva_puSnlWl9Q_^2>V%^(on1r^PpHnmEpqJUDywkdeV1J9g?!?j{kRVIDHYOlyxnci^+@l;z+k zgwKB_ee~UW!&~eAnbEir^-D>{W@q!wy4 z6hSBB#+y#Ux!8Y8rzrRNIQG!Moju$Ha@azpBP2y9;YuqQbmB(T&6+3iJ$7PZ5zJ4U z6I23utQ{l7YfdiHAGxm$>9=-pgZ|Mk?^t@L+*@jnjeGa;ZJ%VObR5}L|J>Ha(lsHv zhowh+%hs)1wjzD6Og#)g5A>{cW&ZSab|03wed?xNzv%ydvOd?IZnct%2PZ8wnTp0f zHfT||jtlraZ>rwY^sP9HIRf;8TqQ3%AHwqiIAr2C6F-ZY+okj8{rPj}uX>E75%?~G z_L9%(N6t-nzEAIED&grVY(zx*ADmAhpJ3e7S#d@>m)oenY}%jf;T9&X8#OU(#l%{M zB}mF}o`&p&mvf%RN2Wo$T&3^iXV{`$!rO!LQzg1ggW5Q_k(pyxJvJ zlZ6`gCXYPL`Cd|&fd(2T{ve@QH)v;g?@1mhK=;WHJtLiFlX6nXdWY#Jum8hk$zgP% z?=(Dvg}v7)%rdzwT%mt$%0qi?&Mh7k%tGQB{N~lE3MDLuxoJQ!hqI4F4pIo`fy>* z>s{%PsD+&q{iV{q;9lCQiubTTjNtEK4>m`8SDtI#iCdxBJ1J{$_qXN?NQ-Z|T~D(l zt|{mQ#g;oI$`tko7H;o#T;w@lTSHQKJ-2Ao%-{u)#@fV{W{x(OOy#+?;c2)PdJ8Dt zKwcw1>o6lN?Axkj+Au3m3vDO2z2SACX>G_*)QJ2C6IOLiVCe_%0OB##Oc({oYbM5} z^4f&;`egOojJh1At}S?;qpq!57*17`>3xvr{p#8mYs(wewI5cQx2o$Vn8UxWuKgjy zEQQ5MsP$pB)X z#P;Pms02Ou2P$o0d00+ZMOjX6-n^W$xnU)fNLt3ou^By(5<;;APT(p~?YUrr3WP8W980uP7~qxRc!qqfK)fA< zXC=5R((;fZPK$wIZybgeCE84c3WQXIBIM$kvX57aFjtyBDu?+0@cuTGQU(_-g~+oQ z<#Clp-9CbWB6$)R`@OQfo$-_nr0J>=L57rXn=zu zA(~;<)g02NC06)bW1Xul=JTPDQDN|n+5tXlIzdK7VAUiF^X9JDKh+&T^aNm%vEot9 zHhA<+!f2;AQ0$AdQ~PTJ;KL;uR(u)ifB0423Kk4(|*jp?e;MlX}MhschI0 z-iA5q1oX3$w8=Pqd#W}~o370OiC)#7fz8Zn?Op8=?I)Z&@&L|Qe*$)?&l0BHOH5eR zdI(nizhfuMquN?x*1prOYENn}Ykz8gXn$!>Yp-daYoBU6!1}XrzHqK~5Y_mZc0fC- z9nubKN3=RH)N!p^I|kX9uU*j|)V|Wb)J~wve%5}`?m-_^2*#U-YAM#Xqmt&M(m1!bV>VF+lz$)PRHp@`jEb)AKa4-AOp2p?FX!+rI1vT z2HkxI$s~iwU^0XZCBw*YGJ!j1{52SQuO;`8 zd&zy|ezFcn1mTQd@*vqr9wHBuP2>^sC=AdZBaf3UFh+ZVJW00Uob#v1)8rZQEF6wL zPhKD|!f5Pe`1RR_<1Sw#JK&nlPIeLpd4udCZ<5{QEwYEaP4<#^$UgEed5`SJ{@xF? zE#yNucB>*ElTXN}87 zlIx_NG!Pxfyla$DxbC1PYNjwNrH!Z$ZA^WsA8kVYX#fqRLGabmls2OwFq3LQThdmv zHEl!N(sneIwx?kT9G&Z49qVs4mEup1!J}sl=w1QUB z1#}@@L~o~y=@NPeT}qeH<@8Rvg07@@(N%OcT|@7tYw11oUV0zBpRS`1(Dif!eUNUX z57CF|Ci)0{ly0Vv(Z}f)x|KdbpM(?L?er=7G<}9XOP{09(--KA^d*Jo}gdRujx1RBt1pHrKjl``W>uFzo%zuE&YN1NYByp^aA~f z*3pafXL^bLLVu-~=@ojF{ziYNf6zbaU-WPK54}eJrPpaaZJ;`JGK~>N8Dl1#7hz#m z)`C4+HUPFt%|kNc45Eh z$2g_wL+x%Biak4@u=XrWdr#Ys{kQvA2i8&hn{{HHSpW! zDJ+$xv2>QfGT9(Dm>Byg=`VKoh@cd*d1&s zTgH~NJJ|}hlHJ8tvDIu1yPK_L_pp1}ee8a=jy=HEvkmM)wvj!=9%h@^BkWPO89o3X zXIt1-_5^#9ZDZTnQ|xK>411P6$DU^|uou}&>}B=}dzHP$cCgo(o$X`}_6FO<-ekMk zTWk+|o9$)quzl=Z_8!~M-e(`M57|eoihayJVV|^!@`eqwd(BKw(LV!yCo*=2Tx zU1h(q-`OARPxcr4oBhMCv47chR?ixk&YUI3J$zrnN{5c;}W0Nn=ZfRoj zHwBmiO+lt$)5vkdhg&O)3u9tZ(k=6HW|fr``wCcU6i-Tcb5BFlo1<~qT*Cb`m7w3WyzIM zIttFIz#WgQ8LC8#w>dzT3|V5Hh#F_)mQ>`-;@~yTb6r|96dZXnwq(eR^F-u3$PI~a zzWZ826Bnc6SieCS#m~!;x%=gN-1rW5f8|@?zBUb>nNwyez>j4}MPX5Ho~2Mk)*%Xq zLWRQ+i9?~J+7Jb8p^O_3aUnv745fv0e22P=@SWqn_8aB_liysgoBXo8yyBvq;@rYn zmf<VPNcnxS{C=dzCw|2qH%9~Yk_624j zQju4bSD53MQCeOIw8i5_85QdN5J(O2$S-SNAr~w1W}HXj##!_7@@1l+LL~Pp%Ph(= zrRG)Sm+7*$^A$MZ4Z2v7-(`^Gsg^!@TY@Mr`8 zBvCjP;;LDtd~bXz9-77XrkO}?&KD@<3Y1h{rp0){_)DlQfD+HlJXfGpBv6udl$u71 zE9s&_Yl*~*l@*j&$~jW8W)ZOp;HJuFrNCCksaB}9D-YS-TS3->B;)IxV z^*lqxnKF)5<;AMWAv%H^Ah*SB-sr=$pe(?(Kc=dg}f;V2}6R+TmS8&EFIOA1*@e0m( zm2bR}uYxl{<)5J7Oi<+{sB#iiISC5h1O;z`DmOuuo1n@|P~|14@)A^e393AsD$l0M zv#IiIsyv&jf14`TrpmRca&4+yn=03)@U^M(ZK`~mD&MBcx2f_IRe6aj-$a#fqRKZ> z<(sJTO;q_Ns(cewzKJT|M3rxn!YfJPm89TLQu!yT{F7AvNh<#&m47eweJ}NWF9l~W zMVDTRF1=K~y%hYtRKC4bzP(hwy^MSn+`U!)y;c6bRsOwI{=L^3zrM>8kv6ReriEKV6lduF6kW<)^Fi(^dKDs{9O9eukoFhAKZp zm7k%?&rs!OsPZ#Z`57wz43&R|$~RNxm#Ol}RQY78d@@x&nJS-5g-52sBU9m#sqn~D zL&Aqx&?PoQ%2ULdmhocvWLYSq@nQ&N zStuf(@kUqbv(UKWaz8dBMbsN&m7i=^xEJ(BEbv7v@I@@}MJ(_|Ea;6`;EPz$8?nF_ zv7k3%L2twYU&I36*bLbY5v%fLJH)*zU$#TstMX+##JwtCwnN;j@?|^3y((X}L)@$K zQ&st?s(jflV>8lJ`DvoQaj(ixQ~1kvi{}b|*=}*K@R#it_X>a6ZgH>hm+cn!s(jgQ zV>4vCMXbt~?H2c{eA#Ysuke@c7WWE&*=}*K@R#it_X>a6ZgH>hm+cn!3V+#dV>4vC zMXd0b?H2b6f7xzvuke@c7WWGO3{`%H!aqZmFWWWJsq!-v{<3|?W@IY<%Z#;5l#F}3 zh$MH)){ayPd)eA?udtV`9rv>IOxf$l#mH8ISWp$QzzVUTDq?{ZVv#RmffZtrFJgfe zVv#RmffZtb71jf7O~lF_W;M`-%q(g$zY^07t^g^+svd_uE+)=qnO9iM=iB9&(-i0W zU}41ReoB4OvwYsHQlH$M^1_^wMTI#&BI}+&rcrTWah|v+FI*&3%`T}d6IYD| zc;d2A#ljMK0pMk_#d&4&k!6y2V3{bR2{IZlquXROPDa@>8Y`nQG8!!+ACX^CapgQq zmV7=^M2#@TD=+6W_Yv~(a2XAg(NGx;kSw2*LoU`!05w9suPf>ULLe(d~^bwUTufTRP9p?a% zPH>;Rl#C)03QplymQjKzKAd62l~wQ#m`Oh27lMZ>OMMgzRhC)>2jPiZEg@J5i6tlD zmE=WmYGcKp{89oV_*Y$4mNqf)7L@aqT1Np2^aKSGI4{ZouySKLa)int7L>sZ8nleAfRxu~HWap#-iQJ7S_i06SBHx%Zd;Z=a|5RdPM zcmO%%#_xuBe>c(%vu~D19$6lFWZjU5Zj1ZEStXQ; zIhD|zTcC$NS-u=3I7)scV2FzmH$k2nW$`4?Lz67Om5O6=9qgqpRIfdogO*HWD;uKkoU zSG{mkyRp#YipK#Ekn^BgpIKBclTC7$6yR38s&{S%^`Gd)?SUSeT#+`=L**+T_$i&R zxZz`hm;^O3Nl+7ugjmsQ$0Wpxp$rr{qBlpZp2vwH4erH|2C*2@AXXEiI5`1Hh!;bq zn1p!sUA)RqscRDw)bj-OeWIH9C93ZdRk?{OU7{Fb#Uv!jiBv+8dY`1;C#n3C6dXxn zposKhpomz()k{6^rJnav&wHzUdaH7JtNeP4p*yJ?AjvwyBm*BoJ>}b+8nw~j4TSLcOQ4AX#6F2BV3jW@GjW6fsl@)9L@)z&; zdx8|)7Kmz%ZyNvW zPiQCv=Q3prtg4p*u zp62w4nAvF>?iNHIi9Q6_jzk{mv=&g#?7TJh8~(>(;}~JmfqIaX6I8;t{28YRrz-#B z-$l%Hr7Vize;(BSzq!7t25zhok6JKl!-zo}UJC+V4zmiEI4^qqHH+#RH7aUmR8iE* zsI^f$qTY}CBI>6u!Cg9a>Dncs%cL$dx*qAS0gJso`*0dZ{}wYmHX?3g?C{w4IHiCu z|9g+)3HnFy7#y~j|7iriGR?xu&C_a4+N;IE`zyCqyU|iD9rkJ5Hf^M^MjPj{LYoE~ zio1mM*&29Iy$5Ht-3Ke%b+C(g7#0zaz~XE(JZWx)J=&AnHrSdyqdg0&iRWQ6@iMF~ zUWM%$x5VI98GE$-+WXoEu*mpGtAcIDC$LH5R%nM{fmW@3AuP_m(!Pc@)Jg4>vWNN$ z_FvawGv*|OTa3A^#ah5htPSZzB47*F6?R}fNet}25@7pfSbp^<1BHdxXjp0G3HvNz zo24wX%7r}^x5e_Z#Ddk;eZtae18l4|Df_A|!nSI=vaEVuSyCAmRC~Ovr#^(mlwmLB zX({zJ?4!=OY@^O8t0-<0#Vw+^CDb);0VQmm0+cmVC@h&ezGt%vT0-Ozpi>o!CGm95YbSOy)xg#{3|@DcVs!lK99UWQxCqzfyV zk+6*!2U{1njmWL+Lu!>($Yo&#@)xTY79Yya12&w!VZ+%6y+=RTac4mrt zLLtI(gwV2sOAri|_-&Ijt3T;J08uA-{uKfc}magwPJ5lXEIs zPctpuxes2G4rzlB1|tkb7~?#EoiB&9an4$8g7X4SQu$Gvg0K?b+=Z|TVKu@Ugu4-* zMjp=~Jd5xg!t)3(AiRV;UPgEY;Z=m!5OyK&HxYItyoIm_;T@E*58+*e_Yn4@tPc=A zMBX1EpDM&3BmM;Or-(mCJ_ivFBOFCIhHwJmYlM>s-y)nrs6jZ3@B_j*=OGf{JV1gF znj(bYzLVCBL?Co?ULZXXq7h;d;t^~Jy`4Xjbfn9~^_5$L)_~ro0Jchi!#QSgY{5!8VKl;2=K(qc>2eTe zB9!9Ue1tNDI}lKhbQQi?hxh@+>k)52{2=0uh#x}yFyc*!A3^*m;?0O3L;N`6Er_=w zegg56h_@l$j`%6W;0O8)V(s4?VQzr!d3Lm210 zn;q4fv1$a+oSk6bph5PG>8=QwsW&|sEvb~n%JgN1@i4lFAby`2?+uAng+oY58 zB#A&sbk>nxxbK5dh_D=CHNpYsNg9RF)%iB}jOJjtpNE;wu0PY8dZ+*T<4EK0wKl0QYspQ2` z)K5|BrzrI-O8OKfeTtGkMM2+x7 zb!h2zXz6um>2+x7b!h2zXz6um>2+x7b!h2zXz6um-F0ZWb!eS+Xqk0rjdf^^b!c&Q zXl-?9X?196b!cgIXjye=S#@YVb!a_xXgzgkEp=!ub!aVhXf1W%;yQ3~9k{p-TwDh( zt^*g>fs5wMI@CoS>Y@&HQHQ#yLtWINF6yuw zV3Kn)>*IWx^>bFU{?1p}0O!wapmQfnbzWiV2qSR1&uHgWa9wlkB^ZKT0^_kqU=l(j ze0!GM>HLs94Gv5N)Sw>H|Ah1xkv;&uQH1lV))gTRApzkYgohC}Av}WcD8goh#}Ph5 zIDl{n;RwQa2;U>rBK+vQO8gN55rPq#A*3M;M<_rjM_7!2(^kprh(AI2+a)&kR7U|I`IYk_GkFs%iqwZOC%nAQT* zT3}iWOlyH@EikPGrnSJd7MRuo(^_C!3%b7rY?}hxT3}lXY-@pSEwHTxwza^v7TDGT z+ge~-3v6qFZ7r~^1-7-owiejd0^3?(TMKM!fo(0Ytp&EVz_u3H)&kpFU|S1pYk_So zu&u=ypc7HEV z)<7B^M6XzbUaQ|NQv*qN5R&d7B;7$s zx`XTsNcG>G2TU4pib9)Pi8i+qZEhtntpbKsz_1D!Rsq8*U|0nVtAJe*aUL;SU726>jzk+Q13bOI5|I^;R$H`HY zdjs$8nO(xNY%*CwxGE|jM?9b+A{P-6Q9LLF@F*ak1A-`uq9W))f|Az@UWkYa2ngX4 z1QAd`53+JexF^68E}LYt$+A0}-O0|3dtv2H`h2T)6B0pD-+$ge-s#WN{Y*`FPgOln zKhLkKy6RyYJCcL*mB|crNZy_t>Ufy6PvncCvM#8si{0#CH#^wP4tBGH-Rwwia^B6( z|2_Hx`X6))YIEJ6&~4~;bcgHbp$A;E5Iuz2(IWH&T8x&WWoS9-Mm?yER^}aPW4`C~E^mFq{`qR80vg%4N$m`HmdwOABX45;^^bR$*0?JyP&dKj#y&g=j$e&2(`hJ!3 ze&zU>dpz#ECmcJ_Vzd->qGitMa$Js9IOj?K(TndxE71U2g{pq5p+Dn?9RK3@40;wl zmv?28(1vKEyn`?4$TrJYX0J!vp*N!){kFH`K4?Gm5$7C)W}w4;KN204w?k}Q5L*|- z)|DME?X&1}=u6J~GWrTS9er2YRgPC9vgeaJ_@oX#se@1I;FCHat9Hn$9kOcA?nLv^ z-RNF)zuy<22hqd6KjQdkz8JDvoOPfjs1tRe6-d26R$Y))7i85{P*;Wiyq%Bh;Nv>@ zxQ=22+JKIonl$8(CrxPoyiSwrG`UWb>om7cb89rWgXVV7+&ayz)7(1Et<&5(4Xx48 z8V#+{&>9WxprIW!v`$0oG_+1b>ol}ZL+doOPDAT7v`$0oG_+1b>ol}ZL+iA#P77DEcNPP%o{t-}Bf`D~J% zog845qfc0Q`tZDsB-==Bh}4EiZHUx{NUe?3+SL4znjcd0Lu!6V%@3*hAvNEo)`!&k zY&AZlw%gQpHG0AG@fWK3OY$4l@(`=|xLO`k%R_3nO^vpx(Ka>OrY76eUYpu$Q+sVn zKcw_SN1QkbY`sX6wU|q2 zYqWn}rpIM^T&BlmdR(TvWx89YyJfmtrn_Z2TBf6AI$EZqWjb1>qh&f;rlVy#TBf6A zI$EZqWjb1>qh&f;riW$vSEhUAsB;%P{t`WoR-h`Xp`pA?kIM9@Ooz&Js7!~-^rlR2 z%Jim8Z_4zhOmE8crc7_j^rlR2%Jim8Z_4zhOmE8crc7_j+4ITtY_*<(44J=9{$=tn zlYg1~%j93?t1f}_&w%+`VEz{M(x+bf)Jq>U*`jXxc&P#P)2HXH+3e3|_0*@H`qWdO zdg@b8eLT|u&osa@4e(3@Jd-xRbLXK4{Js!9gxb*}^aNUrmZD{7IqF6|sEk%>D^K=( zmQX92=KJpEyzl2YBWY2ued@I@JzLsre)fFFUpVhV$BU)^5?$iFtE4@SU*@>N@kzf| z9cyR^J!j7QBoyYpZ=`)W8SRMwh~q(M20BVR^k`|vpiiMMN&7PT3OXHKg|0^Ggk1W_ zrH@?t$fb{5`dIA&R(pWe9$>WxSnUB;dw|s*AhSL)>m##1R(pWl`Z9UUdQl(hN6#iL z+FdQ$T`k&OE!tfz+FdQ$T`l6FOfI|!y;rMqpL}cr=MTd9s|qu;6+V{VMDBeo`vBSZ zk$oT8_mO=c%RZpBccd2SQEG56v^UxZUB+kjb^bKRxNEC;jxKpPuy7j|%;$(2olJ zsL+oJ{ix873jL_ij|%;$(2olJsL+oJ{ix87e)`c*KlF>nMl;>0(2dFj;uxkE z{dA&2Co1ea>%;zE!M?>s5 zOxDe0-AvZaWZg{G&1Bt7jumq3C&vmoR>-kJjumpGCFD3vj>F_QOpe3kI827aWH?NQ z!(=#2hQnkyOoqdHLk`bJ<5iz@JW4C+4##=uhl(kz~ zyOp&^Syw1)Pqvre4srZE`Wl+)clXO?N$U#r*#Z28}pQo zCG+UP?r4T*;B3e9QFnYo$2^^5p3X5(=a{E+43--6RE~Kn$LRN%r*h0wIp(Px^Hh#y z&(e+O&pe)<;P z2i=DraPC6%5Nb#Ig?ZyDZ(QY#tGscQH?H!=Ro=Lol+j8yd4052oj_|8CJCQa3_U z&n`i8koq?w5?XXsD4?3Db10w+1yrGcDilzK0;;cp0IS&o=RAnU5n%OI5a48}U@}xN z87i0z6-J+6&Lnh7Yf&haV>E!_ceg*T1Sa)5vu{v_?PH=tR_oWQwP` zD(5TYe1)fa9bHJo4^wAB_Pg*0yQ00M?d!O%?2q&POU_v%?;z(hq|I{fYsu|?=dO{> zBkFWnGA-71sOM+$AFf-^$u<*ss?JJ}u7{=8;HM=#_&A2D)8vs?!BJ$PNBdARSRYG~ ziS)n5Q_DhR9CqX>ZTXa}n)r%#zM`F9XlK{k+4XjIy`5ceXV=@=^>%jMInG?^%$3eu z>CBbRT=QmJdk`r%-EUvp*;V4lB@GyCb#D2q~FS~g#w1s zALqxXKh2NT#{YhLQGQH%Nq%KIN7`li-1PGN)6n*D>7)7ux~2E~Udcb34m$7o{L`M_ zuV-84-^t#9-!(rj+YP-tKP%fkpOJkOCOauVIQwdTMs{j`Np@O(Y&J6~k>AYhoBY$B z+9Z3yEbm48KJpU}e_L)LWkH}E| zIdYs^&}RUheavc2N8~?FzOZ_JGIRBPRxX{Rr0-YKos=}-okzs=9Z=FwDCxV!bX~5b zUrgsnyDUE>y?ph`^orHJO1h(x9-Q|3URgbu4m$67;ea+?y;@1P%-(?Cb#-sH8+v!X zjVOfkmGqCdqw1O0&K>sxTwto#8z7CS55?LlVn^I>E&OKyKfE{o-M7{0tBJ0+h7a~&p^-zw~p zf1NBA8>PD2C{>WjZT@Y{zl{|ZC7a6O*~td(R#(OmWgKzU5J?Ov!-!uOc~kBPHVpoC zpF#Jj>z(L^K2kZYSm$a(%C{*E{XPx7n}&X!+nxlra$^IOR9cG`NTQih)9TgmWI+8TPBuPUVZ`LwGhCnHKS zqVBF$nroHjT6K559GztJk6(@`IP^MP#E0o|Q2#^f{|4i4XXSUL|Lr-S$>#nXBAuCc zy62c{2g&m;?*<;BJPqz$ckjA;*WJ5rY_28QP|LoxuowIO9>@36-!zh=LIyYE4l_LAfGM0pO!lpZNVQYz}D-@oiZW_#*w z7(XZUIE*AsQm=B}!%}}u8&)~@hKX|4l(RZf&P~=UK}1L~hYOTr{C}$4!hTIyy{`AlQrb?*tL1^`vR@32Paxt3)$x0+)Zm_A^Y45Mfbw@ z3*h?&arA3<-@^#ljC`Kf$-;De#4EJG7DCy*P_?BMcUe3?ZimkOnr629QW6-D2vC=~e?nLP) zdq3iaXmau#O&j?a23pF=O87tu@kJobDZ zdp=M5V}bU^f{7O2U}1@OB=jYF4#Rr{4;Ct@rj-<0e@{t&+PTl7EqRI7t(v!WJnREtZkkGHKA{kGS?`4*&S zBGEIU7CY%`r^ zopU$37wJ!EIySmNPZFV;LG5QJmDKbu;e z!s#@_x>7tMnH2PMd+s_tcb$b*czx5frL*w7Q&@j-xpxniBfVdcDsug|_t4 zmJ)3#(UuZzDbbb^Z7I=+UK&xN5hWT?BH=ZCYQe(wl5UA)OQhOMswGk_k!XoDd($&v z?(aC>?05^mcB}Ne@UhR$@gex2o%MsMJd0&g?IYC^sg_7G^uYy-*PHDGUyt{{^}y$$ z53Ywa`$%#MNtQ^mha~$*vP6=l>=5b4N;^*4=kea8Ajw{moI;ABZ>~&=Q%JEyiX~Dk zkz$DyOQcvL#S$r&NU=nUB~ldd80qaL#S*Cmiy5qCu#~+dGTyHju*sv)(PMg8L*HJ& z_~9*!3L2F@-mh2oqW_elkpk=RHWx$B_l8P;uTqs+kEL?^fZVQ<)7f(RfIHpdiN6Ob z*$sAqBFJQf94el)iYIM^97fdth&mrp-y`Zb^deQ%--!AfQFj%29+Bq}c^;AH5qYlA zqF_%d*7Q7>g`I);55BF=>IQ3DV{L1!ZH=`J7B*Pd8mn4kRcowjja99&sx?-%#;Vp> z)ta(Y`LH^xT4PmftZI!_t+A@!1V(>B&!A^nxy{h)(RS#~XlIyiZ^wPmekinGcmftQ zSj`%%Sz|S8{8?yU)>zFNgjD5s>MZ0a3po_pFzTzu$JArZ4$`YZdNW982I<_tz{u!!P>uoUPLeXj|QYZtd88BUmx%DRXI47H8=y3=*b`R&rkT* z*%2#GhbcFKCzl5-312)0lRXBjJO*DpRtRl_Mo2Lr{XX}rxW_7`xYzZo*xV!XKfs!8 z&bZ32*Fk{`{Ns1or$=~;@A=nny5elMsOH{laConK_rdg!$jy~i^!Yg=YlT|CO_o^$?r_iJp;P9$QKK z!uspt{zb`F{Y=Y-%)vD>#c3Z05wm`eB({=Y` zTk7q61AbRlcsEvecO%Xx<-gNTTdMW436y)OR?9T?bcuFXyY|*%t(49r+hEf$b7sTy zCO>Z+Vr#Q$p2!c$Z^*CASLN5_*O)K!4}Vsd|KU^r?uogP$NUVa{Ga~l=l&-?p4_z` z|2dF9tn^nq_U8|+{o!vi`BTmw^z$a`pOV*D{J;6p1N49K33t|y|B8#^Z_Nx(+{HyMH59-T*_;UI<=bm~;{D#(iR?o_B%9rNH`(9d; z=8Vf;<^z{soO?d<>vAnP&t%w=eAoEPeAi!^8+*u)^SzWGpV#xb-T-b^B!eZQ|OWvbCQG4#>uTY| zFZ3!%`PIuAwDj+N*pR>R(cAQQKicizODrX2P^_pJObzXvS{&q;h%c8tHwi~FP5vHr;25ccbT zVhyRKzyHy9wa#blv$js!y6m{siB>!&uYCg2bZ;X-Vf?4WC$3r5{pHzl>(7h}^Wye5 zGwwguuGAjp#vK{gXa8qr!(Fp>KHN>Om=SlE*>GVl+yiFAEu2`Bz5Q>_i5sw{`^&T9 z8m+9}B(Up0H#=@(U36=rA7I7v@%eF9E|060hq-ZKUR+q8{7f_A&N3tJC+5Qa!aC!x zoc%VwZuq+M-WJ8R!NYvF6>%-_0V{xy&vcuZ=N4zVjj!1K$~kVkS#kS-?6qgOeLJp= z9cH-Awj%bqR>M9oyTCfw7h3~+j`go!d-dyobtc=|wW{MP)nSe5u%2;P$=KVSRxp0W z47I{b>&;Qi(kXN=%zG}`QaZS`{dTs@`^?*$Ul1k_7rlYK13mqBTcbz=JLv}vffFVP zeee#y?qb`}JLtR7i1GW-<-e1gv z57}Dj^?&eRAqv4JW_}*xxjocY5{)3tbvWEMowgro#=|FV(?umX${d^T+r%C)546Ed z&~xSIXXb$x%>=zfPA;{zm<#$V|9FjUD&4=%f8Au;g!bR!U+%C?PwuppyuCILf0u2F zC*dCZ`)pG@5%>F-2W(S484K_WZBsoV58@xPO*MD)VSKx7swd_Vd3e-TG@JBsd3(av zDw=_QS~E)*B?WSf-a zwn^rrruL0#qt?QtbdtTN(7q*Yu>|#G(G8}VtvUr?N=x{u=~R4c+KQi^P8SRSw@tTAn#^Y1-hPL42m80AZ_yHZYx-9Eozk7`-?)db&2`8R;2v z`%UvITUd)TlZ{!7@8Hi$&r%y_8;73Gdi(@`zPAUbvm(Dx0~e+G4p@_`@V`l~^Doz@ z*Zaqt(wmg)=JfaC2K^!Zga5ik@5ol76Z}c8ZcA@-&h6>#&Yz#o_v^jsy>fDYdcT}J z5TfmfRIosP7N!g3WKp_E86Qg@OSTrR;Bl?1WvPA#mT9?vUy-gzrm|2^%2iL=qZK%i z4kWK*ttx(Hv7GLpKIhCA(if5~g9Y=}=N8!(W{qx{ZJBH>`oLEB*JZCu zHZX5@8~ZnAZ?xaRyu&7Qc;BL>xTDeY>EaN)6~B|7n~lxs-8tDz3vkzt;)bDaY zMlS5%fk}f|z8}#qc2IVZ{fume{UO;Qt~xY3)K#C%KA9BF{yi$0W(M$o>3ccady~^e z9r(1I9G@L8SD(v1Xa9LUJsXQZaH6ZeoqgN=zngv6KYlO!o)P))oB!I(j{eYob~f9u z=Va&DpPQX)|Fi68_UC2i*{0Y zmc*|#Q@BN(gMR!#HsEg0dm}g%$zZk75^)UdAJy}PO3|NfA<|B02ZWGOQ^mG9 z6F*B2Z;0-D7Cv~pR^BdP=-}sC#fJC={z6-8^m)^w&udENB$r7Kp0AKxZA*B+>*VTs z@hOt%{~DtIYfS!_+$yClxn16ZFDyl0m_%P#(yKh*zXy+4(7Sv$KKR9gUgmr8!8;bB zcWjN`u^7E$YxIuA=p9?5cPvKl*c!cKF?z>DjGr(999COMqIWDt@0djISd88=iQcgo zy<-x+V=;QiBzni9=t3)$y4zM1ujWa7kF6-iPA}fO`FJBB_?5Pz7&w3NU%{JhnLLv= zSga*@%?2@nHoynp*&sF$EsY+uAw&nl2S3^x{b*zKqpi`8Hby_ni}It5Vg_yEoN4Ja zK7c26)mG_N?D*E{)_lY3(%0c%FNV+t={D&$_&20)z`rqlBmPb4oAAN!PD-~+w{vaq zzLU~7r}R8}-)SNY?Z|%wA3RN@p?}8*PdqK%Io%l_{PDE(?djX`!7EQocS(1_2j4s` z-8J16A3XG?>2B$6?iu`aQ}olN=%<^apKghMdW-0%TcV%dBKql;=%=>`F^gRL{`CDy zx?j4Vl71llfRz2y{oVhA=?C#2GSZMmk6s87jiely9_aj!q#wb5RCJ@w#5Ouec@Itx zmNLVeVyQSspTZw&gd!8i=rj0$16m^vXbRDcq?~BnB8xGN5-Vq%gb$b?iI|`z{Yv^3 zd_V@RA;J;5Yn^KVm6Ys4a3!Xh_AKfh1^ zM@qmV8;O^sKg+5gw<>8H6q1OQbeD7PPVd17MA90fCgB4jX^n_v(gY%TFxC4dj?zOg z&BN&<_<&Ig;we3f4@jjUB9)@pN{jJJ(j_p*(iBdO*rhQr#9`r3JCDF2e_GS`ZWK3VcAQg@{m-h)|0Wp(YWb79&DUB0?=jgqlQzT8s!ai3qhA z5o!_Z8!_%QQM?|!JC6^*fZi*P;A_myg$y71Gp21rs z2yPDO*}NR@-i~AF<}5Fy34uY<&0>N9ykQj~sCt9K!+5I&d0%gl5&vWzoV}6wQgRz{ z_U0l>$!$d2#fY{OQKsM)k*4~juPm&@2jtxtk@tooP8n+yb!yOiQ`JHhUn`6_^JOf) zd5AwHB_Q%iqEJ1Df4=a%GhZmY;HrSpTO&p<>2J;BF@po+tz(LCib=yU$DAlU>iGV|6FzfH zvVB;DHhF7U5*A7yzi-%Qk(U#ESU;~RJEC|_@wfPpVs$<~sb3_sV+x=8t9WD|6TxhT z)tU|x(d;84oBf9fX9rlr>4RdP?Pmq2eZ@fAM=Z3x#YEdnY_vVZNc&f=SvRKHDtc$gtW`VNDEQY zLY%Y^DQ&JuX(3ixh?aIuVQ1U5F=pC$)U*&cEksTWvC~5Iv=BcnL{JMc)It=s5JxRU zQVX%vLNv7yPc1}L3o+F~RJ9OSEkstkQ*5;mU2VR2Z{HAOEks!han?elwR=Tr`?Uyb z4~w(*kZ5ZUin+GH_JHkv+pNMY5)Wf_p{=*A_EwWt)={|7(%D-J=K|j^(vCSy8|GJ9 zOV{Y9J6a3n4lSa2+7_p1P4pz+g6s#hf(DaOS3I3O=j<2KESZ}&q)o|naJzm!XnddK zk74~aXTPjZw3&6-u?p$B)Ir4m9{c-%`B+ TYqd&VnqJLHIX9iq_T+y7#;O#c literal 0 HcmV?d00001 diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/fonts/Roboto-Regular.ttf b/frameworks/projects/mobiletheme/src/spark/skins/android4/assets/fonts/Roboto-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3e6e2e76134cd6040a49ccf9961d302f7d139fcd GIT binary patch literal 145348 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 Date: Mon, 4 Aug 2014 19:03:55 +0100 Subject: [PATCH 055/392] FLEX-34450 - remove invalid domain checks from thise locale because the length is no longer checked --- .../JA_EmailValidator_invalidDomainError.png | Bin 2100 -> 0 bytes .../Japanese/tests/emailValidatorTests.mxml | 16 ---------------- 2 files changed, 16 deletions(-) delete mode 100644 mustella/tests/LangPacks/Japanese/tests/baselines/JA_EmailValidator_invalidDomainError.png diff --git a/mustella/tests/LangPacks/Japanese/tests/baselines/JA_EmailValidator_invalidDomainError.png b/mustella/tests/LangPacks/Japanese/tests/baselines/JA_EmailValidator_invalidDomainError.png deleted file mode 100644 index b79d4b004d419e3988b199eeb66dbc5b3b823012..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2100 zcmV-42+Q}0P)tBNpOk7K)0B3+=+C(5(UKst8sJ1>JTbDB3Op6-p6>kVZi_)+_=sNui0U z@uM0wQW`b2Qf%VWM1zUbcYftO?z_(K`$O z?(7Wr_wN@3VFd#I4;}y?lxA+-3fJ4)!`Oxm;oAE3g3xrp_Jr00JODl@-PpD*DHvV5 zHVn752}08W+Y>o14{`<{lo%mgU9%?H96@L{Mpmzu9$JiF*R87(LkSraU}HcGg3xTp z_F4~13?*d9)>4ijQV=nS6a&%(Vv47jUcS(!5+tHEyd1LqPYTnn!5S!1+i1Ds%-dt0UwKa2t^5j{H4kr#ANXBZU zoS4_|H|wT4J2UMG@=P8*`i|{cIk9f&9_=`08tmGjzk2KnbHr?jby<3|^U%hM>}%dU zX>If5X$mPo2na~!V?&wGQa(2H?+X`_P0ljZJkm%55;r1dfH8ua9Uo8nc_*VWqa5-; z?pS?M=fFH5Pp=P*ZQEg<*aqjxQ?CxOhmgsxd%CYL(OVYb6p^RPVB~$?wd2(9a+r9SoQ7?(-MX_85 zofQcL>aPoaZchr<(UJVMn!e+GChf3@t+yql00sKc&l^xtQm_leSe|L$hZJB`emp8T z>#AeJbr?$C^&yL37-D-FC>Lb3Yw<=@M4mhy+K8y=sR`X+EUu}D%rR16RB3f%Z36Sy zDAL-VB@5~RWo+w7A@6ke&g#Q@8zsfA4*=%p361_%MysPNlJ_>7Cvz-_>_O3} z>qNszp~$AB87;=n+MKjgkjkK;Fq-j>8Y7B&<+b$~FGdIq)d}lFkd2P*PyU$9MNYb+ zIxIp8Ff>Fr_T(%ov%fxMotUvOnAZoiwdBY4){&P=3ihqS%a}&)v@ht@uGi`rI`OvU z=dlm)NNJCR_HDvn8|-1}b&Gv#2`R)LiFkUaot9~#H6OTqc|8lj7&KrTCi_SLZBU*! zei2e&bPyvLl9XMSeQcwV(Rq2#CfU{_PrW)!BL$Q(3Q4Ru>_`3Zag*0i zwzHwRCy07U0eyL5bF8$hLR!>EouH0#fymPK#vTf3BmoCz8^2#zzkhqNZESQ!-vc-w zA7t~cME}e06zt|Qw6e@e;)p4Y3x6L*^RMkZ(fp=jp0vl#dOj7=@X;nVR@%DwQtZQM zeqxNB26^%53ij=Q52IeLv`^lIl?!sD zeXh*>XKN!+?EUX?a+3;W>S=(#NQorWu$4i2Ky!3&A z?|ON^X%Q(1A_YODAczzMk%Ayn5JU=sNI?)O2qFbRq#%eC1d)OuQV^C4DF{NVAg5f0 zq#&gTf=D4Dh2et-MGAr-{P!P1RrsJZGBOed%)fji2*Sz)cmRA*x_$e0=qU!0+Mz-~sSKX=-XJ$qWw zlamP#fDcLw3kzX>em=}?-5P$ZpxdBp%a-uNrcL47_V#eB;`GC-R%HTh&=!5rx7&>; z2r;|>Uc3-mgqJU0hPk=9ga^O}rRu*Bx_x`_X>oBe%+Ah+hYuen4-kVL9not9%20l_y3D2HA3*+PC;mY~*VPxaRcYl#qDMKCFpe_0+DTx$5 zP*QmN_H7bcJbLsfjE;_mD~Ar%L<`6#WtBR#L0j}uQW7bAprpWPVS0Kx+`D%#3|zik z6D=T*GSs1sq68(8!U`Y-ocym|y-Ge;jE#+jD@Tqb5ke9rRPrc89oi^LP!cJu08&80 ztK`hgO!5gM`L4NRM*`$ghB~xqL>d^q)|AEHxO+cLm&+{~?~kcJ6Et(ZA_b*}k^&-t z*RNlPCr_S)n>TNUUydJ7fIP}j*QhArqxP-ulzqHsZ;eBJdF-3;$!DKe@%4A#*F6vV zHPRvL84vPO=Gb~g3QEhK6c{Z$fBroAp~Ia!cM>3vvPMM-MV#c*FTSdtvP>X}dL3M( eAW~3zH~kAyu5o8wH+c2{0000fOHp diff --git a/mustella/tests/LangPacks/Japanese/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Japanese/tests/emailValidatorTests.mxml index e0447f706f..5b5aa6d6b3 100644 --- a/mustella/tests/LangPacks/Japanese/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Japanese/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - From 881908ffae0cd0b6d8a11da8de155d58f5c9e8de Mon Sep 17 00:00:00 2001 From: Tom Chiverton Date: Mon, 4 Aug 2014 19:19:11 +0100 Subject: [PATCH 056/392] FLEX-34450 remove now redundant email domain length checks from locale unit tests --- .../tests/emailValidatorTests.mxml | 16 ---------------- .../tests/emailValidatorTests.mxml | 16 ---------------- .../Danish/tests/emailValidatorTests.mxml | 16 ---------------- .../Dutch/tests/emailValidatorTests.mxml | 16 ---------------- .../Finnish/tests/emailValidatorTests.mxml | 16 ---------------- .../French/tests/emailValidatorTests.mxml | 16 ---------------- .../German/tests/emailValidatorTests.mxml | 16 ---------------- .../Italian/tests/emailValidatorTests.mxml | 16 ---------------- .../Korean/tests/emailValidatorTests.mxml | 16 ---------------- .../Norwegian/tests/emailValidatorTests.mxml | 16 ---------------- .../Portuguese/tests/emailValidatorTests.mxml | 16 ---------------- .../Russian/tests/emailValidatorTests.mxml | 16 ---------------- .../Spanish/tests/emailValidatorTests.mxml | 16 ---------------- .../Swedish/tests/emailValidatorTests.mxml | 16 ---------------- 14 files changed, 224 deletions(-) diff --git a/mustella/tests/LangPacks/ChineseSimplified/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/ChineseSimplified/tests/emailValidatorTests.mxml index 6751b1aea3..8c7c0340c3 100644 --- a/mustella/tests/LangPacks/ChineseSimplified/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/ChineseSimplified/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/ChineseTraditional/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/ChineseTraditional/tests/emailValidatorTests.mxml index 073b6e4d75..4f237808c1 100644 --- a/mustella/tests/LangPacks/ChineseTraditional/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/ChineseTraditional/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/Danish/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Danish/tests/emailValidatorTests.mxml index aa3b21e538..909d136f40 100644 --- a/mustella/tests/LangPacks/Danish/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Danish/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/Dutch/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Dutch/tests/emailValidatorTests.mxml index 6f4e1b93d4..292b244c36 100644 --- a/mustella/tests/LangPacks/Dutch/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Dutch/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/Finnish/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Finnish/tests/emailValidatorTests.mxml index 0577ab35a8..875519e9de 100644 --- a/mustella/tests/LangPacks/Finnish/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Finnish/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/French/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/French/tests/emailValidatorTests.mxml index 8ea4962e04..a547a28afa 100644 --- a/mustella/tests/LangPacks/French/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/French/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/German/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/German/tests/emailValidatorTests.mxml index 832536e284..957beacddd 100644 --- a/mustella/tests/LangPacks/German/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/German/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/Italian/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Italian/tests/emailValidatorTests.mxml index b305147284..80db0638fe 100644 --- a/mustella/tests/LangPacks/Italian/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Italian/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/Korean/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Korean/tests/emailValidatorTests.mxml index 1c3b34523d..a917ab4959 100644 --- a/mustella/tests/LangPacks/Korean/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Korean/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/Norwegian/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Norwegian/tests/emailValidatorTests.mxml index 17f5a846ae..14b906d125 100644 --- a/mustella/tests/LangPacks/Norwegian/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Norwegian/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/Portuguese/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Portuguese/tests/emailValidatorTests.mxml index 46dd16de50..2e72d91451 100644 --- a/mustella/tests/LangPacks/Portuguese/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Portuguese/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/Russian/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Russian/tests/emailValidatorTests.mxml index 8484d6654c..e04e9642f0 100644 --- a/mustella/tests/LangPacks/Russian/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Russian/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/Spanish/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Spanish/tests/emailValidatorTests.mxml index 6576d0fd24..5fb30dc219 100644 --- a/mustella/tests/LangPacks/Spanish/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Spanish/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - diff --git a/mustella/tests/LangPacks/Swedish/tests/emailValidatorTests.mxml b/mustella/tests/LangPacks/Swedish/tests/emailValidatorTests.mxml index 247d1377d9..1daedf1a79 100644 --- a/mustella/tests/LangPacks/Swedish/tests/emailValidatorTests.mxml +++ b/mustella/tests/LangPacks/Swedish/tests/emailValidatorTests.mxml @@ -87,22 +87,6 @@ - - - - - - - - - - - - - - - - From 3ba58beca62781341ea89e9a6967df22df6644f1 Mon Sep 17 00:00:00 2001 From: Tom Chiverton Date: Tue, 5 Aug 2014 22:31:10 +0100 Subject: [PATCH 057/392] FLEX-34450 updated baseline image --- .../baselines/invalidDomainError_spark.png | Bin 5439 -> 3009 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/mustella/tests/Validators/EmailValidator/Properties/baselines/invalidDomainError_spark.png b/mustella/tests/Validators/EmailValidator/Properties/baselines/invalidDomainError_spark.png index 4758a8ad4faf8aa01fb7357f5bf7cc9ca769b722..a898ac5bcfd44648a8534a3ecd29abde7287cf3c 100644 GIT binary patch literal 3009 zcmd58dsLEn7B^JZCY5o_Opz%o%}g?9G$|xXv6>xfv&3wZiBikZFNHEuq2TOEOC{GS zY%HUlF{ez;nG#WfS}ALNz?6#lqU0zTA}RO)L>8x~=S*j}vu7XQ`F(%f_v3ee_ulLs zxbOvbi|s%l(1NX7LU)2dRyxa{3bCrAkh4*t)ZA*C%V+Vgd^WRh20&kpGROI z?y=9!$B6DHghhhGLc#YL&)WM|S|dm<^(lKdo!x=hR_SX!Kc=KNwys1Hebm|cOYF^P zXu)@d=maWKcvUz&w0~_y;6W`Z1itRziU4hMK;x*s^`dKPxE)B6=6MYS0>eO#6wpSA z6$oQHd)~sYQLo^4EEf1J%ljhA_30C&VA^!e{+s$Ng$D$A^Dpae;c&Rc558la7&*mh z@UekynA|8y-eE09fnn~uMtkOAY;AYX;_hLb#VALL`!1=Zt!)?YxVkGfMJAWaW!?Jg z0JnqetArp)cbN&H$0!uaOG0&i{wb;O#3(U1)1VuhdM0f4JYtjvdnk;8tVMxn*~D>p z?E*}=1S7#Ok4l`jhaubGP+g5L7M$61VRbB#NbI#mMLw#EUPy7IObP98KpV2QRggS$ z)cVg(<)}1WcgpB{LYL6-bs74xF&|!EhhD2-%gFK5n43rhz+9=?5XE$D8t;Z>jkv}<;*}6R> zZ&5PxPWMC8qJ=u@-tj~*40AdUjQZU|=$UyL?rri6@EUwwQD9OvD#z_t7%i+ z;=E^#)k}So_dGyU7cI%XQWONtT-kl>5^%st$Wkwe#xzz3R3vwIat$FVZpnOuw!br` zXT=1ai^tBL#qbGN-~r;VQ2!t0=Y*ALank0? z+{0Y9^z2mF7u}|^hD*jm>2-XW>+~lkj_C+<4cN?~46?aD9vmS5k_5MD2{ST&5G1Ax1W7&dDBCK;Dr-J%^b235 zTBEJzQ7bi?eXDdB?aL*E4yW089CeWKTkR<{|8kl!Q{t0R0;;ZRHQtcp30{$`@v}@r z`N(v=ppQ08s0dZLc(3Arb*`+E6@#~Kw1QA`>Qvd9%c5b%z*zjpFM$0Dmz!f+wV^4&wcZf$EGN#?0oeG{s()!QR^Vl6LS|o#c#9O8<6VCH zr<^T!5KCXMpk2=CqBR~7F&@VJE)yV@vX)pKQa^^414<bxFNYOsxLh*Cjk#pm>=$QIr7 zPJK6eVUvr~`~hyzU`}5aFDp2kXXQxg;7j>2`}in4ZIt~q<$2k~u^|N__ccAOeCQDnzK9<7uX2G89hC`IjlLGGmn8Q zWze48J+&5v+84v8#_F(d@bQe90`wo)CgUXA1rei@B?QJ9E$+T`NYl-&G{bjg)9H!L zI>pk#P>B_$Gy)^5O9j#cCp{Km4*YLscuuVU?CPiTOt#{aHn$TNi-s+$IcRGbF0^9P HH;jJ($(N-8 literal 5439 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 Date: Tue, 5 Aug 2014 22:43:35 +0100 Subject: [PATCH 058/392] correct case for osmf.swc so compiles on Linux work --- frameworks/flex-config-template.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/flex-config-template.xml b/frameworks/flex-config-template.xml index 53776fddc1..b9395dcd5a 100644 --- a/frameworks/flex-config-template.xml +++ b/frameworks/flex-config-template.xml @@ -406,7 +406,7 @@ Flex Library project, FlashBuilder will allow "Link Type" to be external. --> - libs/OSMF.swc + libs/osmf.swc osmf_${build.number}.swf From 00e718d515dc4a2a6d6bc3a6e33ce844d177ac4b Mon Sep 17 00:00:00 2001 From: Mihai C Date: Thu, 7 Aug 2014 17:47:46 +0100 Subject: [PATCH 059/392] FLEX-34456 Added a unit test which reproduces the bug. --- ...calCollectionViewCursor_FLEX_34456_Test.as | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34456_Test.as diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34456_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34456_Test.as new file mode 100644 index 0000000000..5682c7fff3 --- /dev/null +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34456_Test.as @@ -0,0 +1,166 @@ +package mx.collections +{ + import flash.events.UncaughtErrorEvent; + + import mx.core.FlexGlobals; + + import spark.components.WindowedApplication; + + import org.flexunit.asserts.assertEquals; + import org.flexunit.asserts.assertNotNull; + import org.flexunit.asserts.assertTrue; + import org.flexunit.runners.Parameterized; + + [RunWith("org.flexunit.runners.Parameterized")] + public class HierarchicalCollectionViewCursor_FLEX_34456_Test + { + public static var positionAndOperation:Array = [[11, 5, 0], [11, 5, 1]]; + + private static const OP_ADD:int = 0; + private static const OP_REMOVE:int = 1; + private static var _generatedHierarchy:HierarchicalCollectionView; + private static var _utils:HierarchicalCollectionViewTestUtils = new HierarchicalCollectionViewTestUtils(); + private static var _noErrorsThrown:Boolean = true; + private static var _currentHierarchy:HierarchicalCollectionView; + private static var _sut:HierarchicalCollectionViewCursor; + private static var _operationCursor:HierarchicalCollectionViewCursor; + private static var _mirrorCursor:HierarchicalCollectionViewCursor; + + private static var foo:Parameterized; + + [BeforeClass] + public static function setUpBeforeClass():void + { + _generatedHierarchy = _utils.generateOpenHierarchyFromRootList(_utils.generateHierarchySourceFromString(HIERARCHY_STRING)); + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtClientError); + } + + [AfterClass] + public static function tearDownAfterClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.removeEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtClientError); + _generatedHierarchy = null; + _utils = null; + } + + [Before] + public function setUp():void + { + _currentHierarchy = _utils.clone(_generatedHierarchy); + _utils.openAllNodes(_currentHierarchy); + _sut = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + _sut.name = "_sut"; + } + + [After] + public function tearDown():void + { + _sut = null; + _currentHierarchy = null; + _operationCursor = null; + _mirrorCursor = null; + } + + + [Test(dataProvider="positionAndOperation")] + public function testReproduce_FLEX_34119_Comprehensive(selectedItemIndex:int, operationIndex:int, operation:int):void + { + //WHEN + //1. Select a random node + _sut.seek(new CursorBookmark(selectedItemIndex)); + + var selectedNode:DataNode = DataNode(_sut.current); + assertNotNull(selectedNode); + + selectedNode.isSelected = true; + + //2. Perform operation + _operationCursor = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + _operationCursor.name = "_operationCursor"; + _operationCursor.seek(new CursorBookmark(operationIndex)); + + if (operation == OP_ADD) + testAddition(_operationCursor); + else if (operation == OP_REMOVE) + testRemoval(_operationCursor, selectedNode); + + //THEN 1 + assertTrue(_noErrorsThrown); + + //3. Create mirror HierarchicalCollectionView from the changed root, as the source of truth + _mirrorCursor = _utils.navigateToItem(_currentHierarchy.createCursor() as HierarchicalCollectionViewCursor, selectedNode) as HierarchicalCollectionViewCursor; + _mirrorCursor.name = "_mirrorCursor"; + + //4. Navigate somewhere in both HierarchicalCollectionViews and make sure they do the same thing + _sut.moveNext(); + _mirrorCursor.moveNext(); + + //THEN 2 + assertEquals(_mirrorCursor.current, _sut.current); + } + + + private function testRemoval(where:HierarchicalCollectionViewCursor, selectedNode:DataNode):void + { + var itemToDelete:DataNode = where.current as DataNode; + assertNotNull(itemToDelete); + + //mark the next item, so we know which item disappeared + where.moveNext(); + var nextItem:DataNode = where.current as DataNode; + if (nextItem) + nextItem.isPreviousSiblingRemoved = true; + + //remove the item + var parentOfItemToRemove:DataNode = _currentHierarchy.getParentItem(itemToDelete) as DataNode; + var collectionToChange:ArrayCollection = parentOfItemToRemove ? parentOfItemToRemove.children : _utils.getRoot(_currentHierarchy) as ArrayCollection; + + collectionToChange.removeItem(itemToDelete); + } + + + private function testAddition(where:HierarchicalCollectionViewCursor):void + { + var itemBeforeWhichWereAdding:DataNode = where.current as DataNode; + assertNotNull(itemBeforeWhichWereAdding); + + var parentOfAdditionLocation:DataNode = _currentHierarchy.getParentItem(itemBeforeWhichWereAdding) as DataNode; + var collectionToChange:ArrayCollection = parentOfAdditionLocation ? parentOfAdditionLocation.children : _utils.getRoot(_currentHierarchy) as ArrayCollection; + var positionOfItemBeforeWhichWereAdding:int = collectionToChange.getItemIndex(itemBeforeWhichWereAdding); + + collectionToChange.addItemAt(_utils.createSimpleNode(itemBeforeWhichWereAdding.label + " [INSERTED NODE]"), positionOfItemBeforeWhichWereAdding); + } + + + + + private static function onUncaughtClientError(event:UncaughtErrorEvent):void + { + event.preventDefault(); + event.stopImmediatePropagation(); + _noErrorsThrown = false; + + trace("\n" + event.error); + _utils.printHCollectionView(_currentHierarchy); + } + + + private static const HIERARCHY_STRING:String = (City(1) + Region(2)->City(1)->Company(1) + Region(2)->City(1)->Company(2) + Region(2)->City(1)->Company(2)->Department(1)[REM] + Region(2)->City(1)->Company(2)->Department(1)[REM]->Employee(1) + Region(2)->City(1)->Company(2)->Department(1)[REM]->Employee(2) + Region(2)->City(1)->Company(2)->Department(2) + Region(2)->City(1)->Company(2)->Department(2)->Employee(1) + Region(2)->City(1)->Company(2)->Department(2)->Employee(2) + Region(2)->City(1)->Company(2)->Department(2)->Employee(3)[SEL] + Region(2)->City(1)->Company(2)->Department(3) + Region(2)->City(1)->Company(2)->Department(3)->Employee(1) + Region(2)->City(1)->Company(3) + ]]>).toString(); + } +} \ No newline at end of file From 04bb0b27de8885b7378bcdc2b0ed11a7a1ccd984 Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Fri, 8 Aug 2014 12:39:57 +0100 Subject: [PATCH 060/392] FLEX-34456 CAUSE: When a HierarchicalCollectionViewCursor reacted to a deletion before another cursor, it deleted the parent mappings of the deleted nodes (even if it didn't need to make any changes to its internal state). Since these mappings are on the HierarchicalCollectionView, the next cursor to react to the same deletion would compute that the parent of the deleted node is null, and everything went bad from there. In other words, if two or more cursors are created on the same HierarchicalCollectionView, they will work against each other when deletions occur (or replacements, which HierarchicalCollectionView transforms into deletions). I think that the mapping deletions happened because HierarchicalCollectionViewCursor was written with the assumption that there would only be one cursor for each collection. SOLUTION: Now we're making it HierarchicalCollectionView's job to remove the parent mappings of all the affected nodes after it gives all the cursors the chance to react to the deletion. NOTES: -As part of this change I realised that the conditions for an if clause introduced for FLEX-34424 were not enough: in order to choose strategy 2) (see revision 82d6b51 for details) for adjusting the cursor position, not only does current need to be null, but afterLast and beforeFirst need to be false (because if they are true, it's expected and correct that current == null). -This led me to notice that HierarchicalCollectionViewCursor.beforeFirst was true if current == null && currentIndex <= collection.length, which doesn't make sense. In fact, these conditions correctly determine the FLEX-34424 situation, in which the item at the current index doesn't exist anymore, which makes current null, although currentIndex is within bounds. (Another clue that the condition for beforeFirst is wrong is ListCollectionView.beforeFirst, which checks the index against -1, rather than allowing any index up to the collection length.) So I changed this to check for any value < 0. -I also made HierarchicalCollectionView.parentMap private instead of accessible from other classes (like HierarchicalCollectionViewCursor), and made the operations on it (deletions and additions) accessible only through functions, rather than using the map directly, as before. -Both the above changes also affected LeafNodeCursor, which also had a defective beforeFirst getter, and also directly manipulated the collection's parentMap. -Also removed unused variables from HierarchicalCollectionView.removeChild() and .collectionChangeHandler(). --- .../collections/HierarchicalCollectionView.as | 39 ++++++++++++------- .../HierarchicalCollectionViewCursor.as | 21 +++------- .../src/mx/collections/LeafNodeCursor.as | 7 ++-- ...calCollectionViewCursor_FLEX_34456_Test.as | 3 -- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as index 40ba8c39b7..6d4901c147 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionView.as @@ -139,7 +139,7 @@ public class HierarchicalCollectionView extends EventDispatcher * Mapping of UID to parents. Must be maintained as things get removed/added * This map is created as objects are visited */ - mx_internal var parentMap:Object; + private var parentMap:Object; //-------------------------------------------------------------------------- // @@ -536,6 +536,17 @@ public class HierarchicalCollectionView extends EventDispatcher return undefined; } + + public function deleteParentMapping(uid:String):void + { + delete parentMap[uid]; + } + + public function addParentMapping(uid:String, parent:Object, replaceExisting:Boolean = true):void + { + if(replaceExisting || !parentMap.hasOwnProperty(uid)) + parentMap[uid] = parent; + } /** * @inheritDoc @@ -677,7 +688,7 @@ public class HierarchicalCollectionView extends EventDispatcher while (!cursor.afterLast) { var uid:String = UIDUtil.getUID(cursor.current); - delete parentMap[uid]; + deleteParentMapping(uid); try { @@ -722,7 +733,6 @@ public class HierarchicalCollectionView extends EventDispatcher public function removeChild(parent:Object, child:Object):Boolean { var cursor:IViewCursor; - var index:int = 0; if (parent == null) { cursor = treeData.createCursor(); @@ -1007,7 +1017,7 @@ public class HierarchicalCollectionView extends EventDispatcher { var item:Object = cursor.current; var uid:String = UIDUtil.getUID(item); - parentMap[uid] = node; + addParentMapping(uid, node); // check that the node is opened or not. // If it is open, then update the length with the node's children. @@ -1091,7 +1101,7 @@ public class HierarchicalCollectionView extends EventDispatcher else { var uid:String = UIDUtil.getUID(node); - parentMap[uid] = parent; + addParentMapping(uid, parent); if (node != null && openNodes[uid] && hierarchicalData.canHaveChildren(node) && @@ -1303,7 +1313,7 @@ public class HierarchicalCollectionView extends EventDispatcher nodeArray.push(node); var uid:String = UIDUtil.getUID(node); - parentMap[uid] = parent; + addParentMapping(uid, parent); if (openNodes[uid] != null && hierarchicalData.canHaveChildren(node) && hierarchicalData.hasChildren(node)) @@ -1422,11 +1432,7 @@ public class HierarchicalCollectionView extends EventDispatcher { var i:int; var n:int; - var location:int; - var uid:String; - var parent:Object; var node:Object; - var items:Array; var convertedEvent:CollectionEvent; @@ -1482,9 +1488,16 @@ public class HierarchicalCollectionView extends EventDispatcher stopTrackUpdates(node); getVisibleNodes(node, null, convertedEvent.items); } + currentLength -= convertedEvent.items.length; + dispatchEvent(convertedEvent); - + + n = convertedEvent.items.length; + for (i = 0; i < n; i++) + { + deleteParentMapping(UIDUtil.getUID(convertedEvent.items[i])); + } } else if (ce.kind == CollectionEventKind.UPDATE) { @@ -1629,7 +1642,7 @@ public class HierarchicalCollectionView extends EventDispatcher // if the parent node is opened if (_openNodes[UIDUtil.getUID(parentOfChangingNode)] != null) { - parentMap[UIDUtil.getUID(ce.items[i].newValue)] = parentOfChangingNode; + addParentMapping(UIDUtil.getUID(ce.items[i].newValue), parentOfChangingNode); } } } @@ -1659,7 +1672,7 @@ public class HierarchicalCollectionView extends EventDispatcher parentOfChangingNode = getParentItem(changingNode); if (parentOfChangingNode != null && _openNodes[UIDUtil.getUID(parentOfChangingNode)] != null) - delete parentMap[UIDUtil.getUID(changingNode)]; + deleteParentMapping(UIDUtil.getUID(changingNode)); } } else if (ce.kind == CollectionEventKind.RESET) diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as index 954d94877b..ef89479c47 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as @@ -265,7 +265,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher */ public function get beforeFirst():Boolean { - return (currentIndex <= collection.length && current == null); + return currentIndex < 0 && current == null; } //---------------------------------- @@ -281,7 +281,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher */ public function get afterLast():Boolean { - return (currentIndex >= collection.length && current == null); + return currentIndex >= collection.length && current == null; } //---------------------------------- @@ -755,7 +755,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher updateParentMap(currentNode); - currentIndex--; + currentIndex--; return true; } @@ -878,7 +878,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher public function insert(item:Object):void { var parent:Object = collection.getParentItem(current); - collection.addChildAt(parent, item, currentIndex); + collection.addChildAt(parent, item, currentIndex); } /** @@ -923,8 +923,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher if (currentNode != null) { var uid:String = UIDUtil.getUID(currentNode); - if (!collection.parentMap.hasOwnProperty(uid)) - collection.parentMap[uid] = parentNodes[parentNodes.length - 1]; + collection.addParentMapping(uid, parentNodes[parentNodes.length - 1], false); } } @@ -1227,7 +1226,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher { var lastIndexAffectedByDeletion:int = event.location + n; var isCurrentIndexAmongRemovedNodes:Boolean = lastIndexAffectedByDeletion >= currentIndex; - var currentItemNotFoundAmongItsSiblings:Boolean = isCurrentIndexAmongRemovedNodes ? false : current == null; + var currentItemNotFoundAmongItsSiblings:Boolean = isCurrentIndexAmongRemovedNodes ? false : (!afterLast && !beforeFirst && current == null); if (isCurrentIndexAmongRemovedNodes || currentItemNotFoundAmongItsSiblings) { @@ -1237,10 +1236,6 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher var indexToReturnTo:int = isCurrentIndexAmongRemovedNodes ? event.location : currentIndex - n; moveToFirst(); seek(CursorBookmark.FIRST, indexToReturnTo); - for (i = 0; i < n; i++) - { - delete collection.parentMap[UIDUtil.getUID(event.items[i])]; - } return; } @@ -1303,10 +1298,7 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } } } - - delete collection.parentMap[UIDUtil.getUID(changingNode)]; } - } else if (event.kind == CollectionEventKind.RESET) { @@ -1326,5 +1318,4 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher } } } - } diff --git a/frameworks/projects/advancedgrids/src/mx/collections/LeafNodeCursor.as b/frameworks/projects/advancedgrids/src/mx/collections/LeafNodeCursor.as index 143e44f9d5..0703b78fef 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/LeafNodeCursor.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/LeafNodeCursor.as @@ -236,7 +236,7 @@ public class LeafNodeCursor extends EventDispatcher */ public function get beforeFirst():Boolean { - return (currentIndex <= collection.length && current == null); + return currentIndex < 0 && current == null; } //---------------------------------- @@ -247,7 +247,7 @@ public class LeafNodeCursor extends EventDispatcher */ public function get afterLast():Boolean { - return (currentIndex >= collection.length && current == null); + return currentIndex >= collection.length && current == null; } //---------------------------------- @@ -304,8 +304,7 @@ public class LeafNodeCursor extends EventDispatcher return false; var uid:String = UIDUtil.getUID(currentNode); - if (!collection.parentMap.hasOwnProperty(uid)) - collection.parentMap[uid] = parentNodes[parentNodes.length - 1]; + collection.addParentMapping(uid, parentNodes[parentNodes.length - 1], false); var flag:Boolean = true; // If current node is a branch and is open, the first child is our next item so return it diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34456_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34456_Test.as index 5682c7fff3..21275976b2 100644 --- a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34456_Test.as +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34456_Test.as @@ -49,7 +49,6 @@ package mx.collections _currentHierarchy = _utils.clone(_generatedHierarchy); _utils.openAllNodes(_currentHierarchy); _sut = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; - _sut.name = "_sut"; } [After] @@ -76,7 +75,6 @@ package mx.collections //2. Perform operation _operationCursor = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; - _operationCursor.name = "_operationCursor"; _operationCursor.seek(new CursorBookmark(operationIndex)); if (operation == OP_ADD) @@ -89,7 +87,6 @@ package mx.collections //3. Create mirror HierarchicalCollectionView from the changed root, as the source of truth _mirrorCursor = _utils.navigateToItem(_currentHierarchy.createCursor() as HierarchicalCollectionViewCursor, selectedNode) as HierarchicalCollectionViewCursor; - _mirrorCursor.name = "_mirrorCursor"; //4. Navigate somewhere in both HierarchicalCollectionViews and make sure they do the same thing _sut.moveNext(); From 979c7a8f69fb1f4697c11874e94220fe5c2106aa Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Fri, 8 Aug 2014 15:44:21 +0100 Subject: [PATCH 061/392] FLEX-34458 Added a unit test which reproduces the bug (which means it currently fails). --- ...calCollectionViewCursor_FLEX_34458_Test.as | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34458_Test.as diff --git a/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34458_Test.as b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34458_Test.as new file mode 100644 index 0000000000..e87529117f --- /dev/null +++ b/frameworks/tests/unitTests/mx/collections/HierarchicalCollectionViewCursor_FLEX_34458_Test.as @@ -0,0 +1,111 @@ +package mx.collections +{ +import flash.events.UncaughtErrorEvent; + +import mx.collections.ArrayCollection; +import mx.collections.CursorBookmark; +import mx.collections.HierarchicalCollectionView; +import mx.collections.HierarchicalCollectionViewCursor; +import mx.core.FlexGlobals; + + import org.flexunit.asserts.assertEquals; + + import org.flexunit.asserts.assertNotNull; +import org.flexunit.asserts.assertTrue; +import org.flexunit.runners.Parameterized; + +import spark.components.WindowedApplication; + +public class HierarchicalCollectionViewCursor_FLEX_34458_Test + { + private static var _utils:HierarchicalCollectionViewTestUtils = new HierarchicalCollectionViewTestUtils(); + private static var _noErrorsThrown:Boolean = true; + private static var _currentHierarchy:HierarchicalCollectionView; + private static var _sut:HierarchicalCollectionViewCursor; + private static var _operationCursor:HierarchicalCollectionViewCursor; + + [BeforeClass] + public static function setUpBeforeClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtClientError); + } + + [AfterClass] + public static function tearDownAfterClass():void + { + (FlexGlobals.topLevelApplication as WindowedApplication).loaderInfo.uncaughtErrorEvents.removeEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtClientError); + } + + [Before] + public function setUp():void + { + _currentHierarchy = _utils.generateOpenHierarchyFromRootListWithAllNodesMethod(_utils.generateHierarchySourceFromString(HIERARCHY_STRING)); + _sut = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + } + + [After] + public function tearDown():void + { + _sut = null; + _currentHierarchy = null; + _operationCursor = null; + } + + [Test] + public function testReproduce_FLEX_34458():void + { + //WHEN + //1. Select a specific node + _sut.moveToLast(); + _sut.movePrevious(); //Region(2)->City(3), with currentChildBookmark == CursorBookmark.LAST + + var selectedNode:DataNode = DataNode(_sut.current); + assertNotNull(selectedNode); + selectedNode.isSelected = true; + + //2. Remove previous region + _operationCursor = _currentHierarchy.createCursor() as HierarchicalCollectionViewCursor; + _operationCursor.seek(CursorBookmark.FIRST, 3); //Region(2)->City(1) + performRemoval(_operationCursor); + + //THEN + assertTrue(_noErrorsThrown); + assertEquals(selectedNode, _sut.current); + } + + + private static function performRemoval(where:HierarchicalCollectionViewCursor):void + { + var itemToBeRemoved:DataNode = where.current as DataNode; + assertNotNull(itemToBeRemoved); + + var parentOfReplacementLocation:DataNode = _currentHierarchy.getParentItem(itemToBeRemoved) as DataNode; + var collectionToChange:ArrayCollection = parentOfReplacementLocation ? parentOfReplacementLocation.children : _utils.getRoot(_currentHierarchy) as ArrayCollection; + var removedItemIndex:int = collectionToChange.getItemIndex(itemToBeRemoved); + collectionToChange.removeItemAt(removedItemIndex); + } + + + + private static function onUncaughtClientError(event:UncaughtErrorEvent):void + { + event.preventDefault(); + event.stopImmediatePropagation(); + _noErrorsThrown = false; + + trace("\n FAIL: " + event.error); + _utils.printHCollectionView(_currentHierarchy); + } + + + private static const HIERARCHY_STRING:String = (City(0) + Region(2)->City(1) + Region(2)->City(2) + Region(2)->City(3) + Region(3) + ]]>).toString(); + } +} \ No newline at end of file From 2f1b76827763b32988b1a1ccc73e603d9d7d788a Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Fri, 8 Aug 2014 15:54:05 +0100 Subject: [PATCH 062/392] FLEX-34458 CAUSE: Because HierarchicalCollectionViewCursor uses both relative and absolute bookmarks in the various collections of the hierarchy, there needs to be special treatment for these two cases. Namely, while it works great with absolute bookmarks to call .movePrevious on the bookmark when something is deleted in the same collection before the selected item, it doesn't work well for the relative CursorBookmark.LAST currentChildBookmark value. Because HierarchicalCollectionViewCursor.collectionChangeHandler() did not make any difference between absolute and relative bookmarks, it used to call .movePrevious() on the bookmark even if it was the absolute value CursorBookmark.LAST. SOLUTION: In this situation we don't need to .movePrevious() when items above are deleted, because CursorBookmark.LAST will always point to the last item in the collection, no matter what changes happened to that collection. --- .../src/mx/collections/HierarchicalCollectionViewCursor.as | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as index ef89479c47..1f3244dab6 100644 --- a/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as +++ b/frameworks/projects/advancedgrids/src/mx/collections/HierarchicalCollectionViewCursor.as @@ -1261,6 +1261,9 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher if (parentOfChangingNode == parentOfCurrentNode) { + if(currentChildBookmark == CursorBookmark.LAST) + break; + if (changingNodeAndSiblings != null) { changingCollectionCursor = changingNodeAndSiblings.createCursor(); @@ -1285,6 +1288,10 @@ public class HierarchicalCollectionViewCursor extends EventDispatcher changingNodeCollectionBookmarkIndex = parentOfChangingNodeIndex + 1; changingCollectionCursor = changingNodeAndSiblings.createCursor(); bookmarkInChangingCollection = parentBookmarkStack[changingNodeCollectionBookmarkIndex]; + + if(bookmarkInChangingCollection == CursorBookmark.LAST) + break; + try { changingCollectionCursor.seek(bookmarkInChangingCollection); From af1749fa4c61ffab913105d7158e1b5ddc947b78 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Mon, 11 Aug 2014 22:43:16 -0700 Subject: [PATCH 063/392] add signing targets --- build.xml | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/build.xml b/build.xml index 27395f6f20..8acac59b83 100644 --- a/build.xml +++ b/build.xml @@ -1875,6 +1875,55 @@ There are no known issues. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b88b3452a1029b7966d1f91f9f0b4942423ac529 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Tue, 12 Aug 2014 12:52:05 -0700 Subject: [PATCH 064/392] ant script to generate release candidates for the sdk --- releasecandidate.xml | 463 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 releasecandidate.xml diff --git a/releasecandidate.xml b/releasecandidate.xml new file mode 100644 index 0000000000..10ac156ad3 --- /dev/null +++ b/releasecandidate.xml @@ -0,0 +1,463 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + latest.installer=${release.installer} + + + + + + + + + latest installer is ${latest.installer} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${apache.username} + ${apache.password} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + discuss.thread.body=This is the discussion thread.\n\ +\n\ +Changes in this RC include:\n\ +\n\ +\n\ +Thanks,\n\ +${my.name} + + + + + + + + + vote.thread.body=Hi,\n\ +\n\ +\n\ +This is a Apache Flex SDK ${release.version} release candidate ${rc}.\n\ +Please see the RELEASE_NOTES and the README.\n\ +\n\ +There is a known bug in Adobe Flash Builder 4.6 and 4.7 that causes an issue when creating new applications. Adobe has provided a workaround for FB 4.7 but not for 4.6.\n\ +\n\ +The release candidate can be found here;\n\ + https://dist.apache.org/repos/dist/dev/flex/sdk/${release.version}/rc${rc}/\n\ +\n\ +The binary distributions as a convenience for the respective platforms, are available here:\n\ + https://dist.apache.org/repos/dist/dev/flex/sdk/${release.version}/rc${rc}/binaries\n\ +\n\ +\n\ +The most convenient way to use the binary package is to install it via Ant or the Installer. To use an Installer you must use InstallApacheFlex version 3.1 or later. You can get the latest InstallApacheFlex here:\n\ + http://www.apache.org/dyn/closer.cgi?path=/flex/installer/${latest.installer}/binaries/\n\ +\n\ +\n\ +Before voting please review the section, 'What are the ASF requirements on approving a release?', at:\n\ + http://www.apache.org/dev/release.html#approving-a-release\n\ +\n\ +At a minimum you would be expected to check that:\n\ + - MD5 and signed packages are correct\n\ + - README, RELEASE_NOTES, NOTICE and LICENSE files are all fine\n\ + - That you can compile from source package\n\ + - That the SDK can be used in your IDE of choice\n\ + - That the SDK can be used to make a mobile, desktop and browser application\n\ +\n\ +When testing please check the md5 and asc files and make sure that the source can be compiled.\n\ +\n\ +Please vote to approve this release:\n\ + +1 Approve the release\n\ + -1 Veto the release (please provide specific comments to why)\n\ +\n\ +This vote will be open for 72 hours or until a result can be called.\n\ +\n\ +The vote passes if there is:\n\ + - At least 3 +1 votes from the PMC\n\ + - More positive votes than negative votes\n\ +\n\ +If you find an issue with the release that's a 'show stopper' please don't hold off voting -1. If someone votes -1 please continue testing as we want to try and catch as many issues as we can and cut down on the number of release candidates. Remember existing voters can change their vote during the voting process.\n\ +\n\ +People who are not in PMC are also encouraged to test out the release and vote, although their votes will not be binding, they can influence how the PMC votes.\n\ +\n\ +When voting please indicate what OS, IDE, Flash Player version and AIR version you tested the SDK with.\n\ +\n\ +Please put all discussion about this release in the DISCUSSION thread not this VOTE thread.\n\ +\n\ +For your convenience, there issues an Ant script in the RC folder that automates the checks a voter should perform on the RC. It will:\n\ + - download the default source package for your OS (use -Dpackage.suffix to override)\n\ + - run md5 and gpg checks (this assumes you have gpg installed in your path)\n\ + - uncompress the source package\n\ + - install and run rat\n\ + - display the rat report and ask you to verify\n\ + - display the rat report without the AL files and ask you to verify the non-AL files\n\ + - display the README and ask you to verify\n\ + - display the RELEASE_NOTES and ask you to verify\n\ + - display the NOTICE and ask you to verify\n\ + - display the LICENSE and ask you to verify\n\ + - run the build and any tests run by the build.\n\ +\n\ +\n\ +You should be able to go away for a while during the build. When it is finished, it will display information to be copied into the vote thread.\n\ +You are not required to use this script, and more testing of the packages and build results are always encouraged.\n\ +\n\ +To use this script (assuming you have Ant installed), it should be as simple as:\n\ + - Create an empty folder\n\ + - Download ApproveSDK.xml into that folder from:\n\ + https://dist.apache.org/repos/dist/dev/flex/sdk/${release.version}/rc${rc}/ApproveSDK.xml\n\ + - Run: ant -e -f ApproveSDK.xml -Drelease.version=${release.version} -Drc=${rc}\n\ +\n\ +\n\ +Thanks,\n\ +${my.name} + + + + + + + <html><body><a href="mailto:dev@flex.apache.org?subject=[DISCUSS]%20Discuss%20Release%20Apache%20Flex%20SDK%20${release.version}%20RC${rc}&body=${discuss.thread.body}">Click here to review discuss thread</a><br/><a href="mailto:dev@flex.apache.org?subject=[VOTE]%20Release%20Apache%20Flex%20SDK%20${release.version}%20RC${rc}&body=${vote.thread.body}">Click here to review vote thread</a></body></html> + + + + + + + + + + From 96c064e6b9ef591d6acff136f22182d3af868edf Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Tue, 12 Aug 2014 13:09:41 -0700 Subject: [PATCH 065/392] switch version to 4.14 --- build.properties | 4 ++-- build/set-version.xml | 10 +++++----- .../advancedgrids/src/mx/core/Version.as | 2 +- .../projects/airframework/src/mx/core/Version.as | 2 +- .../projects/airspark/src/spark/core/Version.as | 2 +- frameworks/projects/apache/src/core/Version.as | 2 +- .../projects/automation/src/mx/core/Version.as | 2 +- .../automation_agent/src/mx/core/Version.as | 2 +- .../automation_air/src/mx/core/Version.as | 2 +- .../automation_dmv/src/mx/core/Version.as | 2 +- .../src/mx/core/Version.as | 2 +- .../automation_spark/src/mx/core/Version.as | 2 +- .../automation_spark/src/spark/core/Version.as | 2 +- .../projects/charts/src/mx/core/Version.as | 2 +- .../framework/src/mx/core/FlexVersion.as | 16 +++++++++++++++- .../projects/framework/src/mx/core/Version.as | 2 +- frameworks/projects/mx/src/mx/core/Version.as | 2 +- frameworks/projects/rpc/src/mx/core/Version.as | 2 +- .../projects/spark/src/spark/core/Version.as | 2 +- .../projects/spark_dmv/src/spark/core/Version.as | 2 +- frameworks/projects/tool/src/mx/core/Version.as | 2 +- .../projects/tool_air/src/mx/core/Version.as | 2 +- .../flex2/compiler/common/MxmlConfiguration.java | 5 +++-- .../src/java/flex2/tools/VersionInfo.java | 2 +- 24 files changed, 45 insertions(+), 30 deletions(-) diff --git a/build.properties b/build.properties index 4303f2ab7b..35fd0734e6 100644 --- a/build.properties +++ b/build.properties @@ -18,8 +18,8 @@ ################################################################################ # flex-sdk-description values -release = Apache Flex 4.13.0 -release.version = 4.13.0 +release = Apache Flex 4.14.0 +release.version = 4.14.0 # override on command line with -Dbuild.number=999 or in local.properties build.number = 0 diff --git a/build/set-version.xml b/build/set-version.xml index 1841e0ab4d..bbd83e3b5a 100644 --- a/build/set-version.xml +++ b/build/set-version.xml @@ -102,7 +102,7 @@ with latest version info ${major} ${minor} ${nano} group="3" property="MAJOR"/> - + @@ -130,11 +130,11 @@ update ${FLEX_HOME}/build.properties release.version to ${major}.${minor}.${nano - + - + @@ -148,7 +148,7 @@ update ${FLEX_HOME}/modules/compiler/src/java/flex2/tools/VersionInfo.java with - + diff --git a/frameworks/projects/advancedgrids/src/mx/core/Version.as b/frameworks/projects/advancedgrids/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/advancedgrids/src/mx/core/Version.as +++ b/frameworks/projects/advancedgrids/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/airframework/src/mx/core/Version.as b/frameworks/projects/airframework/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/airframework/src/mx/core/Version.as +++ b/frameworks/projects/airframework/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/airspark/src/spark/core/Version.as b/frameworks/projects/airspark/src/spark/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/airspark/src/spark/core/Version.as +++ b/frameworks/projects/airspark/src/spark/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/apache/src/core/Version.as b/frameworks/projects/apache/src/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/apache/src/core/Version.as +++ b/frameworks/projects/apache/src/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/automation/src/mx/core/Version.as b/frameworks/projects/automation/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/automation/src/mx/core/Version.as +++ b/frameworks/projects/automation/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/automation_agent/src/mx/core/Version.as b/frameworks/projects/automation_agent/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/automation_agent/src/mx/core/Version.as +++ b/frameworks/projects/automation_agent/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/automation_air/src/mx/core/Version.as b/frameworks/projects/automation_air/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/automation_air/src/mx/core/Version.as +++ b/frameworks/projects/automation_air/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/automation_dmv/src/mx/core/Version.as b/frameworks/projects/automation_dmv/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/automation_dmv/src/mx/core/Version.as +++ b/frameworks/projects/automation_dmv/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/automation_flashflexkit/src/mx/core/Version.as b/frameworks/projects/automation_flashflexkit/src/mx/core/Version.as index f261fc5e7a..8d74c4e6cd 100644 --- a/frameworks/projects/automation_flashflexkit/src/mx/core/Version.as +++ b/frameworks/projects/automation_flashflexkit/src/mx/core/Version.as @@ -24,4 +24,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/automation_spark/src/mx/core/Version.as b/frameworks/projects/automation_spark/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/automation_spark/src/mx/core/Version.as +++ b/frameworks/projects/automation_spark/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/automation_spark/src/spark/core/Version.as b/frameworks/projects/automation_spark/src/spark/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/automation_spark/src/spark/core/Version.as +++ b/frameworks/projects/automation_spark/src/spark/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/charts/src/mx/core/Version.as b/frameworks/projects/charts/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/charts/src/mx/core/Version.as +++ b/frameworks/projects/charts/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/framework/src/mx/core/FlexVersion.as b/frameworks/projects/framework/src/mx/core/FlexVersion.as index 41c63fcb1c..f99b634ab4 100644 --- a/frameworks/projects/framework/src/mx/core/FlexVersion.as +++ b/frameworks/projects/framework/src/mx/core/FlexVersion.as @@ -56,8 +56,22 @@ public class FlexVersion * @playerversion AIR 1.1 * @productversion Flex 3 */ - public static const CURRENT_VERSION:uint = 0x040D0000; + public static const CURRENT_VERSION:uint = 0x040E0000; + /** + * The compatibilityVersion value of Flex 4.14, + * encoded numerically as a uint. + * Code can compare this constant against + * the compatibilityVersion + * to implement version-specific behavior. + * + * @langversion 3.0 + * @playerversion Flash 11 + * @playerversion AIR 3 + * @productversion Apache Flex 4.14 + */ + public static const VERSION_4_14:uint = 0x040E0000; + /** * The compatibilityVersion value of Flex 4.13, * encoded numerically as a uint. diff --git a/frameworks/projects/framework/src/mx/core/Version.as b/frameworks/projects/framework/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/framework/src/mx/core/Version.as +++ b/frameworks/projects/framework/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/mx/src/mx/core/Version.as b/frameworks/projects/mx/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/mx/src/mx/core/Version.as +++ b/frameworks/projects/mx/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/rpc/src/mx/core/Version.as b/frameworks/projects/rpc/src/mx/core/Version.as index 9191675da0..496ab63000 100644 --- a/frameworks/projects/rpc/src/mx/core/Version.as +++ b/frameworks/projects/rpc/src/mx/core/Version.as @@ -22,4 +22,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/spark/src/spark/core/Version.as b/frameworks/projects/spark/src/spark/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/spark/src/spark/core/Version.as +++ b/frameworks/projects/spark/src/spark/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/spark_dmv/src/spark/core/Version.as b/frameworks/projects/spark_dmv/src/spark/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/spark_dmv/src/spark/core/Version.as +++ b/frameworks/projects/spark_dmv/src/spark/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/tool/src/mx/core/Version.as b/frameworks/projects/tool/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/tool/src/mx/core/Version.as +++ b/frameworks/projects/tool/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/frameworks/projects/tool_air/src/mx/core/Version.as b/frameworks/projects/tool_air/src/mx/core/Version.as index 90b75ca4f7..647c056b8b 100644 --- a/frameworks/projects/tool_air/src/mx/core/Version.as +++ b/frameworks/projects/tool_air/src/mx/core/Version.as @@ -23,4 +23,4 @@ import mx.core.mx_internal; * @private * Version string for this class. */ -mx_internal static const VERSION:String = "4.13.0.0"; +mx_internal static const VERSION:String = "4.14.0.0"; diff --git a/modules/compiler/src/java/flex2/compiler/common/MxmlConfiguration.java b/modules/compiler/src/java/flex2/compiler/common/MxmlConfiguration.java index 6705d7b586..228a50bc92 100644 --- a/modules/compiler/src/java/flex2/compiler/common/MxmlConfiguration.java +++ b/modules/compiler/src/java/flex2/compiler/common/MxmlConfiguration.java @@ -48,6 +48,7 @@ public void setConfigPathResolver( ConfigurationPathResolver resolver ) // // 'compiler.mxml.compatibility-version' option // + public static final int VERSION_4_14 = 0x040e0000; public static final int VERSION_4_13 = 0x040d0000; public static final int VERSION_4_12_1 = 0x040c0001; public static final int VERSION_4_12 = 0x040c0000; @@ -62,10 +63,10 @@ public void setConfigPathResolver( ConfigurationPathResolver resolver ) public static final int VERSION_3_0 = 0x03000000; public static final int VERSION_2_0_1 = 0x02000001; public static final int VERSION_2_0 = 0x02000000; - public static final int CURRENT_VERSION = VERSION_4_13; + public static final int CURRENT_VERSION = VERSION_4_14; public static final int EARLIEST_MAJOR_VERSION = 3; public static final int LATEST_MAJOR_VERSION = 4; - public static final int LATEST_MINOR_VERSION = 13; + public static final int LATEST_MINOR_VERSION = 14; private int major = LATEST_MAJOR_VERSION; private int minor = LATEST_MINOR_VERSION; diff --git a/modules/compiler/src/java/flex2/tools/VersionInfo.java b/modules/compiler/src/java/flex2/tools/VersionInfo.java index fe87538bf0..8d7db5d991 100644 --- a/modules/compiler/src/java/flex2/tools/VersionInfo.java +++ b/modules/compiler/src/java/flex2/tools/VersionInfo.java @@ -32,7 +32,7 @@ public class VersionInfo { public static String FLEX_MAJOR_VERSION = "4"; - public static String FLEX_MINOR_VERSION = "13"; + public static String FLEX_MINOR_VERSION = "14"; public static String FLEX_NANO_VERSION = "0"; /** From cd88495aa92d42cfd332cd52f5c4e9aac371fcdc Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Tue, 12 Aug 2014 14:01:54 -0700 Subject: [PATCH 066/392] FLEX-34460 adt is missing in windows install --- installer.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/installer.xml b/installer.xml index 4c75227878..6097703899 100644 --- a/installer.xml +++ b/installer.xml @@ -380,8 +380,7 @@ - - + @@ -396,6 +395,8 @@ + + From 5a4ce0e091ad0a98f1fd4b8e37ecba5b4635e260 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Tue, 12 Aug 2014 14:39:02 -0700 Subject: [PATCH 067/392] sdk rcs have release notes in them --- releasecandidate.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/releasecandidate.xml b/releasecandidate.xml index 10ac156ad3..7ef29a48e8 100644 --- a/releasecandidate.xml +++ b/releasecandidate.xml @@ -236,6 +236,7 @@ + From f406e251aadbc89a984c39de043933a8bdbd57c7 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Tue, 12 Aug 2014 22:49:10 -0700 Subject: [PATCH 068/392] add release target to convert final rc --- releasecandidate.xml | 157 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 1 deletion(-) diff --git a/releasecandidate.xml b/releasecandidate.xml index 7ef29a48e8..723c4238d9 100644 --- a/releasecandidate.xml +++ b/releasecandidate.xml @@ -173,7 +173,7 @@ unless="rc"/> - + latest.installer=${release.installer} @@ -318,8 +318,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -461,4 +558,62 @@ ${my.name} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Wait at least 24 hours before running update-installer-config-release target + From 8ebd72f42e71d032c2207eea376cf371cdf9ec36 Mon Sep 17 00:00:00 2001 From: Om Date: Wed, 13 Aug 2014 01:44:56 -0700 Subject: [PATCH 069/392] Adding a mixin to set android os version during AIR simulation Updated android4x.css with os-version media queries. --- .../AndroidVersionMediaQueryParserOverride.as | 23 + .../framework/src/mx/utils/Platform.as | 14 +- .../projects/mobiletheme/src/android4x.css | 504 +++++++++--------- 3 files changed, 289 insertions(+), 252 deletions(-) create mode 100644 frameworks/projects/framework/src/mx/utils/AndroidVersionMediaQueryParserOverride.as diff --git a/frameworks/projects/framework/src/mx/utils/AndroidVersionMediaQueryParserOverride.as b/frameworks/projects/framework/src/mx/utils/AndroidVersionMediaQueryParserOverride.as new file mode 100644 index 0000000000..ba16a2d5b8 --- /dev/null +++ b/frameworks/projects/framework/src/mx/utils/AndroidVersionMediaQueryParserOverride.as @@ -0,0 +1,23 @@ +package mx.utils +{ + import flash.display.DisplayObject; + import flash.system.Capabilities; + + import mx.core.mx_internal; + + [Mixin] + public class AndroidVersionMediaQueryParserOverride + { + public static function init(root:DisplayObject):void + { + var c:Class = Capabilities; + //Set this override value on if we are + // a. on the AIR Simulator + // b. simulating Android + if(c.version.indexOf("AND") > -1 && c.manufacturer != "Android Linux") + { + Platform.mx_internal::androidVersionOverride = "4.1.2"; + } + } + } +} \ No newline at end of file diff --git a/frameworks/projects/framework/src/mx/utils/Platform.as b/frameworks/projects/framework/src/mx/utils/Platform.as index 9f3cd26797..6c404088a3 100644 --- a/frameworks/projects/framework/src/mx/utils/Platform.as +++ b/frameworks/projects/framework/src/mx/utils/Platform.as @@ -51,6 +51,11 @@ public class Platform protected static var _isBrowser:Boolean; protected static var _isAir:Boolean; private static var _osVersion: String = null; + /** + * This value is set from AndroidVersionMediaQueryParserOverride + * + */ + mx_internal static var androidVersionOverride:String; /** * Returns true if the application is running on IOS. @@ -230,7 +235,14 @@ public class Platform { //We needed to compute _osVersion later than getPlatforms, because it relies on resources that ready later if (_osVersion == null){ - _osVersion = computeOSVersionString(); + if(mx_internal::androidVersionOverride == null) + { + _osVersion = computeOSVersionString(); + } + else + { + _osVersion = mx_internal::androidVersionOverride; + } } return _osVersion; } diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css index 5fee06f1bb..3ec0f3d6b3 100644 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -1,21 +1,21 @@ /* - * - * 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. - * - */ +* +* 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. +* +*/ @namespace "library://ns.adobe.com/flex/spark"; @@ -38,236 +38,238 @@ application-dpi=240. embed-as-cff: false; } -global -{ - primaryAccentColor: #33B5E5; - fontFamily: RobotoRegular; - fontWeight: "normal"; - fontSize: 24; - color: #000000; - textShadowColor: #FFFFFF; - textShadowAlpha: 0; -} - -ActionBar -{ - chromeColor: #DEDEDD; - defaultButtonAppearance: normal; - skinClass: ClassReference("spark.skins.android4.ActionBarSkin"); - textShadowAlpha: 0; - textShadowColor: #000000; - paddingBottom: 1; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 1; -} - -ActionBar #titleDisplay -{ - color: #333333; - fontSize: 24; - fontFamily: RobotoBold; -} - -ActionBar ButtonBase -{ - color: #333333; - fontFamily: RobotoBold; - fontWeight: bold; -} - -ActionBar Group#actionGroup Button -{ - skinClass: ClassReference("spark.skins.android4.TransparentActionButtonSkin"); -} - -MobileBusyIndicator -{ - skinClass: ClassReference("spark.skins.android4.BusyIndicatorSkin"); -} - -Button -{ - fontFamily: RobotoBold; - fontWeight: "bold"; - skinClass: ClassReference("spark.skins.android4.ButtonSkin"); -} - -Button.emphasized -{ - skinClass: ClassReference("spark.skins.mobile.DefaultButtonSkin"); -} - -ButtonBar -{ - skinClass: ClassReference("spark.skins.android4.ButtonBarSkin"); - fontFamily: RobotoBold; - fontWeight: "bold"; -} - -Callout -{ - backgroundColor: #33B5E5; - contentBackgroundColor: #FFFFFF; - gap: 12; - skinClass: ClassReference("spark.skins.android4.CalloutSkin"); - borderThickness: 0; - borderColor: 0; -} - -Callout ViewNavigator ActionBar -{ - skinClass: ClassReference("spark.skins.mobile.CalloutActionBarSkin"); - defaultButtonAppearance: none; - paddingLeft: 0; - paddingRight: 0; -} - -Callout ViewNavigator ActionBar.beveled -{ - paddingLeft: 0; - paddingRight: 0; -} - -Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button -{ - skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); -} - -Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button.emphasized -{ - skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); -} - -Callout#viewNavigatorPopUp -{ - contentBackgroundAppearance: none; -} - -CheckBox -{ - skinClass: ClassReference("spark.skins.android4.CheckBoxSkin"); - chromeColor: #FFFFFF; -} - -HScrollBar -{ - skinClass: ClassReference("spark.skins.android4.HScrollBarSkin"); - thumbColor: #333333; -} - -HSlider -{ - skinClass: ClassReference("spark.skins.android4.HSliderSkin"); - fontFamily: RobotoBold; - fontWeight: bold; -} - -RadioButton -{ - skinClass: ClassReference("spark.skins.android4.RadioButtonSkin"); - chromeColor: #FFFFFF; -} - -SpinnerList -{ - skinClass: ClassReference("spark.skins.android4.SpinnerListSkin"); -} - -SpinnerListContainer -{ - skinClass: ClassReference("spark.skins.android4.SpinnerListContainerSkin"); -} - -SpinnerListItemRenderer -{ - paddingTop : 18; - paddingBottom : 18; - paddingLeft : 6; - paddingRight : 6; -} - -TabbedViewNavigator #tabBar -{ - fontFamily: RobotoRegular; - chromeColor: #484848; - color: #000000; - fontSize: 20; - fontWeight: normal; - iconPlacement: top; - interactionMode: mouse; - skinClass: ClassReference("spark.skins.android4.TabbedViewNavigatorTabBarSkin"); - textShadowAlpha: 0; - textShadowColor: #000000; -} - -TextInput -{ - skinClass: ClassReference("spark.skins.android4.StageTextInputSkin"); - contentBackgroundAlpha: 0; - focusColor: #33B5E5; - borderColor: #4e4e4e; - contentBackgroundColor: #DEDEDD; - contentBackgroundBorder: "flat"; - fontFamily: RobotoRegular; - selectionHighlighting: "never"; - focusEnabled:"false"; - focusThickness: 0; -} - -TextArea -{ - skinClass: ClassReference("spark.skins.android4.StageTextAreaSkin"); - contentBackgroundAlpha: 1; - contentBackgroundBorder: "rectangle"; - contentBackgroundColor: #DEDEDD; - focusColor: #33B5E5; - fontFamily: RobotoRegular; - borderColor: #4e4e4e; - selectionHighlighting: "never"; - focusEnabled:"false"; - focusThickness: 0; -} - -ToggleSwitch -{ - accentColor: #3F7FBA; - color: #FFFFFF; - skinClass: ClassReference("spark.skins.android4.ToggleSwitchSkin"); - slideDuration: 125; - textShadowAlpha: .65; - textShadowColor: #000000; -} - -VScrollBar -{ - skinClass: ClassReference("spark.skins.android4.VScrollBarSkin"); - thumbColor: #333333; -} - -ViewMenu -{ - skinClass: ClassReference("spark.skins.android4.ViewMenuSkin"); -} - -ViewMenuItem -{ - iconPlacement : "left"; - chromeColor : #FFFFFF; - focusColor: #DEDEDD; - paddingBottom: 8; - paddingLeft: 8; - paddingRight: 8; - paddingTop: 8; - skinClass : ClassReference("spark.skins.android4.ViewMenuItemSkin"); -} - -ViewNavigator -{ - skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); +@media (os-platform: "android") AND (min-os-version: "4.1.2") { + + global + { + primaryAccentColor: #33B5E5; + fontFamily: RobotoRegular; + fontWeight: "normal"; + fontSize: 24; + color: #000000; + textShadowColor: #FFFFFF; + textShadowAlpha: 0; + } + + ActionBar + { + chromeColor: #DEDEDD; + defaultButtonAppearance: normal; + skinClass: ClassReference("spark.skins.android4.ActionBarSkin"); + textShadowAlpha: 0; + textShadowColor: #000000; + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; + } + + ActionBar #titleDisplay + { + color: #333333; + fontSize: 24; + fontFamily: RobotoBold; + } + + ActionBar ButtonBase + { + color: #333333; + fontFamily: RobotoBold; + fontWeight: bold; + } + + ActionBar Group#actionGroup Button + { + skinClass: ClassReference("spark.skins.android4.TransparentActionButtonSkin"); + } + + MobileBusyIndicator + { + skinClass: ClassReference("spark.skins.android4.BusyIndicatorSkin"); + } + + Button + { + fontFamily: RobotoBold; + fontWeight: "bold"; + skinClass: ClassReference("spark.skins.android4.ButtonSkin"); + } + + Button.emphasized + { + skinClass: ClassReference("spark.skins.mobile.DefaultButtonSkin"); + } + + ButtonBar + { + skinClass: ClassReference("spark.skins.android4.ButtonBarSkin"); + fontFamily: RobotoBold; + fontWeight: "bold"; + } + + Callout + { + backgroundColor: #33B5E5; + contentBackgroundColor: #FFFFFF; + gap: 12; + skinClass: ClassReference("spark.skins.android4.CalloutSkin"); + borderThickness: 0; + borderColor: 0; + } + + Callout ViewNavigator ActionBar + { + skinClass: ClassReference("spark.skins.mobile.CalloutActionBarSkin"); + defaultButtonAppearance: none; + paddingLeft: 0; + paddingRight: 0; + } + + Callout ViewNavigator ActionBar.beveled + { + paddingLeft: 0; + paddingRight: 0; + } + + Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button + { + skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); + } + + Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button.emphasized + { + skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); + } + + Callout#viewNavigatorPopUp + { + contentBackgroundAppearance: none; + } + + CheckBox + { + skinClass: ClassReference("spark.skins.android4.CheckBoxSkin"); + chromeColor: #FFFFFF; + } + + HScrollBar + { + skinClass: ClassReference("spark.skins.android4.HScrollBarSkin"); + thumbColor: #333333; + } + + HSlider + { + skinClass: ClassReference("spark.skins.android4.HSliderSkin"); + fontFamily: RobotoBold; + fontWeight: bold; + } + + RadioButton + { + skinClass: ClassReference("spark.skins.android4.RadioButtonSkin"); + chromeColor: #FFFFFF; + } + + SpinnerList + { + skinClass: ClassReference("spark.skins.android4.SpinnerListSkin"); + } + + SpinnerListContainer + { + skinClass: ClassReference("spark.skins.android4.SpinnerListContainerSkin"); + } + + SpinnerListItemRenderer + { + paddingTop : 18; + paddingBottom : 18; + paddingLeft : 6; + paddingRight : 6; + } + + TabbedViewNavigator #tabBar + { + fontFamily: RobotoRegular; + chromeColor: #484848; + color: #000000; + fontSize: 20; + fontWeight: normal; + iconPlacement: top; + interactionMode: mouse; + skinClass: ClassReference("spark.skins.android4.TabbedViewNavigatorTabBarSkin"); + textShadowAlpha: 0; + textShadowColor: #000000; + } + + TextInput + { + skinClass: ClassReference("spark.skins.android4.StageTextInputSkin"); + contentBackgroundAlpha: 0; + focusColor: #33B5E5; + borderColor: #4e4e4e; + contentBackgroundColor: #DEDEDD; + contentBackgroundBorder: "flat"; + fontFamily: RobotoRegular; + selectionHighlighting: "never"; + focusEnabled:"false"; + focusThickness: 0; + } + + TextArea + { + skinClass: ClassReference("spark.skins.android4.StageTextAreaSkin"); + contentBackgroundAlpha: 1; + contentBackgroundBorder: "rectangle"; + contentBackgroundColor: #DEDEDD; + focusColor: #33B5E5; + fontFamily: RobotoRegular; + borderColor: #4e4e4e; + selectionHighlighting: "never"; + focusEnabled:"false"; + focusThickness: 0; + } + + ToggleSwitch + { + accentColor: #3F7FBA; + color: #FFFFFF; + skinClass: ClassReference("spark.skins.android4.ToggleSwitchSkin"); + slideDuration: 125; + textShadowAlpha: .65; + textShadowColor: #000000; + } + + VScrollBar + { + skinClass: ClassReference("spark.skins.android4.VScrollBarSkin"); + thumbColor: #333333; + } + + ViewMenu + { + skinClass: ClassReference("spark.skins.android4.ViewMenuSkin"); + } + + ViewMenuItem + { + iconPlacement : "left"; + chromeColor : #FFFFFF; + focusColor: #DEDEDD; + paddingBottom: 8; + paddingLeft: 8; + paddingRight: 8; + paddingTop: 8; + skinClass : ClassReference("spark.skins.android4.ViewMenuItemSkin"); + } + + ViewNavigator + { + skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); + } } - -@media (application-dpi: 120) +@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 120) { global { @@ -300,7 +302,7 @@ ViewNavigator } } -@media (application-dpi: 160) +@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 160) { global { @@ -335,7 +337,7 @@ ViewNavigator } } -@media (application-dpi: 320) +@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 320) { global { @@ -369,7 +371,7 @@ ViewNavigator } } -@media (application-dpi: 480) +@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 480) { global { @@ -397,7 +399,7 @@ ViewNavigator paddingLeft : 12; paddingRight : 12; } - + TabbedViewNavigator #tabBar { @@ -405,7 +407,7 @@ ViewNavigator } } -@media (application-dpi: 640) +@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 640) { global { From 0453c9dc4706c62244864ce995e903caef2fcc0f Mon Sep 17 00:00:00 2001 From: Om Date: Wed, 13 Aug 2014 01:51:22 -0700 Subject: [PATCH 070/392] Renaming class to AndroidPlatformVersionOverride as that makes more sense --- ...QueryParserOverride.as => AndroidPlatformVersionOverride.as} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename frameworks/projects/framework/src/mx/utils/{AndroidVersionMediaQueryParserOverride.as => AndroidPlatformVersionOverride.as} (89%) diff --git a/frameworks/projects/framework/src/mx/utils/AndroidVersionMediaQueryParserOverride.as b/frameworks/projects/framework/src/mx/utils/AndroidPlatformVersionOverride.as similarity index 89% rename from frameworks/projects/framework/src/mx/utils/AndroidVersionMediaQueryParserOverride.as rename to frameworks/projects/framework/src/mx/utils/AndroidPlatformVersionOverride.as index ba16a2d5b8..f32517ccd3 100644 --- a/frameworks/projects/framework/src/mx/utils/AndroidVersionMediaQueryParserOverride.as +++ b/frameworks/projects/framework/src/mx/utils/AndroidPlatformVersionOverride.as @@ -6,7 +6,7 @@ package mx.utils import mx.core.mx_internal; [Mixin] - public class AndroidVersionMediaQueryParserOverride + public class AndroidPlatformVersionOverride { public static function init(root:DisplayObject):void { From 0c0540b7f98b7cfbc34ea5e8170c95725f663b72 Mon Sep 17 00:00:00 2001 From: chrsmrtn- Date: Wed, 13 Aug 2014 15:07:43 -0700 Subject: [PATCH 071/392] My test commit to repo Let's make magic happen --- GIT-TEST.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GIT-TEST.txt b/GIT-TEST.txt index 842a9f5380..163a44e760 100644 --- a/GIT-TEST.txt +++ b/GIT-TEST.txt @@ -74,4 +74,6 @@ mamsellem git commit first try (2013/09/29) Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. (Brian Kernighan) -Testing, testing (jfernandes) \ No newline at end of file +Testing, testing (jfernandes) + +I'll just leave this here --chrsmrtn \ No newline at end of file From 2b46cb163f727996839a473a74de06567be5b4e6 Mon Sep 17 00:00:00 2001 From: chrsmrtn Date: Thu, 14 Aug 2014 08:13:05 -0700 Subject: [PATCH 072/392] another test commit, testing using my apache username on commit --- GIT-TEST.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GIT-TEST.txt b/GIT-TEST.txt index 163a44e760..aff051833b 100644 --- a/GIT-TEST.txt +++ b/GIT-TEST.txt @@ -76,4 +76,4 @@ Debugging is twice as hard as writing the code in the first place. Therefore, if Testing, testing (jfernandes) -I'll just leave this here --chrsmrtn \ No newline at end of file +I'll just leave this here... --chrsmrtn \ No newline at end of file From 47a6ecf6fd3dd119f96523986326d54f845598f3 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Wed, 13 Aug 2014 22:14:02 -0700 Subject: [PATCH 073/392] remove temp target --- releasecandidate.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/releasecandidate.xml b/releasecandidate.xml index 723c4238d9..7dc1a86464 100644 --- a/releasecandidate.xml +++ b/releasecandidate.xml @@ -415,9 +415,6 @@ - - - @@ -425,7 +422,7 @@ - + From 78260ece8017a2739bc1f6b66d6cdff340fa7304 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Wed, 13 Aug 2014 22:46:48 -0700 Subject: [PATCH 074/392] fix missing slash --- releasecandidate.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasecandidate.xml b/releasecandidate.xml index 7dc1a86464..7597846703 100644 --- a/releasecandidate.xml +++ b/releasecandidate.xml @@ -356,7 +356,7 @@ - + From c03cf753daadd21eefea2ade7627f7e6eb3cae41 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Thu, 14 Aug 2014 22:07:06 -0700 Subject: [PATCH 075/392] FLEX-34034 handle degenerate case when copying single newline into TextInput --- .../projects/spark/src/spark/components/RichEditableText.as | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frameworks/projects/spark/src/spark/components/RichEditableText.as b/frameworks/projects/spark/src/spark/components/RichEditableText.as index ed5f26d089..283d5ea055 100644 --- a/frameworks/projects/spark/src/spark/components/RichEditableText.as +++ b/frameworks/projects/spark/src/spark/components/RichEditableText.as @@ -4767,6 +4767,9 @@ package spark.components TextClipboard.importToScrap(pastedText, TextConverter.PLAIN_TEXT_FORMAT); } + // don't do anything if nothing left on clipboard + if (pastedText.length == 0) + event.preventDefault(); } } else if (op is DeleteTextOperation || op is CutOperation) From 3748776a63b9971a7c6fd53d02ef060aa3947807 Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:38:37 -0700 Subject: [PATCH 076/392] Add AndroidPlatformVersionOverride to FrameworkClasses --- frameworks/projects/framework/src/FrameworkClasses.as | 1 + 1 file changed, 1 insertion(+) diff --git a/frameworks/projects/framework/src/FrameworkClasses.as b/frameworks/projects/framework/src/FrameworkClasses.as index cd6b13cbae..cb2a0939df 100644 --- a/frameworks/projects/framework/src/FrameworkClasses.as +++ b/frameworks/projects/framework/src/FrameworkClasses.as @@ -155,6 +155,7 @@ internal class FrameworkClasses import mx.styles.IStyleModule; IStyleModule; import mx.styles.AdvancedStyleClient; AdvancedStyleClient; import mx.utils.ArrayUtil; ArrayUtil; + import mx.utils.AndroidPlatformVersionOverride; AndroidPlatformVersionOverride; import mx.utils.Base64Decoder; Base64Decoder; import mx.utils.Base64Encoder; Base64Encoder; import mx.utils.BitFlagUtil; BitFlagUtil; From 210c6fd31dd393c8e046a49692cb440c54c0ba9a Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:40:20 -0700 Subject: [PATCH 077/392] Add android4 specific TextSkinBase --- .../android4/supportClasses/TextSkinBase.as | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/TextSkinBase.as diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/TextSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/TextSkinBase.as new file mode 100644 index 0000000000..31bf7eaa96 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/supportClasses/TextSkinBase.as @@ -0,0 +1,213 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.supportClasses +{ + + import flash.display.DisplayObject; + + import mx.core.mx_internal; + + import spark.components.supportClasses.StyleableTextField; + import spark.skins.mobile.supportClasses.MobileSkin; + + use namespace mx_internal; + + /** + * ActionScript-based skin for text input controls in mobile applications that + * uses a StyleableTextField class for the text display. + * + * @see spark.components.supportClasses.StyleableTextField + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class TextSkinBase extends MobileSkin + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function TextSkinBase() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Graphics variables + // + //-------------------------------------------------------------------------- + + /** + * Defines the border. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + + //-------------------------------------------------------------------------- + // + // Layout variables + // + //-------------------------------------------------------------------------- + + /** + * Defines the corner radius. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + + protected var layoutBorderSize:uint; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * + * Instance of the border graphics. + */ + protected var border:DisplayObject; + + private var borderVisibleChanged:Boolean = false; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * textDisplay skin part. + */ + public var textDisplay:StyleableTextField; + + [Bindable] + /** + * Bindable promptDisplay skin part. Bindings fire when promptDisplay is + * removed and added for proper updating by the SkinnableTextBase. + */ + public var promptDisplay:StyleableTextField; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (!textDisplay) + { + textDisplay = StyleableTextField(createInFontContext(StyleableTextField)); + textDisplay.styleName = this; + textDisplay.editable = true; + textDisplay.useTightTextBounds = false; + addChild(textDisplay); + } + } + + /** + * @private + */ + protected function createPromptDisplay():StyleableTextField + { + var prompt:StyleableTextField = StyleableTextField(createInFontContext(StyleableTextField)); + prompt.styleName = this; + prompt.editable = false; + prompt.mouseEnabled = false; + prompt.useTightTextBounds = false; + prompt.focusEnabled = false; + return prompt; + } + + /** + * @private + */ + + override public function styleChanged(styleProp:String):void + { + var allStyles:Boolean = !styleProp || styleProp == "styleName"; + + if (allStyles || styleProp == "borderVisible") + { + borderVisibleChanged = true; + invalidateProperties(); + } + + if (allStyles || styleProp.indexOf("padding") == 0) + { + invalidateDisplayList(); + } + + super.styleChanged(styleProp); + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + alpha = currentState.indexOf("disabled") == -1 ? 1 : 0.5; + + var showPrompt:Boolean = currentState.indexOf("WithPrompt") >= 0; + + if (showPrompt && !promptDisplay) + { + promptDisplay = createPromptDisplay(); + addChild(promptDisplay); + } + else if (!showPrompt && promptDisplay) + { + removeChild(promptDisplay); + promptDisplay = null; + } + + invalidateDisplayList(); + } + } +} \ No newline at end of file From dca4680d87577951eb0e72ca4215330a4dd7df42 Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:41:02 -0700 Subject: [PATCH 078/392] Add Apache license header --- .../utils/AndroidPlatformVersionOverride.as | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/frameworks/projects/framework/src/mx/utils/AndroidPlatformVersionOverride.as b/frameworks/projects/framework/src/mx/utils/AndroidPlatformVersionOverride.as index f32517ccd3..b19e505342 100644 --- a/frameworks/projects/framework/src/mx/utils/AndroidPlatformVersionOverride.as +++ b/frameworks/projects/framework/src/mx/utils/AndroidPlatformVersionOverride.as @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.utils { import flash.display.DisplayObject; From a29d9651bd38f9266c609600e76fda90f4743ed9 Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:42:18 -0700 Subject: [PATCH 079/392] Fix package name of spark/components/MobileBusyIndicator.as Fix constructor name --- .../src/spark/components/MobileBusyIndicator.as | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frameworks/projects/mobilecomponents/src/spark/components/MobileBusyIndicator.as b/frameworks/projects/mobilecomponents/src/spark/components/MobileBusyIndicator.as index a25755797e..c9f22d1ea3 100644 --- a/frameworks/projects/mobilecomponents/src/spark/components/MobileBusyIndicator.as +++ b/frameworks/projects/mobilecomponents/src/spark/components/MobileBusyIndicator.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package components +package spark.components { import flash.events.Event; import mx.core.IUIComponent; @@ -34,7 +34,7 @@ package components private var effectiveVisibility:Boolean = false; private var effectiveVisibilityChanged:Boolean = true; - public function BusyIndicator() + public function MobileBusyIndicator() { super(); // Listen to added to stage and removed from stage. From 4d1a3c8e22288a7238d48ccb38cbf3d941a67ddc Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:43:07 -0700 Subject: [PATCH 080/392] Include android fonts --- .../projects/mobiletheme/.flexLibProperties | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/frameworks/projects/mobiletheme/.flexLibProperties b/frameworks/projects/mobiletheme/.flexLibProperties index 1627cbbb8c..97f6459c2e 100644 --- a/frameworks/projects/mobiletheme/.flexLibProperties +++ b/frameworks/projects/mobiletheme/.flexLibProperties @@ -1,26 +1,11 @@ - - + - + + + + From b306c4526f7506a7b9ac938616ad5bd651fe0d8d Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:44:05 -0700 Subject: [PATCH 081/392] Add Roboto fonts to compile-config.xml so that they will be included in mobiletheme.swc --- frameworks/projects/mobiletheme/compile-config.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frameworks/projects/mobiletheme/compile-config.xml b/frameworks/projects/mobiletheme/compile-config.xml index ec37add414..b788911a02 100644 --- a/frameworks/projects/mobiletheme/compile-config.xml +++ b/frameworks/projects/mobiletheme/compile-config.xml @@ -55,6 +55,14 @@ defaults.css defaults.css + + spark/skins/android4/assets/fonts/Roboto-Regular.ttf + src/spark/skins/android4/assets/fonts/Roboto-Regular.ttf + + + spark/skins/android4/assets/fonts/Roboto-Bold.ttf + src/spark/skins/android4/assets/fonts/Roboto-Bold.ttf + bundles.properties From 5b1431ffd20a35dbcf730d070a862aebaf085fa8 Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:45:19 -0700 Subject: [PATCH 082/392] Merge Android4.x skins into defaults.css First the default skins appear, then Android specific skins overwrite the default ones --- frameworks/projects/mobiletheme/defaults.css | 1183 ++++++++++++------ 1 file changed, 802 insertions(+), 381 deletions(-) diff --git a/frameworks/projects/mobiletheme/defaults.css b/frameworks/projects/mobiletheme/defaults.css index 66f7fb79be..7de40e3f7e 100644 --- a/frameworks/projects/mobiletheme/defaults.css +++ b/frameworks/projects/mobiletheme/defaults.css @@ -1,24 +1,25 @@ /* - * - * 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. - * - */ +* +* 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. +* +*/ @namespace "library://ns.adobe.com/flex/spark"; + /* Default Mobile Flex skins and styles. Defaults sizes are based on application-dpi=240. @@ -26,153 +27,153 @@ application-dpi=240. ActionBar { - chromeColor: #484848; - defaultButtonAppearance: normal; - skinClass: ClassReference("spark.skins.mobile.ActionBarSkin"); - textShadowAlpha: .65; - textShadowColor: #000000; - paddingBottom: 1; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 1; + chromeColor: #484848; + defaultButtonAppearance: normal; + skinClass: ClassReference("spark.skins.mobile.ActionBarSkin"); + textShadowAlpha: .65; + textShadowColor: #000000; + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; } ActionBar.beveled { - paddingLeft: 7; - paddingRight: 7; - titleAlign: center; + paddingLeft: 7; + paddingRight: 7; + titleAlign: center; } ActionBar #titleDisplay { - color: #FFFFFF; - fontSize: 28; - fontWeight: bold; + color: #FFFFFF; + fontSize: 28; + fontWeight: bold; } ActionBar ButtonBase { - color: #FFFFFF; + color: #FFFFFF; } ActionBar Group#navigationGroup Button { - skinClass: ClassReference("spark.skins.mobile.TransparentNavigationButtonSkin"); + skinClass: ClassReference("spark.skins.mobile.TransparentNavigationButtonSkin"); } ActionBar Group#navigationGroup Button.emphasized { - skinClass: ClassReference("spark.skins.mobile.DefaultTransparentNavigationButtonSkin"); + skinClass: ClassReference("spark.skins.mobile.DefaultTransparentNavigationButtonSkin"); } ActionBar.beveled Group#navigationGroup Button { - fontSize: 18; - skinClass: ClassReference("spark.skins.mobile.BeveledBackButtonSkin"); + fontSize: 18; + skinClass: ClassReference("spark.skins.mobile.BeveledBackButtonSkin"); } ActionBar.beveled Group#navigationGroup Button.emphasized { - skinClass: ClassReference("spark.skins.mobile.DefaultBeveledBackButtonSkin"); + skinClass: ClassReference("spark.skins.mobile.DefaultBeveledBackButtonSkin"); } ActionBar Group#actionGroup Button { - skinClass: ClassReference("spark.skins.mobile.TransparentActionButtonSkin"); + skinClass: ClassReference("spark.skins.mobile.TransparentActionButtonSkin"); } ActionBar Group#actionGroup Button.emphasized { - skinClass: ClassReference("spark.skins.mobile.DefaultTransparentActionButtonSkin"); + skinClass: ClassReference("spark.skins.mobile.DefaultTransparentActionButtonSkin"); } ActionBar.beveled Group#actionGroup Button { - fontSize: 18; - skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); + fontSize: 18; + skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); } ActionBar.beveled Group#actionGroup Button.emphasized { - skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); + skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); } BusyIndicator { - rotationInterval: 50; + rotationInterval: 50; } Button { - fontWeight: "bold"; - skinClass: ClassReference("spark.skins.mobile.ButtonSkin"); + fontWeight: "bold"; + skinClass: ClassReference("spark.skins.mobile.ButtonSkin"); } Button.emphasized { - skinClass: ClassReference("spark.skins.mobile.DefaultButtonSkin"); + skinClass: ClassReference("spark.skins.mobile.DefaultButtonSkin"); } ButtonBar { - fontWeight: "bold"; - skinClass: ClassReference("spark.skins.mobile.ButtonBarSkin"); + fontWeight: "bold"; + skinClass: ClassReference("spark.skins.mobile.ButtonBarSkin"); } ButtonBase { - iconPlacement: "left"; + iconPlacement: "left"; } Callout { - backgroundColor: #484848; - contentBackgroundAppearance: inset; - contentBackgroundColor: #FFFFFF; - gap: 12; - skinClass: ClassReference("spark.skins.mobile.CalloutSkin"); - borderThickness: NaN; - borderColor: 0; + backgroundColor: #484848; + contentBackgroundAppearance: inset; + contentBackgroundColor: #FFFFFF; + gap: 12; + skinClass: ClassReference("spark.skins.mobile.CalloutSkin"); + borderThickness: NaN; + borderColor: 0; } Callout ViewNavigator { - skinClass: ClassReference("spark.skins.mobile.CalloutViewNavigatorSkin"); + skinClass: ClassReference("spark.skins.mobile.CalloutViewNavigatorSkin"); } Callout ViewNavigator ActionBar { - skinClass: ClassReference("spark.skins.mobile.CalloutActionBarSkin"); - defaultButtonAppearance: beveled; - paddingLeft: 0; - paddingRight: 0; + skinClass: ClassReference("spark.skins.mobile.CalloutActionBarSkin"); + defaultButtonAppearance: beveled; + paddingLeft: 0; + paddingRight: 0; } Callout ViewNavigator ActionBar.beveled { - paddingLeft: 0; - paddingRight: 0; + paddingLeft: 0; + paddingRight: 0; } Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button { - skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); + skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); } Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button.emphasized { - skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); + skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); } Callout#viewNavigatorPopUp { - contentBackgroundAppearance: none; + contentBackgroundAppearance: none; } CheckBox { - skinClass: ClassReference("spark.skins.mobile.CheckBoxSkin"); + skinClass: ClassReference("spark.skins.mobile.CheckBoxSkin"); } DateSpinner @@ -182,80 +183,80 @@ DateSpinner HScrollBar { - skinClass: ClassReference("spark.skins.mobile.HScrollBarSkin"); + skinClass: ClassReference("spark.skins.mobile.HScrollBarSkin"); } HSlider { - skinClass: ClassReference("spark.skins.mobile.HSliderSkin"); + skinClass: ClassReference("spark.skins.mobile.HSliderSkin"); } IconItemRenderer { - paddingBottom: 12; - paddingTop: 12; - horizontalGap: 10; - verticalGap: 9; - iconDelay: 500; - messageStyleName: "iconItemRendererMessageStyle"; + paddingBottom: 12; + paddingTop: 12; + horizontalGap: 10; + verticalGap: 9; + iconDelay: 500; + messageStyleName: "iconItemRendererMessageStyle"; } .iconItemRendererMessageStyle { - fontSize: 20; - color: #333333; + fontSize: 20; + color: #333333; } Image { - skinClass: ClassReference("spark.skins.mobile.ImageSkin"); - enableLoadingState: false; - showErrorSkin : false; - smoothingQuality: "default"; + skinClass: ClassReference("spark.skins.mobile.ImageSkin"); + enableLoadingState: false; + showErrorSkin : false; + smoothingQuality: "default"; } LabelItemRenderer { - paddingBottom: 24; - paddingLeft: 15; - paddingRight: 15; - paddingTop: 24; - verticalAlign: "middle"; + paddingBottom: 24; + paddingLeft: 15; + paddingRight: 15; + paddingTop: 24; + verticalAlign: "middle"; } List { - skinClass: ClassReference("spark.skins.mobile.ListSkin"); - borderColor: #000000; - borderAlpha: 1; - verticalScrollPolicy: on; - borderVisible: false; + skinClass: ClassReference("spark.skins.mobile.ListSkin"); + borderColor: #000000; + borderAlpha: 1; + verticalScrollPolicy: on; + borderVisible: false; } RadioButton { - skinClass: ClassReference("spark.skins.mobile.RadioButtonSkin"); + skinClass: ClassReference("spark.skins.mobile.RadioButtonSkin"); } Scroller { - touchDelay: 100; + touchDelay: 100; } SkinnableContainer { - backgroundColor: #FFFFFF; - skinClass: ClassReference("spark.skins.mobile.SkinnableContainerSkin"); + backgroundColor: #FFFFFF; + skinClass: ClassReference("spark.skins.mobile.SkinnableContainerSkin"); } SkinnableTextBase:normalWithPrompt { - fontStyle: normal; + fontStyle: normal; } SkinnableTextBase:disabledWithPrompt { - fontStyle: normal; + fontStyle: normal; } SpinnerList @@ -279,146 +280,146 @@ SpinnerListItemRenderer SplitViewNavigator { skinClass: ClassReference("spark.skins.mobile.SplitViewNavigatorSkin"); - backgroundColor: #000000; + backgroundColor: #000000; } TabbedViewNavigatorApplication { - backgroundColor: #FFFFFF; - skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorApplicationSkin"); + backgroundColor: #FFFFFF; + skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorApplicationSkin"); } TabbedViewNavigator { - skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorSkin"); + skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorSkin"); } TabbedViewNavigator #tabBar { - chromeColor: #484848; - color: #FFFFFF; - fontSize: 20; - fontWeight: normal; - iconPlacement: top; - interactionMode: mouse; - skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorTabBarSkin"); - textShadowAlpha: .65; - textShadowColor: #000000; + chromeColor: #484848; + color: #FFFFFF; + fontSize: 20; + fontWeight: normal; + iconPlacement: top; + interactionMode: mouse; + skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorTabBarSkin"); + textShadowAlpha: .65; + textShadowColor: #000000; } TextArea { - paddingBottom: 9; - paddingLeft: 9; - paddingRight: 9; - paddingTop: 9; - showPromptWhenFocused: true; - skinClass: ClassReference("spark.skins.mobile.ScrollingStageTextAreaSkin"); + paddingBottom: 9; + paddingLeft: 9; + paddingRight: 9; + paddingTop: 9; + showPromptWhenFocused: true; + skinClass: ClassReference("spark.skins.mobile.ScrollingStageTextAreaSkin"); } TextArea VScrollBar { - skinClass: ClassReference("spark.skins.mobile.TextAreaVScrollBarSkin"); + skinClass: ClassReference("spark.skins.mobile.TextAreaVScrollBarSkin"); } TextArea HScrollBar { - skinClass: ClassReference("spark.skins.mobile.TextAreaHScrollBarSkin"); + skinClass: ClassReference("spark.skins.mobile.TextAreaHScrollBarSkin"); } TextInput { - paddingBottom: 9; - paddingLeft: 9; - paddingRight: 9; - paddingTop: 9; - showPromptWhenFocused: true; - skinClass: ClassReference("spark.skins.mobile.ScrollingStageTextInputSkin"); + paddingBottom: 9; + paddingLeft: 9; + paddingRight: 9; + paddingTop: 9; + showPromptWhenFocused: true; + skinClass: ClassReference("spark.skins.mobile.ScrollingStageTextInputSkin"); } ToggleSwitch { - accentColor: #3F7FBA; - color: #FFFFFF; - fontSize: 27; - skinClass: ClassReference("spark.skins.mobile.ToggleSwitchSkin"); - slideDuration: 125; - textShadowAlpha: .65; - textShadowColor: #000000; + accentColor: #3F7FBA; + color: #FFFFFF; + fontSize: 27; + skinClass: ClassReference("spark.skins.mobile.ToggleSwitchSkin"); + slideDuration: 125; + textShadowAlpha: .65; + textShadowColor: #000000; } View { - backgroundColor: #FFFFFF; - skinClass: ClassReference("spark.skins.mobile.SkinnableContainerSkin"); + backgroundColor: #FFFFFF; + skinClass: ClassReference("spark.skins.mobile.SkinnableContainerSkin"); } ViewMenu { - skinClass: ClassReference("spark.skins.mobile.ViewMenuSkin"); + skinClass: ClassReference("spark.skins.mobile.ViewMenuSkin"); } ViewMenuItem { - iconPlacement : "top"; - chromeColor : #FFFFFF; - paddingBottom: 8; - paddingLeft: 8; - paddingRight: 8; - paddingTop: 8; - skinClass : ClassReference("spark.skins.mobile.ViewMenuItemSkin"); + iconPlacement : "top"; + chromeColor : #FFFFFF; + paddingBottom: 8; + paddingLeft: 8; + paddingRight: 8; + paddingTop: 8; + skinClass : ClassReference("spark.skins.mobile.ViewMenuItemSkin"); } ViewNavigator { - skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); + skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); } ViewNavigatorApplication { - backgroundColor: #FFFFFF; - skinClass: ClassReference("spark.skins.mobile.ViewNavigatorApplicationSkin"); + backgroundColor: #FFFFFF; + skinClass: ClassReference("spark.skins.mobile.ViewNavigatorApplicationSkin"); } VScrollBar { - skinClass: ClassReference("spark.skins.mobile.VScrollBarSkin"); + skinClass: ClassReference("spark.skins.mobile.VScrollBarSkin"); } global { - /* framework styles */ - modalTransparency: 0; - modalTransparencyBlur: 0; - - /* Looks */ - backgroundAlpha : 1; - backgroundImageFillMode : "scale"; - borderVisible : true; - borderWeight : 1; - contentBackgroundColor: #F0F0F0; - downColor: #E0E0E0; /* set in here and not in framework's default.css because it only is really applicable for touch interactions*/ - selectionColor: #E0E0E0; - alternatingItemColors: #FFFFFF; - focusThickness: 3; - symbolColor: #000000; - fontLookup: "embeddedCFF"; - fontFamily: "_sans"; - fontSize: 24; - color: #000000; - textShadowColor: #FFFFFF; - textShadowAlpha: .55; - - /* Behaviors */ - autoThumbVisibility: true; - fixedThumbSize: false; - fullScreenHideControlsDelay: 3000; - interactionMode: "touch"; - liveDragging: true; - rollOverOpenDelay: 200; - slideDuration: 300; - smoothScrolling: true; - touchDelay: 0; /* default to 0 here, but Scroller sets it to 100 */ + /* framework styles */ + modalTransparency: 0; + modalTransparencyBlur: 0; + + /* Looks */ + backgroundAlpha : 1; + backgroundImageFillMode : "scale"; + borderVisible : true; + borderWeight : 1; + contentBackgroundColor: #F0F0F0; + downColor: #E0E0E0; /* set in here and not in framework's default.css because it only is really applicable for touch interactions*/ + selectionColor: #E0E0E0; + alternatingItemColors: #FFFFFF; + focusThickness: 3; + symbolColor: #000000; + fontLookup: "embeddedCFF"; + fontFamily: "_sans"; + fontSize: 24; + color: #000000; + textShadowColor: #FFFFFF; + textShadowAlpha: .55; + + /* Behaviors */ + autoThumbVisibility: true; + fixedThumbSize: false; + fullScreenHideControlsDelay: 3000; + interactionMode: "touch"; + liveDragging: true; + rollOverOpenDelay: 200; + slideDuration: 300; + smoothScrolling: true; + touchDelay: 0; /* default to 0 here, but Scroller sets it to 100 */ } @media (application-dpi: 120) @@ -522,61 +523,61 @@ global @media (application-dpi: 160) { - ActionBar - { - paddingBottom: 1; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 1; - } - - ActionBar.beveled - { - paddingLeft: 5; - paddingRight: 5; - } - - ActionBar #titleDisplay - { - fontSize: 18; - } - - ActionBar.beveled Group#navigationGroup Button - { - fontSize: 12; - } - - ActionBar.beveled Group#actionGroup Button - { - fontSize: 12; - } - - Callout - { - gap: 8; - } - - IconItemRenderer - { - paddingBottom: 8; - paddingTop: 8; - horizontalGap: 8; - verticalGap: 6; - } - - .iconItemRendererMessageStyle - { - fontSize: 14; - } - - LabelItemRenderer - { - paddingBottom: 16; - paddingLeft: 10; - paddingRight: 10; - paddingTop: 16; - } - + ActionBar + { + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; + } + + ActionBar.beveled + { + paddingLeft: 5; + paddingRight: 5; + } + + ActionBar #titleDisplay + { + fontSize: 18; + } + + ActionBar.beveled Group#navigationGroup Button + { + fontSize: 12; + } + + ActionBar.beveled Group#actionGroup Button + { + fontSize: 12; + } + + Callout + { + gap: 8; + } + + IconItemRenderer + { + paddingBottom: 8; + paddingTop: 8; + horizontalGap: 8; + verticalGap: 6; + } + + .iconItemRendererMessageStyle + { + fontSize: 14; + } + + LabelItemRenderer + { + paddingBottom: 16; + paddingLeft: 10; + paddingRight: 10; + paddingTop: 16; + } + SpinnerListItemRenderer { paddingTop : 12; @@ -585,96 +586,96 @@ global paddingRight : 8; } - TextArea - { - paddingBottom: 6; - paddingLeft: 6; - paddingRight: 6; - paddingTop: 6; - } - - TextInput - { - paddingBottom: 6; - paddingLeft: 6; - paddingRight: 6; - paddingTop: 6; - } - - TabbedViewNavigator #tabBar - { - fontSize: 14; - } - - ToggleSwitch - { - fontSize: 18; - } - - global - { - fontSize: 16; - focusThickness: 2; - } + TextArea + { + paddingBottom: 6; + paddingLeft: 6; + paddingRight: 6; + paddingTop: 6; + } + + TextInput + { + paddingBottom: 6; + paddingLeft: 6; + paddingRight: 6; + paddingTop: 6; + } + + TabbedViewNavigator #tabBar + { + fontSize: 14; + } + + ToggleSwitch + { + fontSize: 18; + } + + global + { + fontSize: 16; + focusThickness: 2; + } } @media (application-dpi: 320) { - ActionBar - { - paddingBottom: 2; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 2; - } - - ActionBar.beveled - { - paddingLeft: 10; - paddingRight: 10; - } - - ActionBar #titleDisplay - { - fontSize: 36; - } - - ActionBar.beveled Group#navigationGroup Button - { - fontSize: 24; - } - - ActionBar.beveled Group#actionGroup Button - { - fontSize: 24; - } - - Callout - { - gap: 16; - } - - IconItemRenderer - { - paddingBottom: 16; - paddingTop: 16; - horizontalGap: 16; - verticalGap: 12; - } - - .iconItemRendererMessageStyle - { - fontSize: 26; - } - - LabelItemRenderer - { - paddingBottom: 32; - paddingLeft: 20; - paddingRight: 20; - paddingTop: 32; - } - + ActionBar + { + paddingBottom: 2; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 2; + } + + ActionBar.beveled + { + paddingLeft: 10; + paddingRight: 10; + } + + ActionBar #titleDisplay + { + fontSize: 36; + } + + ActionBar.beveled Group#navigationGroup Button + { + fontSize: 24; + } + + ActionBar.beveled Group#actionGroup Button + { + fontSize: 24; + } + + Callout + { + gap: 16; + } + + IconItemRenderer + { + paddingBottom: 16; + paddingTop: 16; + horizontalGap: 16; + verticalGap: 12; + } + + .iconItemRendererMessageStyle + { + fontSize: 26; + } + + LabelItemRenderer + { + paddingBottom: 32; + paddingLeft: 20; + paddingRight: 20; + paddingTop: 32; + } + SpinnerListItemRenderer { paddingTop : 24; @@ -683,36 +684,36 @@ global paddingRight : 24; } - TextArea - { - paddingBottom: 12; - paddingLeft: 12; - paddingRight: 12; - paddingTop: 12; - } - - TextInput - { - paddingBottom: 12; - paddingLeft: 12; - paddingRight: 12; - paddingTop: 12; - } - - TabbedViewNavigator #tabBar - { - fontSize: 28; - } - - ToggleSwitch - { - fontSize: 36; - } - - global - { - fontSize: 32; - } + TextArea + { + paddingBottom: 12; + paddingLeft: 12; + paddingRight: 12; + paddingTop: 12; + } + + TextInput + { + paddingBottom: 12; + paddingLeft: 12; + paddingRight: 12; + paddingTop: 12; + } + + TabbedViewNavigator #tabBar + { + fontSize: 28; + } + + ToggleSwitch + { + fontSize: 36; + } + + global + { + fontSize: 32; + } } @media (application-dpi: 480) @@ -914,61 +915,481 @@ global @media (application-dpi: 160) AND (os-platform:"IOS") { - TextArea - { - leading: 1.5; - paddingRight: 1.5; - } + TextArea + { + leading: 1.5; + paddingRight: 1.5; + } } @media (application-dpi: 320) AND (os-platform:"IOS") { - TextArea - { - leading: 4.5; - paddingRight: 3; - } + TextArea + { + leading: 4.5; + paddingRight: 3; + } } /* ios 7 status bar - requires new os-version selector (cf FLEX-FLEX-33949) - */ +requires new os-version selector (cf FLEX-FLEX-33949) +*/ @media (application-dpi: 120) AND (os-platform:"IOS") AND (min-os-version: 7) { - Application - { - osStatusBarHeight: 15; - } + Application + { + osStatusBarHeight: 15; + } } @media (application-dpi: 160) AND (os-platform:"IOS") AND (min-os-version: 7) { - Application { - osStatusBarHeight: 20; - } + Application { + osStatusBarHeight: 20; + } } @media (application-dpi: 240) AND (os-platform:"IOS") AND (min-os-version: 7) { - Application - { - osStatusBarHeight: 30; - } + Application + { + osStatusBarHeight: 30; + } } @media (application-dpi: 320) AND (os-platform:"IOS") AND (min-os-version: 7) { - Application - { - osStatusBarHeight: 40; - } + Application + { + osStatusBarHeight: 40; + } } @media (application-dpi: 480) AND (os-platform:"IOS") AND (min-os-version: 7) { - Application - { - osStatusBarHeight: 60; - } -} \ No newline at end of file + Application + { + osStatusBarHeight: 60; + } +} + + +/* +Android 4.x specific Mobile Flex skins and styles. Defaults sizes are based on +application-dpi=240. +*/ + +/*Roboto Fonts*/ +@font-face { + src: url("spark/skins/android4/assets/fonts/Roboto-Regular.ttf"); + fontFamily: RobotoRegular; + embed-as-cff: false; +} + +@font-face { + src: url("spark/skins/android4/assets/fonts/Roboto-Bold.ttf"); + fontFamily: RobotoBold; + fontWeight: bold; + embed-as-cff: false; +} + +@media (os-platform: "android") AND (min-os-version: "4.1.2") +{ + global + { + primaryAccentColor: #33B5E5; + fontFamily: RobotoRegular; + fontWeight: "normal"; + fontSize: 24; + color: #000000; + textShadowColor: #FFFFFF; + textShadowAlpha: 0; + } + + ActionBar + { + chromeColor: #DEDEDD; + defaultButtonAppearance: normal; + skinClass: ClassReference("spark.skins.android4.ActionBarSkin"); + textShadowAlpha: 0; + textShadowColor: #000000; + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; + } + + ActionBar #titleDisplay + { + color: #333333; + fontSize: 24; + fontFamily: RobotoBold; + } + + ActionBar ButtonBase + { + color: #333333; + fontFamily: RobotoBold; + fontWeight: bold; + } + + ActionBar Group#actionGroup Button + { + skinClass: ClassReference("spark.skins.android4.TransparentActionButtonSkin"); + } + + MobileBusyIndicator + { + skinClass: ClassReference("spark.skins.android4.BusyIndicatorSkin"); + } + + Button + { + fontFamily: RobotoBold; + fontWeight: "bold"; + skinClass: ClassReference("spark.skins.android4.ButtonSkin"); + } + + Button.emphasized + { + skinClass: ClassReference("spark.skins.mobile.DefaultButtonSkin"); + } + + ButtonBar + { + skinClass: ClassReference("spark.skins.android4.ButtonBarSkin"); + fontFamily: RobotoBold; + fontWeight: "bold"; + } + + Callout + { + backgroundColor: #33B5E5; + contentBackgroundColor: #FFFFFF; + gap: 12; + skinClass: ClassReference("spark.skins.android4.CalloutSkin"); + borderThickness: 0; + borderColor: 0; + } + + Callout ViewNavigator ActionBar + { + skinClass: ClassReference("spark.skins.mobile.CalloutActionBarSkin"); + defaultButtonAppearance: none; + paddingLeft: 0; + paddingRight: 0; + } + + Callout ViewNavigator ActionBar.beveled + { + paddingLeft: 0; + paddingRight: 0; + } + + Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button + { + skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); + } + + Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button.emphasized + { + skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); + } + + Callout#viewNavigatorPopUp + { + contentBackgroundAppearance: none; + } + + CheckBox + { + skinClass: ClassReference("spark.skins.android4.CheckBoxSkin"); + chromeColor: #FFFFFF; + } + + HScrollBar + { + skinClass: ClassReference("spark.skins.android4.HScrollBarSkin"); + thumbColor: #333333; + } + + HSlider + { + skinClass: ClassReference("spark.skins.android4.HSliderSkin"); + fontFamily: RobotoBold; + } + + RadioButton + { + skinClass: ClassReference("spark.skins.android4.RadioButtonSkin"); + chromeColor: #FFFFFF; + } + + SpinnerList + { + skinClass: ClassReference("spark.skins.android4.SpinnerListSkin"); + } + + SpinnerListContainer + { + skinClass: ClassReference("spark.skins.android4.SpinnerListContainerSkin"); + } + + SpinnerListItemRenderer + { + paddingTop : 18; + paddingBottom : 18; + paddingLeft : 6; + paddingRight : 6; + } + + TabbedViewNavigator #tabBar + { + fontFamily: RobotoRegular; + chromeColor: #484848; + color: #000000; + fontSize: 20; + fontWeight: normal; + iconPlacement: top; + interactionMode: mouse; + skinClass: ClassReference("spark.skins.android4.TabbedViewNavigatorTabBarSkin"); + textShadowAlpha: 0; + textShadowColor: #000000; + } + + TextInput + { + skinClass: ClassReference("spark.skins.android4.StageTextInputSkin"); + contentBackgroundAlpha: 0; + focusColor: #33B5E5; + contentBackgroundColor: #DEDEDD; + contentBackgroundBorder: "flat"; + fontFamily: RobotoRegular; + selectionHighlighting: "never"; + focusEnabled:"false"; + focusThickness: 0; + } + + TextArea + { + skinClass: ClassReference("spark.skins.android4.StageTextAreaSkin"); + contentBackgroundAlpha: 1; + contentBackgroundBorder: "rectangle"; + contentBackgroundColor: #DEDEDD; + focusColor: #33B5E5; + fontFamily: RobotoRegular; + selectionHighlighting: "never"; + focusEnabled:"false"; + focusThickness: 0; + } + + ToggleSwitch + { + accentColor: #3F7FBA; + color: #FFFFFF; + skinClass: ClassReference("spark.skins.android4.ToggleSwitchSkin"); + slideDuration: 125; + textShadowAlpha: .65; + textShadowColor: #000000; + } + + VScrollBar + { + skinClass: ClassReference("spark.skins.android4.VScrollBarSkin"); + thumbColor: #333333; + } + + ViewMenu + { + skinClass: ClassReference("spark.skins.android4.ViewMenuSkin"); + } + + ViewMenuItem + { + iconPlacement : "left"; + chromeColor : #FFFFFF; + focusColor: #DEDEDD; + paddingBottom: 8; + paddingLeft: 8; + paddingRight: 8; + paddingTop: 8; + skinClass : ClassReference("spark.skins.android4.ViewMenuItemSkin"); + } + + ViewNavigator + { + skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); + } +} +@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 120) +{ + global + { + fontSize: 12; + focusThickness: 2; + } + + ActionBar.beveled + { + paddingLeft: 4; + paddingRight: 4; + } + + ActionBar #titleDisplay + { + fontSize: 14; + } + + SpinnerListItemRenderer + { + paddingTop : 9; + paddingBottom : 9; + paddingLeft : 3; + paddingRight : 3; + } + + TabbedViewNavigator #tabBar + { + fontSize: 11; + } +} + +@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 160) +{ + global + { + fontSize: 16; + focusThickness: 2; + } + + ActionBar + { + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; + } + + ActionBar #titleDisplay + { + fontSize: 18; + } + + SpinnerListItemRenderer + { + paddingTop : 12; + paddingBottom : 12; + paddingLeft : 4; + paddingRight : 4; + } + + TabbedViewNavigator #tabBar + { + fontSize: 14; + } +} + +@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 320) +{ + global + { + fontSize: 32; + } + + ActionBar + { + paddingBottom: 2; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 2; + } + + ActionBar #titleDisplay + { + fontSize: 36; + } + + SpinnerListItemRenderer + { + paddingTop : 24; + paddingBottom : 24; + paddingLeft : 8; + paddingRight : 8; + } + + TabbedViewNavigator #tabBar + { + fontSize: 28; + } +} + +@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 480) +{ + global + { + fontSize: 48; + focusThickness: 6; + } + + ActionBar + { + paddingBottom: 4; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 4; + } + + ActionBar #titleDisplay + { + fontSize: 54; + } + + SpinnerListItemRenderer + { + paddingTop : 36; + paddingBottom : 36; + paddingLeft : 12; + paddingRight : 12; + } + + + TabbedViewNavigator #tabBar + { + fontSize: 42; + } +} + +@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 640) +{ + global + { + fontSize: 64; + } + + ActionBar + { + paddingBottom: 4; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 4; + } + + ActionBar #titleDisplay + { + fontSize: 72; + } + + SpinnerListItemRenderer + { + paddingTop : 48; + paddingBottom : 48; + paddingLeft : 16; + paddingRight : 16; + } + + TabbedViewNavigator #tabBar + { + fontSize: 56; + } +} From 3e987e414e5a92580dc227e39fb754c234ae0a1f Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:46:58 -0700 Subject: [PATCH 083/392] Include all Android skins so that they will get compiled into mobiletheme.swc --- .../mobiletheme/src/MobileThemeClasses.as | 145 +++++++++++------- 1 file changed, 89 insertions(+), 56 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/MobileThemeClasses.as b/frameworks/projects/mobiletheme/src/MobileThemeClasses.as index 1f890cbef8..1dff34d5ce 100644 --- a/frameworks/projects/mobiletheme/src/MobileThemeClasses.as +++ b/frameworks/projects/mobiletheme/src/MobileThemeClasses.as @@ -19,60 +19,93 @@ package { - -internal class MobileThemeClasses -{ - -/** - * @private - * This class is used to link additional classes into mobile.swc - * beyond those that are found by dependecy analysis starting - * from the classes specified in manifest.xml. - */ -import spark.skins.mobile.ActionBarSkin; ActionBarSkin; -import spark.skins.mobile.BeveledActionButtonSkin; BeveledActionButtonSkin; -import spark.skins.mobile.BeveledBackButtonSkin; BeveledBackButtonSkin; -import spark.skins.mobile.ButtonBarSkin; spark.skins.mobile.ButtonBarSkin; -import spark.skins.mobile.ButtonSkin; spark.skins.mobile.ButtonSkin; -import spark.skins.mobile.CalloutSkin; spark.skins.mobile.CalloutSkin; - import spark.skins.mobile.supportClasses.CalloutArrow; spark.skins.mobile.supportClasses.CalloutArrow; - import spark.skins.mobile.CalloutActionBarSkin; spark.skins.mobile.CalloutActionBarSkin; -import spark.skins.mobile.CalloutViewNavigatorSkin; spark.skins.mobile.CalloutViewNavigatorSkin; -import spark.skins.mobile.CheckBoxSkin; spark.skins.mobile.CheckBoxSkin; -import spark.skins.mobile.DefaultBeveledActionButtonSkin; spark.skins.mobile.DefaultBeveledActionButtonSkin; -import spark.skins.mobile.DefaultBeveledBackButtonSkin; spark.skins.mobile.DefaultBeveledBackButtonSkin; -import spark.skins.mobile.DefaultButtonSkin; spark.skins.mobile.DefaultButtonSkin; -import spark.skins.mobile.DefaultTransparentActionButtonSkin; spark.skins.mobile.DefaultTransparentActionButtonSkin; -import spark.skins.mobile.DefaultTransparentNavigationButtonSkin; spark.skins.mobile.DefaultTransparentNavigationButtonSkin; -import spark.skins.mobile.DateSpinnerSkin; spark.skins.mobile.DateSpinnerSkin; -import spark.skins.mobile.HScrollBarSkin; spark.skins.mobile.HScrollBarSkin; -import spark.skins.mobile.HSliderSkin; spark.skins.mobile.HSliderSkin; -import spark.skins.mobile.ImageSkin; spark.skins.mobile.ImageSkin; -import spark.skins.mobile.ListSkin; spark.skins.mobile.ListSkin; -import spark.skins.mobile.RadioButtonSkin; spark.skins.mobile.RadioButtonSkin; -import spark.skins.mobile.SpinnerListContainerSkin; SpinnerListContainerSkin; -import spark.skins.mobile.SpinnerListScrollerSkin; SpinnerListScrollerSkin; -import spark.skins.mobile.SpinnerListSkin; SpinnerListSkin; -import spark.skins.mobile.SkinnableContainerSkin; SkinnableContainerSkin; -import spark.skins.mobile.SplitViewNavigatorSkin; SplitViewNavigatorSkin; -import spark.skins.mobile.StageTextAreaSkin; StageTextAreaSkin; -import spark.skins.mobile.StageTextInputSkin; StageTextInputSkin; - import spark.skins.mobile.ScrollingStageTextInputSkin; ScrollingStageTextInputSkin; -import spark.skins.mobile.ScrollingStageTextAreaSkin; ScrollingStageTextAreaSkin -import spark.skins.mobile.TabbedViewNavigatorApplicationSkin; TabbedViewNavigatorApplicationSkin; -import spark.skins.mobile.TabbedViewNavigatorSkin; TabbedViewNavigatorSkin; -import spark.skins.mobile.TabbedViewNavigatorTabBarSkin; TabbedViewNavigatorTabBarSkin; -import spark.skins.mobile.TextAreaSkin; TextAreaSkin; -import spark.skins.mobile.TextAreaHScrollBarSkin; TextAreaHScrollBarSkin; -import spark.skins.mobile.TextAreaVScrollBarSkin; TextAreaVScrollBarSkin; -import spark.skins.mobile.TextInputSkin; TextInputSkin; -import spark.skins.mobile.ToggleSwitchSkin; ToggleSwitchSkin; -import spark.skins.mobile.TransparentActionButtonSkin; TransparentActionButtonSkin; -import spark.skins.mobile.TransparentNavigationButtonSkin; TransparentNavigationButtonSkin; -import spark.skins.mobile.ViewMenuItemSkin; ViewMenuItemSkin; -import spark.skins.mobile.ViewMenuSkin; ViewMenuSkin; -import spark.skins.mobile.ViewNavigatorApplicationSkin; ViewNavigatorApplicationSkin; -import spark.skins.mobile.ViewNavigatorSkin; ViewNavigatorSkin; -import spark.skins.mobile.VScrollBarSkin; spark.skins.mobile.VScrollBarSkin; -} + + internal class MobileThemeClasses + { + + /** + * @private + * This class is used to link additional classes into mobile.swc + * beyond those that are found by dependecy analysis starting + * from the classes specified in manifest.xml. + */ + import spark.skins.mobile.ActionBarSkin; spark.skins.mobile.ActionBarSkin; + import spark.skins.mobile.BeveledActionButtonSkin; spark.skins.mobile.BeveledActionButtonSkin; + import spark.skins.mobile.BeveledBackButtonSkin; spark.skins.mobile.BeveledBackButtonSkin; + import spark.skins.mobile.ButtonBarSkin; spark.skins.mobile.ButtonBarSkin; + import spark.skins.mobile.ButtonSkin; spark.skins.mobile.ButtonSkin; + import spark.skins.mobile.CalloutSkin; spark.skins.mobile.CalloutSkin; + import spark.skins.mobile.supportClasses.CalloutArrow; spark.skins.mobile.supportClasses.CalloutArrow; + import spark.skins.mobile.CalloutActionBarSkin; spark.skins.mobile.CalloutActionBarSkin; + import spark.skins.mobile.CalloutViewNavigatorSkin; spark.skins.mobile.CalloutViewNavigatorSkin; + import spark.skins.mobile.CheckBoxSkin; spark.skins.mobile.CheckBoxSkin; + import spark.skins.mobile.DefaultBeveledActionButtonSkin; spark.skins.mobile.DefaultBeveledActionButtonSkin; + import spark.skins.mobile.DefaultBeveledBackButtonSkin; spark.skins.mobile.DefaultBeveledBackButtonSkin; + import spark.skins.mobile.DefaultButtonSkin; spark.skins.mobile.DefaultButtonSkin; + import spark.skins.mobile.DefaultTransparentActionButtonSkin; spark.skins.mobile.DefaultTransparentActionButtonSkin; + import spark.skins.mobile.DefaultTransparentNavigationButtonSkin; spark.skins.mobile.DefaultTransparentNavigationButtonSkin; + import spark.skins.mobile.DateSpinnerSkin; spark.skins.mobile.DateSpinnerSkin; + import spark.skins.mobile.HScrollBarSkin; spark.skins.mobile.HScrollBarSkin; + import spark.skins.mobile.HSliderSkin; spark.skins.mobile.HSliderSkin; + import spark.skins.mobile.ImageSkin; spark.skins.mobile.ImageSkin; + import spark.skins.mobile.ListSkin; spark.skins.mobile.ListSkin; + import spark.skins.mobile.RadioButtonSkin; spark.skins.mobile.RadioButtonSkin; + import spark.skins.mobile.SpinnerListContainerSkin; spark.skins.mobile.SpinnerListContainerSkin; + import spark.skins.mobile.SpinnerListScrollerSkin; spark.skins.mobile.SpinnerListScrollerSkin; + import spark.skins.mobile.SpinnerListSkin; spark.skins.mobile.SpinnerListSkin; + import spark.skins.mobile.SkinnableContainerSkin; spark.skins.mobile.SkinnableContainerSkin; + import spark.skins.mobile.SplitViewNavigatorSkin; spark.skins.mobile.SplitViewNavigatorSkin; + import spark.skins.mobile.StageTextAreaSkin; spark.skins.mobile.StageTextAreaSkin; + import spark.skins.mobile.StageTextInputSkin; spark.skins.mobile.StageTextInputSkin; + import spark.skins.mobile.ScrollingStageTextInputSkin; spark.skins.mobile.ScrollingStageTextInputSkin; + import spark.skins.mobile.ScrollingStageTextAreaSkin; spark.skins.mobile.ScrollingStageTextAreaSkin + import spark.skins.mobile.TabbedViewNavigatorApplicationSkin; spark.skins.mobile.TabbedViewNavigatorApplicationSkin; + import spark.skins.mobile.TabbedViewNavigatorSkin; spark.skins.mobile.TabbedViewNavigatorSkin; + import spark.skins.mobile.TabbedViewNavigatorTabBarSkin; spark.skins.mobile.TabbedViewNavigatorTabBarSkin; + import spark.skins.mobile.TextAreaSkin; spark.skins.mobile.TextAreaSkin; + import spark.skins.mobile.TextAreaHScrollBarSkin; spark.skins.mobile.TextAreaHScrollBarSkin; + import spark.skins.mobile.TextAreaVScrollBarSkin; spark.skins.mobile.TextAreaVScrollBarSkin; + import spark.skins.mobile.TextInputSkin; spark.skins.mobile.TextInputSkin; + import spark.skins.mobile.ToggleSwitchSkin; spark.skins.mobile.ToggleSwitchSkin; + import spark.skins.mobile.TransparentActionButtonSkin; spark.skins.mobile.TransparentActionButtonSkin; + import spark.skins.mobile.TransparentNavigationButtonSkin; spark.skins.mobile.TransparentNavigationButtonSkin; + import spark.skins.mobile.ViewMenuItemSkin; spark.skins.mobile.ViewMenuItemSkin; + import spark.skins.mobile.ViewMenuSkin; spark.skins.mobile.ViewMenuSkin; + import spark.skins.mobile.ViewNavigatorApplicationSkin; spark.skins.mobile.ViewNavigatorApplicationSkin; + import spark.skins.mobile.ViewNavigatorSkin; spark.skins.mobile.ViewNavigatorSkin; + import spark.skins.mobile.VScrollBarSkin; spark.skins.mobile.VScrollBarSkin; + + //Android skins + import spark.skins.android4.ActionBarSkin; spark.skins.android4.ActionBarSkin; + import spark.skins.android4.BusyIndicatorSkin; spark.skins.android4.BusyIndicatorSkin; + import spark.skins.android4.ButtonBarFirstButtonSkin; spark.skins.android4.ButtonBarFirstButtonSkin; + import spark.skins.android4.ButtonBarMiddleButtonSkin; spark.skins.android4.ButtonBarMiddleButtonSkin; + import spark.skins.android4.ButtonBarSkin; spark.skins.android4.ButtonBarSkin; + import spark.skins.android4.ButtonSkin; spark.skins.android4.ButtonSkin; + import spark.skins.android4.CalloutSkin; spark.skins.android4.CalloutSkin; + import spark.skins.android4.CheckBoxSkin; spark.skins.android4.CheckBoxSkin; + import spark.skins.android4.HScrollBarSkin; spark.skins.android4.HScrollBarSkin; + import spark.skins.android4.HScrollBarThumbSkin; spark.skins.android4.HScrollBarThumbSkin; + import spark.skins.android4.HSliderSkin; spark.skins.android4.HSliderSkin; + import spark.skins.android4.HSliderThumbSkin; spark.skins.android4.HSliderThumbSkin; + import spark.skins.android4.HSliderTrackSkin; spark.skins.android4.HSliderTrackSkin; + import spark.skins.android4.RadioButtonSkin; spark.skins.android4.RadioButtonSkin; + import spark.skins.android4.SpinnerListContainerSkin; spark.skins.android4.SpinnerListContainerSkin; + import spark.skins.android4.SpinnerListScrollerSkin; spark.skins.android4.SpinnerListScrollerSkin; + import spark.skins.android4.SpinnerListSkin; spark.skins.android4.SpinnerListSkin; + import spark.skins.android4.StageTextAreaSkin; spark.skins.android4.StageTextAreaSkin; + import spark.skins.android4.StageTextInputSkin; spark.skins.android4.StageTextInputSkin; + import spark.skins.android4.TabbedViewNavigatorTabBarSkin; spark.skins.android4.TabbedViewNavigatorTabBarSkin; + import spark.skins.android4.TextAreaSkin; spark.skins.android4.TextAreaSkin; + import spark.skins.android4.TextInputSkin; spark.skins.android4.TextInputSkin; + import spark.skins.android4.ToggleSwitchSkin; spark.skins.android4.ToggleSwitchSkin; + import spark.skins.android4.TransparentActionButtonSkin; spark.skins.android4.TransparentActionButtonSkin; + import spark.skins.android4.TransparentNavigationButtonSkin; spark.skins.android4.TransparentNavigationButtonSkin; + import spark.skins.android4.ViewMenuItemSkin; spark.skins.android4.ViewMenuItemSkin; + import spark.skins.android4.ViewMenuSkin; spark.skins.android4.ViewMenuSkin; + import spark.skins.android4.VScrollBarSkin; spark.skins.android4.VScrollBarSkin; + import spark.skins.android4.VScrollBarThumbSkin; spark.skins.android4.VScrollBarThumbSkin; + import spark.skins.android4.supportClasses.CalloutArrow; spark.skins.android4.supportClasses.CalloutArrow; + + } } From 263736c39ab2cd818f295635fe0427a2487aee04 Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:48:16 -0700 Subject: [PATCH 084/392] Update android css skins (this file will be deleted shortly) --- .../projects/mobiletheme/src/android4x.css | 960 +++++++++++++++++- 1 file changed, 958 insertions(+), 2 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css index 3ec0f3d6b3..2cdf3f4806 100644 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ b/frameworks/projects/mobiletheme/src/android4x.css @@ -19,6 +19,962 @@ @namespace "library://ns.adobe.com/flex/spark"; + +/* +Default Mobile Flex skins and styles. Defaults sizes are based on +application-dpi=240. +*/ + +ActionBar +{ + chromeColor: #484848; + defaultButtonAppearance: normal; + skinClass: ClassReference("spark.skins.mobile.ActionBarSkin"); + textShadowAlpha: .65; + textShadowColor: #000000; + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; +} + +ActionBar.beveled +{ + paddingLeft: 7; + paddingRight: 7; + titleAlign: center; +} + +ActionBar #titleDisplay +{ + color: #FFFFFF; + fontSize: 28; + fontWeight: bold; +} + +ActionBar ButtonBase +{ + color: #FFFFFF; +} + +ActionBar Group#navigationGroup Button +{ + skinClass: ClassReference("spark.skins.mobile.TransparentNavigationButtonSkin"); +} + +ActionBar Group#navigationGroup Button.emphasized +{ + skinClass: ClassReference("spark.skins.mobile.DefaultTransparentNavigationButtonSkin"); +} + +ActionBar.beveled Group#navigationGroup Button +{ + fontSize: 18; + skinClass: ClassReference("spark.skins.mobile.BeveledBackButtonSkin"); +} + +ActionBar.beveled Group#navigationGroup Button.emphasized +{ + skinClass: ClassReference("spark.skins.mobile.DefaultBeveledBackButtonSkin"); +} + +ActionBar Group#actionGroup Button +{ + skinClass: ClassReference("spark.skins.mobile.TransparentActionButtonSkin"); +} + +ActionBar Group#actionGroup Button.emphasized +{ + skinClass: ClassReference("spark.skins.mobile.DefaultTransparentActionButtonSkin"); +} + +ActionBar.beveled Group#actionGroup Button +{ + fontSize: 18; + skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); +} + +ActionBar.beveled Group#actionGroup Button.emphasized +{ + skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); +} + +BusyIndicator +{ + rotationInterval: 50; +} + +Button +{ + fontWeight: "bold"; + skinClass: ClassReference("spark.skins.mobile.ButtonSkin"); +} + +Button.emphasized +{ + skinClass: ClassReference("spark.skins.mobile.DefaultButtonSkin"); +} + +ButtonBar +{ + fontWeight: "bold"; + skinClass: ClassReference("spark.skins.mobile.ButtonBarSkin"); +} + +ButtonBase +{ + iconPlacement: "left"; +} + +Callout +{ + backgroundColor: #484848; + contentBackgroundAppearance: inset; + contentBackgroundColor: #FFFFFF; + gap: 12; + skinClass: ClassReference("spark.skins.mobile.CalloutSkin"); + borderThickness: NaN; + borderColor: 0; +} + +Callout ViewNavigator +{ + skinClass: ClassReference("spark.skins.mobile.CalloutViewNavigatorSkin"); +} + +Callout ViewNavigator ActionBar +{ + skinClass: ClassReference("spark.skins.mobile.CalloutActionBarSkin"); + defaultButtonAppearance: beveled; + paddingLeft: 0; + paddingRight: 0; +} + +Callout ViewNavigator ActionBar.beveled +{ + paddingLeft: 0; + paddingRight: 0; +} + +Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button +{ + skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); +} + +Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button.emphasized +{ + skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); +} + +Callout#viewNavigatorPopUp +{ + contentBackgroundAppearance: none; +} + +CheckBox +{ + skinClass: ClassReference("spark.skins.mobile.CheckBoxSkin"); +} + +DateSpinner +{ + skinClass: ClassReference("spark.skins.mobile.DateSpinnerSkin"); +} + +HScrollBar +{ + skinClass: ClassReference("spark.skins.mobile.HScrollBarSkin"); +} + +HSlider +{ + skinClass: ClassReference("spark.skins.mobile.HSliderSkin"); +} + +IconItemRenderer +{ + paddingBottom: 12; + paddingTop: 12; + horizontalGap: 10; + verticalGap: 9; + iconDelay: 500; + messageStyleName: "iconItemRendererMessageStyle"; +} + +.iconItemRendererMessageStyle +{ + fontSize: 20; + color: #333333; +} + +Image +{ + skinClass: ClassReference("spark.skins.mobile.ImageSkin"); + enableLoadingState: false; + showErrorSkin : false; + smoothingQuality: "default"; +} + +LabelItemRenderer +{ + paddingBottom: 24; + paddingLeft: 15; + paddingRight: 15; + paddingTop: 24; + verticalAlign: "middle"; +} + +List +{ + skinClass: ClassReference("spark.skins.mobile.ListSkin"); + borderColor: #000000; + borderAlpha: 1; + verticalScrollPolicy: on; + borderVisible: false; +} + +RadioButton +{ + skinClass: ClassReference("spark.skins.mobile.RadioButtonSkin"); +} + +Scroller +{ + touchDelay: 100; +} + +SkinnableContainer +{ + backgroundColor: #FFFFFF; + skinClass: ClassReference("spark.skins.mobile.SkinnableContainerSkin"); +} + +SkinnableTextBase:normalWithPrompt +{ + fontStyle: normal; +} + +SkinnableTextBase:disabledWithPrompt +{ + fontStyle: normal; +} + +SpinnerList +{ + skinClass: ClassReference("spark.skins.mobile.SpinnerListSkin"); +} + +SpinnerListContainer +{ + skinClass: ClassReference("spark.skins.mobile.SpinnerListContainerSkin"); +} + +SpinnerListItemRenderer +{ + paddingTop : 18; + paddingBottom : 18; + paddingLeft : 16; + paddingRight : 16; +} + +SplitViewNavigator +{ + skinClass: ClassReference("spark.skins.mobile.SplitViewNavigatorSkin"); + backgroundColor: #000000; +} + +TabbedViewNavigatorApplication +{ + backgroundColor: #FFFFFF; + skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorApplicationSkin"); +} + +TabbedViewNavigator +{ + skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorSkin"); +} + +TabbedViewNavigator #tabBar +{ + chromeColor: #484848; + color: #FFFFFF; + fontSize: 20; + fontWeight: normal; + iconPlacement: top; + interactionMode: mouse; + skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorTabBarSkin"); + textShadowAlpha: .65; + textShadowColor: #000000; +} + +TextArea +{ + paddingBottom: 9; + paddingLeft: 9; + paddingRight: 9; + paddingTop: 9; + showPromptWhenFocused: true; + skinClass: ClassReference("spark.skins.mobile.ScrollingStageTextAreaSkin"); +} + +TextArea VScrollBar +{ + skinClass: ClassReference("spark.skins.mobile.TextAreaVScrollBarSkin"); +} + +TextArea HScrollBar +{ + skinClass: ClassReference("spark.skins.mobile.TextAreaHScrollBarSkin"); +} + +TextInput +{ + paddingBottom: 9; + paddingLeft: 9; + paddingRight: 9; + paddingTop: 9; + showPromptWhenFocused: true; + skinClass: ClassReference("spark.skins.mobile.ScrollingStageTextInputSkin"); +} + +ToggleSwitch +{ + accentColor: #3F7FBA; + color: #FFFFFF; + fontSize: 27; + skinClass: ClassReference("spark.skins.mobile.ToggleSwitchSkin"); + slideDuration: 125; + textShadowAlpha: .65; + textShadowColor: #000000; +} + +View +{ + backgroundColor: #FFFFFF; + skinClass: ClassReference("spark.skins.mobile.SkinnableContainerSkin"); +} + +ViewMenu +{ + skinClass: ClassReference("spark.skins.mobile.ViewMenuSkin"); +} + +ViewMenuItem +{ + iconPlacement : "top"; + chromeColor : #FFFFFF; + paddingBottom: 8; + paddingLeft: 8; + paddingRight: 8; + paddingTop: 8; + skinClass : ClassReference("spark.skins.mobile.ViewMenuItemSkin"); +} + +ViewNavigator +{ + skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); +} + +ViewNavigatorApplication +{ + backgroundColor: #FFFFFF; + skinClass: ClassReference("spark.skins.mobile.ViewNavigatorApplicationSkin"); +} + +VScrollBar +{ + skinClass: ClassReference("spark.skins.mobile.VScrollBarSkin"); +} + +global +{ + /* framework styles */ + modalTransparency: 0; + modalTransparencyBlur: 0; + + /* Looks */ + backgroundAlpha : 1; + backgroundImageFillMode : "scale"; + borderVisible : true; + borderWeight : 1; + contentBackgroundColor: #F0F0F0; + downColor: #E0E0E0; /* set in here and not in framework's default.css because it only is really applicable for touch interactions*/ + selectionColor: #E0E0E0; + alternatingItemColors: #FFFFFF; + focusThickness: 3; + symbolColor: #000000; + fontLookup: "embeddedCFF"; + fontFamily: "_sans"; + fontSize: 24; + color: #000000; + textShadowColor: #FFFFFF; + textShadowAlpha: .55; + + /* Behaviors */ + autoThumbVisibility: true; + fixedThumbSize: false; + fullScreenHideControlsDelay: 3000; + interactionMode: "touch"; + liveDragging: true; + rollOverOpenDelay: 200; + slideDuration: 300; + smoothScrolling: true; + touchDelay: 0; /* default to 0 here, but Scroller sets it to 100 */ +} + +@media (application-dpi: 120) +{ + ActionBar + { + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; + } + + ActionBar.beveled + { + paddingLeft: 4; + paddingRight: 4; + } + + ActionBar #titleDisplay + { + fontSize: 14; + } + + ActionBar.beveled Group#navigationGroup Button + { + fontSize: 9; + } + + ActionBar.beveled Group#actionGroup Button + { + fontSize: 9; + } + + Callout + { + gap: 6; + } + + IconItemRenderer + { + paddingBottom: 6; + paddingTop: 6; + horizontalGap: 6; + verticalGap: 3; + } + + .iconItemRendererMessageStyle + { + fontSize: 11; + } + + LabelItemRenderer + { + paddingBottom: 12; + paddingLeft: 8; + paddingRight: 8; + paddingTop: 12; + } + + SpinnerListItemRenderer + { + paddingTop : 8; + paddingBottom : 8; + paddingLeft : 6; + paddingRight : 6; + } + + TextArea + { + paddingBottom: 4; + paddingLeft: 4; + paddingRight: 4; + paddingTop: 4; + } + + TextInput + { + paddingBottom: 4; + paddingLeft: 4; + paddingRight: 4; + paddingTop: 4; + } + + TabbedViewNavigator #tabBar + { + fontSize: 11; + } + + ToggleSwitch + { + fontSize: 14; + } + + global + { + fontSize: 12; + focusThickness: 2; + } +} + + +@media (application-dpi: 160) +{ + ActionBar + { + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; + } + + ActionBar.beveled + { + paddingLeft: 5; + paddingRight: 5; + } + + ActionBar #titleDisplay + { + fontSize: 18; + } + + ActionBar.beveled Group#navigationGroup Button + { + fontSize: 12; + } + + ActionBar.beveled Group#actionGroup Button + { + fontSize: 12; + } + + Callout + { + gap: 8; + } + + IconItemRenderer + { + paddingBottom: 8; + paddingTop: 8; + horizontalGap: 8; + verticalGap: 6; + } + + .iconItemRendererMessageStyle + { + fontSize: 14; + } + + LabelItemRenderer + { + paddingBottom: 16; + paddingLeft: 10; + paddingRight: 10; + paddingTop: 16; + } + + SpinnerListItemRenderer + { + paddingTop : 12; + paddingBottom : 12; + paddingLeft : 8; + paddingRight : 8; + } + + TextArea + { + paddingBottom: 6; + paddingLeft: 6; + paddingRight: 6; + paddingTop: 6; + } + + TextInput + { + paddingBottom: 6; + paddingLeft: 6; + paddingRight: 6; + paddingTop: 6; + } + + TabbedViewNavigator #tabBar + { + fontSize: 14; + } + + ToggleSwitch + { + fontSize: 18; + } + + global + { + fontSize: 16; + focusThickness: 2; + } +} + +@media (application-dpi: 320) +{ + ActionBar + { + paddingBottom: 2; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 2; + } + + ActionBar.beveled + { + paddingLeft: 10; + paddingRight: 10; + } + + ActionBar #titleDisplay + { + fontSize: 36; + } + + ActionBar.beveled Group#navigationGroup Button + { + fontSize: 24; + } + + ActionBar.beveled Group#actionGroup Button + { + fontSize: 24; + } + + Callout + { + gap: 16; + } + + IconItemRenderer + { + paddingBottom: 16; + paddingTop: 16; + horizontalGap: 16; + verticalGap: 12; + } + + .iconItemRendererMessageStyle + { + fontSize: 26; + } + + LabelItemRenderer + { + paddingBottom: 32; + paddingLeft: 20; + paddingRight: 20; + paddingTop: 32; + } + + SpinnerListItemRenderer + { + paddingTop : 24; + paddingBottom : 24; + paddingLeft : 24; + paddingRight : 24; + } + + TextArea + { + paddingBottom: 12; + paddingLeft: 12; + paddingRight: 12; + paddingTop: 12; + } + + TextInput + { + paddingBottom: 12; + paddingLeft: 12; + paddingRight: 12; + paddingTop: 12; + } + + TabbedViewNavigator #tabBar + { + fontSize: 28; + } + + ToggleSwitch + { + fontSize: 36; + } + + global + { + fontSize: 32; + } +} + +@media (application-dpi: 480) +{ + ActionBar + { + paddingBottom: 4; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 4; + } + + ActionBar.beveled + { + paddingLeft: 15; + paddingRight: 15; + } + + ActionBar #titleDisplay + { + fontSize: 54; + } + + ActionBar.beveled Group#navigationGroup Button + { + fontSize: 36; + } + + ActionBar.beveled Group#actionGroup Button + { + fontSize: 36; + } + + Callout + { + gap: 24; + } + + IconItemRenderer + { + paddingBottom: 24; + paddingTop: 24; + horizontalGap: 24; + verticalGap: 18; + } + + .iconItemRendererMessageStyle + { + fontSize: 42; + } + + LabelItemRenderer + { + paddingBottom: 48; + paddingLeft: 30; + paddingRight: 30; + paddingTop: 48; + } + + SpinnerListItemRenderer + { + paddingTop : 36; + paddingBottom : 36; + paddingLeft : 24; + paddingRight : 24; + } + + TextArea + { + paddingBottom: 18; + paddingLeft: 18; + paddingRight: 18; + paddingTop: 18; + } + + TextInput + { + paddingBottom: 18; + paddingLeft: 18; + paddingRight: 18; + paddingTop: 18; + } + + TabbedViewNavigator #tabBar + { + fontSize: 42; + } + + ToggleSwitch + { + fontSize: 54; + } + + global + { + fontSize: 48; + focusThickness: 6; + } +} + + +@media (application-dpi: 640) +{ + ActionBar + { + paddingBottom: 4; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 4; + } + + ActionBar.beveled + { + paddingLeft: 20; + paddingRight: 20; + } + + ActionBar #titleDisplay + { + fontSize: 72; + } + + ActionBar.beveled Group#navigationGroup Button + { + fontSize: 48; + } + + ActionBar.beveled Group#actionGroup Button + { + fontSize: 48; + } + + Callout + { + gap: 32; + } + + IconItemRenderer + { + paddingBottom: 32; + paddingTop: 32; + horizontalGap: 32; + verticalGap: 36; + } + + .iconItemRendererMessageStyle + { + fontSize: 26; + } + + LabelItemRenderer + { + paddingBottom: 64; + paddingLeft: 40; + paddingRight: 40; + paddingTop: 64; + } + + SpinnerListItemRenderer + { + paddingTop : 48; + paddingBottom : 48; + paddingLeft : 48; + paddingRight : 48; + } + + TextArea + { + paddingBottom: 24; + paddingLeft: 24; + paddingRight: 24; + paddingTop: 24; + } + + TextInput + { + paddingBottom: 24; + paddingLeft: 24; + paddingRight: 24; + paddingTop: 24; + } + + TabbedViewNavigator #tabBar + { + fontSize: 56; + } + + ToggleSwitch + { + fontSize: 72; + } + + global + { + fontSize: 64; + } +} + + +@media (application-dpi: 160) AND (os-platform:"IOS") +{ + TextArea + { + leading: 1.5; + paddingRight: 1.5; + } +} + +@media (application-dpi: 320) AND (os-platform:"IOS") +{ + TextArea + { + leading: 4.5; + paddingRight: 3; + } +} + +/* ios 7 status bar +requires new os-version selector (cf FLEX-FLEX-33949) +*/ + +@media (application-dpi: 120) AND (os-platform:"IOS") AND (min-os-version: 7) +{ + Application + { + osStatusBarHeight: 15; + } +} + +@media (application-dpi: 160) AND (os-platform:"IOS") AND (min-os-version: 7) +{ + Application { + osStatusBarHeight: 20; + } +} + +@media (application-dpi: 240) AND (os-platform:"IOS") AND (min-os-version: 7) +{ + Application + { + osStatusBarHeight: 30; + } +} + +@media (application-dpi: 320) AND (os-platform:"IOS") AND (min-os-version: 7) +{ + Application + { + osStatusBarHeight: 40; + } +} + +@media (application-dpi: 480) AND (os-platform:"IOS") AND (min-os-version: 7) +{ + Application + { + osStatusBarHeight: 60; + } +} + + /* Android 4.x specific Mobile Flex skins and styles. Defaults sizes are based on application-dpi=240. @@ -38,8 +994,8 @@ application-dpi=240. embed-as-cff: false; } -@media (os-platform: "android") AND (min-os-version: "4.1.2") { - +@media (os-platform: "android") AND (min-os-version: "4.1.2") +{ global { primaryAccentColor: #33B5E5; From b2ac46a7ca6355783b06af38eddb308ca0b30d56 Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:50:02 -0700 Subject: [PATCH 085/392] Cleanup imports --- .../mobiletheme/src/spark/skins/android4/TextInputSkin.as | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/TextInputSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/TextInputSkin.as index 5a7a35e524..7488e1f7c7 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/TextInputSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/TextInputSkin.as @@ -22,7 +22,6 @@ package spark.skins.android4 import flash.events.Event; import flash.events.FocusEvent; import flash.events.SoftKeyboardEvent; - import flash.system.Capabilities; import mx.core.DPIClassification; import mx.core.EventPriority; @@ -46,7 +45,7 @@ package spark.skins.android4 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ - public class TextInputSkin extends TextSkinBase + public class TextInputSkin extends TextSkinBase { //-------------------------------------------------------------------------- // From e9945296137102a4894fe9d2544ec56efe94d203 Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:51:49 -0700 Subject: [PATCH 086/392] Move to correct package --- .../src/spark/skins/android4/TransparentNavigationButtonSkin.as | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentNavigationButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentNavigationButtonSkin.as index a19a21c950..0709b65849 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentNavigationButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/android4/TransparentNavigationButtonSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.mobile +package spark.skins.android4 { import flash.display.DisplayObject; import flash.display.Graphics; From fa3ac54d55b10e41c704a4910fc93fa8f64fba91 Mon Sep 17 00:00:00 2001 From: Om Date: Fri, 15 Aug 2014 13:53:37 -0700 Subject: [PATCH 087/392] Move TextSkinBase to correct package --- .../src/spark/skins/android4/TextSkinBase.as | 212 ------------------ 1 file changed, 212 deletions(-) delete mode 100644 frameworks/projects/mobiletheme/src/spark/skins/android4/TextSkinBase.as diff --git a/frameworks/projects/mobiletheme/src/spark/skins/android4/TextSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/android4/TextSkinBase.as deleted file mode 100644 index 79ce852e94..0000000000 --- a/frameworks/projects/mobiletheme/src/spark/skins/android4/TextSkinBase.as +++ /dev/null @@ -1,212 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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.supportClasses -{ - - import flash.display.DisplayObject; - - import mx.core.mx_internal; - - import spark.components.supportClasses.StyleableTextField; - - use namespace mx_internal; - - /** - * ActionScript-based skin for text input controls in mobile applications that - * uses a StyleableTextField class for the text display. - * - * @see spark.components.supportClasses.StyleableTextField - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - public class TextSkinBase extends MobileSkin - { - //-------------------------------------------------------------------------- - // - // Constructor - // - //-------------------------------------------------------------------------- - /** - * Constructor. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - * - */ - public function TextSkinBase() - { - super(); - } - - //-------------------------------------------------------------------------- - // - // Graphics variables - // - //-------------------------------------------------------------------------- - - /** - * Defines the border. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - - //-------------------------------------------------------------------------- - // - // Layout variables - // - //-------------------------------------------------------------------------- - - /** - * Defines the corner radius. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - - protected var layoutBorderSize:uint; - - //-------------------------------------------------------------------------- - // - // Variables - // - //-------------------------------------------------------------------------- - - /** - * @private - * - * Instance of the border graphics. - */ - protected var border:DisplayObject; - - private var borderVisibleChanged:Boolean = false; - - //-------------------------------------------------------------------------- - // - // Skin parts - // - //-------------------------------------------------------------------------- - - /** - * textDisplay skin part. - */ - public var textDisplay:StyleableTextField; - - [Bindable] - /** - * Bindable promptDisplay skin part. Bindings fire when promptDisplay is - * removed and added for proper updating by the SkinnableTextBase. - */ - public var promptDisplay:StyleableTextField; - - //-------------------------------------------------------------------------- - // - // Overridden methods - // - //-------------------------------------------------------------------------- - - /** - * @private - */ - override protected function createChildren():void - { - super.createChildren(); - - if (!textDisplay) - { - textDisplay = StyleableTextField(createInFontContext(StyleableTextField)); - textDisplay.styleName = this; - textDisplay.editable = true; - textDisplay.useTightTextBounds = false; - addChild(textDisplay); - } - } - - /** - * @private - */ - protected function createPromptDisplay():StyleableTextField - { - var prompt:StyleableTextField = StyleableTextField(createInFontContext(StyleableTextField)); - prompt.styleName = this; - prompt.editable = false; - prompt.mouseEnabled = false; - prompt.useTightTextBounds = false; - prompt.focusEnabled = false; - return prompt; - } - - /** - * @private - */ - - override public function styleChanged(styleProp:String):void - { - var allStyles:Boolean = !styleProp || styleProp == "styleName"; - - if (allStyles || styleProp == "borderVisible") - { - borderVisibleChanged = true; - invalidateProperties(); - } - - if (allStyles || styleProp.indexOf("padding") == 0) - { - invalidateDisplayList(); - } - - super.styleChanged(styleProp); - } - - /** - * @private - */ - override protected function commitCurrentState():void - { - super.commitCurrentState(); - - alpha = currentState.indexOf("disabled") == -1 ? 1 : 0.5; - - var showPrompt:Boolean = currentState.indexOf("WithPrompt") >= 0; - - if (showPrompt && !promptDisplay) - { - promptDisplay = createPromptDisplay(); - addChild(promptDisplay); - } - else if (!showPrompt && promptDisplay) - { - removeChild(promptDisplay); - promptDisplay = null; - } - - invalidateDisplayList(); - } - } -} \ No newline at end of file From cf3f86ffff2ae508c9a5f5828dc04d094d5c48c6 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Sat, 16 Aug 2014 01:19:38 +0100 Subject: [PATCH 088/392] fix failing advancedcss test on windows. The button was in the dropshadow of a panel and was rendered differently on FP14 vs FP11. I moved the button below the dropshadow --- .../DescendantSelectors_main_runtimeCSS.mxml | 2 ++ ...cedCSS_descendantSelectors_RuntimeCSS.mxml | 2 +- ...tor_basic_noMatch_style_runtimecss@win.png | Bin 1447 -> 1239 bytes 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mustella/tests/Managers/StyleManager/AdvancedCSS/SWFs/DescendantSelectors_main_runtimeCSS.mxml b/mustella/tests/Managers/StyleManager/AdvancedCSS/SWFs/DescendantSelectors_main_runtimeCSS.mxml index 39674f7eb9..61767cf6be 100644 --- a/mustella/tests/Managers/StyleManager/AdvancedCSS/SWFs/DescendantSelectors_main_runtimeCSS.mxml +++ b/mustella/tests/Managers/StyleManager/AdvancedCSS/SWFs/DescendantSelectors_main_runtimeCSS.mxml @@ -64,6 +64,8 @@ + + diff --git a/mustella/tests/Managers/StyleManager/AdvancedCSS/descendantSelectors/AdvancedCSS_descendantSelectors_RuntimeCSS.mxml b/mustella/tests/Managers/StyleManager/AdvancedCSS/descendantSelectors/AdvancedCSS_descendantSelectors_RuntimeCSS.mxml index f0b3f8f8c9..2089e01bf9 100644 --- a/mustella/tests/Managers/StyleManager/AdvancedCSS/descendantSelectors/AdvancedCSS_descendantSelectors_RuntimeCSS.mxml +++ b/mustella/tests/Managers/StyleManager/AdvancedCSS/descendantSelectors/AdvancedCSS_descendantSelectors_RuntimeCSS.mxml @@ -166,7 +166,7 @@ - + kf;TYcNl`mSeM&-a|O_c_G& z_O|To?39g-jk2Pm!o?2jC$>ZZ=DVm#`Wp8hSyKFLVj~L{2TBj4gT*wz8mO;ZX!QFU)=yv z(+TzU^>TT6*?(GJTwKVAhzNBxx#Z+zWwSRoH|I5{Cy>L;`LCdF@Ll=76U?)`yjWQ0&*QU+OD{$^EN%)cKn@e-w{%YN41T zFIp|AuNj~hx`~{e9C>?ttKN!ESXx?Ah4h-3mX;Qolz)`;g~P^oWMo9v)zw)zJ3CW* zTv_A8wWp^?@iQ|sGAJlW1_uYLJ!;zfz?T^6>bT9#O;uOd(LB(cJsuFFdxEDP9OodX zS=Xs&z$3ff*LjII&+ciaoY$PG$veeppcj3L?CflLb8{mf9v-}ccbc{-%9UZCo}OCv z;^LyUPk(Y60oZ^&{AmupySrQEHRIqt)rUt|opWPjL-FLI_VDnq^#ag2^zd{%wHPio zF95yAmE1aqy|%BO&9kycF8s7W*q+X#=V;c^eR$IXz0gf$Wo4;v?R<`U0}W13Ps@dc z1!cpPmX^w{t}eyVwDt`T4_6K7HF0pB^1`!kw}0(OOic8+N8R=Hb!$yO_+r=A)+*?D z_Ou@{x@UYii?10t^=zKizBYR>;b|tU%^sbAUg##$)6?bE)s;{1`)5r}jcjUaQa0Sc zz<`X8k5?SYv~O5gn4FxPwAR>gItE)KARxf%G3dzEAPg$;im~;^zszdN$8$UpwVCXQIz% z#(4vJp_@oaNl{NrCnqO9QCV53U~&v4CMM<+$3SOiXRS3hoarAD5~8?+5};k9(KS2gs-W+uGU`%)YcA!V&Aq&kk=r+s|rWJLNWK0?2KjtrO4--2^vs zh=YRz7k&ctLN}3+kRXqaj^ys{t_wc_dZC-(K5%$=D0g;tT=)slOFe5uM@P&3{eAVT zfD8WsdZC+$ii(nZdwYspTU&GS!#%+-OX!7ef*(Q$2M5*LQU43Y#m@#`SI|xH$jZ$x iGBVPIpMbA9==L9Fq@SJ3+jT4e0000MNkl^>chsHyaWs4LQ70Kq!n`P6cO|o(0M%l1ogREb_J~m)&hYlT*8#iu< zS6y8#_4W1g_U&7lot>4`-=F&jKhK{(mkSpzNO^g=cA2HYFF}jEBpBI<9~Wz?(6H5(b3WW?Ku!e&Z9?<6v4%_-ngT% zuu!~h+qTK%VMTLvLIozDTfD)|nCG923%dm-Bky;T*xugm6Lt3NSs5A{@(INkH5R^`1us~zKo%}ss6OTl);v=!;?+(0mrn4n?+IXSFdbYYz(p?Eq^V|j}4A<5$5we&zCvkiS7gBa{hFAV+t{jz-d zas{&4SqQdMr%w5{^z?LTZEf{+WNf!@-`0k?KK7amv{Tx6Z8Gjkk znC$FqmG|x2r$FXV7auZs`0%0D9*hg3tE)?E=JUsF)Y8(@GDl9Nhvg=QSXX!Fhyr}& z=H`mWKNxrxB?FBDu8l^qI9@Q!Mp4bp&AyI|4VhTpD3Q6MTP!g(H8lz6K6&z_&gISn zIAqwncdxH6TeeJUYiso-V$YsE3V-B_7cZ)h=PUZ*!-w@lHyD=))Y#F{p)yzW>({TV zzG%@RUrwZlFSc#gby@j{+Fd6Gg*0AKp9v2^KD85|tc zlZq7L0r-M9Z^@D+^6c3&HS*z_!k-OJmf#ECyo!nnX>4rNpX1}lk5h;N_{4%Yk5^V6 kepy*rDZ~VP=D^!`bY^eXJP(8^0RR9107*qoM6N<$f}M!$AOHXW From 994deb0113d63ea437e6be6b5649c5c31d1ec004 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Fri, 15 Aug 2014 17:37:58 -0700 Subject: [PATCH 089/392] update mac baseline now that it is out from under the panel gradient --- ...elector_basic_noMatch_style_runtimecss.png | Bin 1468 -> 1239 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/mustella/tests/Managers/StyleManager/AdvancedCSS/descendantSelectors/baselines/descendantSelector_basic_noMatch_style_runtimecss.png b/mustella/tests/Managers/StyleManager/AdvancedCSS/descendantSelectors/baselines/descendantSelector_basic_noMatch_style_runtimecss.png index b06401c14b32e8e58670c73201042e0c979ac2b6..5bc53c4a53858fa961de3e290071dd7a61ac524b 100644 GIT binary patch delta 1220 zcmV;#1Uvh@3)cydBYy;*Nklkf;TYcNl`mSeM&-a|O_c_G& z_O|To?39g-jk2Pm!o?2jC$>ZZ=DVm#`Wp8hSyKFLVj~L{2TBj4gT*wz8mO;ZX!QFU)=yv z(+TzU^>TT6*?(GJTwKVAhzNBxx#Z+zWwSRoH|I5{Cy>L;`LCdF@Ll=76U?)`yjWQ0&*QU+OD{$^EN%)cKn@e-w{%YN41T zFIp|AuNj~hx`~{e9C>?ttKN!ESXx?Ah4h-3mX;Qolz)`;g~P^oWMo9v)zw)zJ3CW* zTv_A8wWp^?@iQ|sGAJlW1_uYLJ!;zfz?T^6>bT9#O;uOd(LB(cJsuFFdxEDP9OodX zS=Xs&z$3ff*LjII&+ciaoY$PG$veeppcj3L?CflLb8{mf9v-}ccbc{-%9UZCo}OCv z;^LyUPk(Y60oZ^&{AmupySrQEHRIqt)rUt|opWPjL-FLI_VDnq^#ag2^zd{%wHPio zF95yAmE1aqy|%BO&9kycF8s7W*q+X#=V;c^eR$IXz0gf$Wo4;v?R<`U0}W13Ps@dc z1!cpPmX^w{t}eyVwDt`T4_6K7HF0pB^1`!kw}0(OOic8+N8R=Hb!$yO_+r=A)+*?D z_Ou@{x@UYii?10t^=zKizBYR>;b|tU%^sbAUg##$)6?bE)s;{1`)5r}jcjUaQa0Sc zz<`X8k5?SYv~O5gn4FxPwAR>gItE)KARxf%G3dzEAPg$;im~;^zszdN$8$UpwVCXQIz% z#(4vJp_@oaNl{NrCnqO9QCV53U~&v4CMM<+$3SOiXRS3hoarAD5~8?+5};k9(KS2gs-W+uGU`%)YcA!V&Aq&kk=r+s|rWJLNWK0?2KjtrO4--2^vs zh=YRz7k&ctLN}3+kRXqaj^ys{t_wc_dZC-(K5%$=D0g;tT=)slOFe5uM@P&3{eAVT zfD8WsdZC+$ii(nZdwYspTU&GS!#%+-OX!7ef*(Q$2M5*LQU43Y#m@#`SI|xH$jZ$x iGBVPIpMbA9==L9Fq@SJ3+jT4e0000hNklbsbie$@{EwXv@X4$l9lN1&f zh6c>3va(V-Iy%HVd-kj}G&IP^j~^xRv$%hL{`@JgUcHjW#ztMRURhb0eE$4d=H})? ziyuFJ`1t<)`+q!q`}WPp?Ck73%*@P$2F!sQZR5s`;_;}lNxDS{1Ad1nm#nW}zlIXb zQ^_kSDbWcZM@!GeZ`~l$4~O7_RM~lE*l|(=OU0Fdlh-lEmA$Z~H{m*VoJ2 zw{Lwy@qfi?3tvhej89HZ#+=-P2M=^0(`j?=+&Rh5&!1;QCbqr3-7g+^_wJqgxS|uo z_0pwFYA4~8loVOIbgB9nGnfalv|(Ip_xSN+9oNP&yL;!^q$v{m@#Dv(s;WvxMn-fM z*VWZYZ*Q;Kn8%|>kJN4;jyCD(>3SvJd=%UK)_*VUO$TmYhOVwI?Jtr&S{nm=tzW-h zJWd%VCMM#p6z(*WZN*S*R*cE49P?8I2m#0dAMq3rJH@15w_UVVhaF+%`ZV?|oe5h@WJdW3570d;B8p+6Rv4gthun_X15gi14ty{NF zJf58&K75Fa+S*zW(~Eb`H#M-|A}@F5#$>&CG|j)az$mU%hT7e4FPVuO0LzAaDF zIbV*IgA3-mcI{g6U=V}t^rB&4U_i37vlYl@XCl}xT)5!dmMvQ*H*enbb!2QkJ%2q~ zFt@!M8*JwSUvqQwJbQ9-vVIOAkB*LNOjcHw%14eIQ6O`uiw_xaAW!bWxFEW_yESJ% zf6PWMH*VaRCr8qw^-T=1uI`Qz3-GmO%^LCeg@JcbJWwd$+9(v0V}oHflDdBVy00T+ zLnf9TB{ElZizVjt>C+L%K7anaj(_Ej12|9H1&3;2RJj~6)x{?w2_H~?Sp=H=$*%9}TD~8!!?1$4NjKe3*J2b zfv2^#Rd2^<&z>a^0`Q3iZzLXDR$hJ?85s$L1bpVe+h5#acNA~l{~rJV002ovPDHLk FV1gFd=`sKS From 22d3e197778e3cd8a6e622dd560aac4979de061f Mon Sep 17 00:00:00 2001 From: Om Date: Sun, 17 Aug 2014 00:13:25 -0700 Subject: [PATCH 090/392] Remove android.css since it has been merged into defaults.css --- .../projects/mobiletheme/src/android4x.css | 1398 ----------------- 1 file changed, 1398 deletions(-) delete mode 100644 frameworks/projects/mobiletheme/src/android4x.css diff --git a/frameworks/projects/mobiletheme/src/android4x.css b/frameworks/projects/mobiletheme/src/android4x.css deleted file mode 100644 index 2cdf3f4806..0000000000 --- a/frameworks/projects/mobiletheme/src/android4x.css +++ /dev/null @@ -1,1398 +0,0 @@ -/* -* -* 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. -* -*/ - -@namespace "library://ns.adobe.com/flex/spark"; - - -/* -Default Mobile Flex skins and styles. Defaults sizes are based on -application-dpi=240. -*/ - -ActionBar -{ - chromeColor: #484848; - defaultButtonAppearance: normal; - skinClass: ClassReference("spark.skins.mobile.ActionBarSkin"); - textShadowAlpha: .65; - textShadowColor: #000000; - paddingBottom: 1; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 1; -} - -ActionBar.beveled -{ - paddingLeft: 7; - paddingRight: 7; - titleAlign: center; -} - -ActionBar #titleDisplay -{ - color: #FFFFFF; - fontSize: 28; - fontWeight: bold; -} - -ActionBar ButtonBase -{ - color: #FFFFFF; -} - -ActionBar Group#navigationGroup Button -{ - skinClass: ClassReference("spark.skins.mobile.TransparentNavigationButtonSkin"); -} - -ActionBar Group#navigationGroup Button.emphasized -{ - skinClass: ClassReference("spark.skins.mobile.DefaultTransparentNavigationButtonSkin"); -} - -ActionBar.beveled Group#navigationGroup Button -{ - fontSize: 18; - skinClass: ClassReference("spark.skins.mobile.BeveledBackButtonSkin"); -} - -ActionBar.beveled Group#navigationGroup Button.emphasized -{ - skinClass: ClassReference("spark.skins.mobile.DefaultBeveledBackButtonSkin"); -} - -ActionBar Group#actionGroup Button -{ - skinClass: ClassReference("spark.skins.mobile.TransparentActionButtonSkin"); -} - -ActionBar Group#actionGroup Button.emphasized -{ - skinClass: ClassReference("spark.skins.mobile.DefaultTransparentActionButtonSkin"); -} - -ActionBar.beveled Group#actionGroup Button -{ - fontSize: 18; - skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); -} - -ActionBar.beveled Group#actionGroup Button.emphasized -{ - skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); -} - -BusyIndicator -{ - rotationInterval: 50; -} - -Button -{ - fontWeight: "bold"; - skinClass: ClassReference("spark.skins.mobile.ButtonSkin"); -} - -Button.emphasized -{ - skinClass: ClassReference("spark.skins.mobile.DefaultButtonSkin"); -} - -ButtonBar -{ - fontWeight: "bold"; - skinClass: ClassReference("spark.skins.mobile.ButtonBarSkin"); -} - -ButtonBase -{ - iconPlacement: "left"; -} - -Callout -{ - backgroundColor: #484848; - contentBackgroundAppearance: inset; - contentBackgroundColor: #FFFFFF; - gap: 12; - skinClass: ClassReference("spark.skins.mobile.CalloutSkin"); - borderThickness: NaN; - borderColor: 0; -} - -Callout ViewNavigator -{ - skinClass: ClassReference("spark.skins.mobile.CalloutViewNavigatorSkin"); -} - -Callout ViewNavigator ActionBar -{ - skinClass: ClassReference("spark.skins.mobile.CalloutActionBarSkin"); - defaultButtonAppearance: beveled; - paddingLeft: 0; - paddingRight: 0; -} - -Callout ViewNavigator ActionBar.beveled -{ - paddingLeft: 0; - paddingRight: 0; -} - -Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button -{ - skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); -} - -Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button.emphasized -{ - skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); -} - -Callout#viewNavigatorPopUp -{ - contentBackgroundAppearance: none; -} - -CheckBox -{ - skinClass: ClassReference("spark.skins.mobile.CheckBoxSkin"); -} - -DateSpinner -{ - skinClass: ClassReference("spark.skins.mobile.DateSpinnerSkin"); -} - -HScrollBar -{ - skinClass: ClassReference("spark.skins.mobile.HScrollBarSkin"); -} - -HSlider -{ - skinClass: ClassReference("spark.skins.mobile.HSliderSkin"); -} - -IconItemRenderer -{ - paddingBottom: 12; - paddingTop: 12; - horizontalGap: 10; - verticalGap: 9; - iconDelay: 500; - messageStyleName: "iconItemRendererMessageStyle"; -} - -.iconItemRendererMessageStyle -{ - fontSize: 20; - color: #333333; -} - -Image -{ - skinClass: ClassReference("spark.skins.mobile.ImageSkin"); - enableLoadingState: false; - showErrorSkin : false; - smoothingQuality: "default"; -} - -LabelItemRenderer -{ - paddingBottom: 24; - paddingLeft: 15; - paddingRight: 15; - paddingTop: 24; - verticalAlign: "middle"; -} - -List -{ - skinClass: ClassReference("spark.skins.mobile.ListSkin"); - borderColor: #000000; - borderAlpha: 1; - verticalScrollPolicy: on; - borderVisible: false; -} - -RadioButton -{ - skinClass: ClassReference("spark.skins.mobile.RadioButtonSkin"); -} - -Scroller -{ - touchDelay: 100; -} - -SkinnableContainer -{ - backgroundColor: #FFFFFF; - skinClass: ClassReference("spark.skins.mobile.SkinnableContainerSkin"); -} - -SkinnableTextBase:normalWithPrompt -{ - fontStyle: normal; -} - -SkinnableTextBase:disabledWithPrompt -{ - fontStyle: normal; -} - -SpinnerList -{ - skinClass: ClassReference("spark.skins.mobile.SpinnerListSkin"); -} - -SpinnerListContainer -{ - skinClass: ClassReference("spark.skins.mobile.SpinnerListContainerSkin"); -} - -SpinnerListItemRenderer -{ - paddingTop : 18; - paddingBottom : 18; - paddingLeft : 16; - paddingRight : 16; -} - -SplitViewNavigator -{ - skinClass: ClassReference("spark.skins.mobile.SplitViewNavigatorSkin"); - backgroundColor: #000000; -} - -TabbedViewNavigatorApplication -{ - backgroundColor: #FFFFFF; - skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorApplicationSkin"); -} - -TabbedViewNavigator -{ - skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorSkin"); -} - -TabbedViewNavigator #tabBar -{ - chromeColor: #484848; - color: #FFFFFF; - fontSize: 20; - fontWeight: normal; - iconPlacement: top; - interactionMode: mouse; - skinClass: ClassReference("spark.skins.mobile.TabbedViewNavigatorTabBarSkin"); - textShadowAlpha: .65; - textShadowColor: #000000; -} - -TextArea -{ - paddingBottom: 9; - paddingLeft: 9; - paddingRight: 9; - paddingTop: 9; - showPromptWhenFocused: true; - skinClass: ClassReference("spark.skins.mobile.ScrollingStageTextAreaSkin"); -} - -TextArea VScrollBar -{ - skinClass: ClassReference("spark.skins.mobile.TextAreaVScrollBarSkin"); -} - -TextArea HScrollBar -{ - skinClass: ClassReference("spark.skins.mobile.TextAreaHScrollBarSkin"); -} - -TextInput -{ - paddingBottom: 9; - paddingLeft: 9; - paddingRight: 9; - paddingTop: 9; - showPromptWhenFocused: true; - skinClass: ClassReference("spark.skins.mobile.ScrollingStageTextInputSkin"); -} - -ToggleSwitch -{ - accentColor: #3F7FBA; - color: #FFFFFF; - fontSize: 27; - skinClass: ClassReference("spark.skins.mobile.ToggleSwitchSkin"); - slideDuration: 125; - textShadowAlpha: .65; - textShadowColor: #000000; -} - -View -{ - backgroundColor: #FFFFFF; - skinClass: ClassReference("spark.skins.mobile.SkinnableContainerSkin"); -} - -ViewMenu -{ - skinClass: ClassReference("spark.skins.mobile.ViewMenuSkin"); -} - -ViewMenuItem -{ - iconPlacement : "top"; - chromeColor : #FFFFFF; - paddingBottom: 8; - paddingLeft: 8; - paddingRight: 8; - paddingTop: 8; - skinClass : ClassReference("spark.skins.mobile.ViewMenuItemSkin"); -} - -ViewNavigator -{ - skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); -} - -ViewNavigatorApplication -{ - backgroundColor: #FFFFFF; - skinClass: ClassReference("spark.skins.mobile.ViewNavigatorApplicationSkin"); -} - -VScrollBar -{ - skinClass: ClassReference("spark.skins.mobile.VScrollBarSkin"); -} - -global -{ - /* framework styles */ - modalTransparency: 0; - modalTransparencyBlur: 0; - - /* Looks */ - backgroundAlpha : 1; - backgroundImageFillMode : "scale"; - borderVisible : true; - borderWeight : 1; - contentBackgroundColor: #F0F0F0; - downColor: #E0E0E0; /* set in here and not in framework's default.css because it only is really applicable for touch interactions*/ - selectionColor: #E0E0E0; - alternatingItemColors: #FFFFFF; - focusThickness: 3; - symbolColor: #000000; - fontLookup: "embeddedCFF"; - fontFamily: "_sans"; - fontSize: 24; - color: #000000; - textShadowColor: #FFFFFF; - textShadowAlpha: .55; - - /* Behaviors */ - autoThumbVisibility: true; - fixedThumbSize: false; - fullScreenHideControlsDelay: 3000; - interactionMode: "touch"; - liveDragging: true; - rollOverOpenDelay: 200; - slideDuration: 300; - smoothScrolling: true; - touchDelay: 0; /* default to 0 here, but Scroller sets it to 100 */ -} - -@media (application-dpi: 120) -{ - ActionBar - { - paddingBottom: 1; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 1; - } - - ActionBar.beveled - { - paddingLeft: 4; - paddingRight: 4; - } - - ActionBar #titleDisplay - { - fontSize: 14; - } - - ActionBar.beveled Group#navigationGroup Button - { - fontSize: 9; - } - - ActionBar.beveled Group#actionGroup Button - { - fontSize: 9; - } - - Callout - { - gap: 6; - } - - IconItemRenderer - { - paddingBottom: 6; - paddingTop: 6; - horizontalGap: 6; - verticalGap: 3; - } - - .iconItemRendererMessageStyle - { - fontSize: 11; - } - - LabelItemRenderer - { - paddingBottom: 12; - paddingLeft: 8; - paddingRight: 8; - paddingTop: 12; - } - - SpinnerListItemRenderer - { - paddingTop : 8; - paddingBottom : 8; - paddingLeft : 6; - paddingRight : 6; - } - - TextArea - { - paddingBottom: 4; - paddingLeft: 4; - paddingRight: 4; - paddingTop: 4; - } - - TextInput - { - paddingBottom: 4; - paddingLeft: 4; - paddingRight: 4; - paddingTop: 4; - } - - TabbedViewNavigator #tabBar - { - fontSize: 11; - } - - ToggleSwitch - { - fontSize: 14; - } - - global - { - fontSize: 12; - focusThickness: 2; - } -} - - -@media (application-dpi: 160) -{ - ActionBar - { - paddingBottom: 1; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 1; - } - - ActionBar.beveled - { - paddingLeft: 5; - paddingRight: 5; - } - - ActionBar #titleDisplay - { - fontSize: 18; - } - - ActionBar.beveled Group#navigationGroup Button - { - fontSize: 12; - } - - ActionBar.beveled Group#actionGroup Button - { - fontSize: 12; - } - - Callout - { - gap: 8; - } - - IconItemRenderer - { - paddingBottom: 8; - paddingTop: 8; - horizontalGap: 8; - verticalGap: 6; - } - - .iconItemRendererMessageStyle - { - fontSize: 14; - } - - LabelItemRenderer - { - paddingBottom: 16; - paddingLeft: 10; - paddingRight: 10; - paddingTop: 16; - } - - SpinnerListItemRenderer - { - paddingTop : 12; - paddingBottom : 12; - paddingLeft : 8; - paddingRight : 8; - } - - TextArea - { - paddingBottom: 6; - paddingLeft: 6; - paddingRight: 6; - paddingTop: 6; - } - - TextInput - { - paddingBottom: 6; - paddingLeft: 6; - paddingRight: 6; - paddingTop: 6; - } - - TabbedViewNavigator #tabBar - { - fontSize: 14; - } - - ToggleSwitch - { - fontSize: 18; - } - - global - { - fontSize: 16; - focusThickness: 2; - } -} - -@media (application-dpi: 320) -{ - ActionBar - { - paddingBottom: 2; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 2; - } - - ActionBar.beveled - { - paddingLeft: 10; - paddingRight: 10; - } - - ActionBar #titleDisplay - { - fontSize: 36; - } - - ActionBar.beveled Group#navigationGroup Button - { - fontSize: 24; - } - - ActionBar.beveled Group#actionGroup Button - { - fontSize: 24; - } - - Callout - { - gap: 16; - } - - IconItemRenderer - { - paddingBottom: 16; - paddingTop: 16; - horizontalGap: 16; - verticalGap: 12; - } - - .iconItemRendererMessageStyle - { - fontSize: 26; - } - - LabelItemRenderer - { - paddingBottom: 32; - paddingLeft: 20; - paddingRight: 20; - paddingTop: 32; - } - - SpinnerListItemRenderer - { - paddingTop : 24; - paddingBottom : 24; - paddingLeft : 24; - paddingRight : 24; - } - - TextArea - { - paddingBottom: 12; - paddingLeft: 12; - paddingRight: 12; - paddingTop: 12; - } - - TextInput - { - paddingBottom: 12; - paddingLeft: 12; - paddingRight: 12; - paddingTop: 12; - } - - TabbedViewNavigator #tabBar - { - fontSize: 28; - } - - ToggleSwitch - { - fontSize: 36; - } - - global - { - fontSize: 32; - } -} - -@media (application-dpi: 480) -{ - ActionBar - { - paddingBottom: 4; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 4; - } - - ActionBar.beveled - { - paddingLeft: 15; - paddingRight: 15; - } - - ActionBar #titleDisplay - { - fontSize: 54; - } - - ActionBar.beveled Group#navigationGroup Button - { - fontSize: 36; - } - - ActionBar.beveled Group#actionGroup Button - { - fontSize: 36; - } - - Callout - { - gap: 24; - } - - IconItemRenderer - { - paddingBottom: 24; - paddingTop: 24; - horizontalGap: 24; - verticalGap: 18; - } - - .iconItemRendererMessageStyle - { - fontSize: 42; - } - - LabelItemRenderer - { - paddingBottom: 48; - paddingLeft: 30; - paddingRight: 30; - paddingTop: 48; - } - - SpinnerListItemRenderer - { - paddingTop : 36; - paddingBottom : 36; - paddingLeft : 24; - paddingRight : 24; - } - - TextArea - { - paddingBottom: 18; - paddingLeft: 18; - paddingRight: 18; - paddingTop: 18; - } - - TextInput - { - paddingBottom: 18; - paddingLeft: 18; - paddingRight: 18; - paddingTop: 18; - } - - TabbedViewNavigator #tabBar - { - fontSize: 42; - } - - ToggleSwitch - { - fontSize: 54; - } - - global - { - fontSize: 48; - focusThickness: 6; - } -} - - -@media (application-dpi: 640) -{ - ActionBar - { - paddingBottom: 4; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 4; - } - - ActionBar.beveled - { - paddingLeft: 20; - paddingRight: 20; - } - - ActionBar #titleDisplay - { - fontSize: 72; - } - - ActionBar.beveled Group#navigationGroup Button - { - fontSize: 48; - } - - ActionBar.beveled Group#actionGroup Button - { - fontSize: 48; - } - - Callout - { - gap: 32; - } - - IconItemRenderer - { - paddingBottom: 32; - paddingTop: 32; - horizontalGap: 32; - verticalGap: 36; - } - - .iconItemRendererMessageStyle - { - fontSize: 26; - } - - LabelItemRenderer - { - paddingBottom: 64; - paddingLeft: 40; - paddingRight: 40; - paddingTop: 64; - } - - SpinnerListItemRenderer - { - paddingTop : 48; - paddingBottom : 48; - paddingLeft : 48; - paddingRight : 48; - } - - TextArea - { - paddingBottom: 24; - paddingLeft: 24; - paddingRight: 24; - paddingTop: 24; - } - - TextInput - { - paddingBottom: 24; - paddingLeft: 24; - paddingRight: 24; - paddingTop: 24; - } - - TabbedViewNavigator #tabBar - { - fontSize: 56; - } - - ToggleSwitch - { - fontSize: 72; - } - - global - { - fontSize: 64; - } -} - - -@media (application-dpi: 160) AND (os-platform:"IOS") -{ - TextArea - { - leading: 1.5; - paddingRight: 1.5; - } -} - -@media (application-dpi: 320) AND (os-platform:"IOS") -{ - TextArea - { - leading: 4.5; - paddingRight: 3; - } -} - -/* ios 7 status bar -requires new os-version selector (cf FLEX-FLEX-33949) -*/ - -@media (application-dpi: 120) AND (os-platform:"IOS") AND (min-os-version: 7) -{ - Application - { - osStatusBarHeight: 15; - } -} - -@media (application-dpi: 160) AND (os-platform:"IOS") AND (min-os-version: 7) -{ - Application { - osStatusBarHeight: 20; - } -} - -@media (application-dpi: 240) AND (os-platform:"IOS") AND (min-os-version: 7) -{ - Application - { - osStatusBarHeight: 30; - } -} - -@media (application-dpi: 320) AND (os-platform:"IOS") AND (min-os-version: 7) -{ - Application - { - osStatusBarHeight: 40; - } -} - -@media (application-dpi: 480) AND (os-platform:"IOS") AND (min-os-version: 7) -{ - Application - { - osStatusBarHeight: 60; - } -} - - -/* -Android 4.x specific Mobile Flex skins and styles. Defaults sizes are based on -application-dpi=240. -*/ - -/*Roboto Fonts*/ -@font-face { - src: url("/spark/skins/android4/assets/fonts/Roboto-Regular.ttf"); - fontFamily: RobotoRegular; - embed-as-cff: false; -} - -@font-face { - src: url("/spark/skins/android4/assets/fonts/Roboto-Bold.ttf"); - fontFamily: RobotoBold; - fontWeight: bold; - embed-as-cff: false; -} - -@media (os-platform: "android") AND (min-os-version: "4.1.2") -{ - global - { - primaryAccentColor: #33B5E5; - fontFamily: RobotoRegular; - fontWeight: "normal"; - fontSize: 24; - color: #000000; - textShadowColor: #FFFFFF; - textShadowAlpha: 0; - } - - ActionBar - { - chromeColor: #DEDEDD; - defaultButtonAppearance: normal; - skinClass: ClassReference("spark.skins.android4.ActionBarSkin"); - textShadowAlpha: 0; - textShadowColor: #000000; - paddingBottom: 1; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 1; - } - - ActionBar #titleDisplay - { - color: #333333; - fontSize: 24; - fontFamily: RobotoBold; - } - - ActionBar ButtonBase - { - color: #333333; - fontFamily: RobotoBold; - fontWeight: bold; - } - - ActionBar Group#actionGroup Button - { - skinClass: ClassReference("spark.skins.android4.TransparentActionButtonSkin"); - } - - MobileBusyIndicator - { - skinClass: ClassReference("spark.skins.android4.BusyIndicatorSkin"); - } - - Button - { - fontFamily: RobotoBold; - fontWeight: "bold"; - skinClass: ClassReference("spark.skins.android4.ButtonSkin"); - } - - Button.emphasized - { - skinClass: ClassReference("spark.skins.mobile.DefaultButtonSkin"); - } - - ButtonBar - { - skinClass: ClassReference("spark.skins.android4.ButtonBarSkin"); - fontFamily: RobotoBold; - fontWeight: "bold"; - } - - Callout - { - backgroundColor: #33B5E5; - contentBackgroundColor: #FFFFFF; - gap: 12; - skinClass: ClassReference("spark.skins.android4.CalloutSkin"); - borderThickness: 0; - borderColor: 0; - } - - Callout ViewNavigator ActionBar - { - skinClass: ClassReference("spark.skins.mobile.CalloutActionBarSkin"); - defaultButtonAppearance: none; - paddingLeft: 0; - paddingRight: 0; - } - - Callout ViewNavigator ActionBar.beveled - { - paddingLeft: 0; - paddingRight: 0; - } - - Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button - { - skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); - } - - Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button.emphasized - { - skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); - } - - Callout#viewNavigatorPopUp - { - contentBackgroundAppearance: none; - } - - CheckBox - { - skinClass: ClassReference("spark.skins.android4.CheckBoxSkin"); - chromeColor: #FFFFFF; - } - - HScrollBar - { - skinClass: ClassReference("spark.skins.android4.HScrollBarSkin"); - thumbColor: #333333; - } - - HSlider - { - skinClass: ClassReference("spark.skins.android4.HSliderSkin"); - fontFamily: RobotoBold; - fontWeight: bold; - } - - RadioButton - { - skinClass: ClassReference("spark.skins.android4.RadioButtonSkin"); - chromeColor: #FFFFFF; - } - - SpinnerList - { - skinClass: ClassReference("spark.skins.android4.SpinnerListSkin"); - } - - SpinnerListContainer - { - skinClass: ClassReference("spark.skins.android4.SpinnerListContainerSkin"); - } - - SpinnerListItemRenderer - { - paddingTop : 18; - paddingBottom : 18; - paddingLeft : 6; - paddingRight : 6; - } - - TabbedViewNavigator #tabBar - { - fontFamily: RobotoRegular; - chromeColor: #484848; - color: #000000; - fontSize: 20; - fontWeight: normal; - iconPlacement: top; - interactionMode: mouse; - skinClass: ClassReference("spark.skins.android4.TabbedViewNavigatorTabBarSkin"); - textShadowAlpha: 0; - textShadowColor: #000000; - } - - TextInput - { - skinClass: ClassReference("spark.skins.android4.StageTextInputSkin"); - contentBackgroundAlpha: 0; - focusColor: #33B5E5; - borderColor: #4e4e4e; - contentBackgroundColor: #DEDEDD; - contentBackgroundBorder: "flat"; - fontFamily: RobotoRegular; - selectionHighlighting: "never"; - focusEnabled:"false"; - focusThickness: 0; - } - - TextArea - { - skinClass: ClassReference("spark.skins.android4.StageTextAreaSkin"); - contentBackgroundAlpha: 1; - contentBackgroundBorder: "rectangle"; - contentBackgroundColor: #DEDEDD; - focusColor: #33B5E5; - fontFamily: RobotoRegular; - borderColor: #4e4e4e; - selectionHighlighting: "never"; - focusEnabled:"false"; - focusThickness: 0; - } - - ToggleSwitch - { - accentColor: #3F7FBA; - color: #FFFFFF; - skinClass: ClassReference("spark.skins.android4.ToggleSwitchSkin"); - slideDuration: 125; - textShadowAlpha: .65; - textShadowColor: #000000; - } - - VScrollBar - { - skinClass: ClassReference("spark.skins.android4.VScrollBarSkin"); - thumbColor: #333333; - } - - ViewMenu - { - skinClass: ClassReference("spark.skins.android4.ViewMenuSkin"); - } - - ViewMenuItem - { - iconPlacement : "left"; - chromeColor : #FFFFFF; - focusColor: #DEDEDD; - paddingBottom: 8; - paddingLeft: 8; - paddingRight: 8; - paddingTop: 8; - skinClass : ClassReference("spark.skins.android4.ViewMenuItemSkin"); - } - - ViewNavigator - { - skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); - } -} -@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 120) -{ - global - { - fontSize: 12; - focusThickness: 2; - } - - ActionBar.beveled - { - paddingLeft: 4; - paddingRight: 4; - } - - ActionBar #titleDisplay - { - fontSize: 14; - } - - SpinnerListItemRenderer - { - paddingTop : 9; - paddingBottom : 9; - paddingLeft : 3; - paddingRight : 3; - } - - TabbedViewNavigator #tabBar - { - fontSize: 11; - } -} - -@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 160) -{ - global - { - fontSize: 16; - focusThickness: 2; - } - - ActionBar - { - paddingBottom: 1; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 1; - } - - ActionBar #titleDisplay - { - fontSize: 18; - } - - SpinnerListItemRenderer - { - paddingTop : 12; - paddingBottom : 12; - paddingLeft : 4; - paddingRight : 4; - } - - TabbedViewNavigator #tabBar - { - fontSize: 14; - } -} - -@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 320) -{ - global - { - fontSize: 32; - } - - ActionBar - { - paddingBottom: 2; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 2; - } - - ActionBar #titleDisplay - { - fontSize: 36; - } - - SpinnerListItemRenderer - { - paddingTop : 24; - paddingBottom : 24; - paddingLeft : 8; - paddingRight : 8; - } - - TabbedViewNavigator #tabBar - { - fontSize: 28; - } -} - -@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 480) -{ - global - { - fontSize: 48; - focusThickness: 6; - } - - ActionBar - { - paddingBottom: 4; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 4; - } - - ActionBar #titleDisplay - { - fontSize: 54; - } - - SpinnerListItemRenderer - { - paddingTop : 36; - paddingBottom : 36; - paddingLeft : 12; - paddingRight : 12; - } - - - TabbedViewNavigator #tabBar - { - fontSize: 42; - } -} - -@media (os-platform: "android") AND (min-os-version: "4.1.2") AND (application-dpi: 640) -{ - global - { - fontSize: 64; - } - - ActionBar - { - paddingBottom: 4; - paddingLeft: 0; - paddingRight: 0; - paddingTop: 4; - } - - ActionBar #titleDisplay - { - fontSize: 72; - } - - SpinnerListItemRenderer - { - paddingTop : 48; - paddingBottom : 48; - paddingLeft : 16; - paddingRight : 16; - } - - TabbedViewNavigator #tabBar - { - fontSize: 56; - } -} From 1f8c801d33406b7f1f6119fe93788f0c30fd5c73 Mon Sep 17 00:00:00 2001 From: dloverin Date: Mon, 18 Aug 2014 21:46:03 -0400 Subject: [PATCH 091/392] Fix for FLEX-34323. Allow mustella test directory to be located at a directory defined by the environment variable "MUSTELLA_TEST_DIR". Use FLEX_HOME for sdk.dir with a fallback of "${basedir}/.." Look for cygwin shell in /cygwin64/bin/sh.exe as well as /cygwin/bin/sh. Add init() method documentation to README file. --- mustella/README | 23 +++ .../as3/src/mustella/ExcludeFileLocation.as | 117 ++++++++-------- .../src/mustella/ExcludeFileLocationApollo.as | 131 ++++++++++-------- mustella/as3/src/mustella/UnitTester.as | 17 ++- mustella/build.xml | 55 +++++--- .../src/mustella/CompileMustellaSwfs.java | 25 ++-- mustella/mini_run.sh | 10 +- 7 files changed, 230 insertions(+), 148 deletions(-) diff --git a/mustella/README b/mustella/README index 39f0509af8..d3c3570c2c 100644 --- a/mustella/README +++ b/mustella/README @@ -60,3 +60,26 @@ Basic Commands ./mini_run.sh -caseName=datagrid_properties_columns_0 tests/components/DataGrid/DataGrid_SparkSkin/Properties/datagrid_properties_columns.mxml This command runs a specific test case from a specific test file. + +Alternate Location of Test Files +================================ + You can create Mustella tests for your own components and put them in your own test directory outside of the Flex SDK and Mustella source directories. To do this set the MUSTELLA_TEST_DIR environment variable to the absolute path of the directory containing your tests. Your test directory will need to include the exclude files (ExcludeList*.txt) for the platforms you are testing on but the files may be empty if you have no tests to exclude. After setting MUSTELLA_TEST_DIR, mini_run.sh will look in the new directory for tests to run instead of the 'tests' subdirectory of mustella. + + To support finding the location of the exclude file during test execution some changes to a test script’s init() method are required. The requirement is the UnitTester.mustellaTestDir property must be set to the url of the test directory. Below is code that sets UnitTester.mustellaTestDir using a relative path from the test script. + + import mx.core.FlexGlobals; + import mx.utils.LoaderUtil; + + public static function init(o:DisplayObject):void + { + // Set the directory where the exclude file loads. + var url:String = LoaderUtil.createAbsoluteURL(o.loaderInfo.url, "../../../"); + if (!url) + throw new Error("could not find mustella test directory"); + + if (url.charAt(url.length - 1) == "/") + url = url.substring(0, url.length - 1); + + mustellaTestDir = url; + } + diff --git a/mustella/as3/src/mustella/ExcludeFileLocation.as b/mustella/as3/src/mustella/ExcludeFileLocation.as index 268cfefcaf..7b5ee20ba5 100644 --- a/mustella/as3/src/mustella/ExcludeFileLocation.as +++ b/mustella/as3/src/mustella/ExcludeFileLocation.as @@ -19,9 +19,9 @@ package { import flash.display.DisplayObject; +import flash.events.Event; import flash.net.URLLoader; import flash.net.URLRequest; -import flash.events.Event; import flash.system.Capabilities; [Mixin] @@ -40,7 +40,7 @@ public class ExcludeFileLocation private static var loader:URLLoader; public static var url:String; - public static var mustellaDir:String; + public static var mustellaTestDir:String; public static var frontURL:String; public static var domain:String; @@ -65,51 +65,59 @@ public class ExcludeFileLocation trace ("Hello from excludes at: " + new Date()); - - var os:String = Capabilities.os; - - - if (os.indexOf ("Windows") != -1) - { - excludeFile = "ExcludeListWin.txt"; - } else if (os.indexOf ("Mac") != -1) - { - excludeFile = "ExcludeListMac.txt"; - } else if (os.indexOf ("Linux") != -1) - { - excludeFile = "ExcludeListLinux.txt"; - } else - { - trace ("Excludes: bad: we didn't see an OS we liked: " + os); - excludeFile = "ExcludeListWin.txt"; - } - - - _root = root; - - url = root.loaderInfo.url; - - mustellaDir = url.substring (0, url.indexOf ("mustella/tests")+14); - - frontURL = url.substring (0, url.indexOf (":")); - - domain = url.substring (url.indexOf (":")+3); - domain = domain.substring (0, domain.indexOf ("/")); - - if (Capabilities.playerType == "PlugIn" || Capabilities.playerType == "ActiveX") - { - loadTryBrowser(); - } else - { - loadTryNormal(); - - } - - } - - - - public static function loadTryBrowser():void + _root = root; + + // set up callback to load excludes after + // UnitTester has initialized. + UnitTester.loadExcludeFile = loadExcludeFile; + + } + + public static function loadExcludeFile():void + { + var os:String = Capabilities.os; + + + if (os.indexOf ("Windows") != -1) + { + excludeFile = "ExcludeListWin.txt"; + } else if (os.indexOf ("Mac") != -1) + { + excludeFile = "ExcludeListMac.txt"; + } else if (os.indexOf ("Linux") != -1) + { + excludeFile = "ExcludeListLinux.txt"; + } else + { + trace ("Excludes: bad: we didn't see an OS we liked: " + os); + excludeFile = "ExcludeListWin.txt"; + } + + url = _root.loaderInfo.url; + + // allow application to override mustella test dir + if (!UnitTester.mustellaTestDir) + mustellaTestDir = url.substring (0, url.indexOf ("mustella/tests")+14); + else + mustellaTestDir = UnitTester.mustellaTestDir; + + frontURL = url.substring (0, url.indexOf (":")); + + domain = url.substring (url.indexOf (":")+3); + domain = domain.substring (0, domain.indexOf ("/")); + + if (Capabilities.playerType == "PlugIn" || Capabilities.playerType == "ActiveX") + { + loadTryBrowser(); + } else + { + loadTryNormal(); + + } + + } + + public static function loadTryBrowser():void { trace ("excludes loadTryBrowser at: " + new Date().toString()); @@ -148,10 +156,9 @@ public class ExcludeFileLocation { trace ("Exclude: in the web read error handler"); - if (!triedNormal) + if (!triedNormal) { - loadTryNormal(); - + loadTryNormal(); } } @@ -166,11 +173,11 @@ public class ExcludeFileLocation var useFile:String; - useFile = mustellaDir +"/" + excludeFile; + useFile = mustellaTestDir +"/" + excludeFile; trace ("Exclude: try load from: " + useFile); - var req:URLRequest = new URLRequest(useFile); + var req:URLRequest = new URLRequest(useFile); loader = new URLLoader(); loader.addEventListener("complete", completeHandler2); loader.addEventListener("ioError", errorHandler2); @@ -181,8 +188,8 @@ public class ExcludeFileLocation private static function errorHandler2(event:Event):void { - trace ("Exclude: error in the exclude file load " +event); - if (!triedBrowser) + trace ("Exclude: error in the exclude file load " +event); + if (!triedBrowser) { loadTryBrowser(); } @@ -192,7 +199,7 @@ public class ExcludeFileLocation private static function completeHandler2(event:Event):void { - trace ("Excludes: Reading from file system at "+mustellaDir ); + trace ("Excludes: Reading from file system at "+mustellaTestDir ); postProcessData(event); } private static function postProcessData(event:Event):void diff --git a/mustella/as3/src/mustella/ExcludeFileLocationApollo.as b/mustella/as3/src/mustella/ExcludeFileLocationApollo.as index 73f730e01f..56bb0a48b2 100644 --- a/mustella/as3/src/mustella/ExcludeFileLocationApollo.as +++ b/mustella/as3/src/mustella/ExcludeFileLocationApollo.as @@ -41,7 +41,7 @@ public class ExcludeFileLocationApollo private static var loader:URLLoader; private static var url:String; - private static var mustellaDir:String; + private static var mustellaTestDir:String; private static var frontURL:String; private static var domain:String; @@ -66,64 +66,79 @@ public class ExcludeFileLocationApollo trace ("Hello from excludes at: " + new Date()); - var os:String = Capabilities.os; + _root = root; + + // set up callback to load excludes after + // UnitTester has initialized. + UnitTester.loadExcludeFile = loadExcludeFile; + + } - // This seems to be a timing issue which pops up on some machines. - if( UnitTester.cv == null ){ - UnitTester.cv = new ConditionalValue(); - } - if( (UnitTester.cv.os != null) && (UnitTester.cv.os.toLowerCase() == DeviceNames.ANDROID.toLowerCase()) ){ - mustellaDir = ".."; - excludeFile = UnitTester.excludeFile; - trace("Doing Android style exclude. Checking in " + mustellaDir + "/" + excludeFile); - }else if( (UnitTester.cv.os != null) && (UnitTester.cv.os.toLowerCase() == DeviceNames.IOS.toLowerCase()) ){ - mustellaDir = File.documentsDirectory.url; - excludeFile = UnitTester.excludeFile; - }else if (os.indexOf ("QNX") > -1) { - mustellaDir = ".."; - excludeFile = UnitTester.excludeFile; - trace("Doing QNX style exclude. Checking in " + mustellaDir + "/" + excludeFile); - } - else{ - - if (os.indexOf ("Windows") != -1) - { - excludeFile = "ExcludeListWinAIR.txt"; - } else if (os.indexOf ("Mac") != -1) - { - excludeFile = "ExcludeListMacAIR.txt"; - } else if (os.indexOf ("Linux") != -1) - { - excludeFile = "ExcludeListLinux.txt"; - } else - { - trace ("Excludes: bad: we didn't see an OS we liked: " + os); - excludeFile = "ExcludeListWin.txt"; - } - - _root = root; - - url = root.loaderInfo.url; - - if (url.indexOf("app:")!=-1) - { - ApolloFilePath.init (root); - url = encodeURI2(ApolloFilePath.apolloAdjust(url)); - trace ("Adjusting path for AIR to: " + url); - // url = adjustPath(url); - } - - mustellaDir = url.substring (0, url.indexOf ("mustella/tests")+14); - frontURL = url.substring (0, url.indexOf (":")); - domain = url.substring (url.indexOf (":")+3); - domain = domain.substring (0, domain.indexOf ("/")); - } - - loadTryNormal(); - } + public static function loadExcludeFile():void + { + var os:String = Capabilities.os; + + // This seems to be a timing issue which pops up on some machines. + if( UnitTester.cv == null ){ + UnitTester.cv = new ConditionalValue(); + } + + if( (UnitTester.cv.os != null) && (UnitTester.cv.os.toLowerCase() == DeviceNames.ANDROID.toLowerCase()) ){ + mustellaTestDir = ".."; + excludeFile = UnitTester.excludeFile; + trace("Doing Android style exclude. Checking in " + mustellaTestDir + "/" + excludeFile); + }else if( (UnitTester.cv.os != null) && (UnitTester.cv.os.toLowerCase() == DeviceNames.IOS.toLowerCase()) ){ + mustellaTestDir = File.documentsDirectory.url; + excludeFile = UnitTester.excludeFile; + }else if (os.indexOf ("QNX") > -1) { + mustellaTestDir = ".."; + excludeFile = UnitTester.excludeFile; + trace("Doing QNX style exclude. Checking in " + mustellaTestDir + "/" + excludeFile); + } + else{ + + if (os.indexOf ("Windows") != -1) + { + excludeFile = "ExcludeListWinAIR.txt"; + } else if (os.indexOf ("Mac") != -1) + { + excludeFile = "ExcludeListMacAIR.txt"; + } else if (os.indexOf ("Linux") != -1) + { + excludeFile = "ExcludeListLinux.txt"; + } else + { + trace ("Excludes: bad: we didn't see an OS we liked: " + os); + excludeFile = "ExcludeListWin.txt"; + } + + url = _root.loaderInfo.url; + + if (url.indexOf("app:")!=-1) + { + ApolloFilePath.init (_root); + url = encodeURI2(ApolloFilePath.apolloAdjust(url)); + trace ("Adjusting path for AIR to: " + url); + // url = adjustPath(url); + } + + // allow application to override mustella test dir + if (!UnitTester.mustellaTestDir) + mustellaTestDir = url.substring (0, url.indexOf ("mustella/tests")+14); + else + mustellaTestDir = UnitTester.mustellaTestDir; + + frontURL = url.substring (0, url.indexOf (":")); + domain = url.substring (url.indexOf (":")+3); + domain = domain.substring (0, domain.indexOf ("/")); + } + + loadTryNormal(); + + } - private static function apolloAdjust(url:String):String + private static function apolloAdjust(url:String):String { var swf:String = _root.loaderInfo.url; @@ -227,7 +242,7 @@ public class ExcludeFileLocationApollo var useFile:String; - useFile = mustellaDir +"/" + excludeFile; + useFile = mustellaTestDir +"/" + excludeFile; trace ("Exclude: try load from: " + useFile); @@ -253,7 +268,7 @@ public class ExcludeFileLocationApollo private static function completeHandler2(event:Event):void { - trace ("Excludes: Reading from file system at "+mustellaDir ); + trace ("Excludes: Reading from file system at "+mustellaTestDir ); postProcessData(event); } private static function postProcessData(event:Event):void diff --git a/mustella/as3/src/mustella/UnitTester.as b/mustella/as3/src/mustella/UnitTester.as index a558b5851d..37fe2a1458 100644 --- a/mustella/as3/src/mustella/UnitTester.as +++ b/mustella/as3/src/mustella/UnitTester.as @@ -95,6 +95,18 @@ public class UnitTester extends EventDispatcher **/ public static var excludeFile:String = ""; + /** + * The location of the mustella test directory. + */ + public static var mustellaTestDir:String = ""; + + /** + * Set by either ExcludeFileLocation or ExcludeFileLocationApollo so they + * can be called back to load the exclude file after mustellaTestDir has + * been set in the init() method. + */ + public static var loadExcludeFile:Function; + /** * This is a placeholder. We don't do portrait and landscape runs right now * and probablay won't. Delete. @@ -153,6 +165,9 @@ public class UnitTester extends EventDispatcher public static function init(root:DisplayObject):void { + if (waitForExcludes && loadExcludeFile != null) + loadExcludeFile(); + // don't let child swfs override this if (!_root) _root = root; @@ -351,7 +366,7 @@ public class UnitTester extends EventDispatcher var q:QName = new QName(mx_internal, "hideCursor"); g[q] = true; } - } + } } /** diff --git a/mustella/build.xml b/mustella/build.xml index 6183026b4e..12c5e0a26b 100644 --- a/mustella/build.xml +++ b/mustella/build.xml @@ -22,12 +22,15 @@ - - - - + + + + + + + @@ -64,7 +67,10 @@ - + + + + - + @@ -178,7 +186,7 @@ - + @@ -187,21 +195,21 @@ - + - + - + @@ -657,10 +665,11 @@ - + + @@ -697,7 +706,7 @@ - run_id=${current.run.id} + run_id=${current.run.id} @@ -729,7 +738,7 @@ - + @@ -890,7 +899,7 @@ - + @@ -944,7 +953,7 @@ - + @@ -1115,21 +1124,21 @@ - + - + - + - + @@ -1364,7 +1373,7 @@ - + run id is ${current.run.id} @@ -1439,7 +1448,7 @@ - + @@ -1910,7 +1919,7 @@ - + diff --git a/mustella/java/src/mustella/CompileMustellaSwfs.java b/mustella/java/src/mustella/CompileMustellaSwfs.java index 2f65d6bc33..ba2872eec4 100644 --- a/mustella/java/src/mustella/CompileMustellaSwfs.java +++ b/mustella/java/src/mustella/CompileMustellaSwfs.java @@ -107,6 +107,8 @@ public class CompileMustellaSwfs extends Thread { private static String mustella_framework_dir = null; + private static String mustella_test_dir = null; + private static String apollo_location = ""; private static String file_of_tests = ""; @@ -321,6 +323,11 @@ public class CompileMustellaSwfs extends Thread { } catch (Exception e) { } + try { + mustella_test_dir = System.getProperty ("sdk.mustella.dir"); + } catch (Exception e) { + } + try { flex_version = Double.parseDouble(System.getProperty ("flex_version")); } catch (Exception e) { @@ -398,7 +405,6 @@ public class CompileMustellaSwfs extends Thread { } - if (System.getProperty ("socket_mixin")!=null) { socket_mixin = System.getProperty ("socket_mixin"); if (socket_mixin != null && !socket_mixin.equals ("")) @@ -697,7 +703,6 @@ public String finalFixUp (String s) { /// System.out.println ("This is the filename I've fixed: " + s); - return "-includes=" +s; } @@ -867,8 +872,10 @@ public void compileMxml(String mxml, ArrayList ermineArgs) { } } - front = front.substring (front.indexOf("mustella" + File.separator + "tests")+15); front = front.replaceAll ("\\\\", "/") + "/"; + int test_index = front.indexOf(mustella_test_dir); + int test_length = mustella_test_dir.length(); + front = front.substring (test_index + test_length + 1); alinc.add ("\"" + transformName( front + extract, "") + "\": 1,"); alinc.add ("\"" + transformName( front + extract, "") + "$" + include_list + "\": 1"); @@ -1038,20 +1045,20 @@ public void compileMxml(String mxml, ArrayList ermineArgs) { compiler.setDir(dir); - /** + /** System.out.println ("************"); System.out.println ("************"); String defaultArgsDebugString = new String(); - for(int i = 0; i < defaultArgs.length; ++i){ - defaultArgsDebugString += defaultArgs[i]; + for(int i = 0; i < defaultArgs.size(); ++i){ + defaultArgsDebugString += defaultArgs.get(i); } - + System.out.println ("mxml: " + mxml); System.out.println ("defaultArgs: " + defaultArgsDebugString); **/ - + compiler.compile(mxml, defaultArgs); RuntimeExecHelper rh = compiler.getRuntimeExecHelper(); @@ -1061,7 +1068,7 @@ public void compileMxml(String mxml, ArrayList ermineArgs) { // System.out.println("rh output: " + rh.getOutputText()); // System.out.println("rh error out: " + rh.getErrorText()); // System.out.println("exit value=" + rh.getExitValue()); - + String failedFile=null; // InsertErrorResult ier = null; diff --git a/mustella/mini_run.sh b/mustella/mini_run.sh index 96e91397ed..5aeca2e510 100755 --- a/mustella/mini_run.sh +++ b/mustella/mini_run.sh @@ -551,10 +551,16 @@ do ### set a -D property for ant run_this_script. ### otherwise, run a whole directory ### the other args are the same, however. :( + mustellaTestDir=${MUSTELLA_TEST_DIR} - tmpx=tests/${i} - # echo "Looking for $tmpx" + if [ ! -d "$mustellaTestDir" ] + then + mustellaTestDir="tests"; + fi + tmpx=$mustellaTestDir/${i} + + # echo "Looking for $tmpx" end=`echo $tmpx | awk -F"." '{print $NF}'` if [ ! -f "$tmpx" ] && [ ! -d "$tmpx" ] From 827297fc6c0556f3560b1c670ddc80facede5cd5 Mon Sep 17 00:00:00 2001 From: Justin Mclean Date: Sun, 24 Aug 2014 14:08:36 +1000 Subject: [PATCH 092/392] looks like US locale has crept back in, other locale differences (i.e. date format) are fine however --- .../projects/framework/bundles/en_AU/validators.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frameworks/projects/framework/bundles/en_AU/validators.properties b/frameworks/projects/framework/bundles/en_AU/validators.properties index e70cd91ae2..8a5a6feb3c 100644 --- a/frameworks/projects/framework/bundles/en_AU/validators.properties +++ b/frameworks/projects/framework/bundles/en_AU/validators.properties @@ -100,9 +100,9 @@ formatError=Configuration error: Incorrect formatting string. invalidCharErrorEV=Your email address contains invalid characters. invalidDomainErrorEV=The domain in your email address is incorrectly formatted. invalidIPDomainError=The IP domain in your email address is incorrectly formatted. -invalidPeriodsInDomainError=The domain in your email address has consecutive periods. +invalidPeriodsInDomainError=The domain in your email address has consecutive full stops. missingAtSignError=An at sign (@) is missing in your email address. -missingPeriodInDomainError=The domain in your email address is missing a period. +missingPeriodInDomainError=The domain in your email address is missing a full stop. missingUsernameError=The user name in your email address is missing. tooManyAtSignsError=Your email address contains too many @ characters. From 524cdbe33da270e1ce0b7b4f068bfdcf0be87f31 Mon Sep 17 00:00:00 2001 From: Justin Mclean Date: Tue, 26 Aug 2014 23:12:57 +1000 Subject: [PATCH 093/392] FLEX-34476 Added RichTextEditor --- frameworks/projects/experimental/spark-manifest.xml | 2 ++ frameworks/projects/experimental/src/ExperimentalClasses.as | 1 + .../experimental/src/spark/skins/ColorPickerButtonSkin.mxml | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/frameworks/projects/experimental/spark-manifest.xml b/frameworks/projects/experimental/spark-manifest.xml index 803a6f4231..2a92ed41b4 100644 --- a/frameworks/projects/experimental/spark-manifest.xml +++ b/frameworks/projects/experimental/spark-manifest.xml @@ -46,4 +46,6 @@ + + diff --git a/frameworks/projects/experimental/src/ExperimentalClasses.as b/frameworks/projects/experimental/src/ExperimentalClasses.as index b11d626939..33024dab68 100644 --- a/frameworks/projects/experimental/src/ExperimentalClasses.as +++ b/frameworks/projects/experimental/src/ExperimentalClasses.as @@ -32,6 +32,7 @@ package import spark.components.itemRenderers.MenuItemRenderer; MenuItemRenderer; import spark.components.listClasses.IListItemRenderer; IListItemRenderer; import spark.components.supportClasses.IDropDownContainer; IDropDownContainer; + import spark.components.RichTextEditor; RichTextEditor; import spark.containers.supportClasses.DeferredCreationPolicy; DeferredCreationPolicy; import spark.containers.Accordion; Accordion; import spark.events.ColorChangeEvent; ColorChangeEvent; diff --git a/frameworks/projects/experimental/src/spark/skins/ColorPickerButtonSkin.mxml b/frameworks/projects/experimental/src/spark/skins/ColorPickerButtonSkin.mxml index 10dfc66dd7..f8615b2206 100644 --- a/frameworks/projects/experimental/src/spark/skins/ColorPickerButtonSkin.mxml +++ b/frameworks/projects/experimental/src/spark/skins/ColorPickerButtonSkin.mxml @@ -20,7 +20,7 @@ //////////////////////////////////////////////////////////////////////////////// --> + xmlns:fb="http://ns.adobe.com/flashbuilder/2009" minWidth="21" minHeight="21"> Date: Tue, 26 Aug 2014 23:16:17 +1000 Subject: [PATCH 094/392] FLEX-34476 Added RichTextEditor --- .../experimental/assets/tools/bold.png | Bin 0 -> 154 bytes .../experimental/assets/tools/bullet.png | Bin 0 -> 156 bytes .../experimental/assets/tools/center.png | Bin 0 -> 159 bytes .../experimental/assets/tools/italic.png | Bin 0 -> 165 bytes .../experimental/assets/tools/justify.png | Bin 0 -> 148 bytes .../experimental/assets/tools/left.png | Bin 0 -> 163 bytes .../experimental/assets/tools/link.png | Bin 0 -> 166 bytes .../experimental/assets/tools/right.png | Bin 0 -> 160 bytes .../experimental/assets/tools/underline.png | Bin 0 -> 167 bytes .../src/spark/components/RichTextEditor.as | 226 ++++++++++++++++ .../richTextEditorClasses/AlignTool.mxml | 128 +++++++++ .../richTextEditorClasses/BoldTool.mxml | 89 ++++++ .../richTextEditorClasses/BulletTool.mxml | 193 +++++++++++++ .../richTextEditorClasses/ColorTool.mxml | 84 ++++++ .../richTextEditorClasses/FontTool.mxml | 96 +++++++ .../richTextEditorClasses/ItalicTool.mxml | 89 ++++++ .../richTextEditorClasses/LinkTool.mxml | 255 ++++++++++++++++++ .../RichTextEditorToolBar.mxml | 105 ++++++++ .../richTextEditorClasses/SizeTool.mxml | 97 +++++++ .../richTextEditorClasses/UnderlineTool.mxml | 90 +++++++ .../src/spark/skins/AlignToolSkin.mxml | 86 ++++++ 21 files changed, 1538 insertions(+) create mode 100755 frameworks/projects/experimental/assets/tools/bold.png create mode 100755 frameworks/projects/experimental/assets/tools/bullet.png create mode 100755 frameworks/projects/experimental/assets/tools/center.png create mode 100755 frameworks/projects/experimental/assets/tools/italic.png create mode 100755 frameworks/projects/experimental/assets/tools/justify.png create mode 100755 frameworks/projects/experimental/assets/tools/left.png create mode 100755 frameworks/projects/experimental/assets/tools/link.png create mode 100755 frameworks/projects/experimental/assets/tools/right.png create mode 100755 frameworks/projects/experimental/assets/tools/underline.png create mode 100644 frameworks/projects/experimental/src/spark/components/RichTextEditor.as create mode 100644 frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml create mode 100644 frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml create mode 100644 frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml create mode 100644 frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml create mode 100644 frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml create mode 100644 frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml create mode 100644 frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml create mode 100755 frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml create mode 100644 frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml create mode 100644 frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml create mode 100644 frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml diff --git a/frameworks/projects/experimental/assets/tools/bold.png b/frameworks/projects/experimental/assets/tools/bold.png new file mode 100755 index 0000000000000000000000000000000000000000..04abf6d6759b448bbd55c3fe9ecfe82dada8d520 GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv`!T#~$+>@vmgqk%%|o-U3d5|@(`6od>8NgO+N?Egj8 s^Yd)Er>7rM;|y+CGc(g7(zlj@VP}=Z9i=+u7eFlxp00i_>zopr0Ou?!WdHyG literal 0 HcmV?d00001 diff --git a/frameworks/projects/experimental/assets/tools/bullet.png b/frameworks/projects/experimental/assets/tools/bullet.png new file mode 100755 index 0000000000000000000000000000000000000000..aa107ba917a5060d31eaa908c9c7becfedc44a3b GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv`!T#~$6QXvr+9Dzcbo-U3d5|@)x5)u*&%*_6sKXBkc u!M8V&ZW7Xxb8M@pP3%lO!0@=EM43S)Rf5Z^N$Vd_6N9I#pUXO@geCw~7b{8t literal 0 HcmV?d00001 diff --git a/frameworks/projects/experimental/assets/tools/center.png b/frameworks/projects/experimental/assets/tools/center.png new file mode 100755 index 0000000000000000000000000000000000000000..68428a2ed8ed9b1872599145fb6875d9233fdd59 GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv`!T#~$+MqUTBD}X{eo-U3d5|@)x9GH$BJNAF#&-C;2 yQd1N}4U&vGWhbNf4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv`!T#~$+!fY$8)qp}qo-U3d5|@(`6od>GaZ5-`|95{j z-?n<1;PSlWFVdQ&MBb@ E0HJy=&j0`b literal 0 HcmV?d00001 diff --git a/frameworks/projects/experimental/assets/tools/justify.png b/frameworks/projects/experimental/assets/tools/justify.png new file mode 100755 index 0000000000000000000000000000000000000000..5ea6b2a821d16c479fa914545a919744ec7a1f7a GIT binary patch literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv`!T#~$60$X}5f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv`!T#~$+ng^CXngSFu@N{tuk+__kl8}&KU}pC3d;`n5 zdA6rdIOruDNmH^?ILWq0ZPL@QraYCKA#SVMl^K?~i?A85J@y@_m%-E3&t;ucLK6Vo CQZIV| literal 0 HcmV?d00001 diff --git a/frameworks/projects/experimental/assets/tools/link.png b/frameworks/projects/experimental/assets/tools/link.png new file mode 100755 index 0000000000000000000000000000000000000000..f771236a2e6e72659bc313c54de293a2827f9381 GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv`!T#|xJ@;Sj53V=e!o-U3d5|@(`7MLZZBqSsln3?_K z7q+PWrc-8^!Z?E|CrRgoOo?ZTbVNZU=i|xmjx{nfa9>f%-k6vy57f@!>FVdQ&MBb@ E0HE$Hng9R* literal 0 HcmV?d00001 diff --git a/frameworks/projects/experimental/assets/tools/right.png b/frameworks/projects/experimental/assets/tools/right.png new file mode 100755 index 0000000000000000000000000000000000000000..455cc6515c5b8c4137b951ccb15a7c1024041659 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv`!T#~$+*2XfE?*N5#JzX3_BrYeX9AN6{?ftL(`TRWF z(f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv`!T#~$+3Og&K41hu=o-U3d5|@(`8kmxkAOAmg?AZUp zk257YSezXdm5tbMPUiuF^z-vlQxrrEl8iZJC!`oLoOR;M&FC?$0qSS)boFyt=akR{ E04ez`W&i*H literal 0 HcmV?d00001 diff --git a/frameworks/projects/experimental/src/spark/components/RichTextEditor.as b/frameworks/projects/experimental/src/spark/components/RichTextEditor.as new file mode 100644 index 0000000000..1507d9e7a2 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/RichTextEditor.as @@ -0,0 +1,226 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.components +{ + import flash.events.Event; + import flash.utils.Dictionary; + import flashx.textLayout.conversion.ConversionType; + import flashx.textLayout.conversion.TextConverter; + import flashx.textLayout.elements.TextFlow; + import spark.components.richTextEditorClasses.RichTextEditorToolBar; + import spark.events.TextOperationEvent; + + // for asdoc + [Experimental] + [Event(name = "change", type = "flash.events.Event")] + [Style(name = "borderColor", inherit = "no", type = "unit")] + [Style(name = "focusColor", inherit = "yes", type = "unit")] + public class RichTextEditor extends Group + { + private var _htmlText:String; + private var _htmlTextChanged:Boolean = false; + private var _prompt:String = ""; + private var _stylesChanged:Dictionary = new Dictionary; + private var _text:String; + private var _textArea:TextArea; + private var _textFlow:TextFlow; + + public function RichTextEditor() + { + super(); + this.textFlow = new TextFlow; //Prevents a stack trace that happends when you try to access the textflow on click. + } + + [Bindable("change")] + /** + * The htmlText property is here for convenience. It converts the textFlow to TextConverter.TEXT_FIELD_HTML_FORMAT. + */ + public function get htmlText():String + { + if (_htmlTextChanged) + { + if (text == "") + { + _htmlText = ""; + } + else + { + _htmlText = TextConverter.export(textFlow, TextConverter.TEXT_FIELD_HTML_FORMAT, ConversionType.STRING_TYPE) as String; + } + _htmlTextChanged = false; + } + return _htmlText; + } + + /** + * The htmlText property is here for convenience. It converts the textFlow to TextConverter.TEXT_FIELD_HTML_FORMAT. + */ + public function set htmlText(value:String):void + { + if (htmlText != value) + { + _htmlText = value; + if (textFlow) + { + textFlow = TextConverter.importToFlow(_htmlText, TextConverter.TEXT_FIELD_HTML_FORMAT); + } + } + } + + /** + * @private + */ + public function get prompt():String + { + return _prompt; + } + + /** + * @private + */ + public function set prompt(value:String):void + { + _prompt = value; + if (_textArea) + { + _textArea.prompt = _prompt; + } + } + + /** + * @private + */ + public override function styleChanged(styleProp:String):void + { + super.styleChanged(styleProp); + _stylesChanged[styleProp] = getStyle(styleProp); + this.invalidateDisplayList(); + } + + [Bindable("change")] + /** + * The text in the textArea + */ + public function get text():String + { + if (_textArea) + { + return _textArea.text; + } + else + { + return _text; + } + } + + /** + * @private + */ + public function set text(value:String):void + { + _text = value; + if (_textArea) + { + _textArea.text = value; + } + } + + [Bindable("change")] + /** + * The textFlow + */ + public function get textFlow():TextFlow + { + return _textFlow; + } + + /** + * @private + */ + public function set textFlow(value:TextFlow):void + { + _textFlow = value; + if (_textArea) + { + _textArea.textFlow = value; + } + } + + /** + * @private + */ + protected override function createChildren():void + { + super.createChildren(); + var container:VGroup = new VGroup; + container.percentHeight = 100; + container.percentWidth = 100; + this.addElement(container); + + var toolbar:RichTextEditorToolBar = new RichTextEditorToolBar(); + toolbar.percentWidth = 100; + toolbar.bottom = 6; + container.addElement(toolbar); + + _textArea = new TextArea(); + _textArea.percentHeight = 100; + _textArea.percentWidth = 100; + _textArea.addEventListener(TextOperationEvent.CHANGE, handleChange); + _textArea.prompt = prompt; + _textArea.textFlow = textFlow; + if (_htmlText) + { + textFlow = TextConverter.importToFlow(_htmlText, TextConverter.TEXT_FIELD_HTML_FORMAT); + } + else if (_text) + { + _textArea.text = _text; + } + container.addElement(_textArea); + + toolbar.textArea = _textArea; + } + + /** + * @private + */ + protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void + { + super.updateDisplayList(unscaledWidth, unscaledHeight); + if (_textArea) + { + for (var key:String in _stylesChanged) + { + _textArea.setStyle(key, _stylesChanged[key]); + } + _stylesChanged = new Dictionary; //Clear it out + } + } + + /** + * @private + */ + private function handleChange(e:Event):void + { + _htmlTextChanged = true; + this.dispatchEvent(e); + } + } +} diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml new file mode 100644 index 0000000000..20f9c5f689 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml @@ -0,0 +1,128 @@ + + + + + + + + + + diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml new file mode 100644 index 0000000000..8e1e89d97c --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml @@ -0,0 +1,89 @@ + + + + + + + + + + diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml new file mode 100644 index 0000000000..ca93cd86ec --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml @@ -0,0 +1,193 @@ + + + + + = start && selectionState.absoluteEnd == end) + { //Inside touching end + selectionState = new SelectionState(_textArea.textFlow, selectionState.absoluteStart, start); + removeList(listElement); + editManager.createList(null, null, selectionState); + doCreate = false; + break; + } + else if (selectionState.absoluteStart >= start && selectionState.absoluteEnd <= end) + { //Inside + var firstRange:SelectionState = new SelectionState(_textArea.textFlow, selectionState.absoluteStart, start); + var secondRange:SelectionState = new SelectionState(_textArea.textFlow, end, selectionState.absoluteEnd); + removeList(listElement); + editManager.createList(null, null, firstRange); + editManager.createList(null, null, secondRange); + doCreate = false; + break; + } + else if ((selectionState.absoluteStart >= start && selectionState.absoluteStart <= end) || (selectionState.absoluteEnd >= start && selectionState.absoluteEnd <= end)) + { //Overlap. Include this list in the selection + selectionState = new SelectionState(_textArea.textFlow, Math.min(start, selectionState.absoluteStart), Math.max(end, selectionState.absoluteEnd)); + removeList(listElement); + } + else if (selectionState.absoluteStart <= start && selectionState.absoluteEnd >= end) + { //surround. Remove this list since it will get added back in, only expanded. + removeList(listElement); + } + } + if (doCreate) + { + IEditManager(_textArea.textFlow.interactionManager).createList(null, null, selectionState); + } + _textArea.textFlow.interactionManager.setFocus(); + } + } + + /** + * @private + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + if (_textArea.textFlow) + { + var willRemoveBulletsIfClicked:Boolean = false; + var selectionState:SelectionState = getSelectionState(); + var listElements:Array = _textArea.textFlow.getElementsByTypeName("list"); + for each (var listElement:ListElement in listElements) + { + var start:int = listElement.getAbsoluteStart(); + var end:int = listElement.getAbsoluteStart() + listElement.parentRelativeEnd - listElement.parentRelativeStart; + if (selectionState.absoluteStart == start && selectionState.absoluteEnd == end) + { //Same + willRemoveBulletsIfClicked = true; + break; + } + else if (selectionState.absoluteStart >= start && selectionState.absoluteEnd <= end) + { //Inside + willRemoveBulletsIfClicked = true; + break; + } + } + this.selected = willRemoveBulletsIfClicked; + + } + + } + + /** + * @private + */ + private function removeList(listElement:ListElement):void + { + var editManager:IEditManager = IEditManager(_textArea.textFlow.interactionManager); + + var target:FlowGroupElement = listElement.parent; + var targetIndex:int = target.getChildIndex(listElement); + editManager.moveChildren(listElement, 0, listElement.numChildren, target, targetIndex); + } + ]]> + + + + + diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml new file mode 100644 index 0000000000..86d3006e88 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml @@ -0,0 +1,84 @@ + + + + + + + + + + diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml new file mode 100644 index 0000000000..f0eba89a3a --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml @@ -0,0 +1,96 @@ + + + + + + + + + + diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml new file mode 100644 index 0000000000..9c5415983a --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml @@ -0,0 +1,89 @@ + + + + + + + + + + diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml new file mode 100644 index 0000000000..49d9f04983 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml @@ -0,0 +1,255 @@ + + + + + [Event(name="linkSelectedChange", type="flash.events.Event")] + + + 0) + { + if (!isWhitespace(_textArea.textFlow.getCharCodeAtPosition(position))) + { + break; + } + position--; + } + //Find the next whitespace character + while (position > 0) + { + if (isWhitespace(_textArea.textFlow.getCharCodeAtPosition(position))) + { + position++; //Back up one character + break; + } + position--; + } + var testText:String = _textArea.textFlow.getText(position, _textArea.selectionActivePosition); + var result:Array = testText.match(_urlRegExpression); + if (result != null && result.length > 0) + { + if (_textArea.textFlow.interactionManager is IEditManager) + { + var selectionState:SelectionState = new SelectionState(_textArea.textFlow, position, _textArea.selectionActivePosition); + if (testText.substr(0, 3) == "www") + { + testText = "http://" + testText; //Add a missing 'http://' if needed + } + applyLink(testText, "_blank", true, selectionState); + _textArea.setFocus(); + } + } + } + + /** + * @private + */ + private function handleKeyDown(e:KeyboardEvent):void + { + if (e.keyCode == Keyboard.ENTER || e.keyCode == Keyboard.SPACE || e.keyCode == Keyboard.TAB) + { + checkLinks(); + } + } + + /** + * @private + */ + private function handleLinkKeydown(e:KeyboardEvent):void + { + e.stopImmediatePropagation(); + if (e.keyCode == Keyboard.ENTER) + { + _textArea.setFocus(); + } + } + + /** + * @private + * Update the text display based on the selected range + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + var selectionState:SelectionState = _textArea.textFlow.interactionManager.getSelectionState(); + if (selectionState.absoluteStart != -1 && selectionState.absoluteEnd != -1) + { + var range:ElementRange = ElementRange.createElementRange(selectionState.textFlow, selectionState.absoluteStart, selectionState.absoluteEnd); + if (range) + { + var linkString:String = defaultLinkText; + _linkEl = range.firstLeaf.getParentByType(LinkElement) as LinkElement; + if (_linkEl != null) + { + var linkElStart:int = _linkEl.getAbsoluteStart(); + var linkElEnd:int = linkElStart + _linkEl.textLength; + if (linkElEnd < linkElStart) + { + var temp:int = linkElStart; + linkElStart = linkElEnd; + linkElEnd = temp; + } + + var beginRange:int = range.absoluteStart; + var endRange:int = range.absoluteEnd; + + var beginPara:ParagraphElement = range.firstParagraph; + if (endRange == (beginPara.getAbsoluteStart() + beginPara.textLength)) + { + endRange--; + } + + if ((beginRange == endRange) || (endRange <= linkElEnd)) + { + linkString = LinkElement(_linkEl).href; + } + } + var newLinkSelected:Boolean = _linkEl != null; + if (_linkSelected != newLinkSelected) + { + _linkSelected = newLinkSelected; + this.dispatchEvent(new Event("linkSelectedChange")); + } + + this.text = linkString; + + lastRange = range; + } + else + { + lastRange = null; + } + } + + enabled = _textArea.selectionAnchorPosition != _textArea.selectionActivePosition || _linkSelected; + } + + /** + * @private + * Return true if the character is a whitespace character + */ + private function isWhitespace(charCode:uint):Boolean + { + return charCode === 0x0009 || charCode === 0x000A || charCode === 0x000B || charCode === 0x000C || charCode === 0x000D || charCode === 0x0020 || charCode === 0x0085 || charCode === 0x00A0 || charCode === 0x1680 || charCode === 0x180E || charCode === 0x2000 || charCode === 0x2001 || charCode === 0x2002 || charCode === 0x2003 || charCode === 0x2004 || charCode === 0x2005 || charCode === 0x2006 || charCode === 0x2007 || charCode === 0x2008 || charCode === 0x2009 || charCode === 0x200A || charCode === 0x2028 || charCode === 0x2029 || charCode === 0x202F || charCode === 0x205F || charCode === 0x3000; + } + ]]> + + + + + diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml new file mode 100755 index 0000000000..d7ef16765e --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml @@ -0,0 +1,105 @@ + + + + + [Event(name="linkSelectedChange", type="flash.events.Event")] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml new file mode 100644 index 0000000000..0f9b6e65e4 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml @@ -0,0 +1,97 @@ + + + + + + + + + + diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml new file mode 100644 index 0000000000..1ae0663342 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml @@ -0,0 +1,90 @@ + + + + + + + + + + diff --git a/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml b/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml new file mode 100644 index 0000000000..36e58b39fd --- /dev/null +++ b/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 03ae0f58adb0ce33dff7a4b0f1b97fd8009e72bc Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Tue, 26 Aug 2014 16:41:58 +0100 Subject: [PATCH 095/392] FLEX-32541 CAUSE: AdvancedDataGridHeaderRenderer.mouseEventToHeaderPart() assumes that sortItemRendererInstance cannot be null. However, as the project attached to the ticket shows, it can. SOLUTION: When sortItemRendererInstance is null, we return AdvancedDataGrid.HEADER_TEXT_PART, as there is no icon. --- .../advancedDataGridClasses/AdvancedDataGridHeaderRenderer.as | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frameworks/projects/advancedgrids/src/mx/controls/advancedDataGridClasses/AdvancedDataGridHeaderRenderer.as b/frameworks/projects/advancedgrids/src/mx/controls/advancedDataGridClasses/AdvancedDataGridHeaderRenderer.as index a96acefd39..8c49eb169b 100644 --- a/frameworks/projects/advancedgrids/src/mx/controls/advancedDataGridClasses/AdvancedDataGridHeaderRenderer.as +++ b/frameworks/projects/advancedgrids/src/mx/controls/advancedDataGridClasses/AdvancedDataGridHeaderRenderer.as @@ -791,6 +791,9 @@ public class AdvancedDataGridHeaderRenderer */ public function mouseEventToHeaderPart(event:MouseEvent):String { + if(sortItemRendererInstance == null) + return AdvancedDataGrid.HEADER_TEXT_PART; + var point:Point = new Point(event.stageX, event.stageY); point = globalToLocal(point); From f25593ed396f184b0d378899941e59a30615f9ba Mon Sep 17 00:00:00 2001 From: Mihai Chira Date: Tue, 26 Aug 2014 17:47:05 +0100 Subject: [PATCH 096/392] FLEX-32541 Removing unused constant. --- .../advancedDataGridClasses/AdvancedDataGridHeaderRenderer.as | 3 --- 1 file changed, 3 deletions(-) diff --git a/frameworks/projects/advancedgrids/src/mx/controls/advancedDataGridClasses/AdvancedDataGridHeaderRenderer.as b/frameworks/projects/advancedgrids/src/mx/controls/advancedDataGridClasses/AdvancedDataGridHeaderRenderer.as index 8c49eb169b..bd69089c1e 100644 --- a/frameworks/projects/advancedgrids/src/mx/controls/advancedDataGridClasses/AdvancedDataGridHeaderRenderer.as +++ b/frameworks/projects/advancedgrids/src/mx/controls/advancedDataGridClasses/AdvancedDataGridHeaderRenderer.as @@ -622,9 +622,6 @@ public class AdvancedDataGridHeaderRenderer if (sortItemRendererWidth == 0) horizontalGap = 0; - // Size of label - const MINIMUM_SIZE:TextLineMetrics = measureText("w"); - // Adjust to given width var lineMetrics:TextLineMetrics = measureText(label.text); var labelWidth:Number = lineMetrics.width + UITextField.TEXT_WIDTH_PADDING; From 9157522834584e262033e5bd488ed84f864c3e0d Mon Sep 17 00:00:00 2001 From: Mihai C Date: Wed, 3 Sep 2014 12:04:10 +0100 Subject: [PATCH 097/392] FLEX-28291 Removing unused imports. --- frameworks/projects/spark/src/spark/components/ComboBox.as | 6 ------ frameworks/projects/spark/src/spark/components/DataGroup.as | 3 --- .../projects/spark/src/spark/layouts/HorizontalLayout.as | 1 - .../projects/spark/src/spark/layouts/VerticalLayout.as | 1 - 4 files changed, 11 deletions(-) diff --git a/frameworks/projects/spark/src/spark/components/ComboBox.as b/frameworks/projects/spark/src/spark/components/ComboBox.as index 6ef3125190..4c9221135f 100644 --- a/frameworks/projects/spark/src/spark/components/ComboBox.as +++ b/frameworks/projects/spark/src/spark/components/ComboBox.as @@ -19,31 +19,25 @@ package spark.components { -import adobe.utils.CustomActions; - import flash.display.DisplayObject; import flash.display.InteractiveObject; import flash.events.Event; import flash.events.FocusEvent; import flash.events.KeyboardEvent; -import flash.events.MouseEvent; import flash.ui.Keyboard; import flashx.textLayout.operations.CompositeOperation; import flashx.textLayout.operations.CutOperation; import flashx.textLayout.operations.DeleteTextOperation; import flashx.textLayout.operations.FlowOperation; -import flashx.textLayout.operations.InsertTextOperation; import mx.collections.IList; import mx.core.IIMESupport; import mx.core.mx_internal; import mx.events.FlexEvent; -import mx.styles.StyleProxy; import spark.components.supportClasses.DropDownListBase; import spark.components.supportClasses.ListBase; -import spark.core.NavigationUnit; import spark.events.DropDownEvent; import spark.events.TextOperationEvent; import spark.utils.LabelUtil; diff --git a/frameworks/projects/spark/src/spark/components/DataGroup.as b/frameworks/projects/spark/src/spark/components/DataGroup.as index 6004a923db..be4f51d9c0 100644 --- a/frameworks/projects/spark/src/spark/components/DataGroup.as +++ b/frameworks/projects/spark/src/spark/components/DataGroup.as @@ -32,7 +32,6 @@ import mx.core.IFactory; import mx.core.IInvalidating; import mx.core.ILayoutElement; import mx.core.IVisualElement; -import mx.core.UIComponentGlobals; import mx.core.mx_internal; import mx.events.CollectionEvent; import mx.events.CollectionEventKind; @@ -43,8 +42,6 @@ import mx.utils.MatrixUtil; import spark.components.supportClasses.GroupBase; import spark.events.RendererExistenceEvent; -import spark.layouts.HorizontalAlign; -import spark.layouts.VerticalLayout; import spark.layouts.supportClasses.LayoutBase; use namespace mx_internal; // for mx_internal property contentChangeDelta diff --git a/frameworks/projects/spark/src/spark/layouts/HorizontalLayout.as b/frameworks/projects/spark/src/spark/layouts/HorizontalLayout.as index 57cac28f49..d9b59767c0 100644 --- a/frameworks/projects/spark/src/spark/layouts/HorizontalLayout.as +++ b/frameworks/projects/spark/src/spark/layouts/HorizontalLayout.as @@ -27,7 +27,6 @@ import mx.containers.utilityClasses.Flex; import mx.core.FlexVersion; import mx.core.ILayoutElement; import mx.core.IVisualElement; -import mx.core.UIComponentGlobals; import mx.core.mx_internal; import mx.events.PropertyChangeEvent; diff --git a/frameworks/projects/spark/src/spark/layouts/VerticalLayout.as b/frameworks/projects/spark/src/spark/layouts/VerticalLayout.as index 151c327518..6094373af8 100644 --- a/frameworks/projects/spark/src/spark/layouts/VerticalLayout.as +++ b/frameworks/projects/spark/src/spark/layouts/VerticalLayout.as @@ -27,7 +27,6 @@ import mx.containers.utilityClasses.Flex; import mx.core.FlexVersion; import mx.core.ILayoutElement; import mx.core.IVisualElement; -import mx.core.UIComponentGlobals; import mx.core.mx_internal; import mx.events.PropertyChangeEvent; From b0ab9538362fbb7200c7768eb24f342097bc4bdb Mon Sep 17 00:00:00 2001 From: Mihai C Date: Wed, 3 Sep 2014 12:05:40 +0100 Subject: [PATCH 098/392] FLEX-28291 CAUSE: Please see the fxp attached to the ticket for a way to reproduce this bug. From the Flex Jira bugs and the bug reports in our application I'm sure there are many other ways, but that's the one I could find. There are ways to time the call to getMajorSize() on LinearLayoutVector in such a way that at that particular moment it's not yet synchronised with the DataGroup's current dataProvider, but instead with the previous one. (This applies for a snapshot before and after it's refresh()ed, as the items can vary wildly, as in the attachment). But getMajorSize() assumes that its blockTable is an accurate mapping of the current dataProvider length (disguised as DataGroup.numElements), and that therefore the index it receives as a parameter will exist (after the 'index >> BLOCK_SHIFT' mapping) in its blockTable Vector. Indeed, its assumption is correct, because it's the responsibility of VerticalLayout and HorizontaLayout to keep it in sync with the changed dataProvider. This possibility is opened by List.dataProviderRefreshed(), which waits for the validation cycle to finish before asking the list to ensureCaretVisibility() for an index it computed before the validation cycle. However, if the dataProvider somehow changes after the validation cycle and before UPDATE_COMPLETE, we're in trouble. SOLUTION: Get VerticalLayout and HorizontaLayout to call updateLLV(g) before calling getBounds() on their LinearLayoutVector. This way, at least, the blockTable Vector in LinearLayoutVector has the correct length, if incorrect (empty) data. NOTES: -As alluded to above, while this fix does remove the fatal, it does not actually make the list scroll to the previously selected index. To achieve that we would need to call updateDisplayListVirtual() instead of updateLLV, but that's impossible because it will fail with another fatal (Error #1009 in VerticalLayout/calculateElementWidth():1721). That's because now the DataGroup will return null for getVirtualElementAt(), as its 'virtualLayoutUnderway' is false, since the validation cycle has completed. To get around this we'd need to either call validateNow whenever the DataGroup's dataProvider is reset, refreshed or replaced, or instead remove the virtualLayoutUnderway conditional from getVirtualElementAt(), but both of these are major changes with many potential side effects. If anyone wants to investigate them, they are most welcome. -From what I can tell, the only reason List.dataProviderRefreshed() exists is that when it redraws, the list does not keep the current scroll position, but instead starts from the top/left. If we could make it redraw directly on the current scroll position (or the maximum the new items can occupy), we would remove the need for List.dataProviderRefreshed(), thus removing the potential for this bug. --- .../projects/spark/src/spark/layouts/HorizontalLayout.as | 3 +++ frameworks/projects/spark/src/spark/layouts/VerticalLayout.as | 3 +++ 2 files changed, 6 insertions(+) diff --git a/frameworks/projects/spark/src/spark/layouts/HorizontalLayout.as b/frameworks/projects/spark/src/spark/layouts/HorizontalLayout.as index d9b59767c0..b526294b5d 100644 --- a/frameworks/projects/spark/src/spark/layouts/HorizontalLayout.as +++ b/frameworks/projects/spark/src/spark/layouts/HorizontalLayout.as @@ -960,6 +960,9 @@ public class HorizontalLayout extends LayoutBase if (!g || (index < 0) || (index >= g.numElements) || !llv) return null; + // We need a valid LLV for this function + updateLLV(g); + return llv.getBounds(index); } diff --git a/frameworks/projects/spark/src/spark/layouts/VerticalLayout.as b/frameworks/projects/spark/src/spark/layouts/VerticalLayout.as index 6094373af8..5dd34e1d3c 100644 --- a/frameworks/projects/spark/src/spark/layouts/VerticalLayout.as +++ b/frameworks/projects/spark/src/spark/layouts/VerticalLayout.as @@ -911,6 +911,9 @@ public class VerticalLayout extends LayoutBase if (!g || (index < 0) || (index >= g.numElements) || !llv) return null; + // We need a valid LLV for this function + updateLLV(g); + return llv.getBounds(index); } From 7be616520b8f1a14904fb5c5295383b80b933a6d Mon Sep 17 00:00:00 2001 From: Justin Mclean Date: Thu, 4 Sep 2014 14:07:52 +1000 Subject: [PATCH 099/392] add contributors file for SDK --- CONTRIBITORS | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 CONTRIBITORS diff --git a/CONTRIBITORS b/CONTRIBITORS new file mode 100644 index 0000000000..7b8d747004 --- /dev/null +++ b/CONTRIBITORS @@ -0,0 +1,93 @@ +The following volunteers have contributed code, documentation, testing and/or +support to the Apache Flex SDK. + +Apache Flex SDK 4.13.0 +---------------------- +Alex Harui, Alexander Konovalov, Alexander Mazuruk, Alexander Scheibe, Bi Jane Jia, +Chris Martin, Constantin A., Darkstone, Erik de Bruin, Fabiano Citrone, faniprasad, +Frédéric Monjo, Frédéric Thomas, J. Terry Corbet, João Fernandes, Jose Barragan, +Justin Mclean, Kevin lam, Mark Kessler, Maurice Amsellem, Michael Labriola, Mihai Chira, +Nick Kwiatkowski, OmPrakash Muppirala, Quinn Buchanan, Tom Chiverton, Vladimir Cicmanec + +Apache Flex SDK 4.12.1 +---------------------- +Achim, Alex Harui, Alexander Scheibe, Darkstone, Doug Pierce, Erik de Bruin, +Franklyn de Quadros, Frédéric Leroy, Frédéric Thomas, George Yabra, Jeff Spicer, +João Fernandes, Jose Barragan, Josh Weinberg, Justin Mclean, Lee Burrows, Marcus Fritze, +Maurice Amsellem, Mihai Chira, Nick Kwiatkowski, OmPrakash Muppirala, Ori Segal, +Piotr Zarzycki, Shigeru Nakagaki + +Apache Flex SDK 4.12.0 +---------------------- +Alan Holden, Aleksey, Alex Harui, Andrey Gorbatov, Benjamin Chalupka, Benoit Wiart, +Carlos Rovira, Chris Martin, Colin Childs, Curtis Aube, Dan Murray, Danko Kozar, +David Harrburger, Deyan Vitanov, Dudda, Eric De Bruin, Fabiano Citrone, Frédéric Thomas, +Gareth Daniel Smith, Hugo Ferreira, Jaroslav Pavlicek, João Fernandes, Justin Mclean, +Keith Lee, Lee Burrows, Mario Specht, Mark Kessler, Mark Saunders, Maurice Amsellem, +Micha Pooh, Mihai Chira, Morten Gorm Madsen, Nils Berghs, OmPrakash Muppirala, +Paul Hastings, Piotr Zarzycki, Pol Poggi, Santosh Radhakrishna, Shardul Singh Bartwal, +Simon Lavallee, Stepan Hilbert, Stephan Plath, Swen van Zanten, Sword Dragon, +Tamás Nepusz, Thiago Maia, Tom Chiverton, Tristan, Vincent Spallek + +Apache Flex SDK 4.11.0 +---------------------- +Alex Harui, Ashish Desai, Brian Chiu, Bui Sy Nguyen, Caio, Chengxingpeng, Chris Velevitch, +Cosma Colanicchia, Cyrill Zadra, David Lantier, David Yun, Dylan Randall, Erik de Bruin, +Frédéric Monjo, Frédéric Thomas, Gareth Daniel Smith, Gary N Griswold, Gkk, Harbs, +Hugo Ferreira, Igor Costa, Jozef Chutka, Justin Mclean, Karthik Dhanapal, Lee Burrows, +Marcus Fritze, Marcus Wilkinson, Mario Specht, Mark Kessler, Maurice Amsellem, +Michael O'Keefe, MichaÅ‚ Sz., Mihai Chira, Mike Crank, Morgan Bruneau, Nicholas Kwiatkowski, +OmPrakash Muppirala, Paul Hastings, Pol Poggi, Robbyn Gerhardt, Robert Bar, Robert Hunt, +Sebastian Toro, Sebastien Paturel, Stefan Overkamp, Stephan Plath,Swen van Zanten, +Sword Dragon, Tom Chiverton, Vitali Kabak + +Apache Flex SDK 4.10.0 +---------------------- +Al Birdy, Alex Harui, Aurélien Baroiller, aYo, Benjamin Chalupka, Carlos Rovira, +Carol Frampton, Chris Martin, Colin Childs, Cyrill Zadra, Dasa Paddock, David Bel, +Edgar Rivera, Ejaz Asghar, Erik de Bruin, Fabio Todaro, Flex Hdsign, Fréderic Cox, +Frédéric Thomas, Giles Roadnight, Harbs, João Fernandes, João Sérgio Nobre Saleiro, +Jozef Chutka, Justin Mclean, Koen Weyn, Mark Kessler, Mark Saunders, Maxime Cowez, +Mihai Chira, Nazar Yavny, Nick Kwiatkowski, OmPrakash Muppirala, Pedro Varela, +Peter Wharmby, Philip Mair, Ryan, Sebastien Paturel, Sky, Swen van Zanten,Tristan, +Vikas Saurabh, Wilson Silva, Yann Cabon, Yiotis Katsambas + +Apache Flex SDK 4.9.1 +--------------------- +Adnan Doric, Alex Harui, aYo, Bjorn Svensson, Carol Frampton, Frédéric Leroy, +Frédéric Thomas, JC Franco, João Saleiro, Justin Mclean, Mikko Torniainen, +Nicholas Kwiatkowski, OmPrakash Muppirala, Rahul Singh Rawat + +Apache Flex SDK 4.9.0 +--------------------- +Alex Harui, Carol Frampton, Chema Balsas, Cyrill Zadra, Dave Fisher, Deepti Yadav, +Droopy, Erik de Bruin, Frédéric Thomas, Jigar Oza, João Fernandes, Justin Mclean, +Leif Wells, Maurice Amsellem, Maxime Cowez, Mikko Torniainen, Nicholas Kwiatkowski, +OmPrakash Muppirala, Pablo Beltran, Rahul, Raimundas Banevicius, Robbyn Gerhardt, +Sebastian Wichmann, Simon Gladman, Sudhir, Sugan Naicker, Zqlu + +Apache Flex SDK 4.8.0 +--------------------- +Alex Harui, Bertrand Delacretaz, Carlos Rovira, Carol Frampton, Christofer Dutz, +Christophe Herreman, Dave Fisher, David Yun, Heinrich Ruoff, Igor Costa, Jason Moore, +Joan Llenas Masó, João Fernandes, Justin Mclean, Maurice Amsellem, Michael A. Labriola, +Nicholas Kwiatkowski, OmPrakash Muppirala, Peter Elst, Shigeru Nakagaki, +Spiros Aggelopoulos + +Many other people have contributed in other ways to the Apache Flex project by +helping out on the mailing list, maintaining the website, adding and editing wiki +content, raising bugs in JIRA, and with many other tasks. + +Please check the Apache Flex website [1], Apache Flex wiki [2], Apache Flex team +page [3], dev mailing list [4], Apache Flex JIRA [5], GitHub mirror [6] for some +of these contributions. + +If you've missing from any of these lists please get in touch and you'll appear +in future versions of this document. + +1. http://flex.apache.org +2. http://en.wikipedia.org/wiki/Apache_Flex +3. http://flex.apache.org/about-people.html +4. http://markmail.org/search/+list:org.apache.incubator.flex-dev +5. https://issues.apache.org/jira/browse/FLEX +6. https://github.com/apache/flex-sdk \ No newline at end of file From 714210cbefc38f0a326092edd77fd1f1af2f3704 Mon Sep 17 00:00:00 2001 From: Justin Mclean Date: Fri, 5 Sep 2014 17:38:22 +1000 Subject: [PATCH 100/392] fix spelling of file name --- CONTRIBITORS => CONTRIBUTORS | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CONTRIBITORS => CONTRIBUTORS (100%) diff --git a/CONTRIBITORS b/CONTRIBUTORS similarity index 100% rename from CONTRIBITORS rename to CONTRIBUTORS From ec8b9bcce28b3a26aa20337396e52a7cc6f3b4e1 Mon Sep 17 00:00:00 2001 From: Mark Kessler Date: Mon, 8 Sep 2014 21:38:58 -0400 Subject: [PATCH 101/392] FLEX-34531: Removed the exclude statement for hlocColor style. --- frameworks/projects/charts/src/mx/charts/series/HLOCSeries.as | 1 - 1 file changed, 1 deletion(-) diff --git a/frameworks/projects/charts/src/mx/charts/series/HLOCSeries.as b/frameworks/projects/charts/src/mx/charts/series/HLOCSeries.as index 93a5739ff3..dfcc8afe46 100644 --- a/frameworks/projects/charts/src/mx/charts/series/HLOCSeries.as +++ b/frameworks/projects/charts/src/mx/charts/series/HLOCSeries.as @@ -103,7 +103,6 @@ use namespace mx_internal; */ [Style(name="stroke", type="mx.graphics.IStroke", inherit="no")] -[Exclude(name="hlocColor", kind="style")] //this is private style and excluded from tag inspector /** * Represents financial data as a series of elements From 67f1f5d3c522c723e0325b3a4aed68e518cb5e3b Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Tue, 9 Sep 2014 22:37:12 -0700 Subject: [PATCH 102/392] FLEX-34533 fix installation of optional components --- apache-flex-sdk-installer-config.xml | 6 ++-- installer.xml | 54 +++++++++++++++++++++------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/apache-flex-sdk-installer-config.xml b/apache-flex-sdk-installer-config.xml index edd5286c1f..333893b2e5 100755 --- a/apache-flex-sdk-installer-config.xml +++ b/apache-flex-sdk-installer-config.xml @@ -69,7 +69,7 @@ SWFObject (必须的) SWFObject (必須的) - + OSMF (Benötigt) OSMF (ΑπαιτοÏμενο) OSMF (Required) @@ -81,7 +81,7 @@ OSMF (必须的) OSMF (必須的) - + Adobe Bibliothek zur Einbettung von Schriften (Optional) Adobe Embedded Font Libraries and Utilities (ΠÏοαιÏετικό) Adobe Embedded Font Libraries and Utilities (Optional) @@ -93,7 +93,7 @@ Adobe é•¶å…¥å­—ä½“åº“å’Œå®žç”¨ç¨‹åº (å¯é€‰çš„) Adobe é‘²å…¥å­—é«”åº«å’Œå¯¦ç”¨ç¨‹å¼ (å¯é¸çš„) - + Remoting Support (Optional) Remoting Support (ΠÏοαιÏετικό) Remoting Support (Optional) diff --git a/installer.xml b/installer.xml index 6097703899..6191ee3efa 100644 --- a/installer.xml +++ b/installer.xml @@ -334,10 +334,15 @@ - + + + + + + - @@ -549,10 +554,15 @@ - + + + + + + - @@ -580,10 +590,15 @@ - + + + + + + - swfobject.echo=${INFO_DOWNLOADING_FILE_FROM} @@ -623,10 +638,15 @@ - + + + + + + - osmf.echo=${INFO_DOWNLOADING_FILE_FROM} @@ -657,7 +677,7 @@ - + @@ -665,9 +685,14 @@ + + + + + - afe.echo=${INFO_DOWNLOADING_FILE_FROM} @@ -742,10 +767,15 @@ - + + + + + + - blaze.echo=${INFO_DOWNLOADING_FILE_FROM} From 572b3a8b391b8fb03d10dfec6b28cb65804d3d99 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Wed, 10 Sep 2014 12:53:29 -0700 Subject: [PATCH 103/392] FLEX-34534 try to fix cached installs of fontkit and blazeds on Windows --- installer.xml | 87 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/installer.xml b/installer.xml index 6191ee3efa..aaa53d9206 100644 --- a/installer.xml +++ b/installer.xml @@ -65,29 +65,34 @@ - - - + + + + - - - + + + + - - - + + + + - - - + + + + - - - + + + + @@ -650,8 +655,8 @@ description="Copies OSMF from Open@Adobe"> osmf.echo=${INFO_DOWNLOADING_FILE_FROM} - - + + ${osmf.echo} @@ -696,8 +701,8 @@ description="Copies OSMF from Open@Adobe"> afe.echo=${INFO_DOWNLOADING_FILE_FROM} - - + + ${afe.echo} @@ -707,14 +712,15 @@ + agl.echo=${INFO_DOWNLOADING_FILE_FROM} - - + + ${agl.echo} @@ -723,14 +729,15 @@ + fontkit.echo=${INFO_DOWNLOADING_FILE_FROM} - - + + ${fontkit.echo} @@ -739,14 +746,15 @@ + rideau.echo=${INFO_DOWNLOADING_FILE_FROM} - - + + ${rideau.echo} @@ -755,6 +763,7 @@ + @@ -779,8 +788,8 @@ description="Copies BlazeDS from Open@Adobe"> blaze.echo=${INFO_DOWNLOADING_FILE_FROM} - - + + ${blaze.echo} @@ -790,6 +799,7 @@ + @@ -944,16 +954,31 @@ - ${INFO_USING_CACHED_FILE} ${downloadCacheFolder}/${srcFolder}/${srcFile} - + + + + + + ${INFO_USING_CACHED_FILE} ${downloadCacheFolder}/${srcFolder}/${cacheFile} + - + + + + + + - + + + + + + Date: Mon, 15 Sep 2014 08:47:38 +0200 Subject: [PATCH 104/392] Updated Flash/AIR to 15 Signed-off-by: Erik de Bruin --- mustella/jenkins.sh | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/mustella/jenkins.sh b/mustella/jenkins.sh index e2b69318a8..6614b16893 100755 --- a/mustella/jenkins.sh +++ b/mustella/jenkins.sh @@ -49,10 +49,10 @@ fi # Toggle between versions: # 11.1 is the default version -# 11.7 is the "current long term support" version -# 12.0 is the latest release -# 13 is the current beta version -# Note: the previous release and current beta versions of AIR are both '4', +# 11.7 is a much used version? +# 13 is the current "long term support" version +# 15 is the current 'beta' version +# Note: the previous release and current beta versions of AIR are both '4', # so to make the distinction, the beta version is '4.01' on the VM if [ "$FLASH_VERSION" == "11.1" ] then @@ -66,9 +66,9 @@ then AIR_SDK_DIR=4.0 elif [ "$FLASH_VERSION" == "13.0" ] then - FLASH_VERSION=14.0 - AIR_VERSION=14 - AIR_SDK_DIR=14_beta + FLASH_VERSION=15.0 + AIR_VERSION=15 + AIR_SDK_DIR=15 else FLASH_VERSION=11.1 AIR_VERSION=3.7 @@ -80,13 +80,11 @@ LOG=$LOG"- Set FLASH_VERSION to '$FLASH_VERSION' and AIR_VERSION to '$AIR_VERSIO # LOCATIONS -#export AIR_HOME="/Users/erik/Documents/ApacheFlex/dependencies/AdobeAIRSDK" export AIR_HOME="C:\\ApacheFlex\\dependencies\\AdobeAIRSDK\\$AIR_SDK_DIR" LOG=$LOG"- Set AIR_HOME to '$AIR_HOME'"$'\n' case "$FLASH_VERSION" in 11.1) - #export FLASHPLAYER_DEBUGGER="/Applications/Flash Player Debugger.app/Contents/MacOS/Flash Player Debugger" export FLASHPLAYER_DEBUGGER="C:\\ApacheFlex\\dependencies\\FlashPlayer_Debug\\flashplayer11_1r102_55_win_sa_debug_32bit.exe" ;; 11.7) @@ -95,8 +93,8 @@ case "$FLASH_VERSION" in 13.0) export FLASHPLAYER_DEBUGGER="C:\\ApacheFlex\\dependencies\\FlashPlayer_Debug\\flashplayer13_0r0_214_win_sa_debug.exe" ;; - 14.0) - export FLASHPLAYER_DEBUGGER="C:\\ApacheFlex\\dependencies\\FlashPlayer_Debug\\flashplayer14_debugsa_win_32.exe" + 15.0) + export FLASHPLAYER_DEBUGGER="C:\\ApacheFlex\\dependencies\\FlashPlayer_Debug\\flashplayer_15_sa_debug.exe" ;; *) echo "No valid Flash Player Debugger variable value could be parsed." From 3884d2e4043d5d7d007f842ad29d3ff73ae96a00 Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Mon, 15 Sep 2014 10:09:11 +0200 Subject: [PATCH 105/392] Added v15 download info Signed-off-by: Erik de Bruin --- ide/checkAllPlayerGlobals.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ide/checkAllPlayerGlobals.sh b/ide/checkAllPlayerGlobals.sh index c290bbe3e8..27cbf6255c 100755 --- a/ide/checkAllPlayerGlobals.sh +++ b/ide/checkAllPlayerGlobals.sh @@ -123,4 +123,5 @@ downloadPlayerGlobal 11.9 d18244c3c00c61a41f2d4d791d09fedb http://download.macro downloadPlayerGlobal 12.0 4db4e934f39f774ba68fcd9a79654971 http://download.macromedia.com/get/flashplayer/updaters/12/playerglobal12_0.swc downloadPlayerGlobal 13.0 d40ea369a7fc2bcacc4cfcffff1339d4 http://download.macromedia.com/get/flashplayer/updaters/13/playerglobal13_0.swc downloadPlayerGlobal 14.0 0bae2abfe34eed7d718eda123ec9e786 http://download.macromedia.com/get/flashplayer/updaters/14/playerglobal14_0.swc +downloadPlayerGlobal 15.0 051a72a4be1e02894dca0c2c01d8d8e2 http://download.macromedia.com/get/flashplayer/updaters/15/playerglobal15_0.swc From a08efce86820635e5ae2ea80379e64661c5ea33d Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Mon, 15 Sep 2014 10:43:09 +0200 Subject: [PATCH 106/392] Updated with information about FP 15 and the setup of the local security config files Signed-off-by: Erik de Bruin --- README | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/README b/README index f85987012f..33ab812b7e 100644 --- a/README +++ b/README @@ -172,17 +172,17 @@ Install Prerequisites be set correctly and you will get compile errors. *3) The Adobe AIR integration kit for Windows can be downloaded from: - http://airdownload.adobe.com/air/win/download/14.0/AdobeAIRSDK.zip + http://airdownload.adobe.com/air/win/download/15.0/AdobeAIRSDK.zip The Adobe AIR integration kit for Mac can be downloaded from: - http://airdownload.adobe.com/air/mac/download/14.0/AdobeAIRSDK.tbz2 + http://airdownload.adobe.com/air/mac/download/15.0/AdobeAIRSDK.tbz2 The Adobe AIR integration kit for Linux can be downloaded from: http://airdownload.adobe.com/air/lin/download/2.6/AdobeAIRSDK.tbz2 - This version of Apache Flex was certified for use with AIR 14.0, and - should be compatible with other versions of AIR newer than 3.1. However - it hasn't been fully tested on AIR 3.2, 3.3, 3.5, 3.6 or 3.7. + This version of Apache Flex was certified for use with AIR 15.0, and + should be compatible with versions of AIR newer than 3.1. It has been + fully tested on AIR 3.7, 4 and 15. Download the AIR SDK for your platform and unzip it. Set AIR_HOME to the absolute path of the AIR SDK directory. @@ -191,11 +191,11 @@ Install Prerequisites http://www.adobe.com/support/flashplayer/downloads.html This version of Apache Flex was certified for use with Adobe Flash - Player 11.1, and is compatible with versions 10.2 through 14.0. It has - been tested with versions 11.1, 11.7, 11.8, 11.9, 12.0, 13.0 and 14.0 - on Windows and Mac. It has been compiled against other Adobe Flash - Player versions but has not been fully tested. It has not been fully - tested on Linux. + Player 11.1, and is compatible with versions 10.2 through 15.0. It has + been tested with versions 11.1, 11.7, 11.8, 11.9, 12.0, 13.0, 14.0 + and 15.0 on Windows and Mac. It has been compiled against other Adobe + Flash Player versions but has not been fully tested. It has not been + fully tested on Linux. On Windows, set FLASHPLAYER_DEBUGGER to the absolute path including the filename of the FlashPlayerDebugger.exe. Note the filename of flash @@ -219,22 +219,8 @@ Install Prerequisites including the version subdirectory). The target-player option controls which PLAYERGLOBAL_HOME subdirectory is used. - Other more recent versions of Adobe Flash Player playerglobal.swc can be - downloaded from: - http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_2.swc - http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_3.swc - http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_4.swc - http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_5.swc - http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_6.swc - http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_7.swc - http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_8.swc - http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_9.swc - http://download.macromedia.com/get/flashplayer/updaters/12/playerglobal12_0.swc - http://download.macromedia.com/get/flashplayer/updaters/13/playerglobal13_0.swc - http://download.macromedia.com/get/flashplayer/updaters/14/playerglobal14_0.swc - - These can be used with Apache Flex but not all have not been fully - tested. + Information about downloading, setting up and support of the Adobe Flash + Player can be found in the section "Adobe Flash Player Version Support" below. *6) The Adobe Extension Manager is only required for those creating releases or testing changes to the flash-integration swc. Note that if you @@ -267,6 +253,9 @@ FlashPlayer Configuration and a FlashPlayerTrust file must allow local SWFs to access local files. + More information about these settings can be found here: + http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7c95.html + Other Locale Support -------------------- @@ -305,7 +294,7 @@ Adobe Flash Player Version Support The Apache Flex SDK defaults to using the Adobe Flash Player 11.1. The SDK can be used with Flash Player versions 10.2, 10.3, 11.0, 11.1, 11.2, 11.3, - 11.4, 11.5, 11.6, 11.7, 11.8, 11.9, 12.0, 13.0 and 14.0. + 11.4, 11.5, 11.6, 11.7, 11.8, 11.9, 12.0, 13.0, 14.0 and 15.0. It is recommended that you update to the latest version of Adobe Flash Player. Newer versions of the Adobe Flash player address security @@ -352,6 +341,7 @@ Adobe Flash Player Version Support http://download.macromedia.com/get/flashplayer/updaters/12/playerglobal12_0.swc http://download.macromedia.com/get/flashplayer/updaters/13/playerglobal13_0.swc http://download.macromedia.com/get/flashplayer/updaters/14/playerglobal14_0.swc + http://download.macromedia.com/get/flashplayer/updaters/15/playerglobal15_0.swc Copy the target playerglobal.swc to the directory: @@ -396,9 +386,11 @@ Adobe Flash Player Version Support /playerglobal.swc /14.0 /playerglobal.swc + /15.0 + /playerglobal.swc Apache Flex has been tested with Adobe Flash Player 11.1, 11.5, 11.7, 11.8, - 11.9, 12.0, 13.0 and 14.0 on Windows and Mac. + 11.9, 12.0, 13.0, 14.0 and 15.0 on Windows and Mac. Apache Flex has not been tested on Linux so some issues may exist in this release. From 2adc95d52554c2280a814c4eebc82af26c95389b Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Mon, 15 Sep 2014 11:21:03 +0200 Subject: [PATCH 107/392] Added section about preparing SDK for use in FB Signed-off-by: Erik de Bruin --- README | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README b/README index 33ab812b7e..a3e70b377b 100644 --- a/README +++ b/README @@ -585,6 +585,17 @@ Building the Source in the Source Distribution ant -projecthelp +Using the SDK in Flash Builder +------------------------------ + + If you want to use the SDK you build from source in Flash Builder, you + need to run these commands/scripts: + + cd + ant frameworks-rsls + ./ide/constructFlexForIDE.[sh/bat] + [if you're on a Mac, also run: ./ide/addAIRtoSDK.sh] + Thanks for using Apache Flex. Enjoy! The Apache Flex Project From b3aa11c1b001669a81cd79559432d955f292b4c9 Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Mon, 15 Sep 2014 11:21:29 +0200 Subject: [PATCH 108/392] Updated with FP 15 Signed-off-by: Erik de Bruin --- ide/addAIRtoSDK.sh | 21 ++++++++++++++------- ide/setFlashPlayerVersion.sh | 5 +++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/ide/addAIRtoSDK.sh b/ide/addAIRtoSDK.sh index c7b9005a02..97e1810e0d 100755 --- a/ide/addAIRtoSDK.sh +++ b/ide/addAIRtoSDK.sh @@ -30,14 +30,14 @@ AIR_VERSION="$1" OS=`uname` -if [[ "${AIR_VERSION}" != "14.0" - && "${AIR_VERSION}" != "13.0" && "${AIR_VERSION}" != "4.0" && "${AIR_VERSION}" != "3.9" - && "${AIR_VERSION}" != "3.8" && "${AIR_VERSION}" != "3.7" && "${AIR_VERSION}" != "3.6" - && "${AIR_VERSION}" != "3.5" && "${AIR_VERSION}" != "3.4" && "${AIR_VERSION}" != "3.3" - && "${AIR_VERSION}" != "3.2" && "${AIR_VERSION}" != "3.1" && "${AIR_VERSION}" != "3.0" - && "${AIR_VERSION}" != "2.7" && "${AIR_VERSION}" != "2.6" ]] +if [[ "${AIR_VERSION}" != "15.0" + && "${AIR_VERSION}" != "14.0" && "${AIR_VERSION}" != "13.0" && "${AIR_VERSION}" != "4.0" + && "${AIR_VERSION}" != "3.9" && "${AIR_VERSION}" != "3.8" && "${AIR_VERSION}" != "3.7" + && "${AIR_VERSION}" != "3.6" && "${AIR_VERSION}" != "3.5" && "${AIR_VERSION}" != "3.4" + && "${AIR_VERSION}" != "3.3" && "${AIR_VERSION}" != "3.2" && "${AIR_VERSION}" != "3.1" + && "${AIR_VERSION}" != "3.0" && "${AIR_VERSION}" != "2.7" && "${AIR_VERSION}" != "2.6" ]] then - echo Unknown version ${AIR_VERISON} of AIR. Versions 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 13.0 and 14.0 are supported. + echo Unknown version ${AIR_VERISON} of AIR. Versions 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 13.0, 14.0 and 15.0 are supported. exit 1; fi @@ -156,6 +156,13 @@ for configFile in "${configFiles[@]}" do echo Updating ${configFile} + # 15.0 needs FP 15 and swf version 26 + if [ ${AIR_VERSION} = "15.0" ] + then + updatePlayerVersion 15.0 "${configFile}" + updateSWFVersion 26 "${configFile}" + fi + # 14.0 needs FP 14 and swf version 25 if [ ${AIR_VERSION} = "14.0" ] then diff --git a/ide/setFlashPlayerVersion.sh b/ide/setFlashPlayerVersion.sh index 1995346f2a..b76f2ef53c 100755 --- a/ide/setFlashPlayerVersion.sh +++ b/ide/setFlashPlayerVersion.sh @@ -127,9 +127,10 @@ if [[ "${FLASH_VERSION}" != "10.2" && "${FLASH_VERSION}" != "10.3" && "${FLASH_ && "${FLASH_VERSION}" != "11.1" && "${FLASH_VERSION}" != "11.2" && "${FLASH_VERSION}" != "11.3" && "${FLASH_VERSION}" != "11.4" && "${FLASH_VERSION}" != "11.5" && "${FLASH_VERSION}" != "11.6" && "${FLASH_VERSION}" != "11.7" && "${FLASH_VERSION}" != "11.8" && "${FLASH_VERSION}" != "11.9" - && "${FLASH_VERSION}" != "12.0" && "${FLASH_VERSION}" != "13.0" && "${FLASH_VERSION}" != "14.0" ]] + && "${FLASH_VERSION}" != "12.0" && "${FLASH_VERSION}" != "13.0" && "${FLASH_VERSION}" != "14.0" + && "${FLASH_VERSION}" != "15.0" ]] then - echo Unknown version ${FLASH_VERSION} of Flash Player. Versions 10.2, 10.3, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9, 12.0, 13.0 and 14.0 are supported. + echo Unknown version ${FLASH_VERSION} of Flash Player. Versions 10.2, 10.3, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9, 12.0, 13.0, 14.0 and 15.0 are supported. exit 1; fi From 4396efe4935465c504ea7adaa44416fb4a177577 Mon Sep 17 00:00:00 2001 From: Alex Harui Date: Thu, 25 Sep 2014 13:55:25 -0700 Subject: [PATCH 109/392] fix compile issue that Falcon saw but MXMLC did not --- .../src/spark/components/richTextEditorClasses/LinkTool.mxml | 1 - 1 file changed, 1 deletion(-) diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml index 49d9f04983..82fb837731 100644 --- a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml @@ -97,7 +97,6 @@ limitations under the License. //Get the current format var txtLayFmt:TextLayoutFormat = textArea.textFlow.interactionManager.getCommonCharacterFormat(); //Set the link - var operationState:SelectionState = null; if (_linkEl != null) { operationState = new SelectionState(textArea.textFlow, _linkEl.getAbsoluteStart(), _linkEl.getAbsoluteStart() + _linkEl.textLength); From 3fd6027d7b6ee25ecfc8261b2e69ba000cae91ce Mon Sep 17 00:00:00 2001 From: OmPrakash Muppirala Date: Wed, 1 Oct 2014 15:52:16 -0700 Subject: [PATCH 110/392] Adding new branch for working on ios7 skins. --- .../src/spark/skins/ios7/ActionBarSkin.as | 727 ++++++++++++++ .../src/spark/skins/ios7/BusyIndicatorSkin.as | 180 ++++ .../skins/ios7/ButtonBarFirstButtonSkin.as | 93 ++ .../skins/ios7/ButtonBarMiddleButtonSkin.as | 95 ++ .../src/spark/skins/ios7/ButtonBarSkin.as | 166 ++++ .../src/spark/skins/ios7/ButtonSkin.as | 397 ++++++++ .../src/spark/skins/ios7/CalloutSkin.as | 825 ++++++++++++++++ .../src/spark/skins/ios7/CheckBoxSkin.as | 268 ++++++ .../src/spark/skins/ios7/HScrollBarSkin.as | 205 ++++ .../spark/skins/ios7/HScrollBarThumbSkin.as | 169 ++++ .../src/spark/skins/ios7/HSliderSkin.as | 293 ++++++ .../src/spark/skins/ios7/HSliderThumbSkin.as | 333 +++++++ .../src/spark/skins/ios7/HSliderTrackSkin.as | 227 +++++ .../src/spark/skins/ios7/RadioButtonSkin.as | 253 +++++ .../skins/ios7/SpinnerListContainerSkin.as | 326 +++++++ .../skins/ios7/SpinnerListScrollerSkin.mxml | 94 ++ .../src/spark/skins/ios7/SpinnerListSkin.as | 294 ++++++ .../src/spark/skins/ios7/StageTextAreaSkin.as | 190 ++++ .../spark/skins/ios7/StageTextInputSkin.as | 134 +++ .../ios7/TabbedViewNavigatorTabBarSkin.as | 122 +++ .../src/spark/skins/ios7/TextAreaSkin.as | 892 ++++++++++++++++++ .../src/spark/skins/ios7/TextInputSkin.as | 375 ++++++++ .../src/spark/skins/ios7/ToggleSwitchSkin.as | 506 ++++++++++ .../skins/ios7/TransparentActionButtonSkin.as | 108 +++ .../ios7/TransparentNavigationButtonSkin.as | 109 +++ .../src/spark/skins/ios7/VScrollBarSkin.as | 207 ++++ .../spark/skins/ios7/VScrollBarThumbSkin.as | 178 ++++ .../src/spark/skins/ios7/ViewMenuItemSkin.as | 238 +++++ .../src/spark/skins/ios7/ViewMenuSkin.mxml | 183 ++++ .../skins/ios7/assets/ActionBarBackground.fxg | 55 ++ .../spark/skins/ios7/assets/BusyIndicator.fxg | 64 ++ .../ios7/assets/ButtonBarFirstButton_down.fxg | 34 + .../ButtonBarFirstButton_selectedDown.fxg | 39 + .../ButtonBarFirstButton_selectedUp.fxg | 34 + .../ios7/assets/ButtonBarFirstButton_up.fxg | 29 + .../assets/ButtonBarMiddleButton_down.fxg | 70 ++ .../ButtonBarMiddleButton_selectedDown.fxg | 75 ++ .../ButtonBarMiddleButton_selectedUp.fxg | 70 ++ .../ios7/assets/ButtonBarMiddleButton_up.fxg | 65 ++ .../spark/skins/ios7/assets/Button_down.fxg | 73 ++ .../src/spark/skins/ios7/assets/Button_up.fxg | 60 ++ .../ios7/assets/CalloutContentBackground.fxg | 51 + .../spark/skins/ios7/assets/CheckBox_down.fxg | 34 + .../ios7/assets/CheckBox_downSelected.fxg | 35 + .../assets/CheckBox_downSymbolSelected.fxg | 29 + .../spark/skins/ios7/assets/CheckBox_up.fxg | 31 + .../skins/ios7/assets/CheckBox_upSelected.fxg | 29 + .../ios7/assets/CheckBox_upSymbolSelected.fxg | 33 + .../skins/ios7/assets/HSliderThumb_normal.fxg | 34 + .../ios7/assets/HSliderThumb_pressed.fxg | 39 + .../spark/skins/ios7/assets/HSliderTrack.fxg | 29 + .../skins/ios7/assets/HSliderTrack_filled.fxg | 29 + .../skins/ios7/assets/RadioButton_down.fxg | 34 + .../assets/RadioButton_downSymbolSelected.fxg | 29 + .../skins/ios7/assets/RadioButton_up.fxg | 29 + .../assets/RadioButton_upSymbolSelected.fxg | 36 + .../assets/SpinnerListContainerBackground.fxg | 47 + ...SpinnerListContainerSelectionIndicator.fxg | 44 + .../assets/SpinnerListContainerShadow.fxg | 41 + .../ios7/assets/ToggleSwitchBackground.fxg | 29 + .../ios7/assets/ToggleSwitchThumb_off.fxg | 40 + .../ios7/assets/ToggleSwitchThumb_on.fxg | 40 + .../skins/ios7/assets/fonts/Roboto-Bold.ttf | Bin 0 -> 135820 bytes .../ios7/assets/fonts/Roboto-Regular.ttf | Bin 0 -> 145348 bytes .../skins/ios7/supportClasses/CalloutArrow.as | 453 +++++++++ .../ios7/supportClasses/StageTextSkinBase.as | 402 ++++++++ .../skins/ios7/supportClasses/TextSkinBase.as | 213 +++++ 67 files changed, 10665 insertions(+) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/ActionBarSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/BusyIndicatorSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/CalloutSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/CheckBoxSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarThumbSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderThumbSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderTrackSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/RadioButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListContainerSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListScrollerSkin.mxml create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextAreaSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextInputSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/TabbedViewNavigatorTabBarSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/TextAreaSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/TextInputSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/ToggleSwitchSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentActionButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentNavigationButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarThumbSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuItemSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuSkin.mxml create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ActionBarBackground.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/BusyIndicator.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_selectedDown.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_selectedUp.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_up.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_selectedDown.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_selectedUp.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_up.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_up.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CalloutContentBackground.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_downSelected.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_downSymbolSelected.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_up.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_upSelected.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_upSymbolSelected.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderThumb_normal.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderThumb_pressed.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderTrack.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderTrack_filled.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_downSymbolSelected.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_up.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_upSymbolSelected.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerBackground.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerSelectionIndicator.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerShadow.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchBackground.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchThumb_off.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchThumb_on.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/fonts/Roboto-Bold.ttf create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/fonts/Roboto-Regular.ttf create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/CalloutArrow.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/StageTextSkinBase.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/TextSkinBase.as diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ActionBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ActionBarSkin.as new file mode 100644 index 0000000000..8ffbe4d2a1 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ActionBarSkin.as @@ -0,0 +1,727 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.GradientType; +import flash.events.Event; +import flash.text.TextFormatAlign; + +import mx.core.DPIClassification; +import mx.core.mx_internal; +import mx.utils.ColorUtil; + +import spark.components.ActionBar; +import spark.components.Group; +import spark.components.supportClasses.StyleableTextField; +import spark.core.SpriteVisualElement; +import spark.layouts.HorizontalAlign; +import spark.layouts.HorizontalLayout; +import spark.layouts.VerticalAlign; +import spark.skins.android4.assets.ActionBarBackground; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * The default skin class for the Spark ActionBar component in mobile + * applications. + * + * @see spark.components.ActionBar + * @see spark.skins.mobile.TransparentNavigationButtonSkin + * @see spark.skins.mobile.BeveledBackButtonSkin + * @see spark.skins.mobile.TransparentActionButtonSkin + * @see spark.skins.mobile.BeveledActionButtonSkin + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class ActionBarSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + mx_internal static const ACTIONBAR_CHROME_COLOR_RATIOS:Array = [0, 80]; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ActionBarSkin() + { + super(); + + borderClass = spark.skins.android4.assets.ActionBarBackground; + + switch (applicationDPI) + { + + case DPIClassification.DPI_640: + { + borderSize = 2; + layoutContentGroupHeight = 172; + layoutTitleGroupHorizontalPadding = 52; + + break; + } + case DPIClassification.DPI_480: + { + borderSize = 2; + layoutContentGroupHeight = 130; + layoutTitleGroupHorizontalPadding = 40; + + + break; + } + case DPIClassification.DPI_320: + { + borderSize = 2; + layoutContentGroupHeight = 86; + layoutTitleGroupHorizontalPadding = 26; + + break; + } + case DPIClassification.DPI_240: + { + borderSize = 1; + layoutContentGroupHeight = 65; + layoutTitleGroupHorizontalPadding = 20; + + break; + } + case DPIClassification.DPI_120: + { + borderSize = 1; + layoutContentGroupHeight = 32; + layoutTitleGroupHorizontalPadding = 10; + + break; + } + default: + { + // default DPI_160 + borderSize = 1; + layoutContentGroupHeight = 43; + layoutTitleGroupHorizontalPadding = 13; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Graphics variables + // + //-------------------------------------------------------------------------- + + /** + * FXG Class reference for the ActionBar background border graphic. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var borderClass:Class; + + //-------------------------------------------------------------------------- + // + // Layout variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var borderSize:uint; + + /** + * Default height for navigationGroup, titleGroup and actionGroup. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var layoutContentGroupHeight:uint; + + /** + * Default horizontal padding for the titleGroup and titleDisplay. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var layoutTitleGroupHorizontalPadding:uint; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:ActionBar; + + /** + * @private + */ + private var _navigationVisible:Boolean = false; + + /** + * @private + */ + private var _titleContentVisible:Boolean = false; + + /** + * @private + */ + private var _actionVisible:Boolean = false; + + /** + * @private + */ + private var border:SpriteVisualElement; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.components.ActionBar#navigationGroup + */ + public var navigationGroup:Group; + + /** + * @copy spark.components.ActionBar#titleGroup + */ + public var titleGroup:Group; + + /** + * @copy spark.components.ActionBar#actionGroup + */ + public var actionGroup:Group; + + /** + * @copy spark.components.ActionBar#titleDisplay + * + * @private + * Wraps a StyleableTextField in a UIComponent to be compatible with + * ViewTransition effects. + */ + public var titleDisplay:TitleDisplayComponent; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + if (borderClass) + { + border = new borderClass(); + addChild(border); + } + + navigationGroup = new Group(); + var hLayout:HorizontalLayout = new HorizontalLayout(); + hLayout.horizontalAlign = HorizontalAlign.LEFT; + hLayout.verticalAlign = VerticalAlign.MIDDLE; + hLayout.gap = 0; + hLayout.paddingLeft = hLayout.paddingTop = hLayout.paddingRight = + hLayout.paddingBottom = 0; + navigationGroup.layout = hLayout; + navigationGroup.id = "navigationGroup"; + + titleGroup = new Group(); + hLayout = new HorizontalLayout(); + hLayout.horizontalAlign = HorizontalAlign.LEFT; + hLayout.verticalAlign = VerticalAlign.MIDDLE; + hLayout.gap = 0; + hLayout.paddingLeft = hLayout.paddingRight = layoutTitleGroupHorizontalPadding; + hLayout.paddingTop = hLayout.paddingBottom = 0; + titleGroup.layout = hLayout; + titleGroup.id = "titleGroup"; + + actionGroup = new Group(); + hLayout = new HorizontalLayout(); + hLayout.horizontalAlign = HorizontalAlign.RIGHT; + hLayout.verticalAlign = VerticalAlign.MIDDLE; + hLayout.gap = 0; + hLayout.paddingLeft = hLayout.paddingTop = hLayout.paddingRight = + hLayout.paddingBottom = 0; + actionGroup.layout = hLayout; + actionGroup.id = "actionGroup"; + + titleDisplay = new TitleDisplayComponent(); + titleDisplay.id = "titleDisplay"; + + // initialize titleAlign style (center is managed explicitly in layoutContents) + var titleAlign:String = getStyle("titleAlign"); + titleAlign = (titleAlign == "center") ? TextFormatAlign.LEFT : titleAlign; + titleDisplay.setStyle("textAlign", titleAlign); + + addChild(navigationGroup); + addChild(titleGroup); + addChild(actionGroup); + addChild(titleDisplay); + } + + /** + * @private + */ + override protected function measure():void + { + var titleWidth:Number = 0; + var titleHeight:Number = 0; + + if (_titleContentVisible) + { + titleWidth = titleGroup.getPreferredBoundsWidth(); + titleHeight = titleGroup.getPreferredBoundsHeight(); + } + else + { + // use titleLayout for paddingLeft and paddingRight + var layoutObject:Object = hostComponent.titleLayout; + titleWidth = ((layoutObject.paddingLeft) ? Number(layoutObject.paddingLeft) : 0) + + ((layoutObject.paddingRight) ? Number(layoutObject.paddingRight) : 0) + + titleDisplay.getPreferredBoundsWidth(); + + titleHeight = titleDisplay.getPreferredBoundsHeight(); + } + + measuredWidth = + getStyle("paddingLeft") + + navigationGroup.getPreferredBoundsWidth() + + titleWidth + + actionGroup.getPreferredBoundsWidth() + + getStyle("paddingRight"); + + // measuredHeight is contentGroupHeight, 2x border on top and bottom + measuredHeight = + getStyle("paddingTop") + + Math.max(layoutContentGroupHeight, + navigationGroup.getPreferredBoundsHeight(), + actionGroup.getPreferredBoundsHeight(), + titleHeight) + + getStyle("paddingBottom"); + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + _titleContentVisible = currentState.indexOf("titleContent") >= 0; + _navigationVisible = currentState.indexOf("Navigation") >= 0; + _actionVisible = currentState.indexOf("Action") >= 0; + + invalidateSize(); + invalidateDisplayList(); + } + + /** + * @private + */ + override public function styleChanged(styleProp:String):void + { + if (titleDisplay) + { + var allStyles:Boolean = !styleProp || styleProp == "styleName"; + + if (allStyles || (styleProp == "titleAlign")) + { + var titleAlign:String = getStyle("titleAlign"); + + if (titleAlign == "center") + { + // If the title align is set to center, the alignment is set to LEFT + // so that the skin can manually center the component in layoutContents + titleDisplay.setStyle("textAlign", TextFormatAlign.LEFT); + } + else + { + titleDisplay.setStyle("textAlign", titleAlign); + } + } + } + + super.styleChanged(styleProp); + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + var navigationGroupWidth:Number = 0; + + var paddingLeft:Number = getStyle("paddingLeft"); + var paddingRight:Number = getStyle("paddingRight"); + var paddingTop:Number = getStyle("paddingTop"); + var paddingBottom:Number = getStyle("paddingBottom"); + + var titleCompX:Number = paddingLeft; + var titleCompWidth:Number = 0; + + var actionGroupX:Number = unscaledWidth; + var actionGroupWidth:Number = 0; + + // remove top and bottom padding from content group height + var contentGroupsHeight:Number = Math.max(0, unscaledHeight - paddingTop - paddingBottom); + + if (border) + { + // FXG uses scale-9, drop shadow is drawn outside the bounds + setElementSize(border, unscaledWidth, unscaledHeight); + } + + // position groups, overlap of navigation and action groups is allowed + // when overlap occurs, titleDisplay/titleGroup is not visible + if (_navigationVisible) + { + navigationGroupWidth = navigationGroup.getPreferredBoundsWidth(); + titleCompX += navigationGroupWidth; + + setElementSize(navigationGroup, navigationGroupWidth, contentGroupsHeight); + setElementPosition(navigationGroup, paddingLeft, paddingTop); + } + + if (_actionVisible) + { + // actionGroup x position can be negative + actionGroupWidth = actionGroup.getPreferredBoundsWidth(); + actionGroupX = unscaledWidth - actionGroupWidth - paddingRight; + + setElementSize(actionGroup, actionGroupWidth, contentGroupsHeight); + setElementPosition(actionGroup, actionGroupX, paddingTop); + } + + // titleGroup or titleDisplay is given remaining width after navigation + // and action groups preferred widths + titleCompWidth = unscaledWidth - navigationGroupWidth - actionGroupWidth + - paddingLeft - paddingRight; + + if (titleCompWidth <= 0) + { + titleDisplay.visible = false; + titleGroup.visible = false; + } + else if (_titleContentVisible) + { + titleDisplay.visible = false; + titleGroup.visible = true; + + // use titleGroup for titleContent + setElementSize(titleGroup, titleCompWidth, contentGroupsHeight); + setElementPosition(titleGroup, titleCompX, paddingTop); + } + else + { + // use titleDisplay for title text label + titleGroup.visible = false; + + // use titleLayout for paddingLeft and paddingRight + var layoutObject:Object = hostComponent.titleLayout; + var titlePaddingLeft:Number = (layoutObject.paddingLeft) ? Number(layoutObject.paddingLeft) : 0; + var titlePaddingRight:Number = (layoutObject.paddingRight) ? Number(layoutObject.paddingRight) : 0; + + // align titleDisplay to the absolute center + var titleAlign:String = getStyle("titleAlign"); + + // check for available width after padding + if ((titleCompWidth - titlePaddingLeft - titlePaddingRight) <= 0) + { + titleCompX = 0; + titleCompWidth = 0; + } + else if (titleAlign == "center") + { + // use LEFT instead of CENTER + titleCompWidth = titleDisplay.getExplicitOrMeasuredWidth(); + + // use x position of titleDisplay to implement CENTER + titleCompX = Math.round((unscaledWidth - titleCompWidth) / 2); + + var navigationOverlap:Number = navigationGroupWidth + titlePaddingLeft - titleCompX; + var actionOverlap:Number = (titleCompX + titleCompWidth + titlePaddingRight) - actionGroupX; + + // shrink and/or move titleDisplay width if there is any + // overlap after centering + if ((navigationOverlap > 0) && (actionOverlap > 0)) + { + // remaining width + titleCompX = navigationGroupWidth + titlePaddingLeft; + titleCompWidth = unscaledWidth - navigationGroupWidth - actionGroupWidth - titlePaddingLeft - titlePaddingRight; + } + else if ((navigationOverlap > 0) || (actionOverlap > 0)) + { + if (navigationOverlap > 0) + { + // nudge to the right + titleCompX += navigationOverlap; + } + else if (actionOverlap > 0) + { + // nudge to the left + titleCompX -= actionOverlap; + + // force left padding + if (titleCompX < (navigationGroupWidth + titlePaddingLeft)) + titleCompX = navigationGroupWidth + titlePaddingLeft; + } + + // recompute action overlap and force right padding + actionOverlap = (titleCompX + titleCompWidth + titlePaddingRight) - actionGroupX; + + if (actionOverlap > 0) + titleCompWidth -= actionOverlap; + } + } + else + { + // implement padding by adjusting width and position + titleCompX += titlePaddingLeft; + titleCompWidth = titleCompWidth - titlePaddingLeft - titlePaddingRight; + } + + // check for negative width + titleCompWidth = (titleCompWidth < 0) ? 0 : titleCompWidth; + + setElementSize(titleDisplay, titleCompWidth, contentGroupsHeight); + setElementPosition(titleDisplay, titleCompX, paddingTop); + + titleDisplay.visible = true; + } + } + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + super.drawBackground(unscaledWidth, unscaledHeight); + + var chromeColor:uint = getStyle("chromeColor"); + var backgroundAlphaValue:Number = getStyle("backgroundAlpha"); + var colors:Array = []; + + // apply alpha to chromeColor fill only + var backgroundAlphas:Array = [backgroundAlphaValue, backgroundAlphaValue]; + + // exclude top and bottom 1px borders + colorMatrix.createGradientBox(unscaledWidth, unscaledHeight - (borderSize * 2), Math.PI / 2, 0, 0); + + colors[0] = ColorUtil.adjustBrightness2(chromeColor, 20); + colors[1] = chromeColor; + + graphics.beginGradientFill(GradientType.LINEAR, colors, backgroundAlphas, ACTIONBAR_CHROME_COLOR_RATIOS, colorMatrix); + graphics.drawRect(0, borderSize, unscaledWidth, unscaledHeight - (borderSize * 2)); + graphics.endFill(); + } + +} +} +import flash.events.Event; + +import mx.core.UIComponent; +import mx.core.mx_internal; +import mx.events.FlexEvent; + +import spark.components.supportClasses.StyleableTextField; +import spark.core.IDisplayText; + +use namespace mx_internal; + +/** + * @private + * Component that holds StyleableTextFields to produce a drop shadow effect. + * Combines label and shadow into a single component to allow transitions to + * target them both. + */ +class TitleDisplayComponent extends UIComponent implements IDisplayText +{ + private var titleDisplay:StyleableTextField; + private var titleDisplayShadow:StyleableTextField; + private var title:String; + private var titleChanged:Boolean; + + public function TitleDisplayComponent() + { + super(); + title = ""; + } + + override public function get baselinePosition():Number + { + return titleDisplay.baselinePosition; + } + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + titleDisplay = StyleableTextField(createInFontContext(StyleableTextField)); + titleDisplay.styleName = this; + titleDisplay.editable = false; + titleDisplay.selectable = false; + titleDisplay.multiline = false; + titleDisplay.wordWrap = false; + titleDisplay.addEventListener(FlexEvent.VALUE_COMMIT, + titleDisplay_valueCommitHandler); + + titleDisplayShadow = + StyleableTextField(createInFontContext(StyleableTextField)); + titleDisplayShadow.styleName = this; + titleDisplayShadow.colorName = "textShadowColor"; + titleDisplayShadow.editable = false; + titleDisplayShadow.selectable = false; + titleDisplayShadow.multiline = false; + titleDisplayShadow.wordWrap = false; + + addChild(titleDisplayShadow); + addChild(titleDisplay); + } + + /** + * @private + */ + override protected function commitProperties():void + { + super.commitProperties(); + + if (titleChanged) + { + titleDisplay.text = title; + + invalidateSize(); + invalidateDisplayList(); + + titleChanged = false; + } + } + + /** + * @private + */ + override protected function measure():void + { + // reset text if it was truncated before. + if (titleDisplay.isTruncated) + titleDisplay.text = title; + + measuredWidth = titleDisplay.getPreferredBoundsWidth(); + + // tightTextHeight + measuredHeight = titleDisplay.getPreferredBoundsHeight(); + } + + /** + * @private + */ + override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void + { + super.updateDisplayList(unscaledWidth, unscaledHeight); + + // reset text if it was truncated before. + if (titleDisplay.isTruncated) + titleDisplay.text = title; + titleDisplay.commitStyles(); + + // use preferred height, setLayoutBoundsSize will accommodate for tight + // text adjustment + var tightHeight:Number = titleDisplay.getPreferredBoundsHeight(); + var tightY:Number = (unscaledHeight - tightHeight) / 2; + + titleDisplay.setLayoutBoundsSize(unscaledWidth, tightHeight); + titleDisplay.setLayoutBoundsPosition(0, (unscaledHeight - tightHeight) / 2); + + // now truncate the text + titleDisplay.truncateToFit(); + + titleDisplayShadow.commitStyles(); + titleDisplayShadow.setLayoutBoundsSize(unscaledWidth, tightHeight); + titleDisplayShadow.setLayoutBoundsPosition(0, tightY + 1); + + titleDisplayShadow.alpha = getStyle("textShadowAlpha"); + + // if labelDisplay is truncated, then push it down here as well. + // otherwise, it would have gotten pushed in the labelDisplay_valueCommitHandler() + if (titleDisplay.isTruncated) + titleDisplayShadow.text = titleDisplay.text; + } + + /** + * @private + */ + private function titleDisplay_valueCommitHandler(event:Event):void + { + titleDisplayShadow.text = titleDisplay.text; + } + + public function get text():String + { + return title; + } + + public function set text(value:String):void + { + title = value; + titleChanged = true; + + invalidateProperties(); + } + + public function get isTruncated():Boolean + { + return titleDisplay.isTruncated; + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/BusyIndicatorSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/BusyIndicatorSkin.as new file mode 100644 index 0000000000..dd3aa44908 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/BusyIndicatorSkin.as @@ -0,0 +1,180 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + import flash.events.TimerEvent; + import flash.geom.Matrix; + import flash.utils.Timer; + import mx.core.DPIClassification; + import spark.skins.android4.assets.BusyIndicator; + import spark.skins.mobile.supportClasses.MobileSkin; + + import spark.components.MobileBusyIndicator; + + public class BusyIndicatorSkin extends MobileSkin + { + static private const DEFAULT_ROTATION_INTERVAL:Number = 50; + private var busyIndicatorClass:Class; + private var busyIndicator:DisplayObject; + private var busyIndicatorDiameter:Number; + private var rotationTimer:Timer; + private var rotationInterval:Number; + /** + * @private + * + * Current rotation of this component in degrees. + */ + private var currentRotation:Number = 0; + + public function BusyIndicatorSkin() + { + super(); + + busyIndicatorClass = spark.skins.android4.assets.BusyIndicator; + rotationInterval = getStyle("rotationInterval"); + if (isNaN(rotationInterval)) + rotationInterval = DEFAULT_ROTATION_INTERVAL; + if (rotationInterval < 16.6) + rotationInterval = 16.6; + + switch(applicationDPI) + { + case DPIClassification.DPI_640: + { + busyIndicatorDiameter = 144; + break; + } + case DPIClassification.DPI_480: + { + busyIndicatorDiameter = 108; + break; + } + case DPIClassification.DPI_320: + { + busyIndicatorDiameter = 72; + break; + } + case DPIClassification.DPI_240: + { + busyIndicatorDiameter = 54; + break; + } + case DPIClassification.DPI_120: + { + busyIndicatorDiameter = 27; + break; + } + default://160 DPI + { + busyIndicatorDiameter = 36; + break; + } + } + } + + private var _hostComponent:spark.components.MobileBusyIndicator; + + public function get hostComponent():spark.components.MobileBusyIndicator + { + return _hostComponent; + } + + public function set hostComponent(value:spark.components.MobileBusyIndicator):void + { + _hostComponent = value; + } + + override protected function createChildren():void + { + busyIndicator = new busyIndicatorClass(); + busyIndicator.width = busyIndicator.height = busyIndicatorDiameter; + addChild(busyIndicator); + } + + override protected function measure():void + { + measuredWidth = busyIndicatorDiameter; + measuredHeight = busyIndicatorDiameter; + + measuredMinHeight = busyIndicatorDiameter; + measuredMinWidth = busyIndicatorDiameter + } + + override protected function commitCurrentState():void + { + super.commitCurrentState(); + if(currentState == "rotatingState") + { + startRotation(); + } + else + { + stopRotation(); + } + } + + private function startRotation():void + { + rotationTimer = new Timer(rotationInterval); + if (!rotationTimer.hasEventListener(TimerEvent.TIMER)) + { + rotationTimer.addEventListener(TimerEvent.TIMER, timerHandler); + rotationTimer.start(); + } + } + + private function stopRotation():void + { + if (rotationTimer) + { + rotationTimer.removeEventListener(TimerEvent.TIMER, timerHandler); + rotationTimer.stop(); + rotationTimer = null; + } + } + + /** + * @private + * + * Rotate the spinner once for each timer event. + */ + private function timerHandler(event:TimerEvent):void + { + currentRotation += rotationInterval; + if (currentRotation >= 360) + currentRotation = 0; + + rotate(busyIndicator,currentRotation,measuredWidth/2,measuredHeight/2); + event.updateAfterEvent(); + } + + private var rotationMatrix:Matrix; + private function rotate(obj:DisplayObject, angle:Number, aroundX:Number, aroundY:Number):void + { + rotationMatrix = new Matrix(); + rotationMatrix.translate(-aroundX,-aroundY); + rotationMatrix.rotate(Math.PI*angle/180); + rotationMatrix.translate(aroundX,aroundY); + obj.transform.matrix = rotationMatrix; + } + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as new file mode 100644 index 0000000000..4b8cbba095 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as @@ -0,0 +1,93 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.skins.android4.assets.ButtonBarFirstButton_selectedDown; + import spark.skins.android4.assets.ButtonBarFirstButton_selectedUp; + import spark.skins.mobile.supportClasses.ButtonBarButtonSkinBase; + + /** + * Android 4.x specific Button skin for the first Button in a ButtonBar. + * + * @see spark.components.ButtonBar#firstButton + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class ButtonBarFirstButtonSkin extends ButtonBarButtonSkinBase + { + + /** + * Class to use for the border in the selected and down state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var selectedDownBorderSkin:Class; + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ButtonBarFirstButtonSkin() + { + super(); + + upBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_up; + downBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_down; + selectedBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_selectedUp; + selectedDownBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_selectedDown; + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + //Dont draw background + } + + override protected function getBorderClassForCurrentState():Class + { + var isSelected:Boolean = currentState.indexOf("Selected") >= 0; + var isDown:Boolean = currentState.indexOf("down") >= 0; + + if (isSelected && !isDown ) + return selectedBorderSkin; + else if (isSelected && isDown) + return selectedDownBorderSkin; + else if (!isSelected && !isDown) + return upBorderSkin; + else + return downBorderSkin; + } + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as new file mode 100644 index 0000000000..c6206d0f42 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as @@ -0,0 +1,95 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.skins.android4.assets.ButtonBarMiddleButton_selectedDown; + import spark.skins.android4.assets.ButtonBarMiddleButton_selectedUp; + import spark.skins.android4.assets.ButtonBarMiddleButton_up; + import spark.skins.mobile.supportClasses.ButtonBarButtonSkinBase; + + /** + * Android 4.x specific Button skin for middle Buttons in a ButtonBar. + * + * @see spark.components.ButtonBar#middleButton + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class ButtonBarMiddleButtonSkin extends ButtonBarButtonSkinBase + { + + /** + * Class to use for the border in the selected and down state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var selectedDownBorderSkin:Class; + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ButtonBarMiddleButtonSkin() + { + super(); + + upBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_up; + downBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_down; + selectedBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_selectedUp; + selectedDownBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_selectedDown; + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + //Dont draw background + } + + override protected function getBorderClassForCurrentState():Class + { + var isSelected:Boolean = currentState.indexOf("Selected") >= 0; + var isDown:Boolean = currentState.indexOf("down") >= 0; + + if (isSelected && !isDown ) + return selectedBorderSkin; + else if (isSelected && isDown) + return selectedDownBorderSkin; + else if (!isSelected && !isDown) + return upBorderSkin; + else + return downBorderSkin; + } + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as new file mode 100644 index 0000000000..4b194fdd74 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as @@ -0,0 +1,166 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.ButtonBar; +import spark.components.ButtonBarButton; +import spark.components.DataGroup; +import spark.components.supportClasses.ButtonBarHorizontalLayout; +import spark.skins.android4.ButtonBarFirstButtonSkin; +import spark.skins.mobile.ButtonBarLastButtonSkin; +import spark.skins.mobile.supportClasses.ButtonBarButtonClassFactory; +import spark.skins.mobile.supportClasses.MobileSkin; + +/** + * The Android 4.x specific skin class for the Spark ButtonBar component. + * + * @see spark.components.ButtonBar + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class ButtonBarSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function ButtonBarSkin() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:ButtonBar; + + /** + * @copy spark.components.ButtonBar#firstButton + */ + public var firstButton:ButtonBarButtonClassFactory; + + /** + * @copy spark.components.ButtonBar#lastButton + */ + public var lastButton:ButtonBarButtonClassFactory; + + /** + * @copy spark.components.ButtonBar#middleButton + */ + public var middleButton:ButtonBarButtonClassFactory; + + /** + * @copy spark.components.SkinnableDataContainer#dataGroup + */ + public var dataGroup:DataGroup; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + // Set up the class factories for the buttons + 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; + } + + // create the data group to house the buttons + if (!dataGroup) + { + dataGroup = new DataGroup(); + var hLayout:ButtonBarHorizontalLayout = new ButtonBarHorizontalLayout(); + hLayout.gap = 0; + dataGroup.layout = hLayout; + addChild(dataGroup); + } + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + alpha = (currentState == "disabled") ? 0.5 : 1; + } + + /** + * @private + */ + override protected function measure():void + { + measuredWidth = dataGroup.measuredWidth; + measuredHeight = dataGroup.measuredHeight; + + measuredMinWidth = dataGroup.measuredMinWidth; + measuredMinHeight = dataGroup.measuredMinHeight; + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + setElementPosition(dataGroup, 0, 0); + setElementSize(dataGroup, unscaledWidth, unscaledHeight); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as new file mode 100644 index 0000000000..43fb108c61 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as @@ -0,0 +1,397 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + +import mx.core.DPIClassification; +import mx.core.mx_internal; +import mx.events.FlexEvent; + +import spark.components.supportClasses.StyleableTextField; +import spark.skins.android4.assets.Button_down; +import spark.skins.android4.assets.Button_up; +import spark.skins.mobile.supportClasses.ButtonSkinBase; + + +use namespace mx_internal; + +/** + * ActionScript-based skin for Button controls in mobile applications. The skin supports + * iconClass and labelPlacement. It uses FXG classes to + * implement the vector drawing. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class ButtonSkin extends ButtonSkinBase +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * An array of color distribution ratios. + * This is used in the chrome color fill. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + mx_internal static const CHROME_COLOR_RATIOS:Array = [0, 127.5]; + + /** + * An array of alpha values for the corresponding colors in the colors array. + * This is used in the chrome color fill. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + mx_internal static const CHROME_COLOR_ALPHAS:Array = [1, 1]; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ButtonSkin() + { + super(); + upBorderSkin = spark.skins.android4.assets.Button_up; + downBorderSkin = spark.skins.android4.assets.Button_down; + layoutCornerEllipseSize = 0; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + + layoutGap = 20; + layoutPaddingLeft = 40; + layoutPaddingRight = 40; + layoutPaddingTop = 40; + layoutPaddingBottom = 40; + layoutBorderSize = 2; + measuredDefaultWidth = 128; + measuredDefaultHeight = 172; + + break; + } + case DPIClassification.DPI_480: + { + + layoutGap = 14; + layoutPaddingLeft = 30; + layoutPaddingRight = 30; + layoutPaddingTop = 30; + layoutPaddingBottom = 30; + layoutBorderSize = 2; + measuredDefaultWidth = 96; + measuredDefaultHeight = 130; + + break; + } + case DPIClassification.DPI_320: + { + + layoutGap = 10; + layoutPaddingLeft = 20; + layoutPaddingRight = 20; + layoutPaddingTop = 20; + layoutPaddingBottom = 20; + layoutBorderSize = 2; + measuredDefaultWidth = 64; + measuredDefaultHeight = 86; + + break; + } + case DPIClassification.DPI_240: + { + + layoutGap = 7; + layoutPaddingLeft = 15; + layoutPaddingRight = 15; + layoutPaddingTop = 15; + layoutPaddingBottom = 15; + layoutBorderSize = 1; + measuredDefaultWidth = 48; + measuredDefaultHeight = 65; + + break; + } + case DPIClassification.DPI_120: + { + + layoutGap = 4; + layoutPaddingLeft = 8; + layoutPaddingRight = 8; + layoutPaddingTop = 8; + layoutPaddingBottom = 8; + layoutBorderSize = 1; + measuredDefaultWidth = 24; + measuredDefaultHeight = 33; + + break; + } + default: + { + + layoutGap = 5; + layoutPaddingLeft = 10; + layoutPaddingRight = 10; + layoutPaddingTop = 10; + layoutPaddingBottom = 10; + layoutBorderSize = 1; + measuredDefaultWidth = 32; + measuredDefaultHeight = 43; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Layout variables + // + //-------------------------------------------------------------------------- + + /** + * Defines the corner radius. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var layoutCornerEllipseSize:uint; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + private var _border:DisplayObject; + + private var changeFXGSkin:Boolean = false; + + private var borderClass:Class; + + mx_internal var fillColorStyleName:String = "chromeColor"; + + /** + * Defines the shadow for the Button control's label. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var labelDisplayShadow:StyleableTextField; + + /** + * Read-only button border graphic. Use getBorderClassForCurrentState() + * to specify a graphic per-state. + * + * @see #getBorderClassForCurrentState() + */ + protected function get border():DisplayObject + { + return _border; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * Class to use for the border in the up state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + * @default Button_up + */ + protected var upBorderSkin:Class; + + /** + * Class to use for the border in the down state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + * @default Button_down + */ + protected var downBorderSkin:Class; + + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (!labelDisplayShadow && labelDisplay) + { + labelDisplayShadow = StyleableTextField(createInFontContext(StyleableTextField)); + labelDisplayShadow.styleName = this; + labelDisplayShadow.colorName = "textShadowColor"; + labelDisplayShadow.useTightTextBounds = false; + + // add shadow before display + addChildAt(labelDisplayShadow, getChildIndex(labelDisplay)); + } + + setStyle("textAlign", "center"); + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + borderClass = getBorderClassForCurrentState(); + + if (!(_border is borderClass)) + changeFXGSkin = true; + + // update borderClass and background + invalidateDisplayList(); + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + // size the FXG background + if (changeFXGSkin) + { + changeFXGSkin = false; + + if (_border) + { + removeChild(_border); + _border = null; + } + + if (borderClass) + { + _border = new borderClass(); + addChildAt(_border, 0); + } + } + + layoutBorder(unscaledWidth, unscaledHeight); + + // update label shadow + labelDisplayShadow.alpha = getStyle("textShadowAlpha"); + labelDisplayShadow.commitStyles(); + + // don't use tightText positioning on shadow + setElementPosition(labelDisplayShadow, labelDisplay.x, labelDisplay.y + 1); + setElementSize(labelDisplayShadow, labelDisplay.width, labelDisplay.height); + + // if labelDisplay is truncated, then push it down here as well. + // otherwise, it would have gotten pushed in the labelDisplay_valueCommitHandler() + if (labelDisplay.isTruncated) + labelDisplayShadow.text = labelDisplay.text; + } + + /** + * Position the background of the skin. Override this function to re-position the background. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + mx_internal function layoutBorder(unscaledWidth:Number, unscaledHeight:Number):void + { + setElementSize(border, unscaledWidth, unscaledHeight); + setElementPosition(border, 0, 0); + } + + /** + * Returns the borderClass to use based on the currentState. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected function getBorderClassForCurrentState():Class + { + if (currentState == "down") + return downBorderSkin; + else + return upBorderSkin; + } + + //-------------------------------------------------------------------------- + // + // Event Handlers + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function labelDisplay_valueCommitHandler(event:FlexEvent):void + { + super.labelDisplay_valueCommitHandler(event); + labelDisplayShadow.text = labelDisplay.text; + } + +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/CalloutSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/CalloutSkin.as new file mode 100644 index 0000000000..95fefef02c --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/CalloutSkin.as @@ -0,0 +1,825 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.BlendMode; +import flash.display.GradientType; +import flash.display.Graphics; +import flash.display.Sprite; +import flash.events.Event; + +import mx.core.DPIClassification; +import mx.core.UIComponent; +import mx.core.mx_internal; +import mx.events.EffectEvent; +import mx.events.FlexEvent; +import mx.utils.ColorUtil; + +import spark.components.ArrowDirection; +import spark.components.Callout; +import spark.components.ContentBackgroundAppearance; +import spark.components.Group; +import spark.core.SpriteVisualElement; +import spark.effects.Fade; +import spark.primitives.RectangularDropShadow; +import spark.skins.android4.assets.CalloutContentBackground; +import spark.skins.android4.supportClasses.CalloutArrow; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * The default skin class for the Spark Callout component in mobile + * applications. + * + *

The contentGroup lies above a backgroundColor fill + * which frames the contentGroup. The position and size of the frame + * adjust based on the host component arrowDirection, leaving + * space for the arrow to appear on the outside edge of the + * frame.

+ * + *

The arrow skin part is not positioned by the skin. Instead, + * the Callout component positions the arrow relative to the owner in + * updateSkinDisplayList(). This method assumes that Callout skin + * and the arrow use the same coordinate space.

+ * + * @see spark.components.Callout + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ +public class CalloutSkin extends MobileSkin +{ + mx_internal static const BACKGROUND_GRADIENT_BRIGHTNESS_TOP:int = 15; + + mx_internal static const BACKGROUND_GRADIENT_BRIGHTNESS_BOTTOM:int = -15; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public function CalloutSkin() + { + super(); + + dropShadowAlpha = 0.7; + contentBackgroundInsetClass = spark.skins.android4.assets.CalloutContentBackground; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + + backgroundCornerRadius = 24; + backgroundGradientHeight = 440; + frameThickness = 12; + arrowWidth = 160; + arrowHeight = 80; + contentCornerRadius = 40; + dropShadowBlurX = 64; + dropShadowBlurY = 64; + dropShadowDistance = 12; + highlightWeight = 4; + + break; + } + case DPIClassification.DPI_480: + { + backgroundCornerRadius = 16; + backgroundGradientHeight = 330; + frameThickness = 8; + arrowWidth = 120; + arrowHeight = 60; + contentCornerRadius = 28; + dropShadowBlurX = 48; + dropShadowBlurY = 48; + dropShadowDistance = 8; + highlightWeight = 2; + + break; + } + case DPIClassification.DPI_320: + { + + backgroundCornerRadius = 12; + backgroundGradientHeight = 220; + frameThickness = 6; + arrowWidth = 80; + arrowHeight = 40; + contentCornerRadius = 20; + dropShadowBlurX = 32; + dropShadowBlurY = 32; + dropShadowDistance = 6; + highlightWeight = 2; + + break; + } + case DPIClassification.DPI_240: + { + backgroundCornerRadius = 8; + backgroundGradientHeight = 165; + frameThickness = 4; + arrowWidth = 60; + arrowHeight = 30; + contentCornerRadius = 14; + dropShadowBlurX = 24; + dropShadowBlurY = 24; + dropShadowDistance = 4; + highlightWeight = 1; + + break; + } + case DPIClassification.DPI_120: + { + backgroundCornerRadius = 4; + backgroundGradientHeight = 83; + frameThickness = 2; + arrowWidth = 30; + arrowHeight = 15; + contentCornerRadius = 7; + dropShadowBlurX = 12; + dropShadowBlurY = 12; + dropShadowDistance = 2; + highlightWeight = 0.5; + + break; + } + default: + { + // default DPI_160 + backgroundCornerRadius = 6; + backgroundGradientHeight = 110; + frameThickness = 3; + arrowWidth = 40; + arrowHeight = 20; + contentCornerRadius = 10; + dropShadowBlurX = 16; + dropShadowBlurY = 16; + dropShadowDistance = 3; + highlightWeight = 1; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:Callout; + + /** + * Enables a RectangularDropShadow behind the backgroundColor frame. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var dropShadowVisible:Boolean = true; + + /** + * Enables a vertical linear gradient in the backgroundColor frame. This + * gradient fill is drawn across both the arrow and the frame. By default, + * the gradient brightens the background color by 15% and darkens it by 60%. + * + * @default true + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var useBackgroundGradient:Boolean = true; + + /** + * Corner radius used for the contentBackgroundColor fill. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var contentCornerRadius:uint; + + /** + * A class reference to an FXG class that is layered underneath the + * contentGroup. The instance of this class is sized to match the + * contentGroup. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var contentBackgroundInsetClass:Class; + + /** + * Corner radius of the backgroundColor "frame". + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var backgroundCornerRadius:Number; + + /** + * The thickness of the backgroundColor "frame" that surrounds the + * contentGroup. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var frameThickness:Number; + + /** + * Color of the border stroke around the backgroundColor "frame". + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var borderColor:Number = -1; // not set + + /** + * Thickness of the border stroke around the backgroundColor + * "frame". + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var borderThickness:Number = -1 ; // marker that borderThickness was not set directly + + /** + * Width of the arrow in vertical directions. This property also controls + * the height of the arrow in horizontal directions. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var arrowWidth:Number; + + /** + * Height of the arrow in vertical directions. This property also controls + * the width of the arrow in horizontal directions. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var arrowHeight:Number; + + /** + * @private + * Instance of the contentBackgroundClass + */ + mx_internal var contentBackgroundGraphic:SpriteVisualElement; + + /** + * @private + * Tracks changes to the skin state to support the fade out tranisition + * when closed; + */ + mx_internal var isOpen:Boolean; + + private var backgroundGradientHeight:Number; + + private var contentMask:Sprite; + + private var backgroundFill:SpriteVisualElement; + + private var dropShadow:RectangularDropShadow; + + private var dropShadowBlurX:Number; + + private var dropShadowBlurY:Number; + + private var dropShadowDistance:Number; + + private var dropShadowAlpha:Number; + + private var fade:Fade; + + private var highlightWeight:Number; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.components.SkinnableContainer#contentGroup + */ + public var contentGroup:Group; + + /** + * @copy spark.components.Callout#arrow + */ + public var arrow:UIComponent; + + /* helper private accessors */ + + /* returns borderThickness from style if member is -1, or borderThickness. Returns 0 if NaN */ + mx_internal function get actualBorderThickness():Number + { + var border: Number = borderThickness != -1 ? borderThickness : getStyle('borderThickness'); + return isNaN(border)? 0: border; + } + + mx_internal function get actualBorderColor():uint + { + return borderColor != -1 ? borderColor: getStyle('borderColor'); + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (dropShadowVisible) + { + dropShadow = new RectangularDropShadow(); + dropShadow.angle = 90; + dropShadow.distance = dropShadowDistance; + dropShadow.blurX = dropShadowBlurX; + dropShadow.blurY = dropShadowBlurY; + dropShadow.tlRadius = dropShadow.trRadius = dropShadow.blRadius = + dropShadow.brRadius = backgroundCornerRadius ; + dropShadow.mouseEnabled = false; + dropShadow.alpha = dropShadowAlpha; + addChild(dropShadow); + } + + // background fill placed above the drop shadow + backgroundFill = new SpriteVisualElement(); + addChild(backgroundFill); + + // arrow + if (!arrow) + { + arrow = new CalloutArrow(); + arrow.id = "arrow"; + arrow.styleName = this; + addChild(arrow); + } + + // contentGroup + if (!contentGroup) + { + contentGroup = new Group(); + contentGroup.id = "contentGroup"; + addChild(contentGroup); + } + + + } + + /** + * @private + */ + override protected function commitProperties():void + { + super.commitProperties(); + + // add or remove the contentBackgroundGraphic + var contentBackgroundAppearance:String = getStyle("contentBackgroundAppearance"); + + if (contentBackgroundAppearance == ContentBackgroundAppearance.INSET) + { + // create the contentBackgroundGraphic + if (!contentBackgroundGraphic && contentBackgroundInsetClass) + { + contentBackgroundGraphic = new contentBackgroundInsetClass() as SpriteVisualElement; + + // with the current skin structure, contentBackgroundGraphic is + // always the last child + addChild(contentBackgroundGraphic); + } + } + else if (contentBackgroundGraphic) + { + // if already created, remove the graphic for "flat" and "none" + removeChild(contentBackgroundGraphic); + contentBackgroundGraphic = null; + } + + // always invalidate to accomodate arrow direction changes + invalidateSize(); + invalidateDisplayList(); + } + + + /** + * @private + */ + override protected function measure():void + { + super.measure(); + + var borderWeight:Number =actualBorderThickness; + var frameAdjustment:Number = (frameThickness + borderWeight) * 2; + + var arrowMeasuredWidth:Number; + var arrowMeasuredHeight:Number; + + // pad the arrow so that the edges are within the background corner radius + if (isArrowHorizontal) + { + arrowMeasuredWidth = arrowHeight; + arrowMeasuredHeight = arrowWidth + (backgroundCornerRadius * 2); + } + else if (isArrowVertical) + { + arrowMeasuredWidth = arrowWidth + (backgroundCornerRadius * 2); + arrowMeasuredHeight = arrowHeight; + } + + // count the contentGroup size and frame size + measuredMinWidth = contentGroup.measuredMinWidth + frameAdjustment; + measuredMinHeight = contentGroup.measuredMinHeight + frameAdjustment; + + measuredWidth = contentGroup.getPreferredBoundsWidth() + frameAdjustment; + measuredHeight = contentGroup.getPreferredBoundsHeight() + frameAdjustment; + + // add the arrow size based on the arrowDirection + if (isArrowHorizontal) + { + measuredMinWidth += arrowMeasuredWidth; + measuredMinHeight = Math.max(measuredMinHeight, arrowMeasuredHeight); + + measuredWidth += arrowMeasuredWidth; + measuredHeight = Math.max(measuredHeight, arrowMeasuredHeight); + } + else if (isArrowVertical) + { + measuredMinWidth += Math.max(measuredMinWidth, arrowMeasuredWidth); + measuredMinHeight += arrowMeasuredHeight; + + measuredWidth = Math.max(measuredWidth, arrowMeasuredWidth); + measuredHeight += arrowMeasuredHeight; + } + } + + /** + * @private + * SkinnaablePopUpContainer skins must dispatch a + * FlexEvent.STATE_CHANGE_COMPLETE event for the component to properly + * update the skin state. + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + var isNormal:Boolean = (currentState == "normal"); + var isDisabled:Boolean = (currentState == "disabled") + + // play a fade out if the callout was previously open + if (!(isNormal || isDisabled) && isOpen) + { + if (!fade) + { + fade = new Fade(); + fade.target = this; + fade.duration = 200; + fade.alphaTo = 0; + } + + // BlendMode.LAYER while fading out + blendMode = BlendMode.LAYER; + + // play a short fade effect + fade.addEventListener(EffectEvent.EFFECT_END, stateChangeComplete); + fade.play(); + + isOpen = false; + } + else + { + isOpen = isNormal || isDisabled; + + // handle re-opening the Callout while fading out + if (fade && fade.isPlaying) + { + // Do not dispatch a state change complete. + // SkinnablePopUpContainer handles state interruptions. + fade.removeEventListener(EffectEvent.EFFECT_END, stateChangeComplete); + fade.stop(); + } + + if (isDisabled) + { + // BlendMode.LAYER to allow CalloutArrow BlendMode.ERASE + blendMode = BlendMode.LAYER; + + alpha = 0.5; + } + else + { + // BlendMode.NORMAL for non-animated state transitions + blendMode = BlendMode.NORMAL; + + if (isNormal) + alpha = 1; + else + alpha = 0; + } + + stateChangeComplete(); + } + } + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + super.drawBackground(unscaledWidth, unscaledHeight); + + var frameEllipseSize:Number = backgroundCornerRadius * 2; + + // account for borderThickness center stroke alignment + var borderWeight:Number =actualBorderThickness; + var showBorder:Boolean = borderWeight > 0 ; + + + // contentBackgroundGraphic already accounts for the arrow position + // use it's positioning instead of recalculating based on unscaledWidth + // and unscaledHeight + var frameX:Number = Math.floor(contentGroup.getLayoutBoundsX() - frameThickness) - (borderWeight / 2); + var frameY:Number = Math.floor(contentGroup.getLayoutBoundsY() - frameThickness) - (borderWeight / 2); + var frameWidth:Number = contentGroup.getLayoutBoundsWidth() + (frameThickness * 2) + borderWeight; + var frameHeight:Number = contentGroup.getLayoutBoundsHeight() + (frameThickness * 2) + borderWeight; + + var backgroundColor:Number = getStyle("primaryAccentColor"); + var backgroundAlpha:Number = getStyle("backgroundAlpha"); + + var bgFill:Graphics = backgroundFill.graphics; + bgFill.clear(); + + if (showBorder) + bgFill.lineStyle(borderWeight, actualBorderColor, 1, true); + + if (useBackgroundGradient) + { + // top color is brighter if arrowDirection == ArrowDirection.UP + var backgroundColorTop:Number = ColorUtil.adjustBrightness2(backgroundColor, + BACKGROUND_GRADIENT_BRIGHTNESS_TOP); + var backgroundColorBottom:Number = ColorUtil.adjustBrightness2(backgroundColor, + BACKGROUND_GRADIENT_BRIGHTNESS_BOTTOM); + + // max gradient height = backgroundGradientHeight + colorMatrix.createGradientBox(unscaledWidth, backgroundGradientHeight, + Math.PI / 2, 0, 0); + + bgFill.beginGradientFill(GradientType.LINEAR, + [backgroundColorTop, backgroundColorBottom], + [backgroundAlpha, backgroundAlpha], + [0, 255], + colorMatrix); + } + else + { + bgFill.beginFill(backgroundColor, backgroundAlpha); + } + + bgFill.drawRoundRect(frameX, frameY, frameWidth, + frameHeight, frameEllipseSize, frameEllipseSize); + bgFill.endFill(); + + // draw content background styles + var contentBackgroundAppearance:String = getStyle("contentBackgroundAppearance"); + + if (contentBackgroundAppearance != ContentBackgroundAppearance.NONE) + { + var contentEllipseSize:Number = contentCornerRadius * 2; + var contentBackgroundAlpha:Number = getStyle("contentBackgroundAlpha"); + var contentWidth:Number = contentGroup.getLayoutBoundsWidth(); + var contentHeight:Number = contentGroup.getLayoutBoundsHeight(); + + // all appearance values except for "none" use a mask + if (!contentMask) + contentMask = new SpriteVisualElement(); + + contentGroup.mask = contentMask; + + // draw contentMask in contentGroup coordinate space + var maskGraphics:Graphics = contentMask.graphics; + maskGraphics.clear(); + maskGraphics.beginFill(0, 1); + maskGraphics.drawRoundRect(0, 0, contentWidth, contentHeight, + contentEllipseSize, contentEllipseSize); + maskGraphics.endFill(); + + // reset line style to none + if (showBorder) + bgFill.lineStyle(NaN); + + // draw the contentBackgroundColor + bgFill.beginFill(getStyle("contentBackgroundColor"), + contentBackgroundAlpha); + bgFill.drawRoundRect(contentGroup.getLayoutBoundsX(), + contentGroup.getLayoutBoundsY(), + contentWidth, contentHeight, contentEllipseSize, contentEllipseSize); + bgFill.endFill(); + + if (contentBackgroundGraphic) + contentBackgroundGraphic.alpha = contentBackgroundAlpha; + } + else // if (contentBackgroundAppearance == CalloutContentBackgroundAppearance.NONE)) + { + // remove the mask + if (contentMask) + { + contentGroup.mask = null; + contentMask = null; + } + } + + // draw highlight in the callout when the arrow is hidden + if (useBackgroundGradient && !isArrowHorizontal && !isArrowVertical) + { + // highlight width spans the callout width minus the corner radius + var highlightWidth:Number = frameWidth - frameEllipseSize; + var highlightX:Number = frameX + backgroundCornerRadius; + var highlightOffset:Number = (highlightWeight * 1.5); + + // straight line across the top + bgFill.lineStyle(highlightWeight, 0xFFFFFF, 0.2 * backgroundAlpha); + bgFill.moveTo(highlightX, highlightOffset); + bgFill.lineTo(highlightX + highlightWidth, highlightOffset); + } + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + // pad the arrow so that the edges are within the background corner radius + if (isArrowHorizontal) + { + arrow.width = arrowHeight; + arrow.height = arrowWidth + (backgroundCornerRadius * 2); + } + else if (isArrowVertical) + { + arrow.width = arrowWidth + (backgroundCornerRadius * 2); + arrow.height = arrowHeight; + } + + setElementSize(backgroundFill, unscaledWidth, unscaledHeight); + setElementPosition(backgroundFill, 0, 0); + + var frameX:Number = 0; + var frameY:Number = 0; + var frameWidth:Number = unscaledWidth; + var frameHeight:Number = unscaledHeight; + + switch (hostComponent.arrowDirection) + { + case ArrowDirection.UP: + frameY = arrow.height; + frameHeight -= arrow.height; + break; + case ArrowDirection.DOWN: + frameHeight -= arrow.height; + break; + case ArrowDirection.LEFT: + frameX = arrow.width; + frameWidth -= arrow.width; + break; + case ArrowDirection.RIGHT: + frameWidth -= arrow.width; + break; + default: + // no arrow, content takes all available space + break; + } + + if (dropShadow) + { + setElementSize(dropShadow, frameWidth, frameHeight); + setElementPosition(dropShadow, frameX, frameY); + } + + // Show frameThickness by inset of contentGroup + var borderWeight:Number = actualBorderThickness; + var contentBackgroundAdjustment:Number = frameThickness + borderWeight; + + var contentBackgroundX:Number = frameX + contentBackgroundAdjustment; + var contentBackgroundY:Number = frameY + contentBackgroundAdjustment; + + contentBackgroundAdjustment = contentBackgroundAdjustment * 2; + var contentBackgroundWidth:Number = frameWidth - contentBackgroundAdjustment; + var contentBackgroundHeight:Number = frameHeight - contentBackgroundAdjustment; + + if (contentBackgroundGraphic) + { + setElementSize(contentBackgroundGraphic, contentBackgroundWidth, contentBackgroundHeight); + setElementPosition(contentBackgroundGraphic, contentBackgroundX, contentBackgroundY); + } + + setElementSize(contentGroup, contentBackgroundWidth, contentBackgroundHeight); + setElementPosition(contentGroup, contentBackgroundX, contentBackgroundY); + + // mask position is in the contentGroup coordinate space + if (contentMask) + setElementSize(contentMask, contentBackgroundWidth, contentBackgroundHeight); + } + + override public function styleChanged(styleProp:String):void + { + super.styleChanged(styleProp); + + var allStyles:Boolean = !styleProp || styleProp == "styleName"; + + if (allStyles || (styleProp == "contentBackgroundAppearance")) + invalidateProperties(); + + if (allStyles || (styleProp == "backgroundAlpha")) + { + var backgroundAlpha:Number = getStyle("backgroundAlpha"); + + // Use BlendMode.LAYER to allow CalloutArrow to erase the dropShadow + // when the Callout background is transparent + blendMode = (backgroundAlpha < 1) ? BlendMode.LAYER : BlendMode.NORMAL; + } + } + + /** + * @private + */ + mx_internal function get isArrowHorizontal():Boolean + { + return (hostComponent.arrowDirection == ArrowDirection.LEFT + || hostComponent.arrowDirection == ArrowDirection.RIGHT); + } + + /** + * @private + */ + mx_internal function get isArrowVertical():Boolean + { + return (hostComponent.arrowDirection == ArrowDirection.UP + || hostComponent.arrowDirection == ArrowDirection.DOWN); + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + private function stateChangeComplete(event:Event=null):void + { + if (fade && event) + fade.removeEventListener(EffectEvent.EFFECT_END, stateChangeComplete); + + // SkinnablePopUpContainer relies on state changes for open and close + dispatchEvent(new FlexEvent(FlexEvent.STATE_CHANGE_COMPLETE)); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/CheckBoxSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/CheckBoxSkin.as new file mode 100644 index 0000000000..f091205bc4 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/CheckBoxSkin.as @@ -0,0 +1,268 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + + import mx.core.DPIClassification; + + import spark.skins.android4.assets.CheckBox_up; + import spark.skins.mobile.supportClasses.SelectableButtonSkinBase; + + /** + * ActionScript-based skin for CheckBox components in mobile applications. + * + * @see spark.components.CheckBox + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class CheckBoxSkin extends SelectableButtonSkinBase + { + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + private static const exclusions:Array = ["labelDisplay", "labelDisplayShadow"]; + + //-------------------------------------------------------------------------- + // + // Member variables + // + //-------------------------------------------------------------------------- + + protected var symbolOffsetX:Number; + protected var symbolOffsetY:Number; + protected var iconWidth:Number; + protected var iconHeight:Number; + protected var symbolWidth:Number; + protected var symbolHeight:Number; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function CheckBoxSkin() + { + super(); + + layoutPaddingLeft = 0; + layoutPaddingRight = 0; + layoutPaddingTop = 0; + layoutPaddingBottom = 0; + + upIconClass = spark.skins.android4.assets.CheckBox_up; + upSelectedIconClass = spark.skins.android4.assets.CheckBox_upSelected; + downIconClass = spark.skins.android4.assets.CheckBox_down; + downSelectedIconClass = spark.skins.android4.assets.CheckBox_downSelected; + upSymbolIconClass = null; + upSymbolIconSelectedClass = spark.skins.android4.assets.CheckBox_upSymbolSelected; + downSymbolIconSelectedClass = spark.skins.android4.assets.CheckBox_downSymbolSelected; + downSymbolIconClass = null; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + + layoutGap = 16; + minWidth = 128; + minHeight = 128; + layoutBorderSize = 6; + iconWidth = 128; + iconHeight = 128; + symbolWidth = 64; + symbolHeight = 64; + symbolOffsetX = 32; + symbolOffsetY = 32; + + break; + } + case DPIClassification.DPI_480: + { + + layoutGap = 12; + minWidth = 96; + minHeight = 96; + layoutBorderSize = 4; + iconWidth = 96; + iconHeight = 96; + symbolWidth = 48; + symbolHeight = 48; + symbolOffsetX = 24; + symbolOffsetY = 24; + + break; + } + case DPIClassification.DPI_320: + { + + layoutGap = 8; + minWidth = 64; + minHeight = 64; + layoutBorderSize = 3; + iconWidth = 64; + iconHeight = 64; + symbolWidth = 32; + symbolHeight = 32; + symbolOffsetX = 16; + symbolOffsetY = 16; + + break; + } + case DPIClassification.DPI_240: + { + + layoutGap = 6; + minWidth = 48; + minHeight = 48; + layoutBorderSize = 2; + iconWidth = 48; + iconHeight = 48; + symbolWidth = 24; + symbolHeight = 24; + symbolOffsetX = 12; + symbolOffsetY = 12; + + break; + } + case DPIClassification.DPI_120: + { + + layoutGap = 3; + minWidth = 24; + minHeight = 24; + layoutBorderSize = 1; + iconWidth = 24; + iconHeight = 24; + symbolWidth = 12; + symbolHeight = 12; + symbolOffsetX = 6; + symbolOffsetY = 6; + + break; + } + default: + { + // default DPI_160 + + layoutGap = 4; + minWidth = 32; + minHeight = 32; + layoutBorderSize = 2; + iconWidth = 32; + iconHeight = 32; + symbolWidth = 16; + symbolHeight = 16; + symbolOffsetX = 8; + symbolOffsetY = 8; + + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * CheckBox chromeColor is drawn to match the FXG rectangle + * shape and position. + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + // super draws a transparent hit zone + super.drawBackground(unscaledWidth, unscaledHeight); + + // get the size and position of iconDisplay + var currentIcon:DisplayObject = getIconDisplay(); + var widthAdjustment:Number = layoutBorderSize * 2; + + graphics.beginFill(getStyle("chromeColor")); + graphics.drawRoundRect(currentIcon.x + layoutBorderSize, + currentIcon.y + layoutBorderSize, + currentIcon.width - widthAdjustment, + currentIcon.height - widthAdjustment, layoutBorderSize, layoutBorderSize); + graphics.endFill(); + } + + /** + * List of IDs of items that should be excluded when rendering the focus ring. + * Only items of type DisplayObject or GraphicElement should be excluded. Items + * of other types are ignored. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + override protected function get focusSkinExclusions():Array + { + return exclusions; + } + + override protected function commitCurrentState():void + { + super.commitCurrentState(); + if(symbolIcon != null) + { + symbolIcon.width = symbolWidth; + symbolIcon.height = symbolHeight; + } + var iconDisplay:DisplayObject = getIconDisplay(); + if(iconDisplay != null) + { + iconDisplay.width = iconWidth; + iconDisplay.height = iconHeight; + } + } + + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + // position the symbols to align with the background "icon" + if (symbolIcon) + { + var currentIcon:DisplayObject = getIconDisplay(); + setElementPosition(symbolIcon, symbolOffsetX, symbolOffsetY); + } + } + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarSkin.as new file mode 100644 index 0000000000..1e712793fd --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarSkin.as @@ -0,0 +1,205 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 spark.components.Button; +import spark.components.HScrollBar; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * ActionScript-based skin for HScrollBar components in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class HScrollBarSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function HScrollBarSkin() + { + super(); + + minWidth = 20; + thumbSkinClass = HScrollBarThumbSkin; + var paddingBottom:int; + var paddingHorizontal:int; + + // Depending on density set our measured height + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + minHeight = 24; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_640DPI; + paddingHorizontal = HScrollBarThumbSkin.PADDING_HORIZONTAL_640DPI; + break; + } + case DPIClassification.DPI_480: + { + minHeight = 18; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_480DPI; + paddingHorizontal = HScrollBarThumbSkin.PADDING_HORIZONTAL_480DPI; + break; + } + case DPIClassification.DPI_320: + { + minHeight = 12; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_320DPI; + paddingHorizontal = HScrollBarThumbSkin.PADDING_HORIZONTAL_320DPI; + break; + } + case DPIClassification.DPI_240: + { + minHeight = 9; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_240DPI; + paddingHorizontal = HScrollBarThumbSkin.PADDING_HORIZONTAL_240DPI; + break; + } + case DPIClassification.DPI_120: + { + minHeight = 5; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_120DPI; + paddingHorizontal = HScrollBarThumbSkin.PADDING_HORIZONTAL_120DPI; + break; + } + default: + { + // default DPI_160 + minHeight = 6; + paddingBottom = HScrollBarThumbSkin.PADDING_BOTTOM_DEFAULTDPI; + paddingHorizontal = HScrollBarThumbSkin.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); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:HScrollBar; + + /** + * Minimum width for the thumb + */ + protected var minThumbWidth:Number; + + /** + * Skin to use for the thumb Button skin part + */ + protected var thumbSkinClass:Class; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + /** + * HScrollbar track skin part. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var track:Button; + + /** + * HScrollbar thumb skin part. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var thumb:Button; + + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + /** + * @private + */ + override protected function createChildren():void + { + // Create our skin parts if necessary: track and thumb. + if (!track) + { + // We don't want a visible track so we set the skin to MobileSkin + track = new Button(); + track.setStyle("skinClass", spark.skins.mobile.supportClasses.MobileSkin); + track.width = minWidth; + track.height = minHeight; + addChild(track); + } + + if (!thumb) + { + thumb = new Button(); + thumb.minWidth = minThumbWidth; + thumb.setStyle("skinClass", thumbSkinClass); + thumb.width = minHeight; + thumb.height = minHeight; + addChild(thumb); + } + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + setElementSize(track, unscaledWidth, unscaledHeight); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarThumbSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarThumbSkin.as new file mode 100644 index 0000000000..ed3a6b9c2b --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarThumbSkin.as @@ -0,0 +1,169 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.CapsStyle; +import flash.display.JointStyle; +import flash.display.LineScaleMode; + +import mx.core.DPIClassification; +import mx.core.mx_internal; + +import spark.components.Button; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * ActionScript-based skin for the HScrollBar thumb skin part in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class HScrollBarThumbSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + // These constants are also accessed from HScrollBarSkin + mx_internal static const PADDING_BOTTOM_640DPI:int = 10; + mx_internal static const PADDING_HORIZONTAL_640DPI:int = 8; + mx_internal static const PADDING_BOTTOM_480DPI:int = 8; + mx_internal static const PADDING_HORIZONTAL_480DPI:int = 6; + mx_internal static const PADDING_BOTTOM_320DPI:int = 5; + mx_internal static const PADDING_HORIZONTAL_320DPI:int = 4; + mx_internal static const PADDING_BOTTOM_240DPI:int = 4; + mx_internal static const PADDING_HORIZONTAL_240DPI:int = 3; + mx_internal static const PADDING_BOTTOM_120DPI:int = 2; + mx_internal static const PADDING_HORIZONTAL_120DPI:int = 2; + mx_internal static const PADDING_BOTTOM_DEFAULTDPI:int = 3; + mx_internal static const PADDING_HORIZONTAL_DEFAULTDPI:int = 2; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function HScrollBarThumbSkin() + { + super(); + + // Depending on density set padding + switch (applicationDPI) + { + case DPIClassification.DPI_480: + { + paddingBottom = PADDING_BOTTOM_480DPI; + paddingHorizontal = 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; + } + default: + { + paddingBottom = PADDING_BOTTOM_DEFAULTDPI; + paddingHorizontal = PADDING_HORIZONTAL_DEFAULTDPI; + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:Button; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + /** + * Padding from bottom. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var paddingBottom:int; + + /** + * Horizontal padding from left and right. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var paddingHorizontal:int; + + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + super.drawBackground(unscaledWidth, unscaledHeight); + + var thumbHeight:Number = unscaledHeight - paddingBottom; + + graphics.beginFill(getStyle("thumbColor"), 1); + graphics.drawRect(paddingHorizontal + .5, 0.5, unscaledWidth - 2 * paddingHorizontal, thumbHeight); + + graphics.endFill(); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderSkin.as new file mode 100644 index 0000000000..6f970f850e --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderSkin.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.BlendMode; + import flash.display.DisplayObject; + import flash.events.Event; + + import mx.core.ClassFactory; + import mx.core.IFactory; + + import spark.components.Button; + import spark.components.HSlider; + import spark.skins.android4.assets.HSliderTrack_filled; + import spark.skins.mobile.supportClasses.HSliderDataTip; + import spark.skins.mobile.supportClasses.MobileSkin; + + /** + * Android 4.x specific ActionScript-based skin for HSlider controls in mobile applications. + * + *

The base Flex implementation creates an HSlider with fixed height + * and variable width with a fixed-size thumb. As the height of the + * HSlider component increases, the vertical dimensions of the visible HSlider remain + * the same, and the HSlider stays vertically centered.

+ * + *

The thumb and track implementations can be customized by subclassing + * this skin class and overriding the thumbSkinClass, trackSkinClass, + * and/or dataTipClass variables as necessary.

+ * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class HSliderSkin extends MobileSkin + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function HSliderSkin() + { + super(); + + thumbSkinClass = spark.skins.android4.HSliderThumbSkin; + trackSkinClass = spark.skins.android4.HSliderTrackSkin; + filledTrackSkinClass = spark.skins.android4.assets.HSliderTrack_filled; + dataTipClass = spark.skins.mobile.supportClasses.HSliderDataTip; + + blendMode = BlendMode.LAYER; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + private var _hostComponent:HSlider; + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public function get hostComponent():HSlider + { + return _hostComponent; + } + + public function set hostComponent(value:HSlider):void + { + if (_hostComponent) + _hostComponent.removeEventListener(Event.CHANGE, thumbPositionChanged_handler); + _hostComponent = value; + if (_hostComponent) + _hostComponent.addEventListener(Event.CHANGE, thumbPositionChanged_handler); + } + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * HSlider track skin part + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var track:Button; + + /** + * HSlider track skin part that + * depicts area that is filled + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var filledTrack:DisplayObject; + + /** + * HSlider thumb skin part + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var thumb:Button; + + /** + * HSlider dataTip class factory + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var dataTip:IFactory; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * Specifies the skin class that will be used for the HSlider thumb. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbSkinClass:Class; + + /** + * Specifies the skin class that will be used for the HSlider track. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackSkinClass:Class; + /** + * Specifies the skin class that will be used for the HSlider track's + * filled area. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var filledTrackSkinClass:Class; + + /** + * Specifies the class that will be used for the HSlider datatip. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var dataTipClass:Class; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function commitCurrentState():void + { + if (currentState == "disabled") + alpha = 0.5; + else if (currentState == "normal") + alpha = 1; + } + + /** + * @private + */ + override protected function createChildren():void + { + // Create our skin parts: track and thumb + track = new Button(); + track.setStyle("skinClass", trackSkinClass); + addChild(track); + + filledTrack = new filledTrackSkinClass(); + addChild(filledTrack); + + thumb = new Button(); + thumb.setStyle("skinClass", thumbSkinClass); + addChild(thumb); + + // Set up the class factory for the dataTip + dataTip = new ClassFactory(); + ClassFactory(dataTip).generator = dataTipClass; + } + + /** + * @private + * The HSliderSkin width will be no less than the width of the thumb skin. + * The HSliderSkin height will be no less than the greater of the heights of + * the thumb and track skins. + */ + override protected function measure():void + { + measuredWidth = track.getPreferredBoundsWidth(); + measuredHeight = Math.max(track.getPreferredBoundsHeight(), thumb.getPreferredBoundsHeight()); + + measuredMinHeight = Math.max(track.getPreferredBoundsHeight(), thumb.getPreferredBoundsHeight()); + measuredMinWidth = thumb.getPreferredBoundsWidth(); + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + // minimum height is no smaller than the larger of the thumb or track + var calculatedSkinHeight:int = Math.max(Math.max(thumb.getPreferredBoundsHeight(), track.getPreferredBoundsHeight()), + unscaledHeight); + + // minimum width is no smaller than the thumb + var calculatedSkinWidth:int = Math.max(thumb.getPreferredBoundsWidth(), + unscaledWidth); + + // once we know the skin height, center the thumb and track + thumb.y = Math.max(Math.round((calculatedSkinHeight - thumb.getPreferredBoundsHeight()) / 2), 0); + var calculatedTrackY:int = Math.max(Math.round((calculatedSkinHeight - track.getPreferredBoundsHeight()) / 2), 0); + + // size and position + setElementSize(thumb, thumb.getPreferredBoundsWidth(), thumb.getPreferredBoundsHeight()); // thumb does NOT scale + setElementSize(track, calculatedSkinWidth, track.getPreferredBoundsHeight()); // note track is NOT scaled vertically + setElementPosition(track, 0, calculatedTrackY); + + //Set size and position of filled area based on thumb's current location + var filledTrackWidth:Number = thumb.getLayoutBoundsX(); + setElementSize(filledTrack, filledTrackWidth, track.getPreferredBoundsHeight()); // note track is NOT scaled vertically + setElementPosition(filledTrack, track.x + HSliderTrackSkin(track.skin).visibleTrackOffset , calculatedTrackY); + } + + private function thumbPositionChanged_handler(event:Event):void + { + //Just trigger a redraw so that the filled area of the track updates itself + invalidateDisplayList(); + } + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderThumbSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderThumbSkin.as new file mode 100644 index 0000000000..517ff930fb --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderThumbSkin.as @@ -0,0 +1,333 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + +import mx.core.DPIClassification; + +import spark.components.Button; +import spark.skins.android4.assets.HSliderThumb_normal; +import spark.skins.mobile.supportClasses.MobileSkin; + +/** + * Android 4.x specific ActionScript-based skin for the HSlider thumb skin part in mobile applications. + * + *

Note that this particular implementation defines a hit zone which is larger than + * the visible thumb for better usability on mobile screens.

+ * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class HSliderThumbSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function HSliderThumbSkin() + { + super(); + + thumbNormalClass = spark.skins.android4.assets.HSliderThumb_normal; + thumbPressedClass = spark.skins.android4.assets.HSliderThumb_pressed; + + // set the dimensions to use based on the screen density + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + thumbImageWidth = 116; + thumbImageHeight = 116; + + hitZoneOffset = 20; + hitZoneSideLength = 160; + + break; + } + case DPIClassification.DPI_480: + { + // Note provisional may need changes + thumbImageWidth = 88; + thumbImageHeight = 88; + + hitZoneOffset = 20; + hitZoneSideLength = 130; + + break; + } + case DPIClassification.DPI_320: + { + thumbImageWidth = 58; + thumbImageHeight = 58; + + hitZoneOffset = 10; + hitZoneSideLength = 80; + + break; + } + case DPIClassification.DPI_240: + { + thumbImageWidth = 44; + thumbImageHeight = 44; + + hitZoneOffset = 10; + hitZoneSideLength = 65; + + break; + } + case DPIClassification.DPI_120: + { + thumbImageWidth = 22; + thumbImageHeight = 22; + + hitZoneOffset = 5; + hitZoneSideLength = 33; + + break; + } + default: + { + // default DPI_160 + thumbImageWidth = 29; + thumbImageHeight = 29; + + hitZoneOffset = 5; + hitZoneSideLength = 40; + + break; + } + + } + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:Button; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + // FXG thumb classes + /** + * Specifies the FXG class to use when the thumb is in the normal state + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbNormalClass:Class; + + /** + * Specifies the FXG class to use when the thumb is in the pressed state + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbPressedClass:Class; + + /** + * Specifies the DisplayObject to use when the thumb is in the normal state + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbSkin_normal:DisplayObject; + + /** + * Specifies the DisplayObject to use when the thumb is in the pressed state + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbSkin_pressed:DisplayObject; + + /** + * Specifies the current DisplayObject that should be shown + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var currentThumbSkin:DisplayObject; + + /** + * Width of the overall thumb image + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbImageWidth:int; + + /** + * Height of the overall thumb image + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var thumbImageHeight:int; + + /** + * Length of the sizes of the hitzone (assumed to be square) + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var hitZoneSideLength:int; + + /** + * Distance between the left edge of the hitzone and the left edge + * of the thumb + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var hitZoneOffset:int; + + /** + * @private + * Remember which state is currently being displayed + */ + private var displayedState:String; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function commitCurrentState():void + { + if (currentState == "up") + { + // show the normal button + if (!thumbSkin_normal) + { + thumbSkin_normal = new thumbNormalClass(); + addChild(thumbSkin_normal); + } + else + { + thumbSkin_normal.visible = true; + } + currentThumbSkin = thumbSkin_normal; + + // hide the pressed button + if (thumbSkin_pressed) + thumbSkin_pressed.visible = false; + } + else if (currentState == "down") + { + // show the pressed button + if (!thumbSkin_pressed) + { + thumbSkin_pressed = new thumbPressedClass(); + addChild(thumbSkin_pressed); + } + else + { + thumbSkin_pressed.visible = true; + } + currentThumbSkin = thumbSkin_pressed; + + // hide the normal button + if (thumbSkin_normal) + thumbSkin_normal.visible = false; + } + + displayedState = currentState; + + invalidateDisplayList(); + } + + /** + * @private + */ + override protected function measure():void + { + measuredWidth = thumbImageWidth; + measuredHeight = thumbImageHeight; + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + setElementSize(currentThumbSkin, unscaledWidth, unscaledHeight); + setElementPosition(currentThumbSkin, 0, 0) + } + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + // put in a larger hit zone than the thumb + graphics.beginFill(0xffffff, 0); + graphics.drawRect(-hitZoneOffset, -hitZoneOffset, hitZoneSideLength, hitZoneSideLength); + graphics.endFill(); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderTrackSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderTrackSkin.as new file mode 100644 index 0000000000..57dc8f391c --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderTrackSkin.as @@ -0,0 +1,227 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + + import mx.core.DPIClassification; + + import spark.components.Button; + import spark.skins.android4.assets.HSliderTrack; + import spark.skins.mobile.supportClasses.MobileSkin; + + /** + * ActionScript-based skin for the HSlider track skin part in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class HSliderTrackSkin extends MobileSkin + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function HSliderTrackSkin() + { + super(); + + trackClass = spark.skins.android4.assets.HSliderTrack; + + // set the right dimensions to use based on the screen density + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + trackWidth = 1200; + trackHeight = 8; + + visibleTrackOffset = 48; + + break; + } + case DPIClassification.DPI_480: + { + trackWidth = 900; + trackHeight = 6; + + visibleTrackOffset = 38; + + break; + } + case DPIClassification.DPI_320: + { + trackWidth = 600; + trackHeight = 4; + + visibleTrackOffset = 24; + + break; + } + case DPIClassification.DPI_240: + { + trackWidth = 450; + trackHeight = 3; + + visibleTrackOffset = 18; + + break; + } + case DPIClassification.DPI_120: + { + trackWidth = 225; + trackHeight = 2; + + visibleTrackOffset = 9; + + break; + } + default: + { + // default DPI_160 + trackWidth = 300; + trackHeight = 2; + + visibleTrackOffset = 12; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:Button; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * Specifies the FXG class to use for the track image + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackClass:Class; + + /** + * Specifies the DisplayObject for the track image + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackSkin:DisplayObject; + + /** + * Specifies the track image width + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackWidth:int; + + /** + * Specifies the track image height + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var trackHeight:int; + + /** + * Specifies the offset from the left and right edge to where + * the visible track begins. This should match the offset in the FXG assets. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var visibleTrackOffset:int; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + trackSkin = new trackClass(); + addChild(trackSkin); + } + + /** + * @private + */ + override protected function measure():void + { + measuredWidth = trackWidth; + measuredHeight = trackHeight; + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + var unscaledTrackWidth:int = unscaledWidth - (2 * visibleTrackOffset); + setElementSize(trackSkin, unscaledTrackWidth, unscaledHeight); + setElementPosition(trackSkin, visibleTrackOffset, 0); + } + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/RadioButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/RadioButtonSkin.as new file mode 100644 index 0000000000..fd74645065 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/RadioButtonSkin.as @@ -0,0 +1,253 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; + +import mx.core.DPIClassification; + +import spark.skins.android4.assets.RadioButton_up; +import spark.skins.mobile.supportClasses.SelectableButtonSkinBase; + +/** + * ActionScript-based skin for RadioButton controls in mobile applications. + * + * @see spark.components.RadioButton + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class RadioButtonSkin extends SelectableButtonSkinBase +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + static private const exclusions:Array = ["labelDisplay", "labelDisplayShadow"]; + + //-------------------------------------------------------------------------- + // + // Member variables + // + //-------------------------------------------------------------------------- + + protected var symbolOffsetX:Number; + protected var symbolOffsetY:Number; + protected var iconWidth:Number; + protected var iconHeight:Number; + protected var symbolWidth:Number; + protected var symbolHeight:Number; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function RadioButtonSkin() + { + super(); + + layoutPaddingLeft = 0; + layoutPaddingRight = 0; + layoutPaddingTop = 0; + layoutPaddingBottom = 0; + + upIconClass = spark.skins.android4.assets.RadioButton_up; + upSelectedIconClass = spark.skins.android4.assets.RadioButton_up; + downIconClass = spark.skins.android4.assets.RadioButton_down; + downSelectedIconClass = spark.skins.android4.assets.RadioButton_down; + upSymbolIconClass = null; + downSymbolIconClass = null; + upSymbolIconSelectedClass = spark.skins.android4.assets.RadioButton_upSymbolSelected; + downSymbolIconSelectedClass = spark.skins.android4.assets.RadioButton_downSymbolSelected; + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + + layoutGap = 16; + minWidth = 128; + minHeight = 128; + iconWidth = 128; + iconHeight = 128; + symbolWidth = 44; + symbolHeight = 44; + symbolOffsetX = 44; + symbolOffsetY = 44; + + break; + } + case DPIClassification.DPI_480: + { + + layoutGap = 12; + minWidth = 96; + minHeight = 96; + iconWidth = 96; + iconHeight = 96; + symbolWidth = 33; + symbolHeight = 33; + symbolOffsetX = 33; + symbolOffsetY = 33; + + break; + } + case DPIClassification.DPI_320: + { + + layoutGap = 8; + minWidth = 64; + minHeight = 64; + iconWidth = 64; + iconHeight = 64; + symbolWidth = 22; + symbolHeight = 22; + symbolOffsetX = 22; + symbolOffsetY = 22; + + break; + } + case DPIClassification.DPI_240: + { + + layoutGap = 6; + minWidth = 48; + minHeight = 48; + iconWidth = 48; + iconHeight = 48; + symbolWidth = 16.5; + symbolHeight = 16.5; + symbolOffsetX = 16.5; + symbolOffsetY = 16.5; + + break; + } + case DPIClassification.DPI_120: + { + + layoutGap = 3; + minWidth = 24; + minHeight = 24; + iconWidth = 24; + iconHeight = 24; + symbolWidth = 8.25; + symbolHeight = 8.25; + symbolOffsetX = 8.25; + symbolOffsetY = 8.25; + + break; + } + default: + { + + layoutGap = 4; + minWidth = 32; + minHeight = 32; + iconWidth = 32; + iconHeight = 32; + symbolWidth = 11; + symbolHeight = 11; + symbolOffsetX = 11; + symbolOffsetY = 11; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * RadioButton chromeColor is drawn to match the FXG ellipse + * shape and position. + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + // super draws a transparent hit zone + super.drawBackground(unscaledWidth, unscaledHeight); + + // get the size and position of iconDisplay + var currentIcon:DisplayObject = getIconDisplay(); + + graphics.beginFill(getStyle("chromeColor")); + graphics.drawEllipse(currentIcon.x + 1, currentIcon.y + 1, currentIcon.width - 2, currentIcon.height - 2); + graphics.endFill(); + } + + /** + * @private + */ + override protected function get focusSkinExclusions():Array + { + return exclusions; + } + + override protected function commitCurrentState():void + { + super.commitCurrentState(); + if(symbolIcon != null) + { + symbolIcon.width = symbolWidth; + symbolIcon.height = symbolHeight; + } + var iconDisplay:DisplayObject = getIconDisplay(); + if(iconDisplay != null) + { + iconDisplay.width = iconWidth; + iconDisplay.height = iconHeight; + } + } + + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + // position the symbols to align with the background "icon" + if (symbolIcon) + { + var currentIcon:DisplayObject = getIconDisplay(); + setElementPosition(symbolIcon, symbolOffsetX, symbolOffsetY); + } + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListContainerSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListContainerSkin.as new file mode 100644 index 0000000000..e076a6b347 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListContainerSkin.as @@ -0,0 +1,326 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.Graphics; + import flash.display.InteractiveObject; + import flash.display.Sprite; + + import mx.core.DPIClassification; + import mx.core.mx_internal; + + import spark.components.Group; + import spark.components.SpinnerListContainer; + import spark.layouts.HorizontalLayout; + import spark.skins.android4.assets.SpinnerListContainerBackground; + import spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; + import spark.skins.android4.assets.SpinnerListContainerShadow; + import spark.skins.mobile.supportClasses.MobileSkin; + + use namespace mx_internal; + /** + * ActionScript-based skin for the SpinnerListContainer in mobile applications. + * + * @see spark.components.SpinnerListContainer + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public class SpinnerListContainerSkin extends MobileSkin + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + * + */ + public function SpinnerListContainerSkin() + { + super(); + + borderClass = spark.skins.android4.assets.SpinnerListContainerBackground; + selectionIndicatorClass = spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; + shadowClass = spark.skins.android4.assets.SpinnerListContainerShadow; + cornerRadius = 0; + borderThickness = 0; + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + selectionIndicatorHeight = 182; + break; + } + case DPIClassification.DPI_480: + { + selectionIndicatorHeight = 144; + break; + } + case DPIClassification.DPI_320: + { + selectionIndicatorHeight = 96; + break; + } + case DPIClassification.DPI_240: + { + selectionIndicatorHeight = 72; + break; + } + case DPIClassification.DPI_120: + { + selectionIndicatorHeight = 36; + break; + } + default: // default DPI_160 + { + selectionIndicatorHeight = 48; + + break; + } + } + + minWidth = 30; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + /** + * Pixel thickness of the border. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var borderThickness:Number; + + /** + * Radius of the border corners. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var cornerRadius:Number; + + /** + * Height of the selection indicator. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var selectionIndicatorHeight:Number; + + /** + * Class for the border part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var borderClass:Class; + + /** + * Class for the selection indicator skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var selectionIndicatorClass:Class; + + /** + * Class for the shadow skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var shadowClass:Class; + + /** + * Border skin part which includes the background. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var border:InteractiveObject; + + /** + * Selection indicator skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var selectionIndicator:InteractiveObject; + + /** + * Shadow skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var shadow:InteractiveObject; + + /** + * Mask for the content group. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var contentGroupMask:Sprite; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * An optional skin part that defines the Group where the content + * children are pushed into and laid out. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var contentGroup:Group; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var hostComponent:SpinnerListContainer; + + //-------------------------------------------------------------------------- + // + // Overridden Methods + // + //-------------------------------------------------------------------------- + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (!border) + { + // Border and background + border = new borderClass(); + border.mouseEnabled = false; + addChild(border); + } + + if (!contentGroup) + { + // Contains the child elements + contentGroup = new Group(); + var hLayout:HorizontalLayout = new HorizontalLayout(); + hLayout.gap = 0; + hLayout.verticalAlign = "middle"; + contentGroup.layout = hLayout; + contentGroup.id = "contentGroup"; + addChild(contentGroup); + } + + if (!shadow) + { + // Shadowing sits on top of the content + shadow = new shadowClass(); + shadow.mouseEnabled = false; + addChild(shadow); + } + + + if (!contentGroupMask) + { + // Create a mask for the content + contentGroupMask = new Sprite(); + addChild(contentGroupMask); + } + } + + /** + * @private + */ + override protected function measure():void + { + super.measure(); + + var contentW:Number = contentGroup.getPreferredBoundsWidth(); + var contentH:Number = contentGroup.getPreferredBoundsHeight(); + + measuredWidth = measuredMinWidth = contentW + borderThickness * 2; + measuredHeight = contentH + borderThickness * 2; + + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + setElementSize(contentGroup, unscaledWidth - borderThickness * 2, unscaledHeight - borderThickness * 2); + setElementPosition(contentGroup, borderThickness, borderThickness); + + // Inset by the borderThickness horizontally because the selectionIndicator starts at 0 + setElementSize(border, unscaledWidth - borderThickness * 2, unscaledHeight); + setElementPosition(border, borderThickness, 0); + + setElementSize(shadow, unscaledWidth - borderThickness * 4, measuredHeight - borderThickness * 2); + setElementPosition(shadow, borderThickness * 2, unscaledHeight/2 - measuredHeight/2); + + // The SpinnerLists contain a left and right border. We don't want to show the leftmost + // SpinnerLists's left border nor the rightmost one's right border. + // We inset the mask on the left and right sides to accomplish this. + var g:Graphics = contentGroupMask.graphics; + g.clear(); + g.beginFill(0x00FF00); + g.drawRoundRect(borderThickness * 2, borderThickness, unscaledWidth - borderThickness * 4, unscaledHeight - borderThickness * 2, cornerRadius, cornerRadius); + g.endFill(); + + contentGroup.mask = contentGroupMask; + } + } +} diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListScrollerSkin.mxml b/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListScrollerSkin.mxml new file mode 100644 index 0000000000..270818e903 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListScrollerSkin.mxml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListSkin.as new file mode 100644 index 0000000000..b86553fd91 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListSkin.as @@ -0,0 +1,294 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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); + } + + } +} diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextAreaSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextAreaSkin.as new file mode 100644 index 0000000000..d48377d3f3 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextAreaSkin.as @@ -0,0 +1,190 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 spark.components.TextArea; + import spark.components.supportClasses.IStyleableEditableText; + import spark.components.supportClasses.ScrollableStageText; + 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 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); + + 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); + } + } + + override protected function createTextDisplay():IStyleableEditableText + { + return new ScrollableStageText(multiline); + } + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextInputSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextInputSkin.as new file mode 100644 index 0000000000..7f92647f1d --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextInputSkin.as @@ -0,0 +1,134 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.IStyleableEditableText; + import spark.components.supportClasses.ScrollableStageText; + 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); + } + } + + override protected function createTextDisplay():IStyleableEditableText + { + return new ScrollableStageText(multiline); + } + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TabbedViewNavigatorTabBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TabbedViewNavigatorTabBarSkin.as new file mode 100644 index 0000000000..eea5948eea --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/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 diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextAreaSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextAreaSkin.as new file mode 100644 index 0000000000..0c231a6f2d --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/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(); + } + +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextInputSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextInputSkin.as new file mode 100644 index 0000000000..7488e1f7c7 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextInputSkin.as @@ -0,0 +1,375 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.SoftKeyboardEvent; + + 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.android4.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 = 4; + flatheight = 9; + break; + } + case DPIClassification.DPI_480: + { + + measuredDefaultWidth = 880; + measuredDefaultHeight = 100; + layoutBorderSize = 3; + flatheight = 7; + break; + } + case DPIClassification.DPI_320: + { + measuredDefaultWidth = 600; + measuredDefaultHeight = 66; + layoutBorderSize = 2; + flatheight = 6; + break; + } + case DPIClassification.DPI_240: + { + measuredDefaultWidth = 440; + measuredDefaultHeight = 50; + layoutBorderSize = 2; + flatheight = 5; + break; + } + case DPIClassification.DPI_120: + { + measuredDefaultWidth = 220; + measuredDefaultHeight = 25; + layoutBorderSize = 1; + flatheight = 2; + break; + } + default: + { + measuredDefaultWidth = 300; + measuredDefaultHeight = 33; + layoutBorderSize = 1; + flatheight = 3; + break; + } + } + addEventListener(FocusEvent.FOCUS_IN, focusChangeHandler); + addEventListener(FocusEvent.FOCUS_OUT, focusChangeHandler); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + protected var isFocused:Boolean = false; + + protected var flatheight: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 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 + */ + 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(); + } + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ToggleSwitchSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ToggleSwitchSkin.as new file mode 100644 index 0000000000..c5b99aa35e --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ToggleSwitchSkin.as @@ -0,0 +1,506 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.BlendMode; + import flash.events.Event; + + import mx.core.DPIClassification; + import mx.core.IVisualElement; + import mx.core.UIComponent; + + import spark.components.ToggleSwitch; + import spark.components.supportClasses.StyleableTextField; + import spark.core.SpriteVisualElement; + import spark.skins.android4.assets.ToggleSwitchBackground; + import spark.skins.android4.assets.ToggleSwitchThumb_off; + import spark.skins.mobile.supportClasses.MobileSkin; + + + /** + * ActionScript-based Android 4.x specific skin for the ToggleSwitch control. + * This class is responsible for most of the + * graphics drawing, with additional fxg assets. + * + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + * + * @see spark.components.ToggleSwitch + */ + public class ToggleSwitchSkin extends MobileSkin + { + //---------------------------------------------------------------------------------------------- + // + // Skin parts + // + //---------------------------------------------------------------------------------------------- + + /** + * The thumb skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var thumb:IVisualElement; + /** + * The track skin part. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + public var track:IVisualElement; + + //---------------------------------- + // hostComponent + //---------------------------------- + + private var _hostComponent:ToggleSwitch; + public var selectedLabelDisplay:LabelDisplayComponent; + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public function get hostComponent():ToggleSwitch + { + return _hostComponent; + } + + public function set hostComponent(value:ToggleSwitch):void + { + if (_hostComponent) + _hostComponent.removeEventListener("thumbPositionChanged", thumbPositionChanged_handler); + _hostComponent = value; + if (_hostComponent) + _hostComponent.addEventListener("thumbPositionChanged", thumbPositionChanged_handler); + } + + //---------------------------------- + // selectedLabel + //---------------------------------- + + private var _selectedLabel:String; + /** + * The text of the label showing when the component is selected. + * Subclasses can set or override this property to customize the selected label. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected function get selectedLabel():String + { + return _selectedLabel; + } + + protected function set selectedLabel(value:String):void + { + _selectedLabel = value; + } + + //---------------------------------- + // unselectedLabel + //---------------------------------- + + private var _unselectedLabel:String; + /** + * The text of the label showing when the component is not selected. + * Subclasses can set or override this property to customize the unselected label. + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected function get unselectedLabel():String + { + return _unselectedLabel; + } + + protected function set unselectedLabel(value:String):void + { + _unselectedLabel = value; + } + + + /** + * The contents inside the skin, not including the outline + * stroke + */ + private var contents:UIComponent; + private var switchTrack:Class; + private var switchOff:Class; + private var switchOn:Class; + protected var trackWidth:Number; + protected var trackHeight:Number; + protected var layoutThumbWidth:Number; + protected var layoutThumbHeight:Number; + private var thumbOn:IVisualElement; + private var thumbOff:IVisualElement; + + public function ToggleSwitchSkin() + { + super(); + + switchTrack = spark.skins.android4.assets.ToggleSwitchBackground; + switchOn = spark.skins.android4.assets.ToggleSwitchThumb_on; + switchOff = spark.skins.android4.assets.ToggleSwitchThumb_off; + + switch(applicationDPI) + { + case DPIClassification.DPI_640: + { + layoutThumbWidth = 188; + layoutThumbHeight = 96; + trackWidth = 388; + trackHeight = 96; + break; + } + case DPIClassification.DPI_480: + { + layoutThumbWidth = 140; + layoutThumbHeight = 72; + trackWidth = 291; + trackHeight = 72; + break; + } + case DPIClassification.DPI_320: + { + layoutThumbWidth = 94; + layoutThumbHeight = 48; + trackWidth = 194; + trackHeight = 48; + break; + } + case DPIClassification.DPI_240: + { + layoutThumbWidth = 70; + layoutThumbHeight = 36; + trackWidth = 146; + trackHeight = 36; + break; + } + case DPIClassification.DPI_120: + { + layoutThumbWidth = 35; + layoutThumbHeight = 18; + trackWidth = 73; + trackHeight = 18; + break; + } + default: + { + layoutThumbWidth = 47; + layoutThumbHeight = 24; + trackWidth = 97; + trackHeight = 24; + break; + } + } + + selectedLabel = resourceManager.getString("components","toggleSwitchSelectedLabel"); + unselectedLabel = resourceManager.getString("components","toggleSwitchUnselectedLabel"); + } + + override protected function createChildren():void + { + super.createChildren(); + contents = new UIComponent(); + contents.blendMode = BlendMode.LAYER; + addChild(contents); + drawTrack(); + drawThumbs(); + drawLabel(); + } + + override protected function measure():void + { + // The skin must be at least as large as the thumb + measuredMinWidth = layoutThumbWidth; + measuredMinHeight = layoutThumbHeight; + + // The preferred size will display all label text + var labelWidth:Number = getElementPreferredWidth(selectedLabelDisplay); + measuredWidth = layoutThumbWidth + labelWidth; + measuredHeight = layoutThumbHeight; + } + + override protected function commitCurrentState():void + { + toggleSelectionState(); + layoutThumbs(); + layoutLabel(); + } + + //The label is called selectedLabelDisplay because the hostComponent expects it + protected function drawLabel():void + { + selectedLabelDisplay = new LabelDisplayComponent(); + selectedLabelDisplay.id = "selectedLabelDisplay"; + selectedLabelDisplay.text = selectedLabel; + setElementSize(selectedLabelDisplay,thumb.width,thumb.height); + contents.addChild(selectedLabelDisplay); + } + + //Draw the track behind everything else + protected function drawTrack():void + { + if(track == null) + { + track = new switchTrack(); + track.width = trackWidth; + track.height = trackHeight; + contents.addChildAt(SpriteVisualElement(track),0); + } + } + + //Draw both thumbs. Set skinpart thumb to be thumbOff because default state of the switch is OFF + protected function drawThumbs():void + { + drawThumbOff(); + drawThumbOn(); + if(thumb == null) + { + thumb = thumbOff; + } + } + + //Thumb ON the right side; Thumb OFF is on the left side + protected function layoutThumbs():void + { + setElementPosition(thumbOn,trackWidth/2,0); + setElementPosition(thumbOff,0,0); + } + + //Label display sould be at the same location as the thumb + protected function layoutLabel():void + { + if(selectedLabelDisplay != null) + { + if(currentState.indexOf("AndSelected") != -1) + { + setElementPosition(selectedLabelDisplay,trackWidth/2,0); + } + else + { + setElementPosition(selectedLabelDisplay,0,0); + } + } + } + + //Depending on current state, set skinpart thumb accordingly + protected function toggleSelectionState():void + { + if(currentState.indexOf("AndSelected") != -1) + { + thumbOn.visible = true; + thumbOff.visible = false; + thumb = thumbOn; + selectedLabelDisplay.text = selectedLabel; + } + else + { + thumbOff.visible = true; + thumbOn.visible = false; + thumb = thumbOff; + selectedLabelDisplay.text = unselectedLabel; + } + } + + protected function drawThumbOn():void + { + thumbOn = new switchOn(); + thumbOn.width = layoutThumbWidth; + thumbOn.height = layoutThumbHeight; + contents.addChildAt(SpriteVisualElement(thumbOn),1); + } + + protected function drawThumbOff():void + { + thumbOff = new switchOff(); + thumbOff.width = layoutThumbWidth; + thumbOff.height = layoutThumbHeight; + contents.addChildAt(SpriteVisualElement(thumbOff),1); + } + + //Hostcomponent dispatches this event whenever the thumb position changes + protected function thumbPositionChanged_handler(event:Event):void + { + moveSlidingContent(); + } + + //Move the current thumb and label along with the animating content + protected function moveSlidingContent():void + { + if (!hostComponent) + return; + var x:Number = (track.getLayoutBoundsWidth() - thumb.getLayoutBoundsWidth()) * + hostComponent.thumbPosition + track.getLayoutBoundsX(); + var y:Number = thumb.getLayoutBoundsY(); + setElementPosition(thumb, x, y); + setElementPosition(selectedLabelDisplay, x, y); + } + } +} + + +import flash.events.Event; + +import mx.core.UIComponent; +import mx.core.mx_internal; +import mx.events.FlexEvent; + +import spark.components.supportClasses.StyleableTextField; +import spark.core.IDisplayText; + +use namespace mx_internal; + +/** + * @private + * Component combining two labels to create the effect of text and its drop + * shadow. The component can be used with advanced style selectors and the + * styles "color", "textShadowColor", and "textShadowAlpha". Based off of + * ActionBar.TitleDisplayComponent. These two should eventually be factored. + */ +class LabelDisplayComponent extends UIComponent implements IDisplayText +{ + public var shadowYOffset:Number = 0; + private var labelChanged:Boolean = false; + private var labelDisplay:StyleableTextField; + private var labelDisplayShadow:StyleableTextField; + private var _text:String; + + public function LabelDisplayComponent() + { + super(); + _text = ""; + } + + override public function get baselinePosition():Number + { + return labelDisplay.baselinePosition; + } + + override protected function createChildren():void + { + super.createChildren(); + + labelDisplay = StyleableTextField(createInFontContext(StyleableTextField)); + labelDisplay.styleName = this; + labelDisplay.editable = false; + labelDisplay.selectable = false; + labelDisplay.multiline = false; + labelDisplay.wordWrap = false; + labelDisplay.addEventListener(FlexEvent.VALUE_COMMIT, + labelDisplay_valueCommitHandler); + + labelDisplayShadow = StyleableTextField(createInFontContext(StyleableTextField)); + labelDisplayShadow.styleName = this; + labelDisplayShadow.colorName = "textShadowColor"; + labelDisplayShadow.editable = false; + labelDisplayShadow.selectable = false; + labelDisplayShadow.multiline = false; + labelDisplayShadow.wordWrap = false; + + addChild(labelDisplayShadow); + addChild(labelDisplay); + } + + override protected function commitProperties():void + { + super.commitProperties(); + + if (labelChanged) + { + labelDisplay.text = text; + invalidateSize(); + invalidateDisplayList(); + labelChanged = false; + } + } + + override protected function measure():void + { + if (labelDisplay.isTruncated) + labelDisplay.text = text; + labelDisplay.commitStyles(); + measuredWidth = labelDisplay.getPreferredBoundsWidth(); + measuredHeight = labelDisplay.getPreferredBoundsHeight(); + } + + override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void + { + if (labelDisplay.isTruncated) + labelDisplay.text = text; + labelDisplay.commitStyles(); + + var labelHeight:Number = labelDisplay.getPreferredBoundsHeight(); + var labelY:Number = (unscaledHeight - labelHeight) / 2; + + var labelWidth:Number = Math.min(unscaledWidth, labelDisplay.getPreferredBoundsWidth()); + var labelX:Number = (unscaledWidth - labelWidth) / 2; + + labelDisplay.setLayoutBoundsSize(labelWidth, labelHeight); + labelDisplay.setLayoutBoundsPosition(labelX, labelY); + + labelDisplay.truncateToFit(); + + labelDisplayShadow.commitStyles(); + labelDisplayShadow.setLayoutBoundsSize(labelWidth, labelHeight); + labelDisplayShadow.setLayoutBoundsPosition(labelX, labelY + shadowYOffset); + + labelDisplayShadow.alpha = getStyle("textShadowAlpha"); + + // unless the label was truncated, labelDisplayShadow.text was set in + // the value commit handler + if (labelDisplay.isTruncated) + labelDisplayShadow.text = labelDisplay.text; + } + + private function labelDisplay_valueCommitHandler(event:Event):void + { + labelDisplayShadow.text = labelDisplay.text; + } + + public function get text():String + { + return _text; + } + + public function set text(value:String):void + { + _text = value; + labelChanged = true; + invalidateProperties(); + } + + public function get isTruncated():Boolean + { + return labelDisplay.isTruncated; + } + + public function showShadow(value:Boolean):void + { + labelDisplayShadow.visible = value; + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentActionButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentActionButtonSkin.as new file mode 100644 index 0000000000..dadab3c2ae --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentActionButtonSkin.as @@ -0,0 +1,108 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; +import flash.display.Graphics; + +import mx.core.DPIClassification; +import mx.core.mx_internal; + +import spark.skins.mobile.assets.TransparentActionButton_down; +import spark.skins.mobile.assets.TransparentActionButton_up; +import spark.skins.mobile.supportClasses.ActionBarButtonSkinBase; +import spark.skins.mobile.supportClasses.MobileSkin; +import spark.skins.mobile320.assets.TransparentActionButton_down; +import spark.skins.mobile320.assets.TransparentActionButton_up; +import spark.skins.mobile480.assets.TransparentActionButton_down; +import spark.skins.mobile480.assets.TransparentActionButton_up; +import spark.skins.mobile640.assets.TransparentActionButton_down; +import spark.skins.mobile640.assets.TransparentActionButton_up; + +use namespace mx_internal; + +/** + * The default skin class for buttons in the action area of the Spark ActionBar component + * in mobile applications. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class TransparentActionButtonSkin extends ActionBarButtonSkinBase +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function TransparentActionButtonSkin() + { + super(); + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + upBorderSkin = spark.skins.mobile640.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile640.assets.TransparentActionButton_down; + + break; + } + case DPIClassification.DPI_480: + { + upBorderSkin = spark.skins.mobile480.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile480.assets.TransparentActionButton_down; + + break; + } + case DPIClassification.DPI_320: + { + upBorderSkin = spark.skins.mobile320.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile320.assets.TransparentActionButton_down; + + break; + } + default: + { + upBorderSkin = spark.skins.mobile.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile.assets.TransparentActionButton_down; + + break; + } + } + } + + override mx_internal function layoutBorder(unscaledWidth:Number, unscaledHeight:Number):void + { + // don't call super, don't layout twice + // leading vertical separator is outside the left bounds of the button + setElementSize(border, unscaledWidth + layoutBorderSize, unscaledHeight); + setElementPosition(border, -layoutBorderSize, 0); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentNavigationButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentNavigationButtonSkin.as new file mode 100644 index 0000000000..0709b65849 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentNavigationButtonSkin.as @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.DisplayObject; +import flash.display.Graphics; + +import mx.core.DPIClassification; +import mx.core.mx_internal; + +import spark.skins.mobile.assets.TransparentNavigationButton_down; +import spark.skins.mobile.assets.TransparentNavigationButton_up; +import spark.skins.mobile.supportClasses.ActionBarButtonSkinBase; +import spark.skins.mobile.supportClasses.MobileSkin; +import spark.skins.mobile320.assets.TransparentNavigationButton_down; +import spark.skins.mobile320.assets.TransparentNavigationButton_up; +import spark.skins.mobile480.assets.TransparentNavigationButton_down; +import spark.skins.mobile480.assets.TransparentNavigationButton_up; +import spark.skins.mobile640.assets.TransparentNavigationButton_down; +import spark.skins.mobile640.assets.TransparentNavigationButton_up; + +use namespace mx_internal; + +/** + * The default skin class for buttons in the navigation area of the Spark ActionBar component + * in mobile applications. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class TransparentNavigationButtonSkin extends ActionBarButtonSkinBase +{ + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function TransparentNavigationButtonSkin() + { + super(); + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + upBorderSkin = spark.skins.mobile640.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile640.assets.TransparentActionButton_down; + + break; + } + case DPIClassification.DPI_480: + { + upBorderSkin = spark.skins.mobile480.assets.TransparentActionButton_up; + downBorderSkin = spark.skins.mobile480.assets.TransparentActionButton_down; + + break; + } + case DPIClassification.DPI_320: + { + upBorderSkin = spark.skins.mobile320.assets.TransparentNavigationButton_up; + downBorderSkin = spark.skins.mobile320.assets.TransparentNavigationButton_down; + + break; + } + default: + { + upBorderSkin = spark.skins.mobile.assets.TransparentNavigationButton_up; + downBorderSkin = spark.skins.mobile.assets.TransparentNavigationButton_down; + + break; + } + } + } + + override mx_internal function layoutBorder(unscaledWidth:Number, unscaledHeight:Number):void + { + // trailing vertical separator is outside the right bounds of the button + setElementSize(border, unscaledWidth + layoutBorderSize, unscaledHeight); + setElementPosition(border, 0, 0); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarSkin.as new file mode 100644 index 0000000000..1a2a0df78e --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarSkin.as @@ -0,0 +1,207 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 spark.components.Button; +import spark.components.VScrollBar; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * ActionScript-based skin for VScrollBar components in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class VScrollBarSkin extends MobileSkin +{ + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function VScrollBarSkin() + { + super(); + + minHeight = 20; + thumbSkinClass = VScrollBarThumbSkin; + var paddingRight:int; + var paddingVertical:int; + + // Depending on density set our measured width + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + minWidth = 24; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_640DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_640DPI; + break; + } + case DPIClassification.DPI_480: + { + minWidth = 18; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_480DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_480DPI; + break; + } + case DPIClassification.DPI_320: + { + minWidth = 12; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_320DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_320DPI; + break; + } + case DPIClassification.DPI_240: + { + minWidth = 9; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_240DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_240DPI; + break; + } + case DPIClassification.DPI_120: + { + minWidth = 9; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_120DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_120DPI; + break; + } + default: + { + // default DPI_160 + minWidth = 6; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_DEFAULTDPI; + paddingVertical = VScrollBarThumbSkin.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); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:VScrollBar; + + /** + * Minimum height for the thumb + */ + protected var minThumbHeight:Number; + + /** + * Skin to use for the thumb Button skin part + */ + protected var thumbSkinClass:Class; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + /** + * VScrollbar track skin part + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var track:Button; + + /** + * VScrollbar thumb skin part + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public var thumb:Button; + + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + /** + * @private + */ + override protected function createChildren():void + { + // Create our skin parts if necessary: track and thumb. + if (!track) + { + // We don't want a visible track so we set the skin to MobileSkin + track = new Button(); + track.setStyle("skinClass", spark.skins.mobile.supportClasses.MobileSkin); + track.width = minWidth; + track.height = minHeight; + addChild(track); + } + if (!thumb) + { + thumb = new Button(); + thumb.minHeight = minThumbHeight; + thumb.setStyle("skinClass", thumbSkinClass); + thumb.width = minWidth; + thumb.height = minWidth; + addChild(thumb); + } + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutContents(unscaledWidth, unscaledHeight); + + setElementSize(track, unscaledWidth, unscaledHeight); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarThumbSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarThumbSkin.as new file mode 100644 index 0000000000..9d17a9752b --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarThumbSkin.as @@ -0,0 +1,178 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.CapsStyle; +import flash.display.JointStyle; +import flash.display.LineScaleMode; + +import mx.core.DPIClassification; +import mx.core.mx_internal; +use namespace mx_internal; + +import spark.components.Button; +import spark.skins.mobile.supportClasses.MobileSkin; + +/** + * ActionScript-based skin for the VScrollBar thumb skin part in mobile applications. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class VScrollBarThumbSkin extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + // These constants are also accessed from VScrollBarSkin + mx_internal static const PADDING_RIGHT_640DPI:int = 10; + mx_internal static const PADDING_VERTICAL_640DPI:int = 8; + mx_internal static const PADDING_RIGHT_480DPI:int = 8; + mx_internal static const PADDING_VERTICAL_480DPI:int = 6; + mx_internal static const PADDING_RIGHT_320DPI:int = 5; + mx_internal static const PADDING_VERTICAL_320DPI:int = 4; + mx_internal static const PADDING_RIGHT_240DPI:int = 4; + mx_internal static const PADDING_VERTICAL_240DPI:int = 3; + mx_internal static const PADDING_RIGHT_120DPI:int = 2; + mx_internal static const PADDING_VERTICAL_120DPI:int = 1; + mx_internal static const PADDING_RIGHT_DEFAULTDPI:int = 3; + mx_internal static const PADDING_VERTICAL_DEFAULTDPI:int = 2; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function VScrollBarThumbSkin() + { + super(); + + // Depending on density set padding + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + minWidth = 19; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_640DPI; + paddingVertical = VScrollBarThumbSkin.PADDING_VERTICAL_640DPI; + break; + } + case DPIClassification.DPI_480: + { + minWidth = 19; + paddingRight = VScrollBarThumbSkin.PADDING_RIGHT_480DPI; + paddingVertical = VScrollBarThumbSkin.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; + } + default: + { + paddingRight = PADDING_RIGHT_DEFAULTDPI; + paddingVertical = PADDING_VERTICAL_DEFAULTDPI; + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + public var hostComponent:Button; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + /** + * Padding from the right + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var paddingRight:int; + + /** + * Vertical padding from top and bottom + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var paddingVertical:int; + + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @protected + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + super.drawBackground(unscaledWidth, unscaledHeight); + + var thumbWidth:Number = unscaledWidth - paddingRight; + + graphics.beginFill(getStyle("thumbColor"), 1); + graphics.drawRect(0.5, paddingVertical + 0.5,thumbWidth, unscaledHeight - 2 * paddingVertical); + + graphics.endFill(); + } + +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuItemSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuItemSkin.as new file mode 100644 index 0000000000..6a75c65caa --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuItemSkin.as @@ -0,0 +1,238 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.GradientType; +import flash.display.Graphics; + +import mx.core.DPIClassification; +import mx.core.mx_internal; + +import spark.components.IconPlacement; +import spark.skins.mobile.assets.ViewMenuItem_down; +import spark.skins.mobile.assets.ViewMenuItem_showsCaret; +import spark.skins.mobile.assets.ViewMenuItem_up; +import spark.skins.mobile.supportClasses.ButtonSkinBase; +import spark.skins.mobile120.assets.ViewMenuItem_down; +import spark.skins.mobile120.assets.ViewMenuItem_showsCaret; +import spark.skins.mobile120.assets.ViewMenuItem_up; +import spark.skins.mobile320.assets.ViewMenuItem_down; +import spark.skins.mobile320.assets.ViewMenuItem_showsCaret; +import spark.skins.mobile320.assets.ViewMenuItem_up; +import spark.skins.mobile480.assets.ViewMenuItem_down; +import spark.skins.mobile480.assets.ViewMenuItem_showsCaret; +import spark.skins.mobile480.assets.ViewMenuItem_up; +import spark.skins.mobile640.assets.ViewMenuItem_down; +import spark.skins.mobile640.assets.ViewMenuItem_showsCaret; +import spark.skins.mobile640.assets.ViewMenuItem_up; + + +use namespace mx_internal; + +/** + * Default skin for ViewMenuItem. Supports a label, icon and iconPlacement and draws a background. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class ViewMenuItemSkin extends ButtonSkin +{ + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ViewMenuItemSkin() + { + super(); + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + + upBorderSkin = spark.skins.mobile640.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile640.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile640.assets.ViewMenuItem_showsCaret; + + layoutGap = 24; + layoutPaddingLeft = 24; + layoutPaddingRight = 24; + layoutPaddingTop = 24; + layoutPaddingBottom = 24; + layoutBorderSize = 3; + + break; + } + case DPIClassification.DPI_480: + { + // Note provisional may need changes + upBorderSkin = spark.skins.mobile.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile.assets.ViewMenuItem_showsCaret; + + layoutGap = 16; + layoutPaddingLeft = 16; + layoutPaddingRight = 16; + layoutPaddingTop = 16; + layoutPaddingBottom = 16; + layoutBorderSize = 2; + + break; + + } + case DPIClassification.DPI_320: + { + + upBorderSkin = spark.skins.mobile320.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile320.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile320.assets.ViewMenuItem_showsCaret; + + layoutGap = 12; + layoutPaddingLeft = 12; + layoutPaddingRight = 12; + layoutPaddingTop = 12; + layoutPaddingBottom = 12; + layoutBorderSize = 2; + + + break; + } + case DPIClassification.DPI_240: + { + upBorderSkin = spark.skins.mobile.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile.assets.ViewMenuItem_showsCaret; + + layoutGap = 8; + layoutPaddingLeft = 8; + layoutPaddingRight = 8; + layoutPaddingTop = 8; + layoutPaddingBottom = 8; + layoutBorderSize = 1; + + break; + + } + case DPIClassification.DPI_120: + { + upBorderSkin = spark.skins.mobile120.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile120.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile120.assets.ViewMenuItem_showsCaret; + + layoutGap = 4; + layoutPaddingLeft = 4; + layoutPaddingRight = 4; + layoutPaddingTop = 4; + layoutPaddingBottom = 4; + layoutBorderSize = 1; + + break; + + } + default: + { + upBorderSkin = spark.skins.mobile.assets.ViewMenuItem_up; + downBorderSkin = spark.skins.mobile.assets.ViewMenuItem_down; + showsCaretBorderSkin = spark.skins.mobile.assets.ViewMenuItem_showsCaret; + + layoutGap = 6; + layoutPaddingLeft = 6; + layoutPaddingRight = 6; + layoutPaddingTop = 6; + layoutPaddingBottom = 6; + layoutBorderSize = 1; + } + } + + } + + /** + * Class to use for the border in the showsCaret state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + * @default Button_down + */ + protected var showsCaretBorderSkin:Class; + + /** + * @private + */ + override protected function getBorderClassForCurrentState():Class + { + var borderClass:Class = super.getBorderClassForCurrentState(); + + if (currentState == "showsCaret") + borderClass = showsCaretBorderSkin; + + return borderClass; + } + + /** + * @private + */ + override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void + { + var iconPlacement:String = getStyle("iconPlacement"); + useCenterAlignment = (iconPlacement == IconPlacement.LEFT) + || (iconPlacement == IconPlacement.RIGHT); + + super.layoutContents(unscaledWidth, unscaledHeight); + } + + /** + * @private + */ + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + // omit call to super.drawBackground(), drawRect instead + + if (currentState == "showsCaret" || currentState == "down") + { + graphics.beginFill(getStyle("focusColor")); + } + else + { + colorMatrix.createGradientBox(unscaledWidth, + unscaledHeight, + Math.PI / 2, 0, 0); + var chromeColor:uint = getStyle("chromeColor"); + + graphics.beginGradientFill(GradientType.LINEAR, + [chromeColor, chromeColor], + [1.0, 1.0], + [0, 255], + colorMatrix); + } + graphics.drawRect(0,0,unscaledWidth,unscaledHeight); + graphics.lineStyle(0.5,0,0.2); + graphics.drawRect(0,unscaledHeight,unscaledWidth,0.5); + graphics.endFill(); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuSkin.mxml b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuSkin.mxml new file mode 100644 index 0000000000..67fd754ff5 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuSkin.mxml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ActionBarBackground.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ActionBarBackground.fxg new file mode 100644 index 0000000000..febd3f71aa --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ActionBarBackground.fxg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/BusyIndicator.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/BusyIndicator.fxg new file mode 100644 index 0000000000..e04248f34b --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/BusyIndicator.fxg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_down.fxg new file mode 100644 index 0000000000..4a7c5f7f2d --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_down.fxg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_selectedDown.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_selectedDown.fxg new file mode 100644 index 0000000000..110f940605 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_selectedDown.fxg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_selectedUp.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_selectedUp.fxg new file mode 100644 index 0000000000..52b1a90b44 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_selectedUp.fxg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_up.fxg new file mode 100644 index 0000000000..59b9cd9144 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_up.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_down.fxg new file mode 100644 index 0000000000..4e4f80b7af --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_down.fxg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_selectedDown.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_selectedDown.fxg new file mode 100644 index 0000000000..ec3707782d --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_selectedDown.fxg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_selectedUp.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_selectedUp.fxg new file mode 100644 index 0000000000..88a46439a8 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_selectedUp.fxg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_up.fxg new file mode 100644 index 0000000000..be1b61922d --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_up.fxg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_down.fxg new file mode 100644 index 0000000000..79a535c0db --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_down.fxg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_up.fxg new file mode 100644 index 0000000000..af7d033a3f --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_up.fxg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CalloutContentBackground.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CalloutContentBackground.fxg new file mode 100644 index 0000000000..8442b969b6 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CalloutContentBackground.fxg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_down.fxg new file mode 100644 index 0000000000..426df3d2f7 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_down.fxg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_downSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_downSelected.fxg new file mode 100644 index 0000000000..ea7c3c43cf --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_downSelected.fxg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_downSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_downSymbolSelected.fxg new file mode 100644 index 0000000000..f59d001cb8 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_downSymbolSelected.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_up.fxg new file mode 100644 index 0000000000..9d652603bf --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_up.fxg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_upSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_upSelected.fxg new file mode 100644 index 0000000000..f51d7df976 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_upSelected.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_upSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_upSymbolSelected.fxg new file mode 100644 index 0000000000..a728d3d9ab --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/CheckBox_upSymbolSelected.fxg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderThumb_normal.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderThumb_normal.fxg new file mode 100644 index 0000000000..c046673755 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderThumb_normal.fxg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderThumb_pressed.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderThumb_pressed.fxg new file mode 100644 index 0000000000..416f7950fc --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderThumb_pressed.fxg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderTrack.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderTrack.fxg new file mode 100644 index 0000000000..ee464e0b16 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderTrack.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderTrack_filled.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderTrack_filled.fxg new file mode 100644 index 0000000000..c4d4f74bf5 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/HSliderTrack_filled.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_down.fxg new file mode 100644 index 0000000000..dc744148d3 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_down.fxg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_downSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_downSymbolSelected.fxg new file mode 100644 index 0000000000..374c9fc056 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_downSymbolSelected.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_up.fxg new file mode 100644 index 0000000000..4110262b43 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_up.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_upSymbolSelected.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_upSymbolSelected.fxg new file mode 100644 index 0000000000..5b844b6e62 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/RadioButton_upSymbolSelected.fxg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerBackground.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerBackground.fxg new file mode 100644 index 0000000000..78d8c59701 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerBackground.fxg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerSelectionIndicator.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerSelectionIndicator.fxg new file mode 100644 index 0000000000..067afb1e11 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerSelectionIndicator.fxg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerShadow.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerShadow.fxg new file mode 100644 index 0000000000..41c1955e7a --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/SpinnerListContainerShadow.fxg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchBackground.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchBackground.fxg new file mode 100644 index 0000000000..b26457326f --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchBackground.fxg @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchThumb_off.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchThumb_off.fxg new file mode 100644 index 0000000000..8f9d046b9f --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchThumb_off.fxg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchThumb_on.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchThumb_on.fxg new file mode 100644 index 0000000000..d003f72cbf --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ToggleSwitchThumb_on.fxg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/fonts/Roboto-Bold.ttf b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/fonts/Roboto-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..aaf374d2cc000c058f434f43c3b53bcaf315e2a0 GIT binary patch literal 135820 zcmdSCd3Y2>*Ed|%-P4nVER#JXkj!K!`yL2efDrZ&k^mtjBy0h~ju2J>0TGZ*L_kEs zCL)`Yh=|Iq2#6pEsECN55D^g+5fM?6p7&SP)5%QK=efV@yWT(E30*ziT~l@H>~-o? zLntA{9fd>;-E#Zp-&*v=dP3ieB&4rTxBUK{Tl)tsAc9LKA-bNu3-Y^XcMJZK5Dz1M z;MO-kKIxs`=mA6Ml1e)X*m;5eMRl{#xL<^Qdt%%Ts^f@){u#R|uUDIeO&K5r3E?t5BCcPaBOJ zE`Cm3ab1Ax(9z?k&RBQEH4fL02r)#Cn>c)^u3Om+^mm*P2fy({XH1e?i+|zy4AeJH z7&?Arx30SLgszhSf9a%&PfXo^Kjk?>H)0GS2PaJ#Imy^_C!V$3h2LBy3^zD#ojkpv z)UDm`q`3ob5OVyUJNhK#tk;>~$G_Kq;kd+M4(d4)f!)Oizi}8={{JfrR2!&NX^K4;zLS_&idSypa~KGa4kx&2#4{6=r-z-(PWT%7C$BBtQ)N>Laq_v zmt0~TjGIQ*c(;iYN8y``rCx=>VVDqYB($n8>oG%T#xo-ksKV-^U7Xn=#FE9rYLX-! zBMH)aQmp%tlOcn~rYO#&wKI)g^*n^`R zj!rn*;248rIF5EW2II&RvdL;8iA<3^Nt*OBSt13K>0&);sc%jabwR{g7fhDuT9Oi7 z2Rz$=V>7M?k>R@CWQj0{Y?PiO8M5qCXbo0mz zoiEYreTg5=K{{umlMa$TLI(OQB?8Va;(QV#t|3&|N%}~=NxHO%Op-!L2b{Y|?NIj$ z#>>7hB4dOQGEWGyY?gYEN$k7)6Y4G^W3A8G_b$RGxMw68!fE2jek1u1gYG-h5@$~w zIvl-(9V9`}lYKY`>*C26j{7XK68PvY9l;oul3eL~;v?wDJe{5lkvfx2ayA(tu0Y$A z43ox?xeWL0o+OdAVhb`?>O+deVp1*^l0m|kWSn%5JS(M<_QF_FC4`ZoLVJ=UzD6qX z`*PhE2=$|GcqXnk}qlNVl23byk78`Irg5z~vF>tHVfziU| zkTOZ8ZUOEwTG$?p7T=OZI#==x$2-G)lSBT1#Bo}H4vZGsAtYI9z(b30-izZU=_vTM zkq(SjjmK)z(Rw|G(}U4OrHf8lMrJaauyM1#|8{0$2QP3O{L>jcs`DpZ*!ZPw=#$}x z!TGiu7 znapD{B6POwOam=;lc$6Vk}qC{o*7402ph;|Lh63R@du7438`;M z$m8E}mW8*-U1=nFk@dx7RjRX8%C43w;XjsLx@RrF>Lyt_=%!k33a?pKu{umYO4Go1 z7l{w&GmMAJ9$;hok+?p!wlC(ui6xn4X?TI;zxOG06!aOx0e|iUd@{Lvn~dc37@y@pmYD8g^?Hy|tgbGR zY5~2v7PcXUB;$Ir?oX16I=;GxBul^}PqJF|PbsbrQ^WBEe*^`A_nK&VdZjuY$a(He;SR5O`p+uj-+((j4I8N#Z7Z zKwJ&kdS*ktogdQ9#%_gJdm&jOsM>1A^mAoKL$L}IZM|m!J8ox=_bRXpK zSKe17=*W1G*(B&dW|!nUq`7>H_tBE~&-!D1;H(oNznzJPGz<1{9@!_Clb!M-(BLYW z;MkXJbZ|vKEl}4HV`cmRns6HWSUz=JL5dwVf{s@Hm+B!0-C%njld9lS`o{ygcyaI5tzO@SQL0G4T%2jW1>XAIvAK3zAmNZNvPN6S&E#PW%zbM)VV z9y?JVc$a`Ds~j5|&Y%Tg^VBDjXx(ziC1}F%r<;QF9GqVSzs(_~@@mUn>6GO#mkZFB z(L?K_;cWdaqdlV)uiL~PX!5V=bzLpS-Uhyr^{l@~JmkxyIpZm*CCP>@C}2ke4m4b7 z`VzduXltcE^9gj?7?^A``Z6Bld~lBR63a+$#(!+g4QG+`27bFh-%gP)kRrJ+>7)xK zvCSzqW9E2(U!&>J zre{qj{{3v(CH1zn{`>uZv8_#Tto=^I@xPxfQ>Av6jDLI1^#N=l&PyR16PPSBJpKJ_ z`5dw|=a{yr;83%imD;1H&Xsf083Q2=+IRO5+2Yl2*`36~m z-*=IUNjKd!GD3Vn{2h+LUv(vA;I*}kC-vhFOc@qb>byiNPzx7IKCzUY>W;uWS~PTY+Z!q zH8Gn^5f_kBaR}tDHyJ7o1uw^f{{!H+zX)6N0`3(%z%X;^*+Up$o*rc(w{h5d4t{=&2}t{||kX0(+);++k@eS z9k%e}yTdn}x3eV1KN=5VT%3pSTvtZ|k=gQE%mt=fZt6qHQ?e78s#^e_T?AboLR{FP zUxGR_$vFM9jdkIBEP+2ZNqQ3J_V5|3_DA|jwLkJE!1WINp%U1%pJ7WzlA*d7z+j!v z$nL6L)P4tlagnqJxYPR9&KMK_LzF^K^?DlVL-7e?1$E(NL*W`EYu{bs*;ALGV1FZ} zj?|Y%$FWB(DK)ml9V3ot8eqf+ca8BhJ{~`yE%ZUItQuu^BXP8#{*-#KJCrt8PbhH@ zAN(X9J+KeDz$cpDW(B&jepnCagO}*VdbcHNu)jd98b6~N`*T6(tg8CQL5U8=SlAy1 z?1Bm|tjQq$$6#fD_8&)M;~&Nz9~~FZ{s4FqM;*SfpCk(QKcmek)v;T_I6^6nbKxMn z_y)NIqZ#%jKAOhz{^E_X)aZ6++aWT_Z+G0SNNbg{&7KD>Nh~Z z{uzU5BEwHhUtZl^0yfo0yqdEy*ci`%b>x?f@bU3U_#Fq7-(yeFJeqX}dT`;35d(cP z`yT9%`-~enVre3$fl5LCV^(l%8dWAhLx6x|pZ(mY6^+HofP;UbG{6_^xCB-~ns3Jf#{O1=0ECs!Jl zfNw7GEvX-8UJ=+n;L|)(*HEM32^Sa2e!!RmAD!qevNK0z2>XT)`ZYRWJoqr|^G|GW z6waDVsmxCB5v@lHLiyzx++p8Z5J@!SyqC-;GKnEBWGM4~;P3 zAl=t_2GYQeflmki8Pvzv%;;?l1ebT6pN+aVgb}H#7MRP+AZ{TK>L^A$Dus| zXfFX;(g5uuK)X`hE`ADViR3GVNUfzTsh3nHl}l5k`O*?;g|t_CPx?^0CH)pi0y_lG z58MK1iP6*O3vmkrv`NM`4bW}}w0rEJ9c+cx-3Hnb99jx!>ns+_@0RZ@M=fT{cFQ`; zKueA#&C*9HV%qgD<9G?jIvgwO zSD>7OqY}r&8w+oAyP^N?=kGSw-m1M`d#(1{+N-q}YCo?%Tl-1viQ4tGD{3ohN7s(5 zb*UxSmFvGu>v4MA$l1P$BGD#t+B#oq#)}#%|AZgpd&q8Ru6^WnvY(hqHF<-)Ne+;M>C| zDBhXEUxFe^WD!|T7LpZY8CgYEljq1v@&bWOleJ_Md6~RKexrG`kW8ipv_Bm{X3;@( zAk8bxf9-mc^Qd7&DDB#hS$!W42ig z%g!qd8Dv~$T-Ia6GGn%J^w1GzDU6@-VC1qv@kTSrFC2ripwMK_92C&-b>yHy8L?)G zHG^NE_Od}}pu*Y!&iqzy zlr+7OX?p`0=z56G&qY%>g_r`^jSy3a2@np-iZwgN^vW;n3J^^I(J98 zR<|Ts0dHgaeJcz1oukc=mm2CQM3c8@a#X0lfjC6JC3H--eiOT-#|C+@ds22A>o;AW zj)@`O?4I1CU3{6u%e>@l_#>|eGk@=2kup|0+&;3+&u6A_fUA@X(=mSXN83o zeZ5fS94o3=5#+_1Vnvk?p8mp$jy`x$%8GNWu&^S?2UYN3T1{V8Ok_o=A1cpd#VS_3 z&x#;FRPM_PH$POtC4R;V+`}kYfyw~QAC)_@Vje3dTEAfk{;W$BO!-EU>(Ds6p_A4^ zen&&TcjK3giEl?BA5HFfv&}_%U!Ry8Qluq*0>#VJMZ?u;A4n1 z3@}`AZ|Ab-4wb%^mypSu)wgnVXMQog&hbx5q3H3Uii`Q zv*Fjm??*@xei2a-?ILm_Mns&AxE66gQi}9}lieDN&1})G*9}^yv z6q6M*AZB#TteE97n`6u|M`JF<+>WV>b&Z`Hdo%V?oKswjxVX5?xV*R_aTRfM;x@$X zh%?6>jk^$cJD$Y%jvo>~DSmPMrudrplku10ZzUuoOh}lYuqI(w!g~ql6KWG4B^D&s zBpy$^n0PDkuOvfKXwu%~oaD0PDalKcHz%8uk0xJ8zM1?e#VN%nB{(G|B|D`!WlGAT zl(i`rQqxkqrxvA-OP!m#I(1v>q13afH&P#^Noj_(;Ix#q?6ktP32F1w)}-x7JD7Gd z?Q*(P`l9sO)~#DFXnnEutu~~MUz?~l?b_tDDQh#O&7w93+nj9kAR{ThsRAwzX}4Y8Toruid0}i`yM(_gTBk?S5*fv^TU5ZlBUVyZw;%R)^jl3OX$6u)M?C4wpOBcJ%1z-?6;ogpQRRYdhY~%*x!6`A+8f%-YNc zo#ak_o#Hxe>+IDzxAVa+#x8Ta9M6i%D$W{_bv)}#)`hO!yXJP?()CexLH4ff3*CIW z&F}W0dtUcL-OqNf?f#&L+{3TOoF3PD=JYJfdyVKdtJi{FcY7E09@l$r z@72A}_P*FByiZ)8v_2jB?C6w$4Qd6_m%sW^*x%~ zIyWzS9Aw;*p>-o<<=-#OnaKPP`p{$~Y&1(gMd`iJ&k z+y7?&M+2M(tQ@dqpwmE)f&K#%2DTo!rBEmwS2(wDb>X(c+CiCvRt~yYlvK2`sBZAE z!8eOX6xWsPDA`+bprmey$B@7wQA0MCI+PZaZZ5r0T341(*1fE3Xu;6qLoW}#H!Nq^ zjA6Tm-5VY^JY#tF@J++-jVKs#V8oFTXGZ#uTr_g^$W0@6jNCi&L3z9K?&SsLo6B#O z-!E52<&D}o>ei^b(G{a_j=nqkuQBqNvN7+BIWgw5G2e{2J?3GBP~lY3q9U#$tD>-? ztYUP}{dnrdskKw@P9xJiriD&Ro7R0=(X>g^7EfC85 zP5Y@*sPw6fsm!b#P+3tqzj9sW-pV7D7b|a1C)2&AM@{cAy#1g->r z3pMME>o%?1zwXw0uk{_)k6!=IhNunWHk^4e<;98@Z*J_paplI_FXg?o|E0T|CTx1| zWuKQbUtaR^?aiY%Z`^!l^M%cis#;VfRCTT@s4B0TQMJ5kTh%*NC#o)0-L9(JV%QSA zC1p$Ymf|fFw#?tMX3LH(2e*8@<;s@3Tgg_ht>If+Z|%8tz}C@QXKh`+b@Nv9)}vc5 zY`wYl{x)fw&$h^I8QXHV4cj(#+p=w&w(Z*X&bBk#YPUVu?zG*wJz;z2?S}D#JA@ssJB&Nhc68rSv}4?kxjWYG*s`N$$MGE(cii4l_loN){;$k` z<-*SJo$GeqdUe37D_=dcD{$AaT?b!_du`5Zw|0-%eSc5qJsbBt+&f_Jm3_1J-P~Wc zfA9XIuUDGm%zLY&suxvXsVS)0UGv}#@`m#pes6@nQTfKn1Ca;jylHrI%bR=OJoM)A zH_yLW`{sj#IR`f#+;MR4!J`K+9=vr>dCLpl!D7spcf)rOF=tD}yryfRS%?p)77QH* z@e2tTk`pfUW~b=FO95AKTlBmnENG3I_2<#9KEyp)E&R&o-=x_nbPj(C&dSdySld|Ll9mJR z0L&fd0it-b9M5}jL8C4j^aflQvbfOph}6@rTpjHJdt#L?ZOjoIH&Ez(Bt0kt!Oz1qv> zW{AOTJZ}y$R7W@7#5hvIBf`T|Q_|8?lf8X>^m%Bu#!^6|l zOeXo{PJLx`II0k-wiDoP%vov)h{j{s(SZQKZCN-aO>p3{AZXh8sG{^^+6 zR}ypUcyoqIRZcr?#NilmIHMi-#!2IwYImt|(4wK*&*m~5l+)^&!wuE3HqU|#)hUga z35lL5X~{`G-WYC3aCmAmA8Kl`TFqwbUWd-pum zXTk10PqpdSt7D%w{ra_e)JvMu3)0!%a!1z!_%R_O(graB&ct?n5Cp6QHD&Lb7D1dT zS{Tgk=gk;va3jHM64Yl226H&)ko3l991rQ785lM|dp%7{13F~Auig|P1396(q_lLZ zml=PEV3Bk@=V)rb;OVR`m)@-UXjuOe>J=J!IV=0Tpd9Yg|G=oribC(CRwODHJd?vF zxI{+}XdTg`ZKrIyq;gM5@rJ(p&V9Ii*tYhamG{~$&slb>=z(rfdiwXefB;(Tk}x=K z&=cZQ(Sy@lhxU$5${EOIBT2kR{V>l#cCx*O0XvIKi7@@4Yo>fJMDqG`ln%nHa&JVV z9En+sH@n1Rs0Nar>?;fI-Ubg}eMmTQpF_y74qjK6)2M*3llaJE& zLWc5G{pY!>h9P>G?@x2-+rwAp)_u3e_5@YI=VRUmHUB<@W zQTdE+C4ceApIHJz!=MjSO>}G3v25DHMfs3s-C*@5Q$=`1_!is@S^@WVh9uP_yi$Kg zNTJGgR(Cau127{ZqJ(_#1nf;1dpB5$ghUenqX)RzL3PW0%R(^}I1>?PG!qqPnlVdA z)S0M>x+*sWAKKzcITkjg)N)5^ZPht#)n1&?i<72j8Vm+3f_tNEK{p&PZ`oqzG(sM{@CU}DL)??&KnLh_8gv>$Y9t+DdS8mS)%z-PQp1El zlqgP)x(?SJJ++uMk+P9H;GG7eB0 zZyNjpz^{qAXE6LBZ4|=Zyl1mhI*-bFdaEoZt{Zafs`ATMKPcbOo`?HQ33_sL(fA3v z4}$_P`ucuk)+J{scfP!$)Y3N88;J~oG_O8BC3fh(cV3w`6CrwvH4P$l0zI6u?!mdd z8K7^b4W)q{n%N5{Ve}USQEy83@JKhMnxsy*_k_7BS6qXy9jo`k`p!A!t`e?PuU$(! z3C*uV%|cTS2JYGccfMGAiO1Rn?=Kep#cCqvjSFwBbIzCykFj=$t8oj6W%>eQCvsQ9 zWP?<11ESmwz8QdoZqVe4Ih$55LxTIMdYjC-wD~UiKQVIZV&(4DZo3ZIe(54 zMVT_Vc;cjz!IMlSeRGQj_v<@YiW|QxKJnnRkH7lrs6kyn>=VdV<&WayK%!3+j>;6d{82(%sUcNk;e>c z0HXjuG9ul_#}n?7Rl#`q2o6s?m-tN1=^v@r7vEAJ<=DzfHTI}L2J9kvhc@ZSU+BCP7$dQBN0`}3f zQXGd>I82EW67hTr?DGXYkH8Zuj-xVh!-gR|7w{bXmPWdYYhgw7%ootbS4)l5szD+m z2n(Wl>)dH7@;LH^PIcSFAwsx(TDeCjen%%L_n^m?(B6`Rcnut-2OKp-FJh6vPFt9P zI-8c!BzV-F@u@@Id~rTIgva04kNX~jqGUQ?`c{JprcGx96V+f!4RePGxeFiE9~4K| zZ4g4`v(!ad^PTb>bp=ewqZiL{8V6$K@9);GVGh``!R={qNou5jw1Dnh+e*w1VgO8~ zS&asq;&2!Z?kdA2_QbdlW$!n$TmPX&Jx5U(KO{7qzh-%1Qu}a0xs2S6&C3^*WGv1v z9$na9K62)}WAiTch%e1ohG91vU{6%q%Ae_$!SZ(@6NuR@-kcb3j#jy}K?s=z)SMK% z2~VP-ID{IcJJr%)-JI>%E%){MytN1)?rl9~Jt{)?{aRqGF8cU_n=?XMRXzE^jL9fli7N3D2?%rYd&$RKrbuD z3;X8f-94_*q?9zN{#a@Dl`dihm`ER^e^+Q*BUYU`N7@-$KlJFQ zDPGONGy55;-MH7wLliX*ZhlPHLb?o%LcvUPP~&AnVzRr*$lWki)lzqmjH#b6tDh6H zg#Lu=9pl*Yg$tFR>XqN9%hXw;Cn;Ayn>Kge+><)<$&W@>MYcFJ^V2VdMN09=;bSL% zTvj}?46<1Q-T5`-swHOW|FeDaY=p=x8LIsoFTrNMK8%@|hQz5M*r55%p9_-mmGYPJ zr*ep9QbJv6f+Q$6maSR1aoe(`3+XTN9pz^%VmpBVjmrz@1YgXbiK>8ZT8;5=;(>Npxq}V`yALA0R2ONZG0Ue*S}( zYieHBnUy`t@4qS!lzq~dxnBY_2Bh>t7K*s0$FktM4QX7UnBu*L3FV${L0b6_%+SA6DO=D-sQs zf0hI2iYBngO*>b+mt5;ND_6vy>RSuLgp&I0tlx=hzsx_o34SqZIQX|&77MgtuI16# z1Dk8vOg3tQL?_1kLGjgrjg??_WeKXt%1Q`Oa2@2WvQj#?F*iFYKYynDuI#5>!Awpx zKopdp9Tv@7ykqz3@;OFD5yC$ju$cA+FC`#vua8pyxjglmbK8}(JI_T0QP+=E{OL@b zmR4(Ghd(=7+KsS*&zOd3pfWIPLsoUZrbbiUk?qC2b~dpU@@R;P&ia+Y6cwKvLCp66 zhX@JeJf%%?`GwY-+9G|AwSvlkKLd1d!B>OkSMsVF`|-&o({_&M%{oK1i!FY^WA@V^ z;WiBcLNT$0uij!pNx#WDbDi$;ih#Gk0dj!XV=_@a<5LR3 z;(U5Q=8W1~MjZS*S{DdT2&Ce5h93d3>pQvt$Uckf{{M`zm>ML!NsL7ofRNm;%5RF9 z_C+$b8?3>V-7mbbW9Qm+yM+GG$lK@;>P%%Q>{4Z`^1HD6@>hG5NBb^c-p65NI{F7- z$_aL&12Cle1$MOKc7v14&NeXhWTUM>EryvS!h!~2R)cGzdzC9f)?;c^u02++QRA$+ zPtI1Z35NQ6In3wDH;sb zzKsF`Sa`5DjC*NT)%Z8W4aoiVedVvTsU7|edyB5;|FlW*^!|4YayzT@0RH?jhs^um z@CV-afS2Q8MW)T{yT*wGuyFvVHc@H~rHGq zkMx^9PPtt-d*;ka#Uf}#*Z8*N;dEd7nGM!hT==xtHcHsiU^bmMw=`6T*%1Y6ETN&PDJ;O$ z;PY4)SZo7L;>PJOD0PS%CjNYn#_0s*`)6iNo4<6|uaA_k?%!25Gqqc_a{634r=UEx z*V#AT_|EdKJo3rW{d$(BcDeG#yO(~yDleEhjE}7a_`4GH(QA6&&QvvMTPRpNQ_p5N zO!KfQTBR}qal~?|NILg8Q98$j8ySv}Z3ndLjv3_Nb)TI|*Q{O>v*PZ>e7{t5qDE5O zbq>m5IDLESaAAXJW-didezG3#1p)4Y#Y-V(09YiqMZ)6>zj|3 zu^VM0@=Z=>E<0>RrHAZJ+tU=qUAdrm&=k6`ZyzcCuUxcBk)KNB$N1)^>2*6BYo{}L zbWGEx+CVuzI2f#F%oq@0cjW;)2R4NSAjzH#j1UHjr!P&`ImqW}OJy$<+3zwOm0y)T zH1JcIx_?PO7H|HQD}GSd5!eWz;!TG8AdFuR_?jW(mG@=m+1kNp2a+C~tY@4o>$xWd z)y@M)07LsHEX6dxK*^=2lq%&1{OzId;wn$vD@gTm^}&Km{cplI0?WImu%Rj(j>yC1 zaMWcl*`_<%I*YrJgB$e79V?ht;k4I((|^xS`7hCP*qBE0E-;pF2lg#-0{B;RY- zHc08$t$y=##VoYK{%bw71B(<%@hp~MCo6XP0K~=06Z6M~Rx4I0IkZN1?9UG`>F)r3 z0c#WsK{vieA*dQ&bHflR1v3cvSEhWcGynNH>btKAj930GPO)X*KDtr^s${WaVmZI6JN~%d*RX0Uk`SsU~ANoj#g&w*SSdYs9 zxD4c)QbJ2=US~xRp_6Oov2Pt&;fBJ@!xSv9;GmlnB*jZ&2}2)EHwis{R(=&|s&tr2 zGI|wAI&`!f_NE$<=end}RjfHUy16TajJZ_}vd%6vPx!z*msKu8Nlm;9oqV9LOR0-6 zxyIrG0N!UsDJ$@(#N6C&nWx6fr8#3$s72doCL(@ejgxV>L*dp?2|}64$<_d0SVTBq zMTT1QMMy9`RSj$kcMQI|*yqc4ANu{y_PpF~eUzJ2U-!w;`@YTVKkYNadwWS)_prR^ zzN24XU-kNcoB>_iQ+eq}A0AXX^cmy&rOzu4P?XIQ+=-qu-C@59RymV&B zibS8kXh{6R{%0>KS4$>k4G9P+?K-&#YjMgqeS0sK{pw%ul&zdn>NmgLCRIsGOsTTU zR0qo)@jhsY6lRqlxeu%Xtifm}WKf(4E+fS3$x3@)hs9k@zCH-N5;z8kyf$Vpz+n*5 za)wYxQL_B!zVa8<-9Py|LJ1Fx`%Nt^Jw3R$yyGfOx)=BhO}X)oTrly#%E0HUIgYyk z#~$`L=GsQXF-NNga>E*lop=Ag;Mmi^!)w5B2#>c3scLRTy1P1q?Q->0zXe$5ee9R~ zY|%$oX-L_)jzaikPsK!IW=L4=>Mg6m6W zHmq0K(ZUld!L#Ww8H3wSAn#%Es^*9T(@6MsAl{*)19U_JyRn5mA`Wzl8LL@tHZp>Q zALRe|zJwE;U53v!KjiwP5!`f%kuwPbg$41VJiD_U$POCAn*DOE7Q?u9H`Ts`= z;=^WxV-mz?J?sKwakd=`g6=HL4w2zYMCw#@Me^FB*FI3L3@U6_9LQ#)Vs+2_>v!ts z3XA5BiH@lI0T2kt6C-pxzOglmbQ>$EaSAsc8fcnyK>wR{+WLSRQ(+!4-OjpUhm=7@ zm_Atc%}Ou22kpCIwAKilomR6OI`_&zSKCmd*#l`hT3>TFm8|U9PlLjeY(=aLH@#go=IXHeR%lQMOVHDfQIsPXB|3d(ieGJCWie(Oj zHe#-HHC$+(6eq@i8-s!S0x#}HE<;QA$23pbNmnV=^h2y=_|-j2*DF5t%(rf&gKhju z14Bc+ifa|m7yy2UvB+~EH(}T%#o^-MYY=mvSGx#1aV^K$2 zd19;9l&|>usAVI+dD-(Go#&kL@~F{k!;`j*+w!(@CAV*f!2yDDH8;2Ipq9#2DPvVm z|KgH?!>-oWPZn0sE=y1J3~;S)Bdne=JhoNc9qino1WS^VKgQJ{BX;AmQ)ZfB=w7wY=UJ!(*X{pFtW zD-9Vvb@G^sDU-(t5%+1Ta^^Sh2g)Zj`N7&Z4m|I@`run|H{t6jWgI3OU&qe9;f{|+ zY_3H#eaS&*82VrAu+`TAnoJyZ9)I(7zWJ#CqIl&hLSG5b6rK7;xl%r+b4frzNmj*h zYPqLe>Da#hPu=DDaOqB1$cyi#B`M)?2|HIZy#Re*#{E_XKZ}om@$7JFr|h*OIf&}Rghs$$EIz)fN?2WI*%^%Y9Y2+7!l(`#HE zTf%ui(9AW!6%P?>cW?AiYJzzb=YSOK6^w^0MAP^X0BdQmwqB$)L<0wtCBSlBEWBMZ z_sFq3AD;Ui;hCHHx!wBpUvTp1U!PohEDN-mIBsCS{xm8fJ0Rl4Maz#JT`PCW>zb03 z**x;4C2QXPdbvC>Clh0JwA>Na=(@m{>8FmB&*L?e^4tsUq9ICbpM}GGVVeaZ7)<6M zdoc}$Noa_55ri$)BA=CcE$;O6!GkF=z9ygMF$Ib1w{PC8>!SSeN&QzH({%FZZf-jd z2$7#qN7a6?+#o+_h6dea$E@5+Yw$G)8)I;;wV32Jwcjir$pEp2d=Nx-4-s`q8%ON< zfQIDeWREb?&_6fzFaG)mVMYDq%2BZ~qUU2Z=3{`K)B_g#n2*-I9b-1|q89Vv!4?z! z!&COEp^3_BTUbyi5h!Mi8k58ac&;t1ax}*4NAM?@Lx3e#H`iHA)HdZ$91`TZ+ z^U-4YEarn`aS>4;Po#-UE#(!|L;3l^Df!fISk3fUC4YR3E)?E+d_q`MKS#=_?V7Ot^^jc|ir-bs5Z@6dkA zTh-OdL7d;F-Gy3OSpQ@FRXR#}Q3w%yRXB9;UEKkPx8~2;X)>NNqrt&30Zmrp>EOk> z_JmqE-^8N^lln4HI8#`CoOBkTd+02m!}@uTxXqixI>rXO1Uh%m@eWqz(+ql|onU?( zIGBadK`OY zU{TS)SB|yrpFOCkXi#>4WkdSCJ*S6HI5i%rK)9Sdw+sf{zs8F!af?pG(YU<-Yu$T0a=@PPKtIFy8DFU+rrHuin>(!wVw_d(Bg` zFkfSIVQ?mU!+tZcgZpO}4;fON-M{tGodb%C2kbohF^6wUZ0y!C48E7sf7x?-Vp6(&yl-j#`re7XVv>3a54V2ZF*_!*yqo{FFS~S(jTqen_IIVSjMjiRnBR{G zo)uGEMKA_wh(MWn7L3(uu|OIQ#L=vNzOqbw`ys`on{>A{6BS)J-dA9aU2w-<<2B7$ zw&gWUkDyWit2Kwk%5rqRu>ejohhW-W?=y(7>Y^RccGjCT&0jcOEtOQ{wz)ih{_;uJJ*R07kc;4S>5K^HRqj$!>(Wa?B2I;$z_A5 zjk|aqw9K`9uj?%R%Ci;Yh}l~mk!E_TBO006jJ(6x#!HqYz${OMK0?hnOpi!c-DKZ% zU!IT1wl+{LqZ0u;DWT7y5hLI3_0)=I5|dk}u3oXSXZ6ssnqI3{u1#*89RKXfx|w(P z?*8SM-Fxqj?a--H`$eml_I_j7$aiv{U;SKqyY{WuKDR#Str0`3d#!j*nEke5IrNr- z_h^HEd_a@4kXv88`GG5GUra-MHG*go%%AXtM3|NuPlFg;LtK0zo;CA4K`CfcFKw#G z;^?^6pJyB9OPKV4QYhUTD)4+I7Gg(OTK!7}d$T{N|A{i*p_QpDc%U4nSy+F$qU@vj zIGQO7XeTijOXz7QC!D->3x)78XfO|Y=V`8Y^q7k=dBRd9RVl&Q2}7yD3xi4yxFO$4 zz$BI}TQe7M9+eS*la>2oK;8G^Nr5fM3uK6rENudhCga^5h*h!O2-@6?wdCsO6s=J_&0hiKv*WB_vb~~9PS=nUIld6TS*$oY8$<}>PEH(A- zD|KN!XRQ`mT5(CewQx&k}}`jJ7`%< z(u!ewKmF#UT=>MmF1`9BWWH-~@@eMNGa)xW#mS3(=~e{%ZIW(?8?E#}1}By$U}xce zG;Y}wm^TPw^Jx7>ww{LN6_nDAGiT0#1|k`NwY*y0ari*3@P-xeC0aqy(4f+SCj@H@ znhb7D22UPf0eghZ42Tjk1?SnoZRp#-;R1XJ>?dP2wBc5)!5pDo05Bf6Wed72vZL3B zsJXw);J{plsk7hxA`IdBzh<^UI!A9x3bT z807uvnYW|-N!wC0UXb*ma-6N+@Hwb(Ky(3*vb6`~V`8(RbyE&seqd}mPe#zNqm1z3 z>+GA+T1;U9ByyfDI85d%3_O^mX7bsrFf`s}ImKa(m%z_ItuRFV%L+rWgOg~Qm@|FB zYulE#ZPU5?D^%XSeDTZ9uF5mXPm5iUp(5?!`N`IOKY~o)!DD4eE-I}8?1b`3y4Cds9XTg5SOmj3hn4`~|vF)R+_JoB*8(9h8 zS*kW?G3t&nsA;@b=gd3A>bylTb_8#R3~0(#vut^e9$rtIuiu~1IwMu@^pd00rT6A% zx(&}6xxRCTzD)d0os~GB;yjP~iBUty^{SHhA}oO6!)jxLxb+EKamm=PXXt zRYO{Vdji}yrueGjEl#Wt6Ta3tzXCKvFU%Ep2rq$W8J-Io^eVTNjA4&B0%^n%!Tx#d z+a&hw9{ZNb3IhtJTiqI2kvWZ=RgmEjul(XHN!Sk>v~Vt}YPp@#q392%Kn4Zf)oB*h0)mD4)jz zX4Np+wGRiwoH5J7eKnNAtF)W)wuLBf&_3|hT$b4EEir9 zuIWBPzDi$iy_mJpwn5e0V=I$Jwor*sWl7C^R;at7svY_EICY;ecu?I6<==Rd6Cq5? z2=@-LiYP1$);)x4^=oKqyO8+A^wy^Yxq9vD&10V&FVG_4rI+fGy0nT&Z5JJQU?qe-A3U7Kb`>V3SC!U~QwcOaR+0_8Qtmbup5U|uA@CLDO^H|ZB z6~FjnG~cpsM)vI|lS5EK<31^sS`!m_DbR8W*K zrRLixT5^!x?-(UcHn-B`>7cpQkyhth@wHo3NH9~d89&QG=1h`q-B8QKD3xyL(5_8- za+{NaY+k&fx?B6~UT50U+@Z^{vdF1Tr6VeAa_bbhsH0R05 za#VamOj6p@3`gUz-dw@gn^U2Vm`bYYC;+P%b&|L<6 z?P0W$*od4pYYd%(q$y3C7`i(P!6F`uyums7KPvT9{ExWNNwxe#y=5K?f^7{Si#gv+fu)*%s`DI(J8 z;AWWv-rA*4ZkL%8B)4gnmpgX;)>cuiOuCh5J4bktZh^ki@mvy>-+3^DX)7ZW$@xqg zxrB1zwAuOC$iTpE2*O-pbj#=SXrAz*P+7%e7Xqmi-VmeFK8tN*&kx=iz=+hK4_|je z&^6=)(|2h0})qh?u?4MUSD8FE! zRJr!uch^pP;qA9y=s#*y|7rQ7N9V)dmXr13TJa$4tplb9c%K_Z;FgQIUt@M0V zqo=IlnD5oFsZ&XdHF`Fw!|2J|0X;`*^lVax(Ms*Bf^<@8e&8$8rR9w&@vMyd9cnNmt==aM0SZypB5i^b9Vw zuL=tS35+jz9Yki$x(2>Lh*7eDFPN?@Q*{idw#F283a5c7un%{}ig%=kbNOzmchc~#)=5V@YMn~F z-E}gPOJmbI1GPHfhvRe%ujA09odpeb3dtvUbp+Z8YFZ~)s{{GoLZ>j=m{=XNiA|!p ze8Wy=l5^r8(hB&^m{U+F)V<1i z>Y;iX7jM+U> z&uvn+%~5~@FGVo%^f4A~V^ek{%A_YVe*iHyUtdgWSxy{6oDmUJjp?ds{C!uq&s{Zr z^kkOGKLS}Cs}}U_UpFEsP^K56CS|>_Lzv;(OjbIY^5V7Req+$mGx_x!)cpR`gq7ds zUwcaVtLMBJyZ|IN>8m}l1wpi~-pBXyKJW(5S>|+aZKZoNmF}K;M~&`H>r~PsY8{`Z zb(p;Ic0l)2s=WC)G--#)o7z_ec}0~sUseYJEwiUeQ^3XZXgOTiDhPX1NtdgAx?1~$ zu3iM0tb$BB!z;zR0uU#&2Hm*)X;!zv>BLORCM$r=AN}`AM9+q|LEkYM=U25xze5Hm6k35zfkfF)&@_@VTh?)vt77S(DFDM zvJ)5=HSC{&CAU=o&Q=~OA7p04kP#mFPyqcg>Mhs9ZO?w ze$}rkB0XYCr``jU3*(D2dY4LlY9GFD9(OFOwetP){~QkV|JKLUyHhvnva;x8udJeH zrU885oadA3E`vT<7>-Q3jP+tt8SBuTbHYE=VRSL#9mc2?Z>^=ZBNMG1n%ByM)kqMV zwj)P3)G16#;;lF~t)n}w)gcwS0Hy&FSRHeM!5quDrJ=t*)_QupzxOouOQU8ggUOtV zK!?g#km)$f9eD-T(&O-^iO%F3f|M66a>ZX5q|Ipl`z+eN2xR?NnX@(ThromG8-nUV zA((uwV&5#dcc3(6A~vVV;k&LWu zTV|oeCb_sg)^DGXXp@;swcJGMo2O27X?ObBBcCe2y_wguUom^fhvh=~uD0#B55I7~ zzEY6p%qgCe)2GA5x|cHMhP^Rte7+c5Iz%tfivHgHdS?xK^X00&ZTk-H)!IW|eK;LE zqLoLro8?JQN47eY(Bo|TD05-Qwq1n-v&ZOK3_(64=-EL@VY#rNXEqp<`79l{&%$#! zb=PpeyUL&A{Ta`g9Q2L+XM%B zD%25m%%KS0s=Q}GFqH1+F#U#a@QW=Y)&6u1eg@N1!UE36td2}7{dBB9%@bi`w(Lcn zmAaqNA8!Z#N(dljwZsO0Mca5jAaz$OGD-loV5u5CtZbdd&lv= zlw-8{`C91PSSf!+`jW{NvZ00ZWzR10<0n5J^})Kzh?LDwO+xYwurU_;-kpfeARsm?Wj%f@>6GVKS|mG!_TTOM@$uas0iXze zeFC{;@7w!0+^x|xp_X}C-NeViw7;p3!}HuUw}!I~m?eZcJjkiF_i;qk$6?uTNhLAa zTSt|aBLyy@FtXd$Q6tA7V&NfE$+MO3F5A1y*j7H9wn*_=CQBRF4OeQNK zji8ZBV?03hOR7;v^-Crj@gGIQFR}F%csu&hT08JPPt!xJmQ7rNw+pW z6Pmm_D}}lYomp*%=;=T)jmvSmQYf|JHVH7W`$8DM&)@x^XLBOVuQ@%ps&k@XeKSqp z@Vy;eC&8ZaI-aV&33hPP>Zt9oc@u93{_}2i-W2R$-wxMFtR3)S3$BxbMYRy8_~|K|2>8n(Lbc;nt1>GYf{KtTAB|PW_uzRdtJPHsCby!gwz0Y8+@XDb{4Y zAr7JXZ%o!zA8&Fnmvs~KyEGYPJT;!nI&TNEzCn|9`*!3AtsPo0WIE$I?!Vc$lY}~I zJN*$E;2Ob&#idXS`bmZ7S8F?Z$ja}?2S7_2M5wa_x&iMrQQKjQQLX4p`8Nq4gG}o*0dd_JJogyMq72KjjyA&!*q`d zPk%a1)jd3BqmGNi!*$R9;qJZTt0=zy@jJVF_XZM3LP$a&5JEx%1Pl;D01-m(y+i1T zbO<7%fHYAMsUl4UMMM;k5COZ`#U6VDr72*+-bn7``#xuO_wI(^^Lf6{^Zott`}pF1 zvNx02Gc#w-oH^xvs2`foQYCvDSy~h3XS@t2d=$GMAbEZuZ^vnRsBv0u8tzH=r%@sZ z#4y^b@!e<970?HWroL@Ndy|kZhxvy6i)<6H?&F>PeP1fuM3n1{$C^|NbU>RPR+~uB z7UnCg7NaZ1(;$(2{_U_g#K3>N(y`ZbOtNhTWLJZ2dEhWO`@xBWrOnKS0SaSUgq)Vb zO!@%!(?~Lf`ijW99~TNEpvWGCZnBT0P%txle>t`#X%Qe82{mQz zT!oubvP$b8e9Snox448LR19OUK!GeFf@@yGWQtNCZ5hoWpK7314@DplrHsI2OD87V z)(ahP1~ucM(tvg#=%6sO21zISsV&7I09w*5onYf|8D4PVB}oO9APy=4B+}*iOqNX2 zwqEq6HLYGPQZE`W%AdWtO~#9dckTplls;Z z&qrvpg5{Q?XJF|RCAaLGAZUI8ZRVKKc0XcZ>0sYj>L<+U$5&qt6<@HtK|iQTHRKbu z7b@fXqh>YyZBO<6GwS;dSlpKaB9t?+-NrQtW+rnF}1U(}{*NMBkZ;|WU#{ai(DAR~6myO8~Rq;Zwp zDt)WykP=l`N`S^$vKr-lp>1!g?O?gFytbH!CD;QSF|>cl53?u5*qV#*-0;5P zG;auBQ?h!7tVa2Pc7J+0Q25lmRmiQ-w*s|AP>gEJ^!W=z{b~)2owg3gCRhkU655~v z`EJ@@%o|K6XgC_u_jpzb44qex#ho{Guv5pV% ziu6FOBR>++K=?!I?bf@W0KUTUTW`giS7pAO3GA`!GG5L=8Ec+*bqc+j2+ivA9%dqod5&)`&>wQ2nDBr zaAfGyZMRI~YPN$K<6|T*mT?3VsMYSk(%InbmPi_S``Zp%s37b zhpmqsBB2~(q)%pVAG_;GB;JDbxTZB)wdvO{cU*1CqukPO#D`~t?TGl3ra8F3_WYc& znGH%VLaZ_$DnDWX>(i8ZEZ_GGZ1#|4HC_59&WT+AL9|hJv;1~^52)$3t*L%UZ{>cV z)BUCN*4kzI;X6O~BL*(ylaK9h%mR=xs-H1VKf`?gC|_e5^@9Ql2o1B_(j9K5S;3}D z3|qwZyfo4_LoGGm*wXIgGrYp7yt7kY{UXuKs;8UeaU@!OFGD3fw5Y1&ZG7nAis7OM@B4br$-ouun9b_sv{{6tj=MUO?T#&}J6SLzCq z!B(3vHO`EXzghJ|_haq{_r4A4e%v6`hWp|BG4+G{aWvnL8|cbwt>}@B2n$X1!~Up) zrGI0tTggV0my7T5xBPuA{$9P8Y-j4d1ALDt+gZc1@1=QA?;TK&=b>Q**LxFjb^@mi zZE|RgSf{Dh+(+a_^o^jokFe*yf~-t=vuHa+we=OrJNV9=n43s<=0tAE<4MH%IG5Ln z`^7p7B7^U@Vt)}Y0?ZIs>K`e2F`lq3I>C`Y zOsFO*6@vkv5n;zRGrD?H9`Wn1yE2In9kOzX?>qdllT7@{;rl3?82qE+DyC!2I35CV zIUlI|X>3@Sy;s?RBYI}rE7_fzi2m+yN)-A`l6^uza4<)6sU<=biO zW&QB|RM9igSEBBxab^19`>CR5-~zs#R=d2Pwa&N(i>WH!u0{akqN_f8Pnj3h_*$SX z^+&$@?~!W(&q>Vyc}}o+DbCre=8Nt(ioRh!3BEhuWxqzC;`D2e!1Yp{rsJ!Y!{E-lh8K@ANakcas=J zZ6eF|bIIxl=S4%4TR8j%U3d=eRJ(wG6zM}YtD$6(tvwWGQlz_AFQ))Il{~9zS;R)q z0Y*`e#tGFM)a=sJC?tHMPyJ@UZpE+{K-Rnqd&GJ4Pc^G;y@i!ITh?%tul>MAGE%qe zK=8SA4w${XL7;Ruh%PE@Oxnx+q}D7V6(%#jEmWAopXrMi=vO|dIHaw39e63aS1~6W znP*I~S=!$B1mogF#y76fvhDr%e{hrr8J}6ROSkv*=_OO0+l79&8EKhBLMk#X z367gy>;WbkjefVjeW3#c2+kRzMt6q z&B?j;gA-gw=fAC_)9LO)4+4$IXbf1cCJ)pF)9#5m-wr@9yO!tCG0*yk^JHLm1r z8T5+#wLGGbF|hymlfv>KKiI#l=ZofI6XFFQ zufeh3IEy0L_-w`Vs2I3Fb+jj6b3o7cz2+N8?XM0{EZNmj1(`+k*^Z{P?s^Ir_DCBO++$L(O(AY;2kA#%nIxWqVWVAIWPFqAEpu%^E;eTv<7n<$>VJ!= zW;NZ^{=vFg{$B3dvj0K0uS72IRK(zh!Om+ZGZkpwblxM8fuulYKp$D)1}s3nRRC!( zS^CuTcdj}ksy|{B$1h)A{b#%i{fx45F%epRZuq*0 z`y&b>UWh1)@Q*BTvN?0ZcSIhDJR134dn63f?I8^!^Iza)!LJmRfiNX{=Y`fK(4 zpTHg&4{s!5Xbam?qN6jBhuZ3WbVdxKjRNB*saRLab6hfi_|Thg8a90XoEG)#XJ^-M zkmIj1AU}V=z-iM4X5{2#G;Gn5=o=u1Y6kS47^>qIQ&NXrGi_Dgxa{stYGg!&ZHr0BzqZ#Z zT*K(SHyZEi6a4?+y~tSBF;X2Pbz-R~O*uceLR+(sIoh0Q8u>I@*jSN|QORDr!Z8T; z%&l;LrGiQ?R4S@uV4`5+vww?U;OT{^q9|iz;g~4oHzUT5LYjd*o>GU61`7Lu4iJ69 zjUzfAY?j*i-W!LvcmdVK{cn%CeMq}T4MwdRk_#I@)@C($^k?wCANv=YjlDJw#YZB| zBs0?lW5RF^%x!~SMfrQ_dUCv;qJ1jjHF@ScZKH_Me*WHn5y^ah;rGj8^u1l)tW_+1 zk=Gq{Zjov1EP03*LlI@X^7c3zH;KPOqU-i4y?aiY)U(gz+386&>(;HAlzz6$s8L-y z=jC;-nVwdwb~*)tlw2_O%OU=6!Jn}>UtDi4jKqpy45$(-ha7zFoUva3%ckTnqfk!p z|Df7aMvhGgBj%!gmYx#4<=dl=`F}tlQfbLTG{45_Zmt#j@ zv`Er(c6UK2SI>!`BVT~CA<=(&^XBQ<&C^nHR3db2ut`++wtj#24uPj$8JuhUyi%`K z%S=gHK50a5*9tIH`n2LdjI*Uft+fLt1gPc|IVDek0+H#*vLUF?EGqDmHy3XS5Xvvc zo|7C5Y8T`gO~h$Ee)w4vF~$WyFn|f~MRqdzG%md?c~H8(O*0%=AHKAo@fI zpzHR`B05<_Y{{#Uwh1 zyseTrR!nRXXMA+s#xsO8p}g2@h5n(V`tdJ@>H|F^W$?>jaJHyku#r(0Q1bDDeO{YG zamMj^EykAK47%aNY70bSy2zl7m7l4Zz&j;9U@q(#@0-J6tEe}D?qp--3UIjRZ%TJS z4_JYI&F#={?!wXq#5nmUc5GE&Vbw@f z6N7UBC2aAjlGp6H!(G;-xx+Iw5~{C+=ep=y)w{uI0eR9;>2-Y8+H2`}FmYLO-CKw+ z$XjV#6P$^9HirrD9(|B}io-Zx#m~eaNHr$zU@f6+kM3OG0ywT1VH*i;b;0f0NYPMh zeW{{EYjn`aS!hOp5CO6>P_c%^m6K*cn!R zAv-}lWR$W^@J@IY?kuhW7nH$9M1-`{M}f~7|eZj%zXg%bRJsyYw7G*vV=jYVQhQG*ayzd zHd7vy?~044(RES2XUr;IN7L`jy@@(5cwb}Z{p9mA$v1}?9tYmavDQ8u8Zy)1$+>2N z?76m?%QMknGy9Ek1xb>wl1p)s~ujIY*$>Z9O&S^U1Pn2UaR zUj*zlm(PQ32rV>q0zV_sh~pYO=Z*~U3!Q%FodRIg*)<8cLINd&W(%mEvB^SjAvbEv zi~m9?f=>EHWP44~K9ZGAZP7Yutv8>Nal|v0jTUsm&WmTZ6*1kgIbjX21`j<6x}$OX zOo1Jm?6?-#kSEK{30;CU38X{Qz^R4wDR-GuSb5zZs9HdUjw+~4$66fHmV5zF4tGN{ zUk>z7#DeEUBks_TWVb*xuCMy)zeSdO8>M+K$+tzbOCmM+_S*TTIe)F5yZR>6ylJ&@ zE=qp&%_(^%^5vH+iNw?AL~7+Xg0&WIFg2~27Pe){lFbz~luHiVfN>^aT&M+sabtww z2UrXJYcS3P_!4cOJ6maNL1lwo@Zzz9*!~S4aI|+27vN$}B&?E-z!6Jy{RXE*`3Te# zB`5L815DQEnb*%l$o zFJh}!LFt(6tTceEDaaR;qYVHNOZz%+%I))RTt0QI4EufF2GO>(RJ6Zq?j;#Ghhj%R zjyihmjps-GB#V>Jof#o&-Jd6-MV{Oxf699Rm}O^(*Z%X|TAMF950`DBZW|`$1$u0l z-Qa&+V>ZB)vtC4$en5BNCV^`9J$ZEL55`qyeY9Dt+W5XJeV^J~6{v2vnJUKu zhnxCYZ?!qC`iT$4)2zh#yTYt$oxi3S`}VWcOH9BOZ=DZ%TADl11{wzZN8@V0L??80 zA5Lgsio}y&L7BTXAJYyWXd+^thN@L%X0weciHHPFv>zPKqa}872jR+f0cX&fq8MU> zkd+bp%>!`ijqc5{!g|x1Wm|q;!=I8`sY=C!l-vd*mn1h?G2+;XuihB4yvbFv{pe{| z53C^)`?s7r&S)8o$j#D?527M^_U_RKu(%xO+qe7j;A?9iYSb_g87|&z)Z{1*Ez%3t z$mzxk@NQMaflg54Y=EIPunz-|lE9M2j!xjn9Aag7aNWi^z`MJwiUkd#%48`y07c3G zfi2i9HAW?Hg3ql24UDm(UH@t6#9=or|LMK%8?HglWWupgLq`r8JS}~Chih+~HF1pp z)eoK;_h93UrM?%0yzT1hvJnzra&$do8Knz2{I?K&?W z`v@O6_Db>rfDsmII+bW6tsm|teDdAis`3(pIy~AZKw{klxVg=u$q@jv!esI(@U{*c zX~AI=CaAfk_=8-?vN-5?ICZPItlgK%EKH*NjKP}G>k&E-&sRtV+yjpCT9#3w061=T z))&wh+iG>Q;q1mYw&hd*P)^f+pSxArJ3o6QOkgHAV?>2 zqFOCs{&+C>+!JeKEyp&EMWEh1_NmbeTw>`D^}!|eZ7g=SecE%#S{xX|qcFf@=jMv~ z*a;!y0d@jtRwOGd3{;t71kbVvc3D}mTGufXuNygPib&A>^1_S97HZm+;?E}@WsigH+9|g(BtdP=DFRQ=Cpl|#?(U&(%;AW zBr}#$S&f+Gw$1Du_-`@0D?X!8B=I`Mc`zg}oLJy{(10i%7)T$ulrkg$VSH4{$w^Pn z){n_8)APnoY<}0HTH)Pe+x2PFt;vgT+`8%=Em-0&xL5XFa`cvo<2T&4@Qns9CU(xQ zmvMaOR#ENIU;QW5daz7{d~&djS%tHgz{B9I64(QAq!Sc6SB5o6<{*kP0P!3+^w1OX zOLx3b(K(dp%bqhHxa&W{>Ztp!1)sNp45M9^yCu*U5Vs1Q=MwL&h|*t;8)1J)hDXm% zaOM?ch67*P#hwebO3ZGEkZ~*Hh}o_hI#x(pQ4CaavOwl1qF;#nC><0~1@UoBa{xO- z8KFArC3TUnih0ee_06u-Al$6quI~07jl|R{SJ#h7h>WS-A$if=4cC~FM#{9D&W84W zc*OQOB@44V1~l;{Hd=)zmWdU(fM7fp-W0}jBR@xBe89TKGvbS}&#sfXMsw`5t9(G? zfc47KTRle$Cv;mI%q0j(b#?1Nrh<@xuhM`yM{%aVlHAWlOsI;N~DzCfsSNp zo|BTTe=N^WpE`B+=C$`dc*~5Nwmzmka$o$+xNDCg z=a($V%fEBw>KB^qtKO}3qpS->%b(1wb7TKb*Cn6FFfk7-?-65SQXCw{-5dMj6n02F z;vZQRWGQ0r(a`yc6?P!UAxYf#TO6jY!e4YDha-~l(Juo>$3dEc1k%@EbTQxO^28S7 zCVfKiM2H2GO77Jsd_reG)h1{I8A6C+77Bs~6uwK@L!pkN^wh+`*7X)!qQle?*Xg8dH}h#tkNrv| zmxc_OHD>Ah=k{xb8^^YTX7$hSB4G6X`lvZ*#*sT~t=Je_Yw>Nj9Bc4m&5q6MHL9E{ z!oI5Yqi9a^+zx!O5PT2?>?mMgkdo-}(z)(zp2f!v)4i{;_+&m3$?g*$;Aq}ZH)Elc zeJ9Ajzx@RvZjnD}ZA#8hnlpLa_$m35^`Y$#kB>bezouBW??nCJXMDup{^;J@m|t;2 zWL2MNN*>Zje9ir`-i`6c+I~~dy756_ zZwmW(Dp$Z}r)lZQNOF%taj#T}*^So@oIFNvc9n>0ntP`78(9j#xqH_v{BX-HLc4Xc z3~qtp=EdLt(Z*p7vSl6r6QH3Qq%1TTpuf>FcnI;ViIXvN`SrO>#3Js_wP*@z)7{yR zc}OL!j~vLM0N*Wv786i6=jDjKtcMuQ<2am7$UnQZ5|K??ikt2*wI-k1 zVEOm-xYJk1c zom1xU=B*iR%vUhTZBJHhRWCdtDmG6^?a6p-m@&8 z8W|y(126PFCfNvl*4;iS6ep|h?Xgy0)aK99=je8y5U}%*)I6Ki=0Vku7Ny!W zwAw%h+zfv}HR!2?5d{oIE2`OJjCB+|x8Q}U?9R-E+Um{BRlz*51QbODVe4AA`m|J3 zV<9aBnreKGR_D`wdymQI9WXmg!{ADPgPcCvP%VK5U&*M zX*w#LJwX~@G6s~cWHW6Y*k^eUnOhcBOFVXZ&i*D@`)8kdy+*=ZbGh80*Q{3ZeDy@F zL+?IgMAqouy*lanUGE$0-W`MQ>-t8pr04d2eYW@B@%GA1@4SP31pZ%vHJZ+`CiRFP zfx&ns%sz?-RBiqRYmfW8hWZmTxWBn#yoz11bIKrVX5fzgga0+0aF#!Xx!Gm~&mD}J zo(^-E6x^3C@OFZ09>MuugF}2A@wioyI}os|Jiy9rUB`^Gnjt?&8RUQDlfZEEiOL9H zKzW2aHZ0pF4$B@p?^wA^{A_-YiZT6h93~?bVW9l}Cv)A_j~_}pUFYGC?}CjFW8t`L z?A8C@)wBk0l<^n+x9qQ!lUa%F<&7{;8f9;XUClNoj;<~bRLzkABq|)e2Isr`Vg(Qq z*TsrV=x!GQZegw@6*@O@2d5xAXniF1;3;m}JUW`bWCz==0iRt8o|dZM`~@ly;jcz}{Z^&2kDzxCGndN=L5;4f0X^R6t>CIz?n3xZKH^^S?-SMs@tars-a zT!3$oV$j5JwU47iw(MKbCZ6~D3rco~IMt>S#bm2bDg52+2}nm-hbq2gfE z2;Y3U#a|V37*4uJm`iW6XV7!>vGT}QCa{SeE9k~EhYm<{V*NKC?veh0d+rad2+q#p z#zxVlzH*D0gyjQ|b1Wb8Dg4`5Pgn*F@;?*)UoS6Kt4)o=npg_=9T=FaZp9S5=&~B| zLWP#eSt6rhaDlx*AM1zlFn{^-`QW%y@)GeLmL*9>gWpywda*7c|GOo>;eXdmlDD=z z=ke6T9|(%MHBLi5HSmp6XRyb2?cqkJY9pN0ZnF(`pbI^&uxdTF?RX8P7@lJqPfkxu z&8lqxSdfvajpRD1DCeeBKxHq#XXULMR!v_gzkze3)i)5Y&6mzza)&%5|M*rGnnEOQ zoww$pyVkv`51cl3;LOSW&BLcYJ2qi=X2!mSZ=C()9dpo}8%A6|^-CHb^C0sM(2gC2 z+g>VGxa|kqU(C%yT*Q`h(~6fNrb*{56|etF&{k&fFiZd`97LeMB}$MtT+&>qwNU&I z-WD$?-UfeL_&^T-^FHGBz&--Kkb{WSp*TLbZNMhcfpDaeIM;0~fbKf%ybfK=JhOU~ zW=l6e3Qx*$rSh+gdMihf_nqa}aJl;A@1m2oSzOWlE@967#^Yu5Ks~oViC~j5;fpyN8E^~Y+-X6XbZ2q4aJ{6BVW+!sl6U}pE!H4WVq{}L6G3jYQu4QEn8KjP=$cCN9GMSIk z{Gui>MF>VWD1-7W)&IHa)+Kk!cH-!rOP0+8EW~-#+j&g>0qgniNG=r@%{8~b|47EE z_&p!3Tm!kj4LW==XtSqkC?lWx0jGc||0&&f|~XefO^28*M0FF-k-Pg1fLrt80mH zFk+3YL`>xWs!L0vtO3pSg}_>rBDej0DPs6CZ;VQw+R?bK#;Y zs1t?hTY@&rLErxyJP|>*Hq4gAt1zV@tSfdFQ9SX2$d^0B5;@-mC!z&k!JGkug6GV8 zuGw?u$Wof7e>vA&KnF_BCezJ0B|QhqMYNHC8+^$tKW6Gl6I#>U=|jJlFUSwS6}wI& zN1t(4{3|dok#;>1ULBhT4-A@fuk;cG>Ku0mpxP0bgt9Lc)j5L#CafJG(3DH`?mLuPBG-|Ml1(xwzZ<28uEyHIZy z&m$C{$Zf`9=94lyk-ZGe3-*J36uH-+Je>%TF=C=fy;q(n`5flp-TLs7-NC2DM7$E? zg=)mLW@E_XQ`x19*st*xTrGD1e}2`2Mc*}xi+?2FG@C|imSFIH{v% z)$NuBYKDXb-u?gw+rFv&fl@_+k7c?^@K52sZso{aK7(zFSRt|>?8}%tDuqH}OtzY{ z#Ao$btQBdZF8-xn>SE7=K3r8L=J(4jFyxZ#fKCRy|I5Vwl}WJ8f0vU$sUC15Lx5C7 zhPz-wh$Mxm79#0u1S#GbqN-$X@NqE-#3+5ye+EC6t1sGq=(De345W!B;e6w!ud1cIffY;i5tK+5%_bB4kUlZxZBfO-tT- z&TYrog3FY9BYR~`VH5)J3;WcB%w0r3<!N)7%roYonX6{{5BR4nr@gBAmP3bag>eyvucgC!Bm(bFJJiXuZ$sAxoN%`9lC(FM zg@R11|E8vktmdd1U=1%doz(-cOIJK{FQT&E|5-#Ki0d=?SHf04eV(v^O0>#f3aPo$!L~Ly84d#V9Ct-#G&}_kckS9nb=+YTGrIz?ip2^em%ftjVXnyck;<<7SK)zM~E1XWY>k7tbV|Mffo9!G`#c zaQV@8JMwDx0#nbVY!>XJ1lSm!#y+AbHz1j~ZSD3x`hR3=x8!w-(%R{^7qYb*)!+J} zxcCbheg31Lpu!IysyBXX_|T!)Yx2Q2ahQ-c|B8I?%nNUdcOcGB56o@d8^ghPpqrXU zFdnk8PjqbTEDFk%c8+j|_Z%5xi1kLfbKFhc*4Z*vbp#8QXH|F3AJ3V%mK17r4n->s zTU(({qpjirxsy&t@sxy`;S57S&jl7N_IeH9&Hqa{sE0CZw5@@?{@+1DO}-)$YO?mM zeBTR&+OsD%ai;N%{Id)e>NAJ;>>(cJd#L|S?A=enz2qZk1p$t|8y&*+<9h|~Z@SNt ze{r?i-<8T>k}&=Y^kTa6cHuj3^$Ip73F(K>jn$nT`eAudF&_QQIT`2xDOW}0YR!&9 z2LJI|Qj+B}xME`Kkd|Ehn%pKPg6)V0Ik%4d0>p#k*)L#)Ni(zAwmSP&g>3;8T9||n z3XOJ)V!%6sE0O8}Z-MJ{qWD2C0|FGtc3mlfo2A=Ikhc7UW5wIP9TkL5D5pXD4a$UP zv_JgRQ^C(Hw}A$leg3v=YmFrAe@h;)K4cT_Tx@8HR5b-~3k$;Rg2OXTr6i(!GCvE+IzM1=6mI}q8n z=uJ6IPYwIxJGn;;T{bazRBJbJ8U6h0=bE-ExETPs_@4$u0H1#*)+7Pa|2pT~@+fuo zg_=j?(bAo<8q7gpc9(oW5DeI#Mnt+?XeEI{L@|9JpJFfBd~Odc7VDQX@7G5fgyjo8n$` ztaA=a5xTrzVdb?+8EVKK_Zz={!a3Usjy@p1WFpuv8Xt3Mu?k0_{Y!XK@fSxNkThPEfgbFOfl6b%yhY`0IjVbsBlkb|T!GNU2g*bp+6;{LWN&d;V%1&!={e|pHa zAC5$3CuL`51M=W1l1|F~jJtNzkmostb~J+85GO_;8J70}MX`|vtngND&7Xe#Y*`|V zUq$>)Gj3ZWfBd%9)y)UC?!RM?cy3Sbl=*kwwpMfqfwJhMCJyO0^4ts0&E1`mv46pv zXR;c#?p}Rw>I-+SyKCy;8FS9-+ilbq&6g;9>s!qfp05MAxirMh#hNh>!!X96eY3m5 zj1{8PB1{ntacB97K915ylJzmWFbcmdph*q!MfsEZKbcSSO7j`x%;(0uVb@(t^ZBi) zFlzAhIl&K`bZFC~dH-d*#r|zAuAMtCe@Ag>HnrJZ+hpgyvw7pd`6TQq08Qu z&VIw82fLHY3<6d{@L-H5z>pQ2)6r&BUgJGWIHa&nrL=1z&dUpO7f$TEh7J{TcRm;v zenRM1X9UT7%y+o=q1)6obG0-rjC5ZyT0aiE2I5ewQC%ju)C#P6qZnJB>`{U88yR;P zT2Y=Kktp~cwJ5^w#+W$nl)g7^WqF)eI?rFjtPdY@CdG)-Xjw0@MX+OJgSrHL1jjfc zr(Djtf{djI9uS3D|@4{Fx)8~_OY&L&J!n|$3+}$?6`m`A}Zrld@ z*0!4xwAveoH;S7wH7;}bU9sy%WW)~}72jk8%v|8pJh{Qxi?vQDYaz84&lwy}19!9= z#^()hLvpy5O_^r3eNK=gMfGOs-Q|go{t{WG0-ka>KEAnW+0r}jT>7lsAjT$(jz8g? z2p9cj@JDm?iVyb46OVidx(3Vjh7Ml4j4pfE{P*a(+$RIN!sPLz?;&;b_20-HGosJT zv14XGC)bPdwME*8wJwTm_+tjlLZZsr$9b;k{G83m5^6Hn5eNOXZxI)# ziR0)F^EuHUua7ZfakFW}`2Y@FS3#&V>`kD;;xpPl*C>U7%d=v;k^8afW2hRLVtpoV@}4sI?4x4I7BzP%FYVJ2NpgrTJ`fE zU%g{G0s?TCM*DGgdpA}R+M^6(R_wF=S^+2|q2u%&h&l`N<;Yh8Mg9YjTuG2NZ4u2q z+BXwX1vmH#24)sa$SjzXS2=0p`V zKYz@a*@iZ`aaPl-`tcL}5>J`(1J7e{i04m(gYer>Pg3<{|LrxNevRME zKG$8>$INKeDnov6_MU{F8@9yH6+%A;{|Wsp&zgNFPV9>|EgSl)yZ-hH?P;C6$?<`e zi1Q&EPG#)31muCGAm$r5JiwYlxzF@WURO`ob7Dk%4E$|9!*3qbxm5%{v|5!!FS)yTP9^!c7_Q%| ziFO_9%F>eGH2Fd&k(Lp?vE6I>nCQFPzuLO8+%Lu&J1awe-7ljf7L4n1HGPQ3qPBL7 zKUlIRdTXa++F4C>?j%1hIityEx`+%uOK*{T&18y2rFcDGx)@0Dday}F8jDLmC%aWt z$R;&a?)9hGaTvZdio>9m`ckW9C?4bg$~IKSEL0e!L4W)I-ZljMI>^^90URIDQu&m1 zb}${ej=;zD)8!_^CbVu>MwcM2$J#ywlQD*qm__mh28kQ}H(*A~^P$_)o5BZNeHK6y z(@6tKq0#~LpMGO~3e^!8fL}IDP_&M?n&fKf@1U75rz*j3&NO?j9DL)lFC%=#v>%+& zz}oA!$w5VerRK7*O1?Q$^(#US*X}wek8j+F2;G_Iv@O9&+LpuG_)`)gpaK{lDjum4 z;g(xl!=vCkFGl)DnHK}i4{?nD_HKag=AV|ww{F$W$abg2>$0VI{jhlcG!7Z)hb!bg ze$-0RxMqqq@O<1cd@e!@?Ef8@WA0d_lE0G|!#= z;0%G-!m$Yg&D?!kWqfm}V~v|s(162*158~@v*7X068;>y3}GmD%8Sh(9zfUc0T0&` z2JY9BOU`L|-+aR{ZI1{PZ_|b_tz=)`3uW4&m0e)A$=~A_WLhs%Q#`c73Fd}}$BHAi zr;4IgchL&XHY>)s2Z5Z~a@9lCH(yPpUcEVC8!pF)brN^)+owt?9X%@UWqy1KbJ~mN zbfIILX1kj`tsegLsOg?*b&MkfZ|}MXFA6|7;Fe;9E_Zk&gNQ+{jlwFv$WX|ZWh{U5v@EPv~$a~^>V>o|56?@@b-V0x-@Aba4Zj`$bxQpe8m6ysa zWiJ{pjm`d^z2LFwIC}xgxqC0bx$a^&B{6Nu20P6@t-pSEaE&Uahop=)+U!%sO{jz( z2vRcC4|5D;+&0Hz&lK5n@8S4zDLS+WP7Q73Ap)A!@(b4rvK`RJXhD_Y=_83gj^evR zeZ**Lgr7&!@8MI%2VJP#V}kMD74&-9nBW)FZbT5P6?UfGDvq2gd00iB(fTA}9h}+| zv_2XlpzwWTe2+^rWEJIM<9oq)Pta#=%&pek&cesReIIHm=E(ZJFN*Lep!Yf_mc44_ z`etAQnTNGMKo01!@q40)p7J~yo)AP{O}6?(kxv8s67AaGI22Hek#P3wTIY~RrmRNn ziY$HnYx4Nv`SN`Q=~D=VpQ1Y}$id>#!}u`wa&yYLJKfr!5l=%`}&U}WfCQ+VXC-@BD=5hsZlB)wgSH7DQn@}t_ zQt#Bk&e2Z@oP1uihob-)y_BDG5h45c8tgUFv1zN_Rme}C+QNXmwf zaM5J%1J6Or6HMktFs@62()_Z#_t+pAAQhRG5S2ujA}j0*?d4YmT!9=frw==IDtNmS zF^4%_g*iP+bBc&lC1cw0oL+}HJ;po?-sQRO&U3m7JWM$UiGo|AEMTBcIdsD6 zg4k4w3QP|K9^N@_32K4IBt0A!sGA0E7!qcP4V$X;o~Ysj>ql1CiyvN4w2xsV;o*WKZY?%0vrGy`>$d=xsE>bvP7fF-H7q8|Nj^KNhV=nwE zuNJSao*q*(?W_}E7vCzorb7fUagVg6UJ_I1gZ zXzeIZa)(1LHapy#sM6|2pTY?bvEYV8gaYYG@y1oFwhyodfa;Hnui}eGP63Cfz|c*F zWOTcl6mWj`%asxLJp4-g_Q_(*ADHMCB?GkzMIX+2x@VJSU;n!Lu^3HTz9}BXbz4?D zeqivdSl|N;6mp|{X_NWCxA@0Rp9kPd`{U3 z(4IXjzYCkc?1}pI>y#Yz7Sg)UuAU`ooJf6bW7|N3k3u+tU zjALC8>v9LL3*GCJd0ki+#El+zf3zpL&NX{%xvstG$n|KqT&FlS)qh|C_m7BiT6h1K z+|LrccLTTo+~S8{`1`Va!+ED%7bz7SMR~Dr*|~#;ojc2UJX~jx%yx*2$>TbMjVqvP zgpFUJ`k~x>?g!aDb5yOKD2l;Rp$K+c?vLy6aDTojey%gf{S`EZpAB-G#s-&fpd0fz zG-Nz;tqG1*+oQ$~8T>!4l@1&K;L`8>LtqD|I5lV-?x<^XOrv` zzuoy*RV_GRkN;JfFAq16FUji!N=>!dWIlT?y~e4;oZ|R5AM__}dkXV18Zxk|(MKTX z#TaX}!Z`pNH7TJE)pQbq4bYayYJLJ=5Kb^ylBW1pi!|~DrBaRJg48Hi{VML){}$|S zR={5-gaQ``FI9&%gXj}y6dzR&tK>pL;jvJB;p>E%0)LijI|xnAN1oCPo|$hTa5Xy>fEw|r={=> z#JrDXoeK6Jm?ddOJi?OiMx+KJFxYhqEwm7k5nE6rbjTE%H5g#zvJgND+$J!(*s2C_ zoa}gR#z)N7e$s=x^g03X%N%(94a3$hI}D!X{AQ{FNA^C9Qt)F0^x z+!B3qelp_|G`0BZe#KW@r?sHA+qbIlO%Y=Z?<@0F8p}Pd*Xp$7J&HV6s%0kLv-+GQ zt|tymV_BN!t{cZW1{~K2dmHg;P}OW@I_;L|Q`K9e8t!@AE-C*Fx|blPCdR+}lm z@|bH-8>(!_Qp9T4lIzcLUt(A{ZdaSV$rv$3X_2hqI|t7z_n)++%@f3 z?`QlY+*@cIN!(`w)r=p-v4 z13*ttpD#`Ce4s7fJ{luXeAE-Q;tS9Z=z`Xc-xsV$Rie>81f5aU1fNM25iqVK=ws{n z99MVb(YhK}4EoNlMnULCn_Q*hOuhU3?j}ADo*V2`eXB z4f=i=d$5nAy};~Zt ztqac5Dg}Sj)*aTS21_&}h{YhtL{KYR z0oeLW6zd3@V2WcXZZJz9I}d^JPaHfbkBC|`uLb;R*PZ5W(Ov#IL%a`6vg_nl@%~|P zh{EV3R+M4@@PEc?tZ_w9PW9rHr1#wRSnNa6dyEgz6}*W=qy{5N5IKS|9OMTNxjq?y z@TZF9=OVqhxOo1uW%Km{&4>8Pun&ra8Ehf29N)Pz)U^rK?k<*}iF(Dw zBBj%a@rFpMC2A(80=($_n{rF>f@QbPS0rn`;wED9sGhrfkKfego#Q8+0cgJAxQE^j zyKWt1*jOGD)=+Glu19J2grzxXr%2&;l?zJ9~!x5ln{@T!a| z=_rH(T+8$Ri&(FQHUxs47edI)I9_k1~e*N*DC&R=o z<8L}MY5LG{XFq;x&h92DyBBn9+(wQ4w#SFl*!2ew(w-5%My03yvDo7c38^eW?U*_k zR2}KJt<&B0m1Ef8U8Cf(@I96*u?X)o0>{E}x#$ zXD{v1w8h7*TeR+a`q8a@@>3J$4pL)lFN?T7NF93}*_2|(nX>=c-hZ}SD1gDlwQwjP z;f9whtQk>>))DONLS=)=75?iKFz021vqKAGvy7Y_FSom{Ui^*Ik<|Cw{CPLsAd5`V z{BKcx?(D_+yTXB3+_?6}PLrnHeCoQ16Xu>e^H%=OrfCP4O~o3`kk1VWR1$ zazNja10JdX;yXrG&%I~x5FPNdfBHFbWJ56yK zi^kI*9Q~l8@wRLxXUjEbf}*xoQ2dIL7H?g5^K;0<#EyBM6cb*20q97{Udz zHIV@dW68ss&>hJc3-1SeO*{sF<}ce4Dmjhshg73vzmk}?Hz2(tC&bst~?ob39M>1#X^Ps8LNMrd0*GAH<50&=DbZ#i&LN-%Cz8xl8{2@JVs=^uCXc+C8<$*FVsH z=nQ%R7Y%wXTn10rtdr{Q-plX-xaw*^dP0#w6&@%uz^D#^GuV`q`DH?<9KB)%kyxp+ z@$FM3sAE&@mr-jnugbisQ~sQV?~1FhX*Rt{y9tf<9uzIlHaIO>r6e7%k@U>M28*92 z8f)V`9}iwnAj|?pE@jI`Z~hoU1G+W@3H+20uD@aGbJc&z%Q*vmVrjdNh$VC0q+%hhXEX|nJboFzXUaO!_SIVej(4UpSpV8(mP&#b^V52>*n4g zY9Q^V@GM}R4vQ@d_Ge`7o^b55Q-{s|lV=VbJMGJ<*N?bi&LH!hPtLNgZq@&Qj~KDG z(A^uz9%cu=x3Ya+A8l;m?;~;VyN2(rY@gRh>rnR5J_6}!z-Pm~FBOMf0H}u@`1>5W z!iaN|u02;c>@YUiCwEW&18ZQ{CTV*@h5{fK|t4qv#_0g`#VU?_+gukF9`r7I@l5$nBo_ z9b>5bc8=WVncp!+GoIfFj5#UrI{LAAPWIljuRVohsB1U06spQ#V7SAkkKrQRriqJ^@vP;E!h zcG~H4h;IwH1udhvPXzT+VOx-b9PJj^cai4(PM<^S5oXfkjk!vZzzfZ>bdc=Ih>2qkxxhwY>W;%Ti^W7_AJbik`4mw|R`W%-0p{Gx4?ARw*$3T14 zr!QUf_ub9$l)Roa_w0XA_9$Lk)2I4`MW%oKDp*NgVQSe}zs#TUvqS4}*c?ZBXr-DV zJf{lh%zV_Lb$IV;9$MRa<}}nN`C7QoSY9XaCzZl0zt4fAxzB7~r|c+YttL4^Yah12 zp>u@qjWTp5dixACcjz3^$4h5VH=SX~7g4;f=lR|#(`VVSUvm1)8|v-T89T2NVqRIE zA?B6*Og#J8cjc~An(7nl1b>rfo!ow>6>^U;o#kXb^h2xpxUVASrI*}pXuwiSV=OiL z9>ymnC*d=qeT??#b(Ded4|;z@m}$#N#$hvWQ-8~CZpCRz91<3=+d$r;pXp9NLwz&E z9BLC;wx5PpKc*PkEQ?yCR8YOx^!U57JYaet4<^|10Q7j}0qF8q=6%)`v_l>=s7QR4 zN>q{OhCLYA<8zock_W2KA?VYR2h^uW9uPjVWY2bKru(?{~)T1y^)FKG6oT)mOJKg=!rv=@#BtT0c-LQ33?Uj4Lua$9 zAwcI8{+9XsK{evUFx?&t_49C`)b0m-`LY;8GhUm{Z}L`94io;2XhGXXd`?k+;LC1& zPSvK~oR;9r57Dkh;4i1oA?R}`^;wJitQA#I*Y2})82B=m&neaCkZycV)#5(wmUuVO z*)brUK8O0A7p=?hlkKN(^8QhM4(-V2RJncD2&>`rIn4L0sORa^Gj`BP=alMmSbILF zoIbfF(MjtV7^3=QzI;VYCT^qkr1h!gS|3_lKBv$p@nv^)PGN<)rLI92(z9s2FOuFTST8}F_O{;Vthef?j?+&E8$&z&%KZ?A2J4UMA-+-l zJZJX<{%a&!5IxOZ<0E=Z@SmawGI*EbzvgB88SnHn)OSKm;c=DiC&TIo@^wH{ZsD09 zx!tU8_EY`PKJkYup2ZwrG~QEx%ll_5@1J>CCyu|MEKhD9&)-jmtfBa{1? z2mT9Vxq$Y(-|8s2P=)+UO7EDA@m6EGfWPJawo}Q4s-Zj&^kXh&|8ys79p6#zJv=RU^zx z4~=btc&uAl0R1YL^}-TS%?pd z4ZYVd_i(-4H01IsIeUiXvOo9>;T2kcOMEr3bO3G}sFK3|JoSD$ulW6;`$EMKU-0{x zFX;WGi%eI15ob|=^;G?ko}u~~>f5g7&*l48{b2soIKtY9UXT{J%}< zU3z2rp6@ds;ssMP5A{hMor=gel?04;ZqbGrAL-ZlI_tH*@D0eyB^kd>8|E)k-xc^K zPQ2oGnoQT%VW!f3?GeeZ!4KMA_+WD?ETHJ_`$d7!X6IJ$i6~K9G#19lBkBizw*5nX zZul&*SgaG&)(__>I)nDrRR2qihwq4U3(3~P?l*XjaKlG}S8PhtooieFVLTzm%KKkL z`B#p?QhYIzXvVav;6%( z{QWWjiZxoOQbTR8gYPiK8|#U z;jlf9*87({ruRQD=E^(h{o_y%kMMkAe7C6gd+hI?S6(6F9P8+q9%)f+hl3|R8a^*q zv_H9bQPU5=BPONI6vSZ40Qiajh+;)G3~9+mTTuR`d{_*le}LQsKsEl25%-_e=AEV? zoDQx~^LV@XUYlT4M2-XY1JM2|hLSvCJtFi+f`3FU(%Y9#ow0Pu^yy3WvyGdJG0ykt>U;V<{Qoes_=c7K5SR`dSORuSHY8L( zpdxMX74*-fbXJ-U24t{pVvfCzeq(#js1kW7i5F9Y;Dl70{DT<={itf;0x7bo>k;bBx_`=^rlHQMWI#I=j|w`kI&#r{RT7Vpbx+B9e1Vtw(V$M!GowP@eIMXmbu zY1OM$zkaPs7PsuzqiwI2{ra}ThxOCnJ&f5sc+dVUfQN=S+KOrq8-W&r6sr+h(?DTp~m16$EKEKq zA>{;N{$zTfbQ2`YC8r}!B062yewZ=igv>C+N2gDiXY^Yiee}@<+RWflQD2iEW2}R< z)wifIhyak45y6L?1`#w?9+6+W3iL zPQcA@DY{U;AnGkH1oo2^ET0lxT01qr`=S*e=eBLqA^H>lWAk&nY`PDa*w0=&v_V?% zQ$3mIvI=D79L#YvqUL#SJhIAm4;Dt55i~1NnVtYRO?``5b#m@0TOJdMxr0a175j`y zD|8Ih)Bw<=}SMj*I2-xpVa&f-SUh+Q_Gmefi}vqtYoklh#lG?bl~4#u_#N z@+8ye0i1H^ORJCC>+mGG@Q#U+`cVZ9ob?3^1)d=z5&i`+iO;&u@2WEPRPB%xt*~a@ z+88m&CZ{Y&&ql;VHqoA*lAc9qH7+=7LY>4vtzO+!?SYcSijA+@yUVy=Y}0>v^V1)x zYE@pB&V6+;K|W|eHO@037ub(i-jJRRwR6JqKnr}69-WFIB zLZ(^dL}k-HCR^_J%?3;yLVSTbR~s1SVOwy`6Z z@c!>0A2xQ9et^7PXij=(ah=v0I%|91#?qz8eJ|Wmhagq!w5_9I7vbl^MfCAAKA;{6 zUl-t#{s`X5zHI z!gP;P1n(mHDG5JOz-~R~Z^2V|jxQnvNKXKOU2~dpK-m8Vt|HD-fEceTg<&(?2`k$mNDb+1-^+SChPvv}X$CB2qDx_60n^7U)UCm-*>>ZL#G2QXJPpnpIrTJ;@0 zdBKi$7Ej56o*)qThF$Jfg7tBUl>+$cT-y!7AOSNqI~#nEjo9~STdUFnG#$F7GEi|* z?Sj8l2AfRCYJP2_Hx6t%`sL9vb8_p~s$0+O)UC3nU;Ic93wx$Q1+n@4i_hHGV4XZR zzS;bn^nMMSv>q}LuqrKg;XGZ0(8}xmFCng_2{h#t-vWP1X%MGql>gn*ILMtb`dtS3 zZ6lFM0)%@yDOtH9O)JJ(UIY^&6AJnj}374Il0>mbIFby&3W6F$cV-3ZV_vY{Q|Y78VLERR#R>gQ-Ws@XxLj6PR4S8u8pW~ zvlc$@SnPR3Wm^$Be2CksyAw}T#i0UccbxrDI4chOv?vZS^_76tW`B)Kh1!8sodC$$ z$*Q(=4$#6WyEHA$s50c)(O<}SMTa-u6SafBoYlQXKRraQ>%O{`me_pNsGO@sxAUh( zANl6FO!;JX%hql52JKqP??tU<*MM})`S8*|%rX8#NUzzx2<5*&3~#rUt|(LafYz2Eu)fL|=dTUqBx-p$l!~zBpV~w0lEz61U6lsV9rM9s}c3psi>e)R6#$xuil_* zG52*N#;E#gZjEtjs4tIZ;lzS8Eh?xURnUg}!dfG4+nDA55jpP3@N7~{sT<3LUJe-y z$%=psR|E!yk$EHi3?!JKt{{bPa?m4U8Pkzol8qw0q=i?F6W<|N_geYd_vhvD(o+NO z%G9f0*!|+v4Jo${yMFFs|BtQZ6#3clKja%Cq3Dc=YMNo*^Gd_wqUYDlZ;-Qn$*iq7 zUv`y-89!ma1mI5~?UFVOYb3OgNd3q03zfuEg2T_r!nlR~^lfnIIittl{r(b?G5rk;xz6JDLQm)Ew;W{N7VjI zj{5Gouf#24+kpc{97=V?;WeOSOzHOl!Wvh1L&Qltw?11Gu*k9>9J%1A{X_yTd9Ex? znoXBXrd@~IVpDLWAP!QYv33a)R{u_3Y5sR7q_13-t*MvpCHz1 z{J8a0&+ezU{U8$M@f&6?-W648&%#-AFYTK%d%>Pckq<4JHAfTQ$&U)|Sam=&{I2;0 zan-)tZrdk6xNyh5^8;!P`u&mBs~`DeV6FZa_QAS+9@<52x}X_4_w6I$ke@O<4KjT5RjsM)1wy`zAJKVb%j z_cy*c@@ja+PyJ>;m~TifU+eqM$TD=SMHVpb3gc=*2glU9YvJLpf&&7_1P+L(BIFd% z)TZ!f8hsvY98xw58Wa^|Vb)`Cqi!BY#lcmYEG+?QpF<`GuK1gonG4!=9yz#sUWeQT zjVm@@klP`z`{0qC+bzhfdd1)9(>wMSwHu@*_3HF-r(Q{E4Qh+t9X~JUJL2yZzTJq; z{|v z%K;PLxz4H`(-{V$F=jFR#W(lho2%lRaO^4Y{>EQ?gZb`svC~4-C-zPVy%|4?yNhgD zUei{0V-lS|Dwgl|k$WD(89X^N?g4>3fMb{D>l@&FaRdGM&8C)SZ|ueotOp|-y3nu5 zC+X>WAH8pSI_G7rzjGDoMfmyOhHlZ?5l#(BuN8~?zgP0}pO1piu<68<1AcD-j(^B? z8liy3Xy>BKG;T!SV%)e!YFq-i3@Fj$JvW|RX#~Q(--wUpakVSD(ZEKSQpE5F+xaW4 z5k?LWuO1Uxt|15kUxNY|-tfUYAI`c4?|jz8)o*V;_0+Z6k(+b74p}%NFYm+&x|W+O zvU!Jx9y-&$-TCG%r%W7rxc(urC$)I zHiSRt-w@{mh9b=WyQLcoh*hEY(VbbkL=DhYkexRJ?Z5*>}@c#m%i`Z4O z;PL{{uw((5zkB+Ek_F2bu&E9w?i>LNu-9?vK(s_m)O_8}hL?d&65 z35N&TXII;2R@SLsJJ1WqYU089lksLbDU&qLQvv&95>?eOmI* zJYuUdUm}Sm^;4F`+2W(k)uegDWD?Rag*2m$-Pil3_UYHV|J3@c^Tj`FhZ6CRd^z*> z+h>-}SORz0duZoj!y*R+G|B8SJc|rY&&oeRa^hok#~)}^U;+n zE%6BhZE+*oCQX_!`Q?V;rpD_`upM{_9q}sjPVC9WYy!HG`OXl)h*d(fvcG`*EHjnR z1DMb9^^|r}u6vE^4U=bZSJU_q*0FO^JRF0N2WKBYKUP2caM9@3P4|=DA^Hc8?LBl4 zu*Tehg|P0B#~rZ;8oNb%NI@Z7BOBX2#Pke}?GcIn$BY?}G_2Kt?r~}9aozjVQGF)2X*+9R>C&a81822uGr3Pe zMY(S4)~i>yZt?MCS?6wDBO;=)cuH|r^Plt&;N2Q}fw8WAWnP-l@6a-W_0V zZ;i&5Og?Gx#1@m54(?=_jPm)3)Ug?g0FEhl4S}gklF|lX>aww4cG&2y4?Mkl!$VaE zAGMXob?lZsqQ`*1;k|4FlQ)d$-?Lr2hnJRZdE>t0E1wMx+#KXj{Mxn*&73+oE-?dm z>Jg~1{xe*f`CHzAQnQ!2M*_PKv5Ue?QDmP$S?ycGK=mAv=;Cr)e@Y&xK z<2R+_Nr<3x$E;3?PMnuE`>xp4S$huPT#V24KS<=;sX47m#ta=l%Jf95mM5AuU0RgU zE#~8wcAjy5PTC&3*07{oL}2ckse`hh78YmLn_8RJfvW@g*%VMiD+fMeiOoR8SUZDS z+OyB+T53x*@5M1X_2Ehk@D~~=mw%Cvec0j>X=NMNu7wM*!@)FzbZq#Lw6O$`SI!M z?27I>uq#?fNmn$P8vb-S|JlZG_v6RjHE?Ga_kJTsvo2l5_x`PUFZo}d_-%-z|FB)Y z$*@7gGcvOEbJX(Zo@2kw$exj}UqAMi{x7!8x1K&?$fPl257Av2skV^=6D^x}zq)WB{a&U)%C`%9L@VPpG@&Qxo~BQQzZEqH6Nlr&-ki$MruFy>&`F9DD-LGhRf@?IP2 zQm~xIS-pG}(?$%*st$1zJEd^g6Wd+?5_u^lt$*s!G1y>e`Tg*xC)Yju6eTCWBNgd$ zWBQJnI@t33H}Ac&bnnVXx!MK$K{Xl(_{^BI2u>H8mz&@-nZ+UMrfAREj95_ZXzNS^ z_0v1Z7wnPqd<{jmr=G~O3C5>Z$*6@>~6P;`{9>(nLR9IB8Nr!IX&wWJ}dgKUIhlO{9?5L6y&Z-ZuJGEAAOAc(crJ=k!NWgFOSbza)b=vr_+G)qGvOT@TvxvAmGUaDR}n; z$Ksq5EnKg{J3zjbo9~TA!2=GE(fSi)uD*fuu)kp!xkq0}htm`e z11u`dR{=u+RyhR>UO0Qf;3XEk01~9($_a=g%q20z6-Q*MLtVIsd#*?|^|GvlJGo!= zAKtXs^&j+KN$d*Cn^(vO^w9Nk`clJq)4GN@ik)*Dt^m+xqo|7>5-w4oK^w2SFc#9F zodFT}EDi;N1R0G$A1g|ninG6_Ku6OnA{<9Al_OVzvBoc^Rk#Y$5@r5gU5Hoy2C6sg z?Fv#Hug+<=1({aqKV14n|ABN`P+D36WC9QUcj3UM{M&82kfOAcYc0S-5$Kn>4 z|3IbhrSkA@YBLd+fV`y2ItiL})&!K7HxABiD|f{>x1D^JAw? z96f2>*I(#=oxZ65W`R(q^JnEwC#i{p+jigf)I%SB@}gz%$kg~g>Fv5dy=CjWpS)_x z&P;)9!b+NH1ZdS7V>O&?ruOIyiDV!pS`6nrj;^75cC6RX^OR`ld7S(wK;vuC{HjL1 znCcjaD=w`)Mg)$=n(%%C(dIoc;D#u!sS3scEkeT823})xa!2wUrp8CUcIMA2%kvA+ zkNaAtPI}|zaZ{#^eVIJ1PcyB1@0scMcMIK6IJQ%MQ~#y~aid3Ne?KZCX}MW`h#`nIqNa}K5X(+q*E1~I16 zeo5=!c|4{d>AZf1gkLz%PYz#NShVQ&qB%>*;c43kY`Fiiz#!s#;`c9!kN($jGH=8B z;y+3se6W@$pc_WyOC5eOBf*nEf;j2lj@H;vZ+Ja?Y=8RyTT&p0zD zFRyUU?A$_{q}S*tNH?_b6cR>y=*RW%Lw9W7zC&Ma-@e_BhCsE+Xa`0AD}5S=4~u*E^x-Y|Xw$KrlBqaX zz%_jV+n{e`eH(J$;}D=_jF)DSDrT=qa%dn)9xNG3)cST^Jc@?H?VQPW~x zJLCU>dhrn)|9UTZPfsn=We{Mb8>Q%O7^f0f2nK^PHn$Tj_7fwbu~^^*O(Xb zx`t;sj10VhI#TUo*zu1n3nDwQ{OE8QOJk4I2I1Yh$uUk9*Cu?wM_4S9WN%e&#bTkjwhzfhmKB>>b!I zgBv6eZK-J#S#M#G`+?FT(oj4b_To#+w{^U1a>60OZ_j~u=9u=e_LhyXAZo^aDWO%l z4#KfFGrqOE4WPJaliT)L>6iUe z1`aTvc*_Dik=3gUEnE-B$0K|$a}4!lMQg(NPK{%4*}6sjxh6Cqv!{vy%ksjQ94m2h ziUTv1F?zqD`a$v$=}$h?59ukS(+11Yqo#G&%gO6}n>sIllIcg&kCtulP3FgKP)yN| zfhJU~U{H?k?f}C5xI@Y3Hibl4&@kcGeb|o6gnD;Cenc{gZO`S$b6x}vVOgVys2Lmus8!vKeEkkSTGV9h>?iBkx?DH0~)n& z6x|50xa$C8226nP>tm#{`x;lf1AN>2M*9j_BqW{V%^g`d+YCT9Zw4Tw%N0H4&#T5cU{zZr|o)r1QV0nhda+Oappbw>~$-28bVf%+j!=g0*aA32jp{ene7# zE36d&|9I2SY`x`oz~5GzcnkQGeI+3WHp*&bzL}U9jJYc|fG_!SyLR9ug_#B$8YG|s(tC`=VZop z2+f$6p4bsE8|^Uw?Xg7g;d1byBY^)*=RbliU2r+P5& zO`ow2)=;$LFn)Rk+RuLk`6xj40CG8iRPcovkh?j>F_2qDyEw#y_OO(0Z=}Bk>^JdG z8)A89+f)0jZZ`n*tA)!5X*wg0TdwCfXS|>^|DArrqUs z42MLtLu*F@7daf4!%)FT!)n4Vhq3w6#NuX@o8F55ni0$1C!c%=g12At!2T~z&**hu zWV?vlrw;12scV}^j`*E-=32VhY~4uU(7EYxox+FAON;G<_CvHu`WCWGyNF&CYxk5J z^kXPOmho|=zJ+FqG~xPm9Je+Q^YU1i)E2TCwkpX1-Y1q>r+Qnpcjh_~V+BO6Kt__N z$rCrs8#uXB#}+->+d9X@4NSOiSl;|rO&@RM*R4(Tpm^BFJgE;OuV{axKH6*c0FU_y zw~z4}(R9AS`8Ih44y;o<`!|nk-Dkkx`dRL``g^nWwlUcKM4Vfl-?Qdy6Y>p_t3T16 z1_3d#EF2Eynp5t{7!GzihUb!=*cz2g-+yz-tbDTdKK%=}>H4KL`qke1y06Yzwu;jb zI~wRr*iCusqYW*9F*0-fXMJ=y^wBdhoWD$MrX5ETc%CECR}P16p*8k|i_TE!;BU(F zf1-oOY+m?UBQ3&B!|OxI*RyY%H94;kI(Rc_^5a=T9(+M>pfqYJ*7+ZUs zQ45|_OV<#);gJzSvo5`?-Rw1(tbcZHe*VlcrF!C3{}!jB;-92$_J}o;W)vKR2}PhaR4zkT2ApEK{l%4M7T!+4a_ki(xN_&AdLANO3RG%)xduT!=V z27N%lP@{RTQ@%Ck-pu&a{EXF4Sy=2N(?0r6zkKRE{9z4!XLMQnr3I7b6`Mb9ANo~` z7N6S9@qP54zdo(klHS#`&{MikMMShF)TZb`tZZ5xm){TpyFZ& z%!52ftYTS$Qf3WL3J&I0W9;yWtziNBY5%Z8I~yjm_e@8z9?)5LJp3?8p{>rI1??F0 zcR$0H`(w36pC`)`2E0bI607w_;f8KhEXaE_D%Pe48kHlUhtTTs<$3o@J71o6-=e8` zG%P5%x%*JDl%>p4Zd#rv;re0tgnLo{Td!^(dc0K&axt;z z>g~@zyI|>N$^R=LLuO&l!~N@G?)6{m?!D&fTn*!)l|j{TT?H4}ChT6qxDzc@>f8VR z4w~WLzdL?>>fE_gp`+FxGOauNy8Zj_XAd4S2RzHywl4#Q_S`1fQ-&DR_x~V69P7Cf z%3EXzx#FG(D+yxd5=3_NaN_PIK}rVEt9o#&h`fQjk6yTRK)=F;aNgMQ6Q+*J7!ySP zp_{(@Eu<}>2X;BWc;RVFVBgG~v7@GUX$wBchmA^*vekh0#Zz_|7AgiG@R_*PZHM6q z^p}vg5i>bqnBv&b2BR}>?ch!aqTX!nAS_eB`(9Fn)F#b}>kv6QW@t|5+(^@tQ8Z^0IoDJLCx$MXR zKEBwqcatnPu<@z~Hc#Z`Pe{RYm#knA*rm9JaB+L2K~Na zBeC`C7#*9`>vL+^`S6A(=PsQ`$)qjyacNy5ZT+Gxa~@f_V9e6|sj$Z2{nQ3iHvQ1@ z3C6!c&<{eh5)i#RAfSCfbO6jn_w)JbHa`DzZQ1eAU$~VUZuJu;v95hcAa!^PGxP=0 zm`cnfl03MYyiYW$U)xC+Ro<3eUY>nh<^1@_&attbBjYWfSPq+i9XV&t$WgQNvm#?- zBO~MEMLWdUnrsI>y~pb}(bHqR?nSzeFt_YXwjWYz{3v>mFYGWOg3=v${p!B1xTh)`>KKgLmlOG=+k(N4QczW7!62I}) zS2yl`aL0}Z$*>6%hP^X#+&G|zebbQnhoGl#|%-hb7ncsCz?~l(C%aIQV(SQ4V*z-eU`Y*^&%5FV-#GrBG50~AR(V^op`}1d= z+GqL)dw*%&@>tVgvbIzEA=8)588kW{d^H^F7(T$^mNtq87RIPY+9pUEZW_cl448B%oIEd`v{hcKHGboX&~(q zx3?N+S}1nGVat>lXXd`LK|lTN9SvWy1h(T_y|(`Cx37^^i(n~%aRC_RG01iAG7OIO zD#Oj9SiA(6!Xg$jT$Vu}TVD9Z*715N9Zy$pet*aE<*yOnkz0WV_Aksa-OEDZ6AULS z@p~r42dBrr&a1k(Bh8r-E+eip@;lnSP%P(Ua)TgW~k=EMENXzD9 zPsdp3N#1B9d^qBi6lh5C8_vG~zSDJ^32Q<~KN=ZWwZ#Nx9C(f~FFPSS^F$p>Z;d}?rvuvENp_G>d zOwz_W%{fEXpV{j2akm`pVX2wJ#M4-`1x-cmHBu=!Zq65w<{mSLN#sMuI789vp>~W^ zNP~$YeOT0uENhPbFXS+2F4AB$XXi#tlIDDVuu0T)2B!mj#;t>vP1LrF4#JK@zz%Cb z!Fx|h*xR}Nm&r6ZM~KNZ3s4u`c|O9YovbgB#uBX3fcK_%@=PJ>PNwPaN;9D#ou_H9 zJd?T8n9mq#v^jlyaO_;(^Mtb{)Ss{|6L7;~Q^37P!rj!0lZGTrGEFV=l4%x@dC=se z#O61ou~3x;a4%@)nL@xM^Eqs!(dNL<7GM&&a20G*#k0b;QPi>Yxx0XLk$mUxE-wIe za!lCc%eFOtjTA$Nq_G63G{8AK+%ttoKAaatK4csTg#sWD6M^Dm>q{vXUFyE8n9a+u8i~iY7O-wz;A`%(EKWD*fn9P%z$x<|J5azW+Pcx6gzbZ%+X#Wnm_k9f z&Z?!j=vHB|A%C8Rp9{EETg_oGQgPYFZ74)Lmube4cuCt1?lw1F_v3aJMm{VO2WPJZ z*X@ewoZK0`*Y?it{)5watH=*NFj3|h%w@c!8RW*3kQ?_2Ss-Q01ZSj_1y;8oPR{3B zEWRjHv==f_dj(@|aC%$Lzi|Cwcl$$zrzTn=>ORz4B>V~fEWAW(BDUr zO#MMYqu}IVPNQHqjlg|ut(mvs!Msdu!H-s6D~(kTD|tX~&S3MT((wFz^^GWXcZ_ec z?8Q{DQI?8Xu zRsMisa1Z`%50$(4HU>Dk$AO%$Yzn6)$xTKz;W#xh>Q0Oq1x{kjNXFU(PKE>%`HB9> z(gS7kb_&0b%M*B+A<8SJJB`_4q|xTj6P}ox-jv1?rPA>FeMwuybGoU2ggft9?q%p(C zH{)p_y9w0KRJ&{(;akbWw##8|A(mC(JPk83k26o~INQq85DGgY_{o7-_~Bcs zpu^QVM@C{lm|SJyid~q^J)n2WxV_guq=WvCe!)y0n?qJsrY-CTPk#^M4D6%&y1?MN z)1T^2Mh~wjn0uF>Z^Kg4(X&7WxOB$;g}uO~1^ny@Tm<-ry4-cByi4+N#9;L9g-ftF zbHW^b!!apv)nP#Hb(XgTdYRs(@4^^yqW75<>tYu70=UnZCIZ}deS9zeruQRN>xzAS z8pd%@sn!N(&3_L#{V}hAUcw|-cD>w+xQt6-bpuBe&?tHmwrMptoW<6B?^Boo42<~V z6N9m$1-F(KYQ?>|O8=++F8%iwr6VgFN3l0vtKaH#TPE$Tuht(Yv-QVmW*QdbTGH(6 zG4zfz`hh}rnbQ)!Q>=A>F+{sd(o%qDthtDe!yv5KIB48>Ez!D-m%vyq?GLBFShV5v z2NIqq01-H@^D}YHF6>V*)51J~k6MNKtU8`hoM8u$Nw|67%lere`dg!L7ER$Bl4?`eURSjf|WRvq-1Uz$T0?2(pd?c`=Vzu3aaLIdn=b*zP)+RPfIpIE z(GIOyV@@C~+Tqu@nIKg(X9p|TNLcO!M|-Ys^39r_i(PH2`B|`-4YaR@T`b^@$C*EY zS~%Y@*il}_W7i-wZcs4_T1!1IJI!$J;{^Q${R-1hTd-935>Ar~f`PgI)#G;izk*X#-r2X0q@zs)l9BuM(feP2c{e@M5cl>=ufNObzZ(423H0|hY$=5b z*Py@PFR+KFEj7muZYi+Q(Jc1Gi``Y6l_;(Zv;pOR1 zTX}s4@bYjTF0XR0A_W)oB0bkEp|_{P$Q}iImlw}?x$ZI#G}EtMgYYrRy&i*YmfVpH z+IwrrrC)IAhnmC+HZR(1`P#tNfz9H)KC#G-g=`N4j~m-R^rs%DIAPTT&0Q{v0VwQ_ zmo?U!?*6v{Hm%=WZ>ZIaakFIgDjX%=aPIFT$DF344d?Xk_Y~$X*3D_L{(PHd>=}%#&_w* z!wS`E!4{vy#1?KHIhHi&+#dX4?CG>FTDYq=ypc7ZjAwFPy`4 z{X$L1d3aR|JO3aYBd_cpXzOSBa9PKu_bxHV4mi7YNyzG_o?0EUWGnb19A^n$!dX+i z&l==vL1G_*;i1G}V^8yPNN*@hxiE)i9>xP+9TqT1;;bv0QaFRqM2FRJd2WW;%tE)K zK{c$l2cM&stc@17ifOe{0{Mc>p*nS>+JTl7+5&m~;N0Wq=L!*u6z{mims*yS-z z-$hDK&LJJ=o+72Vo{DFi^#UH~Fqk-tE8GPM$5q_62`h@?Z19~S6PENEu_{Ou34!ON!*zzfxcf9(v zrPt7ry)#qx@_sK}pJCnvf0^wuL+fjdON9V3T9+707}Y48;lLzLkVp()`M?5ee%OHu zT>v-`3rxK{DAbWZ-x=%iq$|c4?o(y?@H7~1*I)}25s7-_ojJA1JzrMoTF<6jWx=-| zmmlfJ_IFMRB^^W4Blmyt#k-N2q57H7^iJAy;4t(?p*{p7m>j!(~Gr-)~|Ka~XU7f%VT(MjvHt|Kn(f z9nhDw2CsYcB|-`s#PE>QzEbY+QF&tzTwx{PIhKUv_L7c}+`@!+|k3aMFYcljz^aUVZTkod4EvM*nPlTKXg|7m2n6e$C8~-(iN$o|iqGAtLOi zYPfX^-~gg8^PD7L>VvKGFbc&u1wJXm_~1{iukmVt3kKI7Ti1ovtY0}*va5HWH;YeU zmtd#-DKqAhh&j`y7Mkt#L1+IQl{IqopJ&o=8Y~YSyP$N#|jM$4nrd zM~xaX!qlhX_S2U#@$b@UdUwO^3(CqC@Oa^T9&6MIEvhNQ(hB4BHbx5(n?#KkV!-DJ z3Kacy5Ii2c*EBKG2;{^UBMrAu2w$?~=N6X<-^ z{Gtn}+r{YRMZaR)MBq&XZ_-=@ib}wZBREVPjm(c1=RJ$PvFKksIU7fadtwvj<)wh1 zThm4`8b6bU#M>|g4%1p#v71+{HuJ4WVvRj1yoH6o3bKhUeB`-3B$;T$hlDnTLu9`d z>+gCTI~s;PeeasZexwa;qMz4w{hGdK4>uoX69bVlTfh3J<>5^yo(n!3^TLVsn=F^n zD~}a>gWX)icfon=F2=3Bbu~Z2s)Hb5B8+L98~d4HL;(+RV*X^|z z-Hed$KKm2?r2m>SdRC5=lD_)K)SNSWLOwM;}biLUcPCmNm7%~62*OFbbW{!E6 zxp>Xw;g)C55c8LMKQvkVW3VUs*&GHtIX+%&kNt9Yz|Cz-!6~8z7(8T5J{=l-x;c*W zA3OHEJCfDLcaMEdytnY=8?*usBU12+;1qF8D!9U|*u>Tu%gusy%whU#`c?gR-A=M_ z4r&0Z|8&FJ=O2FL#fG(X(aW1QJxj-7dh|4z28Zz$OptJJf&S;W-CjF&>b06KFP}R3 z3dalQd6))(CnR3WfLE}>%d77*nmGsBLySAVhf3M8od0A61K0M!*!6x{UCrNhXYht# zTILAk@2}t$7s8$ui9M%2cQ+e)lD*|!Wk!3aGPqD2S>Gtx`CM{fnbZi>@XI|;!EiwCZ~izksuk?|qsCKl|b z(tkfyWbf10K8JU|ekUwb@W2*-I;{RDQh%Y|L{F|bC}Q(w#H&MWUveke9>KDdNR#Y6WtSGO1 z>7~l@ips+k735Dh!Dl4RTV0NErx@2+t2UMkACap_&rT?i8z z!Jt-y743rMg;UQ8oh)&bsNFk0n^coRc|Dl`l(fHNQ8@yV1I9G z%BhdUp7mchb#vd*h4-LsmExQwob`xxi!R&?As^<7vAn@W!bs7$5qe5vw(Bzd^Yh`F zU`J3hp$GFmDV;08JT?J1B0KO+5J1RDh&;Q`@wen%=)_MZuKj#Cc_-iIq;7A2YCj!!`4%WtqeEPn_Cg z?4mdn`6ALw!a31r+zW|IL zIkI`r-iD`m-w#;l>b2||2Ad&Th6`4&p2gL~(Vuv(B++2ePta&jrUKEy)~?-kT)Ydw z*a$r(9xySCo74iBRTFWN9&$rqA*7w~WtFX;Z$E;9{M}qY&ji&H3M%fpD5e^wG z--+j~^et9@er6pzEuWh=i09+=NK-Aw$Pr|}d`|9m-j8ujrXI!hOGtl4J@4w|{;;r) zX5sbY&;3vE`tTZhd5x=G>r_0M(aGZMA)(G}O@5yy0-R;=Gls{f1E3n*d!4D@ipD8Mori2&HR`Co-m z`c<1ys-Mv->5lw5DDt+E75W`ylRg*95IvV{)oDW&=*jtfXQL3%v89&l+QH*>?2l^| zj8o5GB!?C({VHHp=Y}0r-kuiVj=^J|0z%%3<+;!@ci8OB^qVmw0pptLce!=tmbVYlTE09k+%oPPs{-=W^)zvq`kF; zl4FIMz>xP|3zTl9Z?%J2_}NI@z}wRyHk^}q><>gYva_puSnlWl9Q_^2>V%^(on1r^PpHnmEpqJUDywkdeV1J9g?!?j{kRVIDHYOlyxnci^+@l;z+k zgwKB_ee~UW!&~eAnbEir^-D>{W@q!wy4 z6hSBB#+y#Ux!8Y8rzrRNIQG!Moju$Ha@azpBP2y9;YuqQbmB(T&6+3iJ$7PZ5zJ4U z6I23utQ{l7YfdiHAGxm$>9=-pgZ|Mk?^t@L+*@jnjeGa;ZJ%VObR5}L|J>Ha(lsHv zhowh+%hs)1wjzD6Og#)g5A>{cW&ZSab|03wed?xNzv%ydvOd?IZnct%2PZ8wnTp0f zHfT||jtlraZ>rwY^sP9HIRf;8TqQ3%AHwqiIAr2C6F-ZY+okj8{rPj}uX>E75%?~G z_L9%(N6t-nzEAIED&grVY(zx*ADmAhpJ3e7S#d@>m)oenY}%jf;T9&X8#OU(#l%{M zB}mF}o`&p&mvf%RN2Wo$T&3^iXV{`$!rO!LQzg1ggW5Q_k(pyxJvJ zlZ6`gCXYPL`Cd|&fd(2T{ve@QH)v;g?@1mhK=;WHJtLiFlX6nXdWY#Jum8hk$zgP% z?=(Dvg}v7)%rdzwT%mt$%0qi?&Mh7k%tGQB{N~lE3MDLuxoJQ!hqI4F4pIo`fy>* z>s{%PsD+&q{iV{q;9lCQiubTTjNtEK4>m`8SDtI#iCdxBJ1J{$_qXN?NQ-Z|T~D(l zt|{mQ#g;oI$`tko7H;o#T;w@lTSHQKJ-2Ao%-{u)#@fV{W{x(OOy#+?;c2)PdJ8Dt zKwcw1>o6lN?Axkj+Au3m3vDO2z2SACX>G_*)QJ2C6IOLiVCe_%0OB##Oc({oYbM5} z^4f&;`egOojJh1At}S?;qpq!57*17`>3xvr{p#8mYs(wewI5cQx2o$Vn8UxWuKgjy zEQQ5MsP$pB)X z#P;Pms02Ou2P$o0d00+ZMOjX6-n^W$xnU)fNLt3ou^By(5<;;APT(p~?YUrr3WP8W980uP7~qxRc!qqfK)fA< zXC=5R((;fZPK$wIZybgeCE84c3WQXIBIM$kvX57aFjtyBDu?+0@cuTGQU(_-g~+oQ z<#Clp-9CbWB6$)R`@OQfo$-_nr0J>=L57rXn=zu zA(~;<)g02NC06)bW1Xul=JTPDQDN|n+5tXlIzdK7VAUiF^X9JDKh+&T^aNm%vEot9 zHhA<+!f2;AQ0$AdQ~PTJ;KL;uR(u)ifB0423Kk4(|*jp?e;MlX}MhschI0 z-iA5q1oX3$w8=Pqd#W}~o370OiC)#7fz8Zn?Op8=?I)Z&@&L|Qe*$)?&l0BHOH5eR zdI(nizhfuMquN?x*1prOYENn}Ykz8gXn$!>Yp-daYoBU6!1}XrzHqK~5Y_mZc0fC- z9nubKN3=RH)N!p^I|kX9uU*j|)V|Wb)J~wve%5}`?m-_^2*#U-YAM#Xqmt&M(m1!bV>VF+lz$)PRHp@`jEb)AKa4-AOp2p?FX!+rI1vT z2HkxI$s~iwU^0XZCBw*YGJ!j1{52SQuO;`8 zd&zy|ezFcn1mTQd@*vqr9wHBuP2>^sC=AdZBaf3UFh+ZVJW00Uob#v1)8rZQEF6wL zPhKD|!f5Pe`1RR_<1Sw#JK&nlPIeLpd4udCZ<5{QEwYEaP4<#^$UgEed5`SJ{@xF? zE#yNucB>*ElTXN}87 zlIx_NG!Pxfyla$DxbC1PYNjwNrH!Z$ZA^WsA8kVYX#fqRLGabmls2OwFq3LQThdmv zHEl!N(sneIwx?kT9G&Z49qVs4mEup1!J}sl=w1QUB z1#}@@L~o~y=@NPeT}qeH<@8Rvg07@@(N%OcT|@7tYw11oUV0zBpRS`1(Dif!eUNUX z57CF|Ci)0{ly0Vv(Z}f)x|KdbpM(?L?er=7G<}9XOP{09(--KA^d*Jo}gdRujx1RBt1pHrKjl``W>uFzo%zuE&YN1NYByp^aA~f z*3pafXL^bLLVu-~=@ojF{ziYNf6zbaU-WPK54}eJrPpaaZJ;`JGK~>N8Dl1#7hz#m z)`C4+HUPFt%|kNc45Eh z$2g_wL+x%Biak4@u=XrWdr#Ys{kQvA2i8&hn{{HHSpW! zDJ+$xv2>QfGT9(Dm>Byg=`VKoh@cd*d1&s zTgH~NJJ|}hlHJ8tvDIu1yPK_L_pp1}ee8a=jy=HEvkmM)wvj!=9%h@^BkWPO89o3X zXIt1-_5^#9ZDZTnQ|xK>411P6$DU^|uou}&>}B=}dzHP$cCgo(o$X`}_6FO<-ekMk zTWk+|o9$)quzl=Z_8!~M-e(`M57|eoihayJVV|^!@`eqwd(BKw(LV!yCo*=2Tx zU1h(q-`OARPxcr4oBhMCv47chR?ixk&YUI3J$zrnN{5c;}W0Nn=ZfRoj zHwBmiO+lt$)5vkdhg&O)3u9tZ(k=6HW|fr``wCcU6i-Tcb5BFlo1<~qT*Cb`m7w3WyzIM zIttFIz#WgQ8LC8#w>dzT3|V5Hh#F_)mQ>`-;@~yTb6r|96dZXnwq(eR^F-u3$PI~a zzWZ826Bnc6SieCS#m~!;x%=gN-1rW5f8|@?zBUb>nNwyez>j4}MPX5Ho~2Mk)*%Xq zLWRQ+i9?~J+7Jb8p^O_3aUnv745fv0e22P=@SWqn_8aB_liysgoBXo8yyBvq;@rYn zmf<VPNcnxS{C=dzCw|2qH%9~Yk_624j zQju4bSD53MQCeOIw8i5_85QdN5J(O2$S-SNAr~w1W}HXj##!_7@@1l+LL~Pp%Ph(= zrRG)Sm+7*$^A$MZ4Z2v7-(`^Gsg^!@TY@Mr`8 zBvCjP;;LDtd~bXz9-77XrkO}?&KD@<3Y1h{rp0){_)DlQfD+HlJXfGpBv6udl$u71 zE9s&_Yl*~*l@*j&$~jW8W)ZOp;HJuFrNCCksaB}9D-YS-TS3->B;)IxV z^*lqxnKF)5<;AMWAv%H^Ah*SB-sr=$pe(?(Kc=dg}f;V2}6R+TmS8&EFIOA1*@e0m( zm2bR}uYxl{<)5J7Oi<+{sB#iiISC5h1O;z`DmOuuo1n@|P~|14@)A^e393AsD$l0M zv#IiIsyv&jf14`TrpmRca&4+yn=03)@U^M(ZK`~mD&MBcx2f_IRe6aj-$a#fqRKZ> z<(sJTO;q_Ns(cewzKJT|M3rxn!YfJPm89TLQu!yT{F7AvNh<#&m47eweJ}NWF9l~W zMVDTRF1=K~y%hYtRKC4bzP(hwy^MSn+`U!)y;c6bRsOwI{=L^3zrM>8kv6ReriEKV6lduF6kW<)^Fi(^dKDs{9O9eukoFhAKZp zm7k%?&rs!OsPZ#Z`57wz43&R|$~RNxm#Ol}RQY78d@@x&nJS-5g-52sBU9m#sqn~D zL&Aqx&?PoQ%2ULdmhocvWLYSq@nQ&N zStuf(@kUqbv(UKWaz8dBMbsN&m7i=^xEJ(BEbv7v@I@@}MJ(_|Ea;6`;EPz$8?nF_ zv7k3%L2twYU&I36*bLbY5v%fLJH)*zU$#TstMX+##JwtCwnN;j@?|^3y((X}L)@$K zQ&st?s(jflV>8lJ`DvoQaj(ixQ~1kvi{}b|*=}*K@R#it_X>a6ZgH>hm+cn!s(jgQ zV>4vCMXbt~?H2c{eA#Ysuke@c7WWE&*=}*K@R#it_X>a6ZgH>hm+cn!3V+#dV>4vC zMXd0b?H2b6f7xzvuke@c7WWGO3{`%H!aqZmFWWWJsq!-v{<3|?W@IY<%Z#;5l#F}3 zh$MH)){ayPd)eA?udtV`9rv>IOxf$l#mH8ISWp$QzzVUTDq?{ZVv#RmffZtrFJgfe zVv#RmffZtb71jf7O~lF_W;M`-%q(g$zY^07t^g^+svd_uE+)=qnO9iM=iB9&(-i0W zU}41ReoB4OvwYsHQlH$M^1_^wMTI#&BI}+&rcrTWah|v+FI*&3%`T}d6IYD| zc;d2A#ljMK0pMk_#d&4&k!6y2V3{bR2{IZlquXROPDa@>8Y`nQG8!!+ACX^CapgQq zmV7=^M2#@TD=+6W_Yv~(a2XAg(NGx;kSw2*LoU`!05w9suPf>ULLe(d~^bwUTufTRP9p?a% zPH>;Rl#C)03QplymQjKzKAd62l~wQ#m`Oh27lMZ>OMMgzRhC)>2jPiZEg@J5i6tlD zmE=WmYGcKp{89oV_*Y$4mNqf)7L@aqT1Np2^aKSGI4{ZouySKLa)int7L>sZ8nleAfRxu~HWap#-iQJ7S_i06SBHx%Zd;Z=a|5RdPM zcmO%%#_xuBe>c(%vu~D19$6lFWZjU5Zj1ZEStXQ; zIhD|zTcC$NS-u=3I7)scV2FzmH$k2nW$`4?Lz67Om5O6=9qgqpRIfdogO*HWD;uKkoU zSG{mkyRp#YipK#Ekn^BgpIKBclTC7$6yR38s&{S%^`Gd)?SUSeT#+`=L**+T_$i&R zxZz`hm;^O3Nl+7ugjmsQ$0Wpxp$rr{qBlpZp2vwH4erH|2C*2@AXXEiI5`1Hh!;bq zn1p!sUA)RqscRDw)bj-OeWIH9C93ZdRk?{OU7{Fb#Uv!jiBv+8dY`1;C#n3C6dXxn zposKhpomz()k{6^rJnav&wHzUdaH7JtNeP4p*yJ?AjvwyBm*BoJ>}b+8nw~j4TSLcOQ4AX#6F2BV3jW@GjW6fsl@)9L@)z&; zdx8|)7Kmz%ZyNvW zPiQCv=Q3prtg4p*u zp62w4nAvF>?iNHIi9Q6_jzk{mv=&g#?7TJh8~(>(;}~JmfqIaX6I8;t{28YRrz-#B z-$l%Hr7Vize;(BSzq!7t25zhok6JKl!-zo}UJC+V4zmiEI4^qqHH+#RH7aUmR8iE* zsI^f$qTY}CBI>6u!Cg9a>Dncs%cL$dx*qAS0gJso`*0dZ{}wYmHX?3g?C{w4IHiCu z|9g+)3HnFy7#y~j|7iriGR?xu&C_a4+N;IE`zyCqyU|iD9rkJ5Hf^M^MjPj{LYoE~ zio1mM*&29Iy$5Ht-3Ke%b+C(g7#0zaz~XE(JZWx)J=&AnHrSdyqdg0&iRWQ6@iMF~ zUWM%$x5VI98GE$-+WXoEu*mpGtAcIDC$LH5R%nM{fmW@3AuP_m(!Pc@)Jg4>vWNN$ z_FvawGv*|OTa3A^#ah5htPSZzB47*F6?R}fNet}25@7pfSbp^<1BHdxXjp0G3HvNz zo24wX%7r}^x5e_Z#Ddk;eZtae18l4|Df_A|!nSI=vaEVuSyCAmRC~Ovr#^(mlwmLB zX({zJ?4!=OY@^O8t0-<0#Vw+^CDb);0VQmm0+cmVC@h&ezGt%vT0-Ozpi>o!CGm95YbSOy)xg#{3|@DcVs!lK99UWQxCqzfyV zk+6*!2U{1njmWL+Lu!>($Yo&#@)xTY79Yya12&w!VZ+%6y+=RTac4mrt zLLtI(gwV2sOAri|_-&Ijt3T;J08uA-{uKfc}magwPJ5lXEIs zPctpuxes2G4rzlB1|tkb7~?#EoiB&9an4$8g7X4SQu$Gvg0K?b+=Z|TVKu@Ugu4-* zMjp=~Jd5xg!t)3(AiRV;UPgEY;Z=m!5OyK&HxYItyoIm_;T@E*58+*e_Yn4@tPc=A zMBX1EpDM&3BmM;Or-(mCJ_ivFBOFCIhHwJmYlM>s-y)nrs6jZ3@B_j*=OGf{JV1gF znj(bYzLVCBL?Co?ULZXXq7h;d;t^~Jy`4Xjbfn9~^_5$L)_~ro0Jchi!#QSgY{5!8VKl;2=K(qc>2eTe zB9!9Ue1tNDI}lKhbQQi?hxh@+>k)52{2=0uh#x}yFyc*!A3^*m;?0O3L;N`6Er_=w zegg56h_@l$j`%6W;0O8)V(s4?VQzr!d3Lm210 zn;q4fv1$a+oSk6bph5PG>8=QwsW&|sEvb~n%JgN1@i4lFAby`2?+uAng+oY58 zB#A&sbk>nxxbK5dh_D=CHNpYsNg9RF)%iB}jOJjtpNE;wu0PY8dZ+*T<4EK0wKl0QYspQ2` z)K5|BrzrI-O8OKfeTtGkMM2+x7 zb!h2zXz6um>2+x7b!h2zXz6um>2+x7b!h2zXz6um-F0ZWb!eS+Xqk0rjdf^^b!c&Q zXl-?9X?196b!cgIXjye=S#@YVb!a_xXgzgkEp=!ub!aVhXf1W%;yQ3~9k{p-TwDh( zt^*g>fs5wMI@CoS>Y@&HQHQ#yLtWINF6yuw zV3Kn)>*IWx^>bFU{?1p}0O!wapmQfnbzWiV2qSR1&uHgWa9wlkB^ZKT0^_kqU=l(j ze0!GM>HLs94Gv5N)Sw>H|Ah1xkv;&uQH1lV))gTRApzkYgohC}Av}WcD8goh#}Ph5 zIDl{n;RwQa2;U>rBK+vQO8gN55rPq#A*3M;M<_rjM_7!2(^kprh(AI2+a)&kR7U|I`IYk_GkFs%iqwZOC%nAQT* zT3}iWOlyH@EikPGrnSJd7MRuo(^_C!3%b7rY?}hxT3}lXY-@pSEwHTxwza^v7TDGT z+ge~-3v6qFZ7r~^1-7-owiejd0^3?(TMKM!fo(0Ytp&EVz_u3H)&kpFU|S1pYk_So zu&u=ypc7HEV z)<7B^M6XzbUaQ|NQv*qN5R&d7B;7$s zx`XTsNcG>G2TU4pib9)Pi8i+qZEhtntpbKsz_1D!Rsq8*U|0nVtAJe*aUL;SU726>jzk+Q13bOI5|I^;R$H`HY zdjs$8nO(xNY%*CwxGE|jM?9b+A{P-6Q9LLF@F*ak1A-`uq9W))f|Az@UWkYa2ngX4 z1QAd`53+JexF^68E}LYt$+A0}-O0|3dtv2H`h2T)6B0pD-+$ge-s#WN{Y*`FPgOln zKhLkKy6RyYJCcL*mB|crNZy_t>Ufy6PvncCvM#8si{0#CH#^wP4tBGH-Rwwia^B6( z|2_Hx`X6))YIEJ6&~4~;bcgHbp$A;E5Iuz2(IWH&T8x&WWoS9-Mm?yER^}aPW4`C~E^mFq{`qR80vg%4N$m`HmdwOABX45;^^bR$*0?JyP&dKj#y&g=j$e&2(`hJ!3 ze&zU>dpz#ECmcJ_Vzd->qGitMa$Js9IOj?K(TndxE71U2g{pq5p+Dn?9RK3@40;wl zmv?28(1vKEyn`?4$TrJYX0J!vp*N!){kFH`K4?Gm5$7C)W}w4;KN204w?k}Q5L*|- z)|DME?X&1}=u6J~GWrTS9er2YRgPC9vgeaJ_@oX#se@1I;FCHat9Hn$9kOcA?nLv^ z-RNF)zuy<22hqd6KjQdkz8JDvoOPfjs1tRe6-d26R$Y))7i85{P*;Wiyq%Bh;Nv>@ zxQ=22+JKIonl$8(CrxPoyiSwrG`UWb>om7cb89rWgXVV7+&ayz)7(1Et<&5(4Xx48 z8V#+{&>9WxprIW!v`$0oG_+1b>ol}ZL+doOPDAT7v`$0oG_+1b>ol}ZL+iA#P77
DEcNPP%o{t-}Bf`D~J% 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 TYqd&VnqJLHIX9iq_T+y7#;O#c literal 0 HcmV?d00001 diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/fonts/Roboto-Regular.ttf b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/fonts/Roboto-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3e6e2e76134cd6040a49ccf9961d302f7d139fcd GIT binary patch literal 145348 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 default implementation matches the gap value with the + * backgroundCornerRadius value in CalloutSkin.

+ * + * @see spark.skins.mobile.CalloutSkin#backgroundCornerRadius + * + * @langversion 3.0 + * @playerversion AIR 3 + * @productversion Flex 4.6 + */ + protected var gap:Number; + + /** + * @copy spark.skins.mobile.CalloutSkin#backgroundGradientHeight + */ + protected var backgroundGradientHeight:Number; + + /** + * @copy spark.skins.mobile.CalloutSkin#highlightWeight + */ + private var highlightWeight:Number; + + /** + * @copy spark.skins.mobile.CalloutSkin#useBackgroundGradient + */ + protected var useBackgroundGradient:Boolean; + + /** + * @copy spark.skins.mobile.CalloutSkin#borderColor + */ + protected var borderColor:Number = -1; // if not set + + /** + * @copy spark.skins.mobile.CalloutSkin#borderThickness + */ + protected var borderThickness:Number = -1 ; // marker that borderThickness was not set directly + + /** + * @private + * A sibling of the arrow used to erase the drop shadow in CalloutSkin + */ + private var eraseFill:Sprite; + + /* helper private accessors */ + + /* returns borderThickness from style if member is -1, or borderThickness. Returns 0 if NaN */ + private function get actualBorderThickness():Number + { + return calloutSkin.actualBorderThickness; + } + + private function get actualBorderColor():uint + { + return calloutSkin.actualBorderColor; + } + + protected function get calloutSkin():CalloutSkin + { + return parent as CalloutSkin ; + } + + protected function get calloutHostComponent():Callout { + return calloutSkin.hostComponent; + } + + /** + * @private + */ + + override protected function createChildren():void + { + super.createChildren(); + + // eraseFill has the same position and arrow shape in order to erase + // the drop shadow under the arrow when backgroundAlpha < 1 + eraseFill = new Sprite(); + eraseFill.blendMode = BlendMode.ERASE; + + // layer eraseFill below the arrow + parent.addChildAt(eraseFill, parent.getChildIndex(this)); + } + + /** + * @private + */ + override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void + { + super.updateDisplayList(unscaledWidth, unscaledHeight); + + graphics.clear(); + eraseFill.graphics.clear(); + var hostComponent: Callout = calloutHostComponent; + var arrowDirection:String = hostComponent.arrowDirection; + + if (arrowDirection == ArrowDirection.NONE) + return; + + // when drawing the arrow, compensate for cornerRadius via padding + var arrowGraphics:Graphics = this.graphics; + var eraseGraphics:Graphics = eraseFill.graphics; + var arrowWidth:Number = unscaledWidth; + var arrowHeight:Number = unscaledHeight; + var arrowX:Number = 0; + var arrowY:Number = 0; + var arrowTipX:Number = 0; + var arrowTipY:Number = 0; + var arrowEndX:Number = 0; + var arrowEndY:Number = 0; + + var borderWeight:Number = actualBorderThickness; + var showBorder:Boolean = borderWeight > 0; + + var borderHalf:Number = borderWeight / 2; + var isHorizontal:Boolean = false; + + if ((arrowDirection == ArrowDirection.LEFT) || + (arrowDirection == ArrowDirection.RIGHT)) + { + isHorizontal = true; + + arrowX = -borderHalf; + arrowY = gap; + arrowHeight = arrowHeight - (gap * 2); + + arrowTipX = arrowWidth - borderHalf; + arrowTipY = arrowY + (arrowHeight / 2); + + arrowEndX = arrowX; + arrowEndY = arrowY + arrowHeight; + + // flip coordinates to point left + if (arrowDirection == ArrowDirection.LEFT) + { + arrowX = arrowWidth - arrowX; + arrowTipX = arrowWidth - arrowTipX; + arrowEndX = arrowWidth - arrowEndX; + } + } + else + { + arrowX = gap; + arrowY = -borderHalf; + arrowWidth = arrowWidth - (gap * 2); + + arrowTipX = arrowX + (arrowWidth / 2); + arrowTipY = arrowHeight - borderHalf; + + arrowEndX = arrowX + arrowWidth; + arrowEndY = arrowY; + + // flip coordinates to point up + if (hostComponent.arrowDirection == ArrowDirection.UP) + { + arrowY = arrowHeight - arrowY; + arrowTipY = arrowHeight - arrowTipY; + arrowEndY = arrowHeight - arrowEndY; + } + } + + var commands:Vector. = new Vector.(3, true); + commands[0] = GraphicsPathCommand.MOVE_TO; + commands[1] = GraphicsPathCommand.LINE_TO; + commands[2] = GraphicsPathCommand.LINE_TO; + + var coords:Vector. = new Vector.(6, true); + coords[0] = arrowX; + coords[1] = arrowY; + coords[2] = arrowTipX + coords[3] = arrowTipY; + coords[4] = arrowEndX + coords[5] = arrowEndY; + + var backgroundColor:Number = getStyle("backgroundColor"); + var backgroundAlpha:Number = getStyle("backgroundAlpha"); + + if (useBackgroundGradient) + { + var backgroundColorTop:Number = ColorUtil.adjustBrightness2(backgroundColor, + CalloutSkin.BACKGROUND_GRADIENT_BRIGHTNESS_TOP); + var backgroundColorBottom:Number = ColorUtil.adjustBrightness2(backgroundColor, + CalloutSkin.BACKGROUND_GRADIENT_BRIGHTNESS_BOTTOM); + + // translate the gradient based on the arrow position + MobileSkin.colorMatrix.createGradientBox(unscaledWidth, + backgroundGradientHeight, Math.PI / 2, 0, -getLayoutBoundsY()); + + arrowGraphics.beginGradientFill(GradientType.LINEAR, + [backgroundColorTop, backgroundColorBottom], + [backgroundAlpha, backgroundAlpha], + [0, 255], + MobileSkin.colorMatrix); + } + else + { + arrowGraphics.beginFill(backgroundColor, backgroundAlpha); + } + + // cover the adjacent border from the callout frame + if (showBorder) + { + var coverX:Number = 0; + var coverY:Number = 0; + var coverWidth:Number = 0; + var coverHeight:Number = 0; + + switch (arrowDirection) + { + case ArrowDirection.UP: + { + coverX = arrowX; + coverY = arrowY; + coverWidth = arrowWidth; + coverHeight = borderWeight; + break; + } + case ArrowDirection.DOWN: + { + coverX = arrowX; + coverY = -borderWeight; + coverWidth = arrowWidth; + coverHeight = borderWeight; + break; + } + case ArrowDirection.LEFT: + { + coverX = arrowX; + coverY = arrowY; + coverWidth = borderWeight; + coverHeight = arrowHeight; + break; + } + case ArrowDirection.RIGHT: + { + coverX = -borderWeight; + coverY = arrowY; + coverWidth = borderWeight; + coverHeight = arrowHeight; + break; + } + } + + arrowGraphics.drawRect(coverX, coverY, coverWidth, coverHeight); + } + + // erase the drop shadow from the CalloutSkin + if (backgroundAlpha < 1) + { + // move eraseFill to the same position as the arrow + eraseFill.x = getLayoutBoundsX() + eraseFill.y = getLayoutBoundsY(); + + // draw the arrow shape + eraseGraphics.beginFill(0, 1); + eraseGraphics.drawPath(commands, coords); + eraseGraphics.endFill(); + } + + // draw arrow path + if (showBorder) + arrowGraphics.lineStyle(borderWeight, actualBorderColor, 1, true); + + arrowGraphics.drawPath(commands, coords); + arrowGraphics.endFill(); + + // adjust the highlight position to the origin of the callout + var isArrowUp:Boolean = (arrowDirection == ArrowDirection.UP); + var offsetY:Number = (isArrowUp) ? unscaledHeight : -getLayoutBoundsY(); + + // highlight starts after the backgroundCornerRadius + var highlightX:Number = gap - getLayoutBoundsX(); + + // highlight Y position is based on the stroke weight + var highlightOffset:Number = (highlightWeight * 1.5); + var highlightY:Number = highlightOffset + offsetY; + + // highlight width spans the callout width minus the corner radius + var highlightWidth:Number = IVisualElement(calloutSkin).getLayoutBoundsWidth() - (gap * 2); + + if (isHorizontal) + { + highlightWidth -= arrowWidth; + + if (arrowDirection == ArrowDirection.LEFT) + highlightX += arrowWidth; + } + + // highlight on the top edge is drawn in the arrow only in the UP direction + if (useBackgroundGradient) + { + if (isArrowUp) + { + // highlight follows the top edge, including the arrow + var rightWidth:Number = highlightWidth - arrowWidth; + + // highlight style + arrowGraphics.lineStyle(highlightWeight, 0xFFFFFF, 0.2 * backgroundAlpha); + + // in the arrow coordinate space, the highlightX must be less than 0 + if (highlightX < 0) + { + arrowGraphics.moveTo(highlightX, highlightY); + arrowGraphics.lineTo(arrowX, highlightY); + + // compute the remaining highlight + rightWidth -= (arrowX - highlightX); + } + + // arrow highlight (adjust Y downward) + coords[1] = arrowY + highlightOffset; + coords[3] = arrowTipY + highlightOffset; + coords[5] = arrowEndY + highlightOffset; + arrowGraphics.drawPath(commands, coords); + + // right side + if (rightWidth > 0) + { + arrowGraphics.moveTo(arrowEndX, highlightY); + arrowGraphics.lineTo(arrowEndX + rightWidth, highlightY); + } + } + else + { + // straight line across the top + arrowGraphics.lineStyle(highlightWeight, 0xFFFFFF, 0.2 * backgroundAlpha); + arrowGraphics.moveTo(highlightX, highlightY); + arrowGraphics.lineTo(highlightX + highlightWidth, highlightY); + } + } + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/StageTextSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/StageTextSkinBase.as new file mode 100644 index 0000000000..3dc1966264 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/StageTextSkinBase.as @@ -0,0 +1,402 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.supportClasses +{ +import flash.display.DisplayObject; +import flash.events.FocusEvent; + +import mx.core.DPIClassification; +import mx.core.mx_internal; + +import spark.components.supportClasses.IStyleableEditableText; +import spark.components.supportClasses.SkinnableTextBase; +import spark.components.supportClasses.StyleableStageText; +import spark.components.supportClasses.StyleableTextField; +import spark.core.IDisplayText; +import spark.skins.mobile.supportClasses.MobileSkin; + +use namespace mx_internal; + +/** + * ActionScript-based skin for text input controls in mobile applications. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ +public class StageTextSkinBase extends MobileSkin +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + * + */ + public function StageTextSkinBase() + { + super(); + + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + measuredDefaultWidth = 1200; + measuredDefaultHeight = 132; + layoutBorderSize = 4; + flatheight = 9; + break; + } + case DPIClassification.DPI_480: + { + measuredDefaultWidth = 880; + measuredDefaultHeight = 100; + layoutBorderSize = 3; + flatheight = 7; + break; + } + case DPIClassification.DPI_320: + { + measuredDefaultWidth = 600; + measuredDefaultHeight = 66; + layoutBorderSize = 2; + flatheight = 6; + break; + } + case DPIClassification.DPI_240: + { + measuredDefaultWidth = 440; + measuredDefaultHeight = 50; + layoutBorderSize = 2; + flatheight = 5; + break; + } + case DPIClassification.DPI_120: + { + measuredDefaultWidth = 220; + measuredDefaultHeight = 25; + layoutBorderSize = 1; + flatheight = 2; + break; + } + default: + { + measuredDefaultWidth = 300; + measuredDefaultHeight = 33; + layoutBorderSize = 1; + flatheight = 3; + break; + } + + } + addEventListener(FocusEvent.FOCUS_IN, focusChangeHandler); + addEventListener(FocusEvent.FOCUS_OUT, focusChangeHandler); + } + + //-------------------------------------------------------------------------- + // + // Graphics variables + // + //-------------------------------------------------------------------------- + + + //-------------------------------------------------------------------------- + // + // Layout variables + // + //-------------------------------------------------------------------------- + + /** + * Defines the border's thickness. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + protected var layoutBorderSize:uint; + + protected var flatheight:uint; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + protected var isFocused:Boolean = false; + + /** + * @private + * + * Instance of the border graphics. + */ + protected var border:DisplayObject; + + private var borderVisibleChanged:Boolean = false; + + /** + * @private + * + * Multiline flag. + */ + protected var multiline:Boolean = false; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * textDisplay skin part. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + public var textDisplay:IStyleableEditableText; + + [Bindable] + /** + * Bindable promptDisplay skin part. Bindings fire when promptDisplay is + * removed and added for proper updating by the SkinnableTextBase. + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + public var promptDisplay:IDisplayText; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (!textDisplay) + { + textDisplay = createTextDisplay(); + textDisplay.editable = true; + textDisplay.styleName = this; + this.addChild(DisplayObject(textDisplay)); + } + } + + /** Could be overridden by subclasses + * + * @return instance of IStyleableEditableText + */ + protected function createTextDisplay():IStyleableEditableText + { + return new StyleableStageText(multiline); + } + + /** + * @private + */ + 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; + } + var halfGap:int = flatheight * 2; + // change the border type + if (getStyle("contentBackgroundBorder") == "flat") + { + //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 + */ + override public function styleChanged(styleProp:String):void + { + var allStyles:Boolean = !styleProp || styleProp == "styleName"; + + if (allStyles || styleProp == "borderVisible") + { + borderVisibleChanged = true; + invalidateProperties(); + } + + if (allStyles || styleProp.indexOf("padding") == 0) + { + invalidateDisplayList(); + } + super.styleChanged(styleProp); + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + alpha = currentState.indexOf("disabled") == -1 ? 1 : 0.5; + + var showPrompt:Boolean = currentState.indexOf("WithPrompt") != -1; + + if (showPrompt && !promptDisplay) + { + promptDisplay = createPromptDisplay(); + promptDisplay.addEventListener(FocusEvent.FOCUS_IN, promptDisplay_focusInHandler); + } + else if (!showPrompt && promptDisplay) + { + promptDisplay.removeEventListener(FocusEvent.FOCUS_IN, promptDisplay_focusInHandler); + removeChild(promptDisplay as DisplayObject); + promptDisplay = null; + } + super.commitCurrentState(); + + invalidateDisplayList(); + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Create a control appropriate for displaying the prompt text in a mobile + * input field. + */ + protected function createPromptDisplay():IDisplayText + { + var prompt:StyleableTextField = StyleableTextField(createInFontContext(StyleableTextField)); + prompt.styleName = this; + prompt.editable = false; + prompt.mouseEnabled = false; + prompt.useTightTextBounds = false; + // StageText objects appear in their own layer on top of the display + // list. So, even though this prompt may be created after the StageText + // for textDisplay, textDisplay will still be on top. + addChild(prompt); + + return prompt; + } + + /** + * @private + * Utility function used by subclasses' measure functions to measure their + * text host components. + */ + protected function measureTextComponent(hostComponent:SkinnableTextBase):void + { + 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"); + + if (textDisplay) + { + textHeight = getElementPreferredHeight(textDisplay); + } + // 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, (textHeight - 2)); + measuredWidth = (characterWidth * hostComponent.maxChars) + paddingLeft + paddingRight; + } + + measuredHeight = paddingTop + textHeight + paddingBottom; + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + /** + * Listen to see if the component gains focus then change the style to selected + */ + private function focusChangeHandler(event:FocusEvent):void + { + isFocused = event.type == FocusEvent.FOCUS_IN; + invalidateDisplayList(); + } + + /** + * If the prompt is focused, we need to move focus to the textDisplay + * StageText. This needs to happen outside of the process of setting focus + * to the prompt, so we use callLater to do that. + */ + private function focusTextDisplay():void + { + textDisplay.setFocus(); + } + + private function promptDisplay_focusInHandler(event:FocusEvent):void + { + callLater(focusTextDisplay); + } +} +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/TextSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/TextSkinBase.as new file mode 100644 index 0000000000..31bf7eaa96 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/TextSkinBase.as @@ -0,0 +1,213 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.supportClasses +{ + + import flash.display.DisplayObject; + + import mx.core.mx_internal; + + import spark.components.supportClasses.StyleableTextField; + import spark.skins.mobile.supportClasses.MobileSkin; + + use namespace mx_internal; + + /** + * ActionScript-based skin for text input controls in mobile applications that + * uses a StyleableTextField class for the text display. + * + * @see spark.components.supportClasses.StyleableTextField + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class TextSkinBase extends MobileSkin + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + * + */ + public function TextSkinBase() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Graphics variables + // + //-------------------------------------------------------------------------- + + /** + * Defines the border. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + + //-------------------------------------------------------------------------- + // + // Layout variables + // + //-------------------------------------------------------------------------- + + /** + * Defines the corner radius. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + + protected var layoutBorderSize:uint; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * + * Instance of the border graphics. + */ + protected var border:DisplayObject; + + private var borderVisibleChanged:Boolean = false; + + //-------------------------------------------------------------------------- + // + // Skin parts + // + //-------------------------------------------------------------------------- + + /** + * textDisplay skin part. + */ + public var textDisplay:StyleableTextField; + + [Bindable] + /** + * Bindable promptDisplay skin part. Bindings fire when promptDisplay is + * removed and added for proper updating by the SkinnableTextBase. + */ + public var promptDisplay:StyleableTextField; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function createChildren():void + { + super.createChildren(); + + if (!textDisplay) + { + textDisplay = StyleableTextField(createInFontContext(StyleableTextField)); + textDisplay.styleName = this; + textDisplay.editable = true; + textDisplay.useTightTextBounds = false; + addChild(textDisplay); + } + } + + /** + * @private + */ + protected function createPromptDisplay():StyleableTextField + { + var prompt:StyleableTextField = StyleableTextField(createInFontContext(StyleableTextField)); + prompt.styleName = this; + prompt.editable = false; + prompt.mouseEnabled = false; + prompt.useTightTextBounds = false; + prompt.focusEnabled = false; + return prompt; + } + + /** + * @private + */ + + override public function styleChanged(styleProp:String):void + { + var allStyles:Boolean = !styleProp || styleProp == "styleName"; + + if (allStyles || styleProp == "borderVisible") + { + borderVisibleChanged = true; + invalidateProperties(); + } + + if (allStyles || styleProp.indexOf("padding") == 0) + { + invalidateDisplayList(); + } + + super.styleChanged(styleProp); + } + + /** + * @private + */ + override protected function commitCurrentState():void + { + super.commitCurrentState(); + + alpha = currentState.indexOf("disabled") == -1 ? 1 : 0.5; + + var showPrompt:Boolean = currentState.indexOf("WithPrompt") >= 0; + + if (showPrompt && !promptDisplay) + { + promptDisplay = createPromptDisplay(); + addChild(promptDisplay); + } + else if (!showPrompt && promptDisplay) + { + removeChild(promptDisplay); + promptDisplay = null; + } + + invalidateDisplayList(); + } + } +} \ No newline at end of file From 6dcaaac9304c54d3a8eb600291b4d8e22b0eb05e Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Thu, 2 Oct 2014 09:57:53 +0200 Subject: [PATCH 111/392] Initial search&replace Did a search&replace from 'skins.android4' to 'skins.ios7'. That should fix package names, imports and all references to 'assets'. Signed-off-by: Erik de Bruin --- .../src/spark/skins/ios7/ActionBarSkin.as | 6 +++--- .../src/spark/skins/ios7/BusyIndicatorSkin.as | 6 +++--- .../spark/skins/ios7/ButtonBarFirstButtonSkin.as | 14 +++++++------- .../skins/ios7/ButtonBarMiddleButtonSkin.as | 16 ++++++++-------- .../src/spark/skins/ios7/ButtonBarSkin.as | 10 +++++----- .../src/spark/skins/ios7/ButtonSkin.as | 10 +++++----- .../src/spark/skins/ios7/CalloutSkin.as | 8 ++++---- .../src/spark/skins/ios7/CheckBoxSkin.as | 16 ++++++++-------- .../src/spark/skins/ios7/HScrollBarSkin.as | 2 +- .../src/spark/skins/ios7/HScrollBarThumbSkin.as | 2 +- .../src/spark/skins/ios7/HSliderSkin.as | 10 +++++----- .../src/spark/skins/ios7/HSliderThumbSkin.as | 8 ++++---- .../src/spark/skins/ios7/HSliderTrackSkin.as | 6 +++--- .../src/spark/skins/ios7/RadioButtonSkin.as | 16 ++++++++-------- .../spark/skins/ios7/SpinnerListContainerSkin.as | 14 +++++++------- .../src/spark/skins/ios7/SpinnerListSkin.as | 8 ++++---- .../src/spark/skins/ios7/StageTextAreaSkin.as | 4 ++-- .../src/spark/skins/ios7/StageTextInputSkin.as | 4 ++-- .../skins/ios7/TabbedViewNavigatorTabBarSkin.as | 8 ++++---- .../src/spark/skins/ios7/TextAreaSkin.as | 4 ++-- .../src/spark/skins/ios7/TextInputSkin.as | 4 ++-- .../src/spark/skins/ios7/ToggleSwitchSkin.as | 12 ++++++------ .../skins/ios7/TransparentActionButtonSkin.as | 2 +- .../ios7/TransparentNavigationButtonSkin.as | 2 +- .../src/spark/skins/ios7/VScrollBarSkin.as | 2 +- .../src/spark/skins/ios7/VScrollBarThumbSkin.as | 2 +- .../src/spark/skins/ios7/ViewMenuItemSkin.as | 2 +- .../skins/ios7/supportClasses/CalloutArrow.as | 4 ++-- .../ios7/supportClasses/StageTextSkinBase.as | 2 +- .../skins/ios7/supportClasses/TextSkinBase.as | 2 +- 30 files changed, 103 insertions(+), 103 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ActionBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ActionBarSkin.as index 8ffbe4d2a1..1e46d38427 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ActionBarSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ActionBarSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.GradientType; import flash.events.Event; @@ -34,7 +34,7 @@ import spark.core.SpriteVisualElement; import spark.layouts.HorizontalAlign; import spark.layouts.HorizontalLayout; import spark.layouts.VerticalAlign; -import spark.skins.android4.assets.ActionBarBackground; +import spark.skins.ios7.assets.ActionBarBackground; import spark.skins.mobile.supportClasses.MobileSkin; use namespace mx_internal; @@ -83,7 +83,7 @@ public class ActionBarSkin extends MobileSkin { super(); - borderClass = spark.skins.android4.assets.ActionBarBackground; + borderClass = spark.skins.ios7.assets.ActionBarBackground; switch (applicationDPI) { diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/BusyIndicatorSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/BusyIndicatorSkin.as index dd3aa44908..62f4a8ddbf 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/BusyIndicatorSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/BusyIndicatorSkin.as @@ -17,14 +17,14 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.DisplayObject; import flash.events.TimerEvent; import flash.geom.Matrix; import flash.utils.Timer; import mx.core.DPIClassification; - import spark.skins.android4.assets.BusyIndicator; + import spark.skins.ios7.assets.BusyIndicator; import spark.skins.mobile.supportClasses.MobileSkin; import spark.components.MobileBusyIndicator; @@ -48,7 +48,7 @@ package spark.skins.android4 { super(); - busyIndicatorClass = spark.skins.android4.assets.BusyIndicator; + busyIndicatorClass = spark.skins.ios7.assets.BusyIndicator; rotationInterval = getStyle("rotationInterval"); if (isNaN(rotationInterval)) rotationInterval = DEFAULT_ROTATION_INTERVAL; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as index 4b8cbba095..b1d0e289b6 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as @@ -17,10 +17,10 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { - import spark.skins.android4.assets.ButtonBarFirstButton_selectedDown; - import spark.skins.android4.assets.ButtonBarFirstButton_selectedUp; + import spark.skins.ios7.assets.ButtonBarFirstButton_selectedDown; + import spark.skins.ios7.assets.ButtonBarFirstButton_selectedUp; import spark.skins.mobile.supportClasses.ButtonBarButtonSkinBase; /** @@ -58,10 +58,10 @@ package spark.skins.android4 { super(); - upBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_up; - downBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_down; - selectedBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_selectedUp; - selectedDownBorderSkin = spark.skins.android4.assets.ButtonBarFirstButton_selectedDown; + upBorderSkin = spark.skins.ios7.assets.ButtonBarFirstButton_up; + downBorderSkin = spark.skins.ios7.assets.ButtonBarFirstButton_down; + selectedBorderSkin = spark.skins.ios7.assets.ButtonBarFirstButton_selectedUp; + selectedDownBorderSkin = spark.skins.ios7.assets.ButtonBarFirstButton_selectedDown; } //-------------------------------------------------------------------------- diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as index c6206d0f42..b4740cd99d 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as @@ -17,12 +17,12 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { - import spark.skins.android4.assets.ButtonBarMiddleButton_selectedDown; - import spark.skins.android4.assets.ButtonBarMiddleButton_selectedUp; - import spark.skins.android4.assets.ButtonBarMiddleButton_up; + import spark.skins.ios7.assets.ButtonBarMiddleButton_selectedDown; + import spark.skins.ios7.assets.ButtonBarMiddleButton_selectedUp; + import spark.skins.ios7.assets.ButtonBarMiddleButton_up; import spark.skins.mobile.supportClasses.ButtonBarButtonSkinBase; /** @@ -60,10 +60,10 @@ package spark.skins.android4 { super(); - upBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_up; - downBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_down; - selectedBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_selectedUp; - selectedDownBorderSkin = spark.skins.android4.assets.ButtonBarMiddleButton_selectedDown; + upBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_up; + downBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_down; + selectedBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_selectedUp; + selectedDownBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_selectedDown; } //-------------------------------------------------------------------------- diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as index 4b194fdd74..cd120f50b9 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as @@ -17,13 +17,13 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import spark.components.ButtonBar; import spark.components.ButtonBarButton; import spark.components.DataGroup; import spark.components.supportClasses.ButtonBarHorizontalLayout; -import spark.skins.android4.ButtonBarFirstButtonSkin; +import spark.skins.ios7.ButtonBarFirstButtonSkin; import spark.skins.mobile.ButtonBarLastButtonSkin; import spark.skins.mobile.supportClasses.ButtonBarButtonClassFactory; import spark.skins.mobile.supportClasses.MobileSkin; @@ -106,19 +106,19 @@ public class ButtonBarSkin extends MobileSkin if (!firstButton) { firstButton = new ButtonBarButtonClassFactory(ButtonBarButton); - firstButton.skinClass = spark.skins.android4.ButtonBarFirstButtonSkin; + firstButton.skinClass = spark.skins.ios7.ButtonBarFirstButtonSkin; } if (!lastButton) { lastButton = new ButtonBarButtonClassFactory(ButtonBarButton); - lastButton.skinClass = spark.skins.android4.ButtonBarFirstButtonSkin; + lastButton.skinClass = spark.skins.ios7.ButtonBarFirstButtonSkin; } if (!middleButton) { middleButton = new ButtonBarButtonClassFactory(ButtonBarButton); - middleButton.skinClass = spark.skins.android4.ButtonBarMiddleButtonSkin; + middleButton.skinClass = spark.skins.ios7.ButtonBarMiddleButtonSkin; } // create the data group to house the buttons diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as index 43fb108c61..040b70cea9 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.DisplayObject; @@ -27,8 +27,8 @@ import mx.core.mx_internal; import mx.events.FlexEvent; import spark.components.supportClasses.StyleableTextField; -import spark.skins.android4.assets.Button_down; -import spark.skins.android4.assets.Button_up; +import spark.skins.ios7.assets.Button_down; +import spark.skins.ios7.assets.Button_up; import spark.skins.mobile.supportClasses.ButtonSkinBase; @@ -90,8 +90,8 @@ public class ButtonSkin extends ButtonSkinBase public function ButtonSkin() { super(); - upBorderSkin = spark.skins.android4.assets.Button_up; - downBorderSkin = spark.skins.android4.assets.Button_down; + upBorderSkin = spark.skins.ios7.assets.Button_up; + downBorderSkin = spark.skins.ios7.assets.Button_down; layoutCornerEllipseSize = 0; switch (applicationDPI) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/CalloutSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/CalloutSkin.as index 95fefef02c..a8247498de 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/CalloutSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/CalloutSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.BlendMode; import flash.display.GradientType; @@ -39,8 +39,8 @@ import spark.components.Group; import spark.core.SpriteVisualElement; import spark.effects.Fade; import spark.primitives.RectangularDropShadow; -import spark.skins.android4.assets.CalloutContentBackground; -import spark.skins.android4.supportClasses.CalloutArrow; +import spark.skins.ios7.assets.CalloutContentBackground; +import spark.skins.ios7.supportClasses.CalloutArrow; import spark.skins.mobile.supportClasses.MobileSkin; use namespace mx_internal; @@ -90,7 +90,7 @@ public class CalloutSkin extends MobileSkin super(); dropShadowAlpha = 0.7; - contentBackgroundInsetClass = spark.skins.android4.assets.CalloutContentBackground; + contentBackgroundInsetClass = spark.skins.ios7.assets.CalloutContentBackground; switch (applicationDPI) { diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/CheckBoxSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/CheckBoxSkin.as index f091205bc4..a6a4b9b853 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/CheckBoxSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/CheckBoxSkin.as @@ -17,13 +17,13 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.DisplayObject; import mx.core.DPIClassification; - import spark.skins.android4.assets.CheckBox_up; + import spark.skins.ios7.assets.CheckBox_up; import spark.skins.mobile.supportClasses.SelectableButtonSkinBase; /** @@ -82,13 +82,13 @@ package spark.skins.android4 layoutPaddingTop = 0; layoutPaddingBottom = 0; - upIconClass = spark.skins.android4.assets.CheckBox_up; - upSelectedIconClass = spark.skins.android4.assets.CheckBox_upSelected; - downIconClass = spark.skins.android4.assets.CheckBox_down; - downSelectedIconClass = spark.skins.android4.assets.CheckBox_downSelected; + upIconClass = spark.skins.ios7.assets.CheckBox_up; + upSelectedIconClass = spark.skins.ios7.assets.CheckBox_upSelected; + downIconClass = spark.skins.ios7.assets.CheckBox_down; + downSelectedIconClass = spark.skins.ios7.assets.CheckBox_downSelected; upSymbolIconClass = null; - upSymbolIconSelectedClass = spark.skins.android4.assets.CheckBox_upSymbolSelected; - downSymbolIconSelectedClass = spark.skins.android4.assets.CheckBox_downSymbolSelected; + upSymbolIconSelectedClass = spark.skins.ios7.assets.CheckBox_upSymbolSelected; + downSymbolIconSelectedClass = spark.skins.ios7.assets.CheckBox_downSymbolSelected; downSymbolIconClass = null; switch (applicationDPI) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarSkin.as index 1e712793fd..1a99e1bf1c 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import mx.core.DPIClassification; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarThumbSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarThumbSkin.as index ed3a6b9c2b..f238a4c9d1 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarThumbSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HScrollBarThumbSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.CapsStyle; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderSkin.as index 6f970f850e..762ea64a64 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.BlendMode; import flash.display.DisplayObject; @@ -28,7 +28,7 @@ package spark.skins.android4 import spark.components.Button; import spark.components.HSlider; - import spark.skins.android4.assets.HSliderTrack_filled; + import spark.skins.ios7.assets.HSliderTrack_filled; import spark.skins.mobile.supportClasses.HSliderDataTip; import spark.skins.mobile.supportClasses.MobileSkin; @@ -70,9 +70,9 @@ package spark.skins.android4 { super(); - thumbSkinClass = spark.skins.android4.HSliderThumbSkin; - trackSkinClass = spark.skins.android4.HSliderTrackSkin; - filledTrackSkinClass = spark.skins.android4.assets.HSliderTrack_filled; + thumbSkinClass = spark.skins.ios7.HSliderThumbSkin; + trackSkinClass = spark.skins.ios7.HSliderTrackSkin; + filledTrackSkinClass = spark.skins.ios7.assets.HSliderTrack_filled; dataTipClass = spark.skins.mobile.supportClasses.HSliderDataTip; blendMode = BlendMode.LAYER; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderThumbSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderThumbSkin.as index 517ff930fb..9f770300c1 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderThumbSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderThumbSkin.as @@ -17,14 +17,14 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.DisplayObject; import mx.core.DPIClassification; import spark.components.Button; -import spark.skins.android4.assets.HSliderThumb_normal; +import spark.skins.ios7.assets.HSliderThumb_normal; import spark.skins.mobile.supportClasses.MobileSkin; /** @@ -59,8 +59,8 @@ public class HSliderThumbSkin extends MobileSkin { super(); - thumbNormalClass = spark.skins.android4.assets.HSliderThumb_normal; - thumbPressedClass = spark.skins.android4.assets.HSliderThumb_pressed; + thumbNormalClass = spark.skins.ios7.assets.HSliderThumb_normal; + thumbPressedClass = spark.skins.ios7.assets.HSliderThumb_pressed; // set the dimensions to use based on the screen density switch (applicationDPI) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderTrackSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderTrackSkin.as index 57dc8f391c..3b2aec93ee 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderTrackSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/HSliderTrackSkin.as @@ -17,14 +17,14 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.DisplayObject; import mx.core.DPIClassification; import spark.components.Button; - import spark.skins.android4.assets.HSliderTrack; + import spark.skins.ios7.assets.HSliderTrack; import spark.skins.mobile.supportClasses.MobileSkin; /** @@ -56,7 +56,7 @@ package spark.skins.android4 { super(); - trackClass = spark.skins.android4.assets.HSliderTrack; + trackClass = spark.skins.ios7.assets.HSliderTrack; // set the right dimensions to use based on the screen density switch (applicationDPI) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/RadioButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/RadioButtonSkin.as index fd74645065..4eaf8231fa 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/RadioButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/RadioButtonSkin.as @@ -17,14 +17,14 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.DisplayObject; import mx.core.DPIClassification; -import spark.skins.android4.assets.RadioButton_up; +import spark.skins.ios7.assets.RadioButton_up; import spark.skins.mobile.supportClasses.SelectableButtonSkinBase; /** @@ -87,14 +87,14 @@ public class RadioButtonSkin extends SelectableButtonSkinBase layoutPaddingTop = 0; layoutPaddingBottom = 0; - upIconClass = spark.skins.android4.assets.RadioButton_up; - upSelectedIconClass = spark.skins.android4.assets.RadioButton_up; - downIconClass = spark.skins.android4.assets.RadioButton_down; - downSelectedIconClass = spark.skins.android4.assets.RadioButton_down; + upIconClass = spark.skins.ios7.assets.RadioButton_up; + upSelectedIconClass = spark.skins.ios7.assets.RadioButton_up; + downIconClass = spark.skins.ios7.assets.RadioButton_down; + downSelectedIconClass = spark.skins.ios7.assets.RadioButton_down; upSymbolIconClass = null; downSymbolIconClass = null; - upSymbolIconSelectedClass = spark.skins.android4.assets.RadioButton_upSymbolSelected; - downSymbolIconSelectedClass = spark.skins.android4.assets.RadioButton_downSymbolSelected; + upSymbolIconSelectedClass = spark.skins.ios7.assets.RadioButton_upSymbolSelected; + downSymbolIconSelectedClass = spark.skins.ios7.assets.RadioButton_downSymbolSelected; switch (applicationDPI) { diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListContainerSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListContainerSkin.as index e076a6b347..773fce9637 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListContainerSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListContainerSkin.as @@ -16,7 +16,7 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.Graphics; import flash.display.InteractiveObject; @@ -28,9 +28,9 @@ package spark.skins.android4 import spark.components.Group; import spark.components.SpinnerListContainer; import spark.layouts.HorizontalLayout; - import spark.skins.android4.assets.SpinnerListContainerBackground; - import spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; - import spark.skins.android4.assets.SpinnerListContainerShadow; + import spark.skins.ios7.assets.SpinnerListContainerBackground; + import spark.skins.ios7.assets.SpinnerListContainerSelectionIndicator; + import spark.skins.ios7.assets.SpinnerListContainerShadow; import spark.skins.mobile.supportClasses.MobileSkin; use namespace mx_internal; @@ -62,9 +62,9 @@ package spark.skins.android4 { super(); - borderClass = spark.skins.android4.assets.SpinnerListContainerBackground; - selectionIndicatorClass = spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; - shadowClass = spark.skins.android4.assets.SpinnerListContainerShadow; + borderClass = spark.skins.ios7.assets.SpinnerListContainerBackground; + selectionIndicatorClass = spark.skins.ios7.assets.SpinnerListContainerSelectionIndicator; + shadowClass = spark.skins.ios7.assets.SpinnerListContainerShadow; cornerRadius = 0; borderThickness = 0; switch (applicationDPI) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListSkin.as index b86553fd91..fa1ed35d51 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/SpinnerListSkin.as @@ -16,7 +16,7 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.InteractiveObject; @@ -29,7 +29,7 @@ package spark.skins.android4 import spark.components.SpinnerList; import spark.components.SpinnerListItemRenderer; import spark.layouts.VerticalSpinnerLayout; - import spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; + import spark.skins.ios7.assets.SpinnerListContainerSelectionIndicator; import spark.skins.mobile.supportClasses.MobileSkin; @@ -56,7 +56,7 @@ package spark.skins.android4 { super(); - selectionIndicatorClass = spark.skins.android4.assets.SpinnerListContainerSelectionIndicator; + selectionIndicatorClass = spark.skins.ios7.assets.SpinnerListContainerSelectionIndicator; borderThickness = 1; switch (applicationDPI) { @@ -227,7 +227,7 @@ package spark.skins.android4 // Only support vertical scrolling scroller.setStyle("verticalScrollPolicy","on"); scroller.setStyle("horizontalScrollPolicy", "off"); - scroller.setStyle("skinClass", spark.skins.android4.SpinnerListScrollerSkin); + scroller.setStyle("skinClass", spark.skins.ios7.SpinnerListScrollerSkin); addChild(scroller); } diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextAreaSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextAreaSkin.as index d48377d3f3..aca69348f2 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextAreaSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextAreaSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import mx.core.DPIClassification; @@ -27,7 +27,7 @@ package spark.skins.android4 import spark.components.supportClasses.IStyleableEditableText; import spark.components.supportClasses.ScrollableStageText; import spark.components.supportClasses.StyleableTextField; - import spark.skins.android4.supportClasses.StageTextSkinBase; + import spark.skins.ios7.supportClasses.StageTextSkinBase; use namespace mx_internal; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextInputSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextInputSkin.as index 7f92647f1d..388a599be8 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextInputSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/StageTextInputSkin.as @@ -17,13 +17,13 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import spark.components.TextInput; import spark.components.supportClasses.IStyleableEditableText; import spark.components.supportClasses.ScrollableStageText; import spark.components.supportClasses.StyleableTextField; - import spark.skins.android4.supportClasses.StageTextSkinBase; + import spark.skins.ios7.supportClasses.StageTextSkinBase; /** * ActionScript-based skin for TextInput controls in mobile applications that uses a diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TabbedViewNavigatorTabBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TabbedViewNavigatorTabBarSkin.as index eea5948eea..1cf62b7747 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TabbedViewNavigatorTabBarSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TabbedViewNavigatorTabBarSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import spark.components.ButtonBarButton; import spark.components.DataGroup; @@ -72,19 +72,19 @@ public class TabbedViewNavigatorTabBarSkin extends ButtonBarSkin if (!firstButton) { firstButton = new ButtonBarButtonClassFactory(ButtonBarButton); - firstButton.skinClass = spark.skins.android4.ButtonBarFirstButtonSkin; + firstButton.skinClass = spark.skins.ios7.ButtonBarFirstButtonSkin; } if (!lastButton) { lastButton = new ButtonBarButtonClassFactory(ButtonBarButton); - lastButton.skinClass = spark.skins.android4.ButtonBarFirstButtonSkin; + lastButton.skinClass = spark.skins.ios7.ButtonBarFirstButtonSkin; } if (!middleButton) { middleButton = new ButtonBarButtonClassFactory(ButtonBarButton); - middleButton.skinClass = spark.skins.android4.ButtonBarMiddleButtonSkin; + middleButton.skinClass = spark.skins.ios7.ButtonBarMiddleButtonSkin; } if (!dataGroup) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextAreaSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextAreaSkin.as index 0c231a6f2d..7f87388e30 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextAreaSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextAreaSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.events.Event; @@ -43,7 +43,7 @@ import spark.components.Scroller; import spark.components.TextArea; import spark.components.supportClasses.StyleableTextField; import spark.events.CaretBoundsChangeEvent; -import spark.skins.android4.supportClasses.TextSkinBase; +import spark.skins.ios7.supportClasses.TextSkinBase; use namespace mx_internal; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextInputSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextInputSkin.as index 7488e1f7c7..3533aa1bcb 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextInputSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TextInputSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.events.Event; import flash.events.FocusEvent; @@ -31,7 +31,7 @@ package spark.skins.android4 import spark.components.TextInput; import spark.components.supportClasses.StyleableTextField; - import spark.skins.android4.supportClasses.TextSkinBase; + import spark.skins.ios7.supportClasses.TextSkinBase; use namespace mx_internal; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ToggleSwitchSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ToggleSwitchSkin.as index c5b99aa35e..41144f9be0 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ToggleSwitchSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ToggleSwitchSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.BlendMode; @@ -30,8 +30,8 @@ package spark.skins.android4 import spark.components.ToggleSwitch; import spark.components.supportClasses.StyleableTextField; import spark.core.SpriteVisualElement; - import spark.skins.android4.assets.ToggleSwitchBackground; - import spark.skins.android4.assets.ToggleSwitchThumb_off; + import spark.skins.ios7.assets.ToggleSwitchBackground; + import spark.skins.ios7.assets.ToggleSwitchThumb_off; import spark.skins.mobile.supportClasses.MobileSkin; @@ -162,9 +162,9 @@ package spark.skins.android4 { super(); - switchTrack = spark.skins.android4.assets.ToggleSwitchBackground; - switchOn = spark.skins.android4.assets.ToggleSwitchThumb_on; - switchOff = spark.skins.android4.assets.ToggleSwitchThumb_off; + switchTrack = spark.skins.ios7.assets.ToggleSwitchBackground; + switchOn = spark.skins.ios7.assets.ToggleSwitchThumb_on; + switchOff = spark.skins.ios7.assets.ToggleSwitchThumb_off; switch(applicationDPI) { diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentActionButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentActionButtonSkin.as index dadab3c2ae..b88042a383 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentActionButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentActionButtonSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.DisplayObject; import flash.display.Graphics; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentNavigationButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentNavigationButtonSkin.as index 0709b65849..6eccad4a3f 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentNavigationButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/TransparentNavigationButtonSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.DisplayObject; import flash.display.Graphics; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarSkin.as index 1a2a0df78e..de32b42754 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import mx.core.DPIClassification; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarThumbSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarThumbSkin.as index 9d17a9752b..8d87c76829 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarThumbSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/VScrollBarThumbSkin.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.CapsStyle; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuItemSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuItemSkin.as index 6a75c65caa..77e58650cf 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuItemSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ViewMenuItemSkin.as @@ -16,7 +16,7 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4 +package spark.skins.ios7 { import flash.display.GradientType; import flash.display.Graphics; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/CalloutArrow.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/CalloutArrow.as index c18d457a88..f9c9d167df 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/CalloutArrow.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/CalloutArrow.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4.supportClasses +package spark.skins.ios7.supportClasses { import flash.display.BlendMode; import flash.display.GradientType; @@ -35,7 +35,7 @@ import mx.utils.ColorUtil; import spark.components.Application; import spark.components.ArrowDirection; import spark.components.Callout; -import spark.skins.android4.CalloutSkin; +import spark.skins.ios7.CalloutSkin; import spark.skins.mobile.supportClasses.MobileSkin; use namespace mx_internal; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/StageTextSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/StageTextSkinBase.as index 3dc1966264..10e063261f 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/StageTextSkinBase.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/StageTextSkinBase.as @@ -16,7 +16,7 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4.supportClasses +package spark.skins.ios7.supportClasses { import flash.display.DisplayObject; import flash.events.FocusEvent; diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/TextSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/TextSkinBase.as index 31bf7eaa96..319d4a093f 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/TextSkinBase.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/supportClasses/TextSkinBase.as @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// -package spark.skins.android4.supportClasses +package spark.skins.ios7.supportClasses { import flash.display.DisplayObject; From aa682b4e5fc4cf0316f482efa713102cf65f6548 Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Thu, 2 Oct 2014 13:58:17 +0200 Subject: [PATCH 112/392] Added spark.skins.ios7 variation Signed-off-by: Erik de Bruin --- .../mobiletheme/src/MobileThemeClasses.as | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/frameworks/projects/mobiletheme/src/MobileThemeClasses.as b/frameworks/projects/mobiletheme/src/MobileThemeClasses.as index 1dff34d5ce..82b64f9702 100644 --- a/frameworks/projects/mobiletheme/src/MobileThemeClasses.as +++ b/frameworks/projects/mobiletheme/src/MobileThemeClasses.as @@ -107,5 +107,37 @@ package import spark.skins.android4.VScrollBarThumbSkin; spark.skins.android4.VScrollBarThumbSkin; import spark.skins.android4.supportClasses.CalloutArrow; spark.skins.android4.supportClasses.CalloutArrow; + //iOS7+ skins + import spark.skins.ios7.ActionBarSkin; spark.skins.ios7.ActionBarSkin; + import spark.skins.ios7.BusyIndicatorSkin; spark.skins.ios7.BusyIndicatorSkin; + import spark.skins.ios7.ButtonBarFirstButtonSkin; spark.skins.ios7.ButtonBarFirstButtonSkin; + import spark.skins.ios7.ButtonBarMiddleButtonSkin; spark.skins.ios7.ButtonBarMiddleButtonSkin; + import spark.skins.ios7.ButtonBarSkin; spark.skins.ios7.ButtonBarSkin; + import spark.skins.ios7.ButtonSkin; spark.skins.ios7.ButtonSkin; + import spark.skins.ios7.CalloutSkin; spark.skins.ios7.CalloutSkin; + import spark.skins.ios7.CheckBoxSkin; spark.skins.ios7.CheckBoxSkin; + import spark.skins.ios7.HScrollBarSkin; spark.skins.ios7.HScrollBarSkin; + import spark.skins.ios7.HScrollBarThumbSkin; spark.skins.ios7.HScrollBarThumbSkin; + import spark.skins.ios7.HSliderSkin; spark.skins.ios7.HSliderSkin; + import spark.skins.ios7.HSliderThumbSkin; spark.skins.ios7.HSliderThumbSkin; + import spark.skins.ios7.HSliderTrackSkin; spark.skins.ios7.HSliderTrackSkin; + import spark.skins.ios7.RadioButtonSkin; spark.skins.ios7.RadioButtonSkin; + import spark.skins.ios7.SpinnerListContainerSkin; spark.skins.ios7.SpinnerListContainerSkin; + import spark.skins.ios7.SpinnerListScrollerSkin; spark.skins.ios7.SpinnerListScrollerSkin; + import spark.skins.ios7.SpinnerListSkin; spark.skins.ios7.SpinnerListSkin; + import spark.skins.ios7.StageTextAreaSkin; spark.skins.ios7.StageTextAreaSkin; + import spark.skins.ios7.StageTextInputSkin; spark.skins.ios7.StageTextInputSkin; + import spark.skins.ios7.TabbedViewNavigatorTabBarSkin; spark.skins.ios7.TabbedViewNavigatorTabBarSkin; + import spark.skins.ios7.TextAreaSkin; spark.skins.ios7.TextAreaSkin; + import spark.skins.ios7.TextInputSkin; spark.skins.ios7.TextInputSkin; + import spark.skins.ios7.ToggleSwitchSkin; spark.skins.ios7.ToggleSwitchSkin; + import spark.skins.ios7.TransparentActionButtonSkin; spark.skins.ios7.TransparentActionButtonSkin; + import spark.skins.ios7.TransparentNavigationButtonSkin; spark.skins.ios7.TransparentNavigationButtonSkin; + import spark.skins.ios7.ViewMenuItemSkin; spark.skins.ios7.ViewMenuItemSkin; + import spark.skins.ios7.ViewMenuSkin; spark.skins.ios7.ViewMenuSkin; + import spark.skins.ios7.VScrollBarSkin; spark.skins.ios7.VScrollBarSkin; + import spark.skins.ios7.VScrollBarThumbSkin; spark.skins.ios7.VScrollBarThumbSkin; + import spark.skins.ios7.supportClasses.CalloutArrow; spark.skins.ios7.supportClasses.CalloutArrow; + } } From 6c303132a93e7327c379fa6cb0cb25a38be83396 Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Thu, 2 Oct 2014 14:04:37 +0200 Subject: [PATCH 113/392] Add iOS7+ ButtonSkin to Button style Signed-off-by: Erik de Bruin --- frameworks/projects/mobiletheme/defaults.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frameworks/projects/mobiletheme/defaults.css b/frameworks/projects/mobiletheme/defaults.css index 7de40e3f7e..4172f6e0ae 100644 --- a/frameworks/projects/mobiletheme/defaults.css +++ b/frameworks/projects/mobiletheme/defaults.css @@ -974,6 +974,18 @@ requires new os-version selector (cf FLEX-FLEX-33949) } } +/* ios 7+ new theme */ + +@media (os-platform:"IOS") AND (min-os-version: 7) +{ + + Button + { + skinClass: ClassReference("spark.skins.ios7.ButtonSkin"); + } + +} + /* Android 4.x specific Mobile Flex skins and styles. Defaults sizes are based on From aab543fc34b1f67b234a84bda1320c27c2c32b62 Mon Sep 17 00:00:00 2001 From: OmPrakash Muppirala Date: Thu, 2 Oct 2014 17:13:43 -0700 Subject: [PATCH 114/392] Add a global style for ios7 with basic entries --- frameworks/projects/mobiletheme/defaults.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frameworks/projects/mobiletheme/defaults.css b/frameworks/projects/mobiletheme/defaults.css index 4172f6e0ae..9a4c15bcbc 100644 --- a/frameworks/projects/mobiletheme/defaults.css +++ b/frameworks/projects/mobiletheme/defaults.css @@ -978,10 +978,21 @@ requires new os-version selector (cf FLEX-FLEX-33949) @media (os-platform:"IOS") AND (min-os-version: 7) { + + global + { + fontFamily: RobotoRegular; + fontWeight: "normal"; + fontSize: 24; + color: #007AFF; + textShadowColor: #FFFFFF; + textShadowAlpha: 0; + } Button { skinClass: ClassReference("spark.skins.ios7.ButtonSkin"); + fontFamily: RobotoBold; } } From 8ea85e5f5b797ddd6567152fddf4b3f4b27cfbe5 Mon Sep 17 00:00:00 2001 From: OmPrakash Muppirala Date: Thu, 2 Oct 2014 17:14:39 -0700 Subject: [PATCH 115/392] First shot at ios7 Button skin --- .../src/spark/skins/ios7/ButtonSkin.as | 5 ++- .../src/spark/skins/ios7/assets/Button_up.fxg | 45 +++---------------- 2 files changed, 11 insertions(+), 39 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as index 040b70cea9..91851fe298 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as @@ -90,8 +90,11 @@ public class ButtonSkin extends ButtonSkinBase public function ButtonSkin() { super(); + //In iOS7, buttons look like simple links, without any shape containing the text + //We still need to assign an asset to determine the size of the button + //Button_up is a simple transparent graphic object upBorderSkin = spark.skins.ios7.assets.Button_up; - downBorderSkin = spark.skins.ios7.assets.Button_down; + downBorderSkin = spark.skins.ios7.assets.Button_up; layoutCornerEllipseSize = 0; switch (applicationDPI) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_up.fxg index af7d033a3f..59854b1ce7 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_up.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/Button_up.fxg @@ -19,42 +19,11 @@ --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + \ No newline at end of file From b9e6d76349b8c0b94e9b9fd3d0ed3e1fd9847bc1 Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Fri, 3 Oct 2014 10:57:06 +0200 Subject: [PATCH 116/392] Remove unneeded import Signed-off-by: Erik de Bruin --- .../projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as | 1 - 1 file changed, 1 deletion(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as index 91851fe298..44f362fdbe 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as @@ -27,7 +27,6 @@ import mx.core.mx_internal; import mx.events.FlexEvent; import spark.components.supportClasses.StyleableTextField; -import spark.skins.ios7.assets.Button_down; import spark.skins.ios7.assets.Button_up; import spark.skins.mobile.supportClasses.ButtonSkinBase; From b685eae3206572badbee1a056d6f2b8c9956ff50 Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Fri, 3 Oct 2014 11:02:43 +0200 Subject: [PATCH 117/392] Added 'all' mobile component CSS to iOS7+ selector Signed-off-by: Erik de Bruin --- frameworks/projects/mobiletheme/defaults.css | 208 +++++++++++++++++++ 1 file changed, 208 insertions(+) diff --git a/frameworks/projects/mobiletheme/defaults.css b/frameworks/projects/mobiletheme/defaults.css index 9a4c15bcbc..0b78b0c697 100644 --- a/frameworks/projects/mobiletheme/defaults.css +++ b/frameworks/projects/mobiletheme/defaults.css @@ -989,12 +989,220 @@ requires new os-version selector (cf FLEX-FLEX-33949) textShadowAlpha: 0; } + ActionBar + { + chromeColor: #DEDEDD; + defaultButtonAppearance: normal; + skinClass: ClassReference("spark.skins.ios7.ActionBarSkin"); + textShadowAlpha: 0; + textShadowColor: #000000; + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; + } + + ActionBar #titleDisplay + { + color: #333333; + fontSize: 24; + fontFamily: RobotoBold; + } + + ActionBar ButtonBase + { + color: #333333; + fontFamily: RobotoBold; + fontWeight: bold; + } + + ActionBar Group#actionGroup Button + { + skinClass: ClassReference("spark.skins.ios7.TransparentActionButtonSkin"); + } + Button { skinClass: ClassReference("spark.skins.ios7.ButtonSkin"); fontFamily: RobotoBold; } + Button.emphasized + { + skinClass: ClassReference("spark.skins.mobile.DefaultButtonSkin"); + } + + ButtonBar + { + skinClass: ClassReference("spark.skins.ios7.ButtonBarSkin"); + fontFamily: RobotoBold; + fontWeight: "bold"; + } + + Callout + { + backgroundColor: #33B5E5; + contentBackgroundColor: #FFFFFF; + gap: 12; + skinClass: ClassReference("spark.skins.ios7.CalloutSkin"); + borderThickness: 0; + borderColor: 0; + } + + Callout ViewNavigator ActionBar + { + skinClass: ClassReference("spark.skins.mobile.CalloutActionBarSkin"); + defaultButtonAppearance: none; + paddingLeft: 0; + paddingRight: 0; + } + + Callout ViewNavigator ActionBar.beveled + { + paddingLeft: 0; + paddingRight: 0; + } + + Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button + { + skinClass: ClassReference("spark.skins.mobile.BeveledActionButtonSkin"); + } + + Callout ViewNavigator ActionBar.beveled Group#navigationGroup Button.emphasized + { + skinClass: ClassReference("spark.skins.mobile.DefaultBeveledActionButtonSkin"); + } + + Callout#viewNavigatorPopUp + { + contentBackgroundAppearance: none; + } + + CheckBox + { + skinClass: ClassReference("spark.skins.ios7.CheckBoxSkin"); + chromeColor: #FFFFFF; + } + + HScrollBar + { + skinClass: ClassReference("spark.skins.ios7.HScrollBarSkin"); + thumbColor: #333333; + } + + HSlider + { + skinClass: ClassReference("spark.skins.ios7.HSliderSkin"); + fontFamily: RobotoBold; + } + + RadioButton + { + skinClass: ClassReference("spark.skins.ios7.RadioButtonSkin"); + chromeColor: #FFFFFF; + } + + SpinnerList + { + skinClass: ClassReference("spark.skins.ios7.SpinnerListSkin"); + } + + SpinnerListContainer + { + skinClass: ClassReference("spark.skins.ios7.SpinnerListContainerSkin"); + } + + SpinnerListItemRenderer + { + paddingTop : 18; + paddingBottom : 18; + paddingLeft : 6; + paddingRight : 6; + } + + TabbedViewNavigator #tabBar + { + fontFamily: RobotoRegular; + chromeColor: #484848; + color: #000000; + fontSize: 20; + fontWeight: normal; + iconPlacement: top; + interactionMode: mouse; + skinClass: ClassReference("spark.skins.ios7.TabbedViewNavigatorTabBarSkin"); + textShadowAlpha: 0; + textShadowColor: #000000; + } + + TextInput + { + skinClass: ClassReference("spark.skins.ios7.StageTextInputSkin"); + contentBackgroundAlpha: 0; + focusColor: #33B5E5; + contentBackgroundColor: #DEDEDD; + contentBackgroundBorder: "flat"; + fontFamily: RobotoRegular; + selectionHighlighting: "never"; + focusEnabled:"false"; + focusThickness: 0; + } + + TextArea + { + skinClass: ClassReference("spark.skins.ios7.StageTextAreaSkin"); + contentBackgroundAlpha: 1; + contentBackgroundBorder: "rectangle"; + contentBackgroundColor: #DEDEDD; + focusColor: #33B5E5; + fontFamily: RobotoRegular; + selectionHighlighting: "never"; + focusEnabled:"false"; + focusThickness: 0; + } + + ToggleSwitch + { + accentColor: #3F7FBA; + color: #FFFFFF; + skinClass: ClassReference("spark.skins.ios7.ToggleSwitchSkin"); + slideDuration: 125; + textShadowAlpha: .65; + textShadowColor: #000000; + } + + VScrollBar + { + skinClass: ClassReference("spark.skins.ios7.VScrollBarSkin"); + thumbColor: #333333; + } + + ViewMenu + { + skinClass: ClassReference("spark.skins.ios7.ViewMenuSkin"); + } + + ViewMenuItem + { + iconPlacement : "left"; + chromeColor : #FFFFFF; + focusColor: #DEDEDD; + paddingBottom: 8; + paddingLeft: 8; + paddingRight: 8; + paddingTop: 8; + skinClass : ClassReference("spark.skins.ios7.ViewMenuItemSkin"); + } + + MobileBusyIndicator + { + skinClass: ClassReference("spark.skins.ios7.BusyIndicatorSkin"); + } + + ViewNavigator + { + skinClass: ClassReference("spark.skins.mobile.ViewNavigatorSkin"); + } + } From cea42d7da8e644fd392053e54fe14830e7249155 Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Fri, 3 Oct 2014 11:14:20 +0200 Subject: [PATCH 118/392] Prepare dpi specific styles for iOS7+ Integrated the 'status bar' fix (FLEX-33949) into the new dpi specific styles copied from the Android section. Signed-off-by: Erik de Bruin --- frameworks/projects/mobiletheme/defaults.css | 240 +++++++++++++++---- 1 file changed, 199 insertions(+), 41 deletions(-) diff --git a/frameworks/projects/mobiletheme/defaults.css b/frameworks/projects/mobiletheme/defaults.css index 0b78b0c697..8e93db5861 100644 --- a/frameworks/projects/mobiletheme/defaults.css +++ b/frameworks/projects/mobiletheme/defaults.css @@ -931,48 +931,7 @@ global } } -/* ios 7 status bar -requires new os-version selector (cf FLEX-FLEX-33949) -*/ -@media (application-dpi: 120) AND (os-platform:"IOS") AND (min-os-version: 7) -{ - Application - { - osStatusBarHeight: 15; - } -} - -@media (application-dpi: 160) AND (os-platform:"IOS") AND (min-os-version: 7) -{ - Application { - osStatusBarHeight: 20; - } -} - -@media (application-dpi: 240) AND (os-platform:"IOS") AND (min-os-version: 7) -{ - Application - { - osStatusBarHeight: 30; - } -} - -@media (application-dpi: 320) AND (os-platform:"IOS") AND (min-os-version: 7) -{ - Application - { - osStatusBarHeight: 40; - } -} - -@media (application-dpi: 480) AND (os-platform:"IOS") AND (min-os-version: 7) -{ - Application - { - osStatusBarHeight: 60; - } -} /* ios 7+ new theme */ @@ -1205,6 +1164,205 @@ requires new os-version selector (cf FLEX-FLEX-33949) } +@media (os-platform:"IOS") AND (min-os-version: 7) AND (application-dpi: 120) +{ + global + { + fontSize: 12; + focusThickness: 2; + } + + ActionBar.beveled + { + paddingLeft: 4; + paddingRight: 4; + } + + ActionBar #titleDisplay + { + fontSize: 14; + } + + Application + { + osStatusBarHeight: 15; + } + + SpinnerListItemRenderer + { + paddingTop : 9; + paddingBottom : 9; + paddingLeft : 3; + paddingRight : 3; + } + + TabbedViewNavigator #tabBar + { + fontSize: 11; + } +} + +@media (os-platform:"IOS") AND (min-os-version: 7) AND (application-dpi: 160) +{ + global + { + fontSize: 16; + focusThickness: 2; + } + + ActionBar + { + paddingBottom: 1; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 1; + } + + ActionBar #titleDisplay + { + fontSize: 18; + } + + Application { + osStatusBarHeight: 20; + } + + SpinnerListItemRenderer + { + paddingTop : 12; + paddingBottom : 12; + paddingLeft : 4; + paddingRight : 4; + } + + TabbedViewNavigator #tabBar + { + fontSize: 14; + } +} + +@media (os-platform:"IOS") AND (min-os-version: 7) AND (application-dpi: 240) +{ + Application + { + osStatusBarHeight: 30; + } +} + +@media (os-platform:"IOS") AND (min-os-version: 7) AND (application-dpi: 320) +{ + global + { + fontSize: 32; + } + + ActionBar + { + paddingBottom: 2; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 2; + } + + ActionBar #titleDisplay + { + fontSize: 36; + } + + Application + { + osStatusBarHeight: 40; + } + + SpinnerListItemRenderer + { + paddingTop : 24; + paddingBottom : 24; + paddingLeft : 8; + paddingRight : 8; + } + + TabbedViewNavigator #tabBar + { + fontSize: 28; + } +} + +@media (os-platform:"IOS") AND (min-os-version: 7) AND (application-dpi: 480) +{ + global + { + fontSize: 48; + focusThickness: 6; + } + + ActionBar + { + paddingBottom: 4; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 4; + } + + ActionBar #titleDisplay + { + fontSize: 54; + } + + Application + { + osStatusBarHeight: 60; + } + + SpinnerListItemRenderer + { + paddingTop : 36; + paddingBottom : 36; + paddingLeft : 12; + paddingRight : 12; + } + + TabbedViewNavigator #tabBar + { + fontSize: 42; + } +} + +@media (os-platform:"IOS") AND (min-os-version: 7) AND (application-dpi: 640) +{ + global + { + fontSize: 64; + } + + ActionBar + { + paddingBottom: 4; + paddingLeft: 0; + paddingRight: 0; + paddingTop: 4; + } + + ActionBar #titleDisplay + { + fontSize: 72; + } + + SpinnerListItemRenderer + { + paddingTop : 48; + paddingBottom : 48; + paddingLeft : 16; + paddingRight : 16; + } + + TabbedViewNavigator #tabBar + { + fontSize: 56; + } +} + + /* Android 4.x specific Mobile Flex skins and styles. Defaults sizes are based on From c588633a9871570b85a33b1c8242ccbdbfd56472 Mon Sep 17 00:00:00 2001 From: OmPrakash Muppirala Date: Fri, 3 Oct 2014 15:09:13 -0700 Subject: [PATCH 119/392] Add IOSPlatFormVersionOveride so that we can simulate IOS7 in the AIR simulator. To use this, add -includes=mx.utils.IOSPlatformVersionOverride to your mobile project's Flex Compiler arguments From 33ba189ee87f0050831af8ff61b9f38100828178 Mon Sep 17 00:00:00 2001 From: OmPrakash Muppirala Date: Fri, 3 Oct 2014 15:17:54 -0700 Subject: [PATCH 120/392] Add IOSPlatFormVersionOveride so that we can simulate IOS7 in the AIR simulator. To use this, add -includes=mx.utils.IOSPlatformVersionOverride to your mobile project's Flex Compiler arguments. --- .gitignore | 1 + .../framework/src/FrameworkClasses.as | 1 + .../mx/utils/IOSPlatformVersionOverride.as | 42 +++++++++++++++++++ .../framework/src/mx/utils/Platform.as | 17 ++++++-- 4 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 frameworks/projects/framework/src/mx/utils/IOSPlatformVersionOverride.as diff --git a/.gitignore b/.gitignore index 0eccc826c0..bdec6d1bf9 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,4 @@ air-sdk-description.xml samples/descriptor-sample.xml include/FlashRuntimeExtensions.h +/frameworks/projects/mobiletheme/src/spark/skins/ios7/visualcomps/*.ai diff --git a/frameworks/projects/framework/src/FrameworkClasses.as b/frameworks/projects/framework/src/FrameworkClasses.as index cb2a0939df..51e1c649a9 100644 --- a/frameworks/projects/framework/src/FrameworkClasses.as +++ b/frameworks/projects/framework/src/FrameworkClasses.as @@ -164,6 +164,7 @@ internal class FrameworkClasses import mx.utils.DisplayUtil; DisplayUtil; import mx.utils.GetTimerUtil; GetTimerUtil; import mx.utils.HSBColor; HSBColor; + import mx.utils.IOSPlatformVersionOverride; IOSPlatformVersionOverride; import mx.utils.LinkedList; LinkedList; import mx.utils.LinkedListNode; LinkedListNode; import mx.utils.OnDemandEventDispatcher; OnDemandEventDispatcher; diff --git a/frameworks/projects/framework/src/mx/utils/IOSPlatformVersionOverride.as b/frameworks/projects/framework/src/mx/utils/IOSPlatformVersionOverride.as new file mode 100644 index 0000000000..b1cf61e935 --- /dev/null +++ b/frameworks/projects/framework/src/mx/utils/IOSPlatformVersionOverride.as @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.utils +{ + import flash.display.DisplayObject; + import flash.system.Capabilities; + + import mx.core.mx_internal; + + [Mixin] + public class IOSPlatformVersionOverride + { + public static function init(root:DisplayObject):void + { + var c:Class = Capabilities; + //Set this override value on if we are + // a. on the AIR Simulator + // b. simulating iOS + if(c.version.indexOf("IOS") > -1 && c.manufacturer != "Adobe iOS") + { + Platform.mx_internal::iosVersionOverride = "7.0"; + } + } + } +} \ No newline at end of file diff --git a/frameworks/projects/framework/src/mx/utils/Platform.as b/frameworks/projects/framework/src/mx/utils/Platform.as index 6c404088a3..222425c44a 100644 --- a/frameworks/projects/framework/src/mx/utils/Platform.as +++ b/frameworks/projects/framework/src/mx/utils/Platform.as @@ -51,11 +51,18 @@ public class Platform protected static var _isBrowser:Boolean; protected static var _isAir:Boolean; private static var _osVersion: String = null; + /** - * This value is set from AndroidVersionMediaQueryParserOverride + * This value is set from AndroidPlatformVersionOverride * */ mx_internal static var androidVersionOverride:String; + + /** + * This value is set from IOSPlatformVersionOverride + * + */ + mx_internal static var iosVersionOverride:String; /** * Returns true if the application is running on IOS. @@ -235,14 +242,18 @@ public class Platform { //We needed to compute _osVersion later than getPlatforms, because it relies on resources that ready later if (_osVersion == null){ - if(mx_internal::androidVersionOverride == null) + if(mx_internal::androidVersionOverride == null && mx_internal::iosVersionOverride == null) { _osVersion = computeOSVersionString(); } - else + else if(mx_internal::androidVersionOverride != null) { _osVersion = mx_internal::androidVersionOverride; } + else if(mx_internal::iosVersionOverride != null) + { + _osVersion = mx_internal::iosVersionOverride; + } } return _osVersion; } From 89e7d8c395cba68490c1af404d7039f9cf8aba9b Mon Sep 17 00:00:00 2001 From: OmPrakash Muppirala Date: Fri, 3 Oct 2014 16:20:08 -0700 Subject: [PATCH 121/392] Remove labeldisplayshadow since it is not required --- .../src/spark/skins/ios7/ButtonSkin.as | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as index 44f362fdbe..7c19e5a7d3 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonSkin.as @@ -278,18 +278,6 @@ public class ButtonSkin extends ButtonSkinBase override protected function createChildren():void { super.createChildren(); - - if (!labelDisplayShadow && labelDisplay) - { - labelDisplayShadow = StyleableTextField(createInFontContext(StyleableTextField)); - labelDisplayShadow.styleName = this; - labelDisplayShadow.colorName = "textShadowColor"; - labelDisplayShadow.useTightTextBounds = false; - - // add shadow before display - addChildAt(labelDisplayShadow, getChildIndex(labelDisplay)); - } - setStyle("textAlign", "center"); } @@ -336,18 +324,6 @@ public class ButtonSkin extends ButtonSkinBase layoutBorder(unscaledWidth, unscaledHeight); - // update label shadow - labelDisplayShadow.alpha = getStyle("textShadowAlpha"); - labelDisplayShadow.commitStyles(); - - // don't use tightText positioning on shadow - setElementPosition(labelDisplayShadow, labelDisplay.x, labelDisplay.y + 1); - setElementSize(labelDisplayShadow, labelDisplay.width, labelDisplay.height); - - // if labelDisplay is truncated, then push it down here as well. - // otherwise, it would have gotten pushed in the labelDisplay_valueCommitHandler() - if (labelDisplay.isTruncated) - labelDisplayShadow.text = labelDisplay.text; } /** @@ -392,7 +368,6 @@ public class ButtonSkin extends ButtonSkinBase override protected function labelDisplay_valueCommitHandler(event:FlexEvent):void { super.labelDisplay_valueCommitHandler(event); - labelDisplayShadow.text = labelDisplay.text; } } From e6f039540c7ef80693ef03eadf92823199fa7a64 Mon Sep 17 00:00:00 2001 From: OmPrakash Muppirala Date: Fri, 3 Oct 2014 18:23:13 -0700 Subject: [PATCH 122/392] iOS7+ specific ButtonBar skin for all supported DPIs --- .gitignore | 1 + frameworks/projects/mobiletheme/defaults.css | 1 + .../skins/ios7/ButtonBarFirstButtonSkin.as | 47 +---- .../skins/ios7/ButtonBarLastButtonSkin.as | 56 ++++++ .../skins/ios7/ButtonBarMiddleButtonSkin.as | 46 +---- .../src/spark/skins/ios7/ButtonBarSkin.as | 8 +- .../skins/ios7/IOS7ButtonBarButtonSkinBase.as | 161 ++++++++++++++++++ .../ios7/assets/ButtonBarFirstButton_down.fxg | 13 +- .../ios7/assets/ButtonBarFirstButton_up.fxg | 6 +- .../ios7/assets/ButtonBarLastButton_down.fxg | 35 ++++ .../ios7/assets/ButtonBarLastButton_up.fxg | 30 ++++ .../assets/ButtonBarMiddleButton_down.fxg | 48 +----- .../ios7/assets/ButtonBarMiddleButton_up.fxg | 43 +---- 13 files changed, 311 insertions(+), 184 deletions(-) create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarLastButtonSkin.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/IOS7ButtonBarButtonSkinBase.as create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarLastButton_down.fxg create mode 100644 frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarLastButton_up.fxg diff --git a/.gitignore b/.gitignore index bdec6d1bf9..58381a378e 100644 --- a/.gitignore +++ b/.gitignore @@ -138,3 +138,4 @@ samples/descriptor-sample.xml include/FlashRuntimeExtensions.h /frameworks/projects/mobiletheme/src/spark/skins/ios7/visualcomps/*.ai +visualcomps diff --git a/frameworks/projects/mobiletheme/defaults.css b/frameworks/projects/mobiletheme/defaults.css index 8e93db5861..f552889a09 100644 --- a/frameworks/projects/mobiletheme/defaults.css +++ b/frameworks/projects/mobiletheme/defaults.css @@ -944,6 +944,7 @@ global fontWeight: "normal"; fontSize: 24; color: #007AFF; + highlightTextColor: #FFFFFF; textShadowColor: #FFFFFF; textShadowAlpha: 0; } diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as index b1d0e289b6..ab4a73ab24 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarFirstButtonSkin.as @@ -19,12 +19,10 @@ package spark.skins.ios7 { - import spark.skins.ios7.assets.ButtonBarFirstButton_selectedDown; - import spark.skins.ios7.assets.ButtonBarFirstButton_selectedUp; - import spark.skins.mobile.supportClasses.ButtonBarButtonSkinBase; + import spark.skins.ios7.assets.ButtonBarFirstButton_up; /** - * Android 4.x specific Button skin for the first Button in a ButtonBar. + * iOS7+ specific Button skin for the first Button in a ButtonBar. * * @see spark.components.ButtonBar#firstButton * @@ -33,19 +31,8 @@ package spark.skins.ios7 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ - public class ButtonBarFirstButtonSkin extends ButtonBarButtonSkinBase + public class ButtonBarFirstButtonSkin extends IOS7ButtonBarButtonSkinBase { - - /** - * Class to use for the border in the selected and down state. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - protected var selectedDownBorderSkin:Class; - /** * Constructor. * @@ -60,34 +47,10 @@ package spark.skins.ios7 upBorderSkin = spark.skins.ios7.assets.ButtonBarFirstButton_up; downBorderSkin = spark.skins.ios7.assets.ButtonBarFirstButton_down; - selectedBorderSkin = spark.skins.ios7.assets.ButtonBarFirstButton_selectedUp; - selectedDownBorderSkin = spark.skins.ios7.assets.ButtonBarFirstButton_selectedDown; + selectedBorderSkin = spark.skins.ios7.assets.ButtonBarFirstButton_down; + selectedDownBorderSkin = spark.skins.ios7.assets.ButtonBarFirstButton_up; } - //-------------------------------------------------------------------------- - // - // Overridden methods - // - //-------------------------------------------------------------------------- - - override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void - { - //Dont draw background - } - override protected function getBorderClassForCurrentState():Class - { - var isSelected:Boolean = currentState.indexOf("Selected") >= 0; - var isDown:Boolean = currentState.indexOf("down") >= 0; - - if (isSelected && !isDown ) - return selectedBorderSkin; - else if (isSelected && isDown) - return selectedDownBorderSkin; - else if (!isSelected && !isDown) - return upBorderSkin; - else - return downBorderSkin; - } } } \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarLastButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarLastButtonSkin.as new file mode 100644 index 0000000000..8abe433138 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarLastButtonSkin.as @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 spark.skins.ios7.assets.ButtonBarLastButton_up; + + /** + * iOS7+ specific Button skin for the last Button in a ButtonBar. + * + * @see spark.components.ButtonBar#lastButton + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class ButtonBarLastButtonSkin extends IOS7ButtonBarButtonSkinBase + { + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function ButtonBarLastButtonSkin() + { + super(); + + upBorderSkin = spark.skins.ios7.assets.ButtonBarLastButton_up + downBorderSkin = spark.skins.ios7.assets.ButtonBarLastButton_down; + selectedBorderSkin = spark.skins.ios7.assets.ButtonBarLastButton_down; + selectedDownBorderSkin = spark.skins.ios7.assets.ButtonBarLastButton_up; + } + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as index b4740cd99d..9f69ab8ba9 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarMiddleButtonSkin.as @@ -20,10 +20,7 @@ package spark.skins.ios7 { - import spark.skins.ios7.assets.ButtonBarMiddleButton_selectedDown; - import spark.skins.ios7.assets.ButtonBarMiddleButton_selectedUp; import spark.skins.ios7.assets.ButtonBarMiddleButton_up; - import spark.skins.mobile.supportClasses.ButtonBarButtonSkinBase; /** * Android 4.x specific Button skin for middle Buttons in a ButtonBar. @@ -35,19 +32,9 @@ package spark.skins.ios7 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ - public class ButtonBarMiddleButtonSkin extends ButtonBarButtonSkinBase + public class ButtonBarMiddleButtonSkin extends IOS7ButtonBarButtonSkinBase { - /** - * Class to use for the border in the selected and down state. - * - * @langversion 3.0 - * @playerversion Flash 10 - * @playerversion AIR 2.5 - * @productversion Flex 4.5 - */ - protected var selectedDownBorderSkin:Class; - /** * Constructor. * @@ -60,36 +47,11 @@ package spark.skins.ios7 { super(); - upBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_up; + upBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_up downBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_down; - selectedBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_selectedUp; - selectedDownBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_selectedDown; + selectedBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_down; + selectedDownBorderSkin = spark.skins.ios7.assets.ButtonBarMiddleButton_up; } - //-------------------------------------------------------------------------- - // - // Overridden methods - // - //-------------------------------------------------------------------------- - - override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void - { - //Dont draw background - } - - override protected function getBorderClassForCurrentState():Class - { - var isSelected:Boolean = currentState.indexOf("Selected") >= 0; - var isDown:Boolean = currentState.indexOf("down") >= 0; - - if (isSelected && !isDown ) - return selectedBorderSkin; - else if (isSelected && isDown) - return selectedDownBorderSkin; - else if (!isSelected && !isDown) - return upBorderSkin; - else - return downBorderSkin; - } } } \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as index cd120f50b9..9bc88a2046 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/ButtonBarSkin.as @@ -23,13 +23,11 @@ import spark.components.ButtonBar; import spark.components.ButtonBarButton; import spark.components.DataGroup; import spark.components.supportClasses.ButtonBarHorizontalLayout; -import spark.skins.ios7.ButtonBarFirstButtonSkin; -import spark.skins.mobile.ButtonBarLastButtonSkin; import spark.skins.mobile.supportClasses.ButtonBarButtonClassFactory; import spark.skins.mobile.supportClasses.MobileSkin; /** - * The Android 4.x specific skin class for the Spark ButtonBar component. + * iOS7+ specific skin class for the Spark ButtonBar component. * * @see spark.components.ButtonBar * @@ -112,7 +110,7 @@ public class ButtonBarSkin extends MobileSkin if (!lastButton) { lastButton = new ButtonBarButtonClassFactory(ButtonBarButton); - lastButton.skinClass = spark.skins.ios7.ButtonBarFirstButtonSkin; + lastButton.skinClass = spark.skins.ios7.ButtonBarLastButtonSkin; } if (!middleButton) @@ -126,7 +124,7 @@ public class ButtonBarSkin extends MobileSkin { dataGroup = new DataGroup(); var hLayout:ButtonBarHorizontalLayout = new ButtonBarHorizontalLayout(); - hLayout.gap = 0; + hLayout.gap = -1; dataGroup.layout = hLayout; addChild(dataGroup); } diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/IOS7ButtonBarButtonSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/ios7/IOS7ButtonBarButtonSkinBase.as new file mode 100644 index 0000000000..9ef5d38141 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/IOS7ButtonBarButtonSkinBase.as @@ -0,0 +1,161 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 mx.core.DPIClassification; + + import spark.skins.ios7.assets.ButtonBarMiddleButton_down; + import spark.skins.ios7.assets.ButtonBarMiddleButton_up; + import spark.skins.mobile.supportClasses.ButtonBarButtonSkinBase; + + /** + * iOS7+ specific Button skin base for the Buttons in a ButtonBar. + * + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public class IOS7ButtonBarButtonSkinBase extends ButtonBarButtonSkinBase + { + + /** + * Class to use for the border in the selected and down state. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + protected var selectedDownBorderSkin:Class; + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function IOS7ButtonBarButtonSkinBase() + { + super(); + + // set the dimensions to use based on the screen density + switch (applicationDPI) + { + case DPIClassification.DPI_640: + { + measuredDefaultHeight = 116; + measuredDefaultWidth = 400; + + break; + } + case DPIClassification.DPI_480: + { + measuredDefaultHeight = 88; + measuredDefaultWidth = 300; + + break; + } + case DPIClassification.DPI_320: + { + measuredDefaultHeight = 58; + measuredDefaultWidth = 200; + + break; + } + case DPIClassification.DPI_240: + { + measuredDefaultHeight = 44; + measuredDefaultWidth = 150; + + break; + } + case DPIClassification.DPI_120: + { + measuredDefaultHeight = 22; + measuredDefaultWidth = 75; + + break; + } + default: + { + // default DPI_160 + measuredDefaultHeight = 29; + measuredDefaultWidth = 100; + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void + { + //Dont draw background + } + + override protected function getBorderClassForCurrentState():Class + { + var isSelected:Boolean = currentState.indexOf("Selected") >= 0; + var isDown:Boolean = currentState.indexOf("down") >= 0; + + if (isSelected && !isDown ) + return selectedBorderSkin; + else if (isSelected && isDown) + return selectedDownBorderSkin; + else if (!isSelected && !isDown) + return upBorderSkin; + else + return downBorderSkin; + } + + override protected function commitCurrentState():void + { + super.commitCurrentState(); + var isSelected:Boolean = currentState.indexOf("Selected") >= 0; + var isDown:Boolean = currentState.indexOf("down") >= 0; + + if(xor(isSelected,isDown)) + { + var highlightColor:uint = getStyle("highlightTextColor"); + labelDisplay.setStyle("color",highlightColor); + } + else + { + var color:uint = getStyle("color"); + labelDisplay.setStyle("color",color); + } + + } + + private function xor(lhs:Boolean, rhs:Boolean):Boolean { + return !( lhs && rhs ) && ( lhs || rhs ); + } + + } +} \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_down.fxg index 4a7c5f7f2d..d7fc8a8d33 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_down.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_down.fxg @@ -19,16 +19,11 @@ --> - - - - - - - + + - + - +
\ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_up.fxg index 59b9cd9144..bacd138778 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_up.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarFirstButton_up.fxg @@ -19,10 +19,10 @@ --> - - + + - + diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarLastButton_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarLastButton_down.fxg new file mode 100644 index 0000000000..656cce3b25 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarLastButton_down.fxg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarLastButton_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarLastButton_up.fxg new file mode 100644 index 0000000000..5dfab90532 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarLastButton_up.fxg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_down.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_down.fxg index 4e4f80b7af..b0ae67a77b 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_down.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_down.fxg @@ -19,52 +19,12 @@ --> - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + \ No newline at end of file diff --git a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_up.fxg b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_up.fxg index be1b61922d..0b95878461 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_up.fxg +++ b/frameworks/projects/mobiletheme/src/spark/skins/ios7/assets/ButtonBarMiddleButton_up.fxg @@ -19,47 +19,12 @@ --> - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file From 105ae47b3e46420044b1116370c77e2982cf8c63 Mon Sep 17 00:00:00 2001 From: Erik de Bruin Date: Mon, 6 Oct 2014 09:50:18 +0200 Subject: [PATCH 123/392] FLEX-34213 - allow adjustment of maxDragRate (patch contributed by 'after24') Replaced the private static const MAX_DRAG_RATE by the public static property maxDragRate. This allows the value to be manually set to > 30, smoothing the apparent performace of mobile lists. Note: the patch suggested a default value of 90; I decided to keep the previous default of 30, in order to minimise the chance of side effects. Signed-off-by: Erik de Bruin --- .../spark/src/spark/components/Scroller.as | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/frameworks/projects/spark/src/spark/components/Scroller.as b/frameworks/projects/spark/src/spark/components/Scroller.as index fb18a5656b..3ca9155901 100644 --- a/frameworks/projects/spark/src/spark/components/Scroller.as +++ b/frameworks/projects/spark/src/spark/components/Scroller.as @@ -517,13 +517,6 @@ public class Scroller extends SkinnableComponent */ private static const ZERO_POINT:Point = new Point(0,0); - /** - * @private - * Maximum number of times per second we will change the scroll position - * and update the display while dragging. - */ - private static const MAX_DRAG_RATE:Number = 30; - /** * @private * The name of the viewport's horizontal scroll position property @@ -1352,8 +1345,39 @@ public class Scroller extends SkinnableComponent } } - - + //---------------------------------- + // maxDragRate + //---------------------------------- + + private static var _maxDragRate:Number = 30; + + [Inspectable(category="General", defaultValue="30")] + + /** + * + * Maximum number of times per second the scroll position + * and the display will be updated while dragging. + * + * @default 30 + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + + public static function get maxDragRate():Number + { + return _maxDragRate; + } + + public static function set maxDragRate(value:Number):void + { + _maxDragRate = value; + } + + + //-------------------------------------------------------------------------- // // Methods @@ -3328,7 +3352,7 @@ public class Scroller extends SkinnableComponent canScrollHorizontally, canScrollVertically, Math.round(minSlopInches * Capabilities.screenDPI), - dragEventThinning ? MAX_DRAG_RATE : NaN); + dragEventThinning ? _maxDragRate : NaN); event.stopImmediatePropagation(); } else @@ -3381,7 +3405,7 @@ public class Scroller extends SkinnableComponent canScrollHorizontally, canScrollVertically, Math.round(minSlopInches * Capabilities.screenDPI), - dragEventThinning ? MAX_DRAG_RATE : NaN); + dragEventThinning ? _maxDragRate : NaN); } /** From e72f72e3ab217d5b877884be13573bbf1e3bd051 Mon Sep 17 00:00:00 2001 From: Mahmoud Ali Date: Mon, 6 Oct 2014 22:17:36 -0400 Subject: [PATCH 124/392] Added FlatSpark project --- frameworks/projects/flatspark/defaults.css | 97 +++++ .../src/assets/fonts/awesome/FontAwesome.otf | Bin 0 -> 62856 bytes .../src/assets/fonts/lato/Lato_Black.otf | Bin 0 -> 47436 bytes .../assets/fonts/lato/Lato_Black_Italic.otf | Bin 0 -> 46100 bytes .../src/assets/fonts/lato/Lato_Bold.otf | Bin 0 -> 48800 bytes .../assets/fonts/lato/Lato_Bold_Italic.otf | Bin 0 -> 47364 bytes .../src/assets/fonts/lato/Lato_Hairline.otf | Bin 0 -> 46852 bytes .../fonts/lato/Lato_Hairline_Italic.otf | Bin 0 -> 46568 bytes .../src/assets/fonts/lato/Lato_Light.otf | Bin 0 -> 47040 bytes .../assets/fonts/lato/Lato_Light_Italic.otf | Bin 0 -> 47060 bytes .../src/assets/fonts/lato/Lato_Regular.otf | Bin 0 -> 47088 bytes .../assets/fonts/lato/Lato_Regular_Italic.otf | Bin 0 -> 47356 bytes .../flatspark/src/components/ButtonIcon.as | 41 ++ .../flatspark/src/components/TextInputIcon.as | 35 ++ .../flatspark/src/enums/BrandColorEnum.as | 30 ++ .../flatspark/src/enums/ButtonColorEnum.as | 59 +++ .../flatspark/src/enums/ButtonSizeEnum.as | 31 ++ .../flatspark/src/enums/ColorSwatchEnum.as | 40 ++ .../projects/flatspark/src/enums/SizeEnum.as | 32 ++ .../flatspark/src/enums/TextInputSizeEnum.as | 30 ++ .../itemRenderers/DefaultItemRenderer.mxml | 51 +++ .../flatspark/src/skins/AlertSkin.mxml | 230 ++++++++++ .../flatspark/src/skins/ButtonIconSkin.mxml | 191 +++++++++ .../flatspark/src/skins/ButtonSkin.mxml | 184 ++++++++ .../flatspark/src/skins/CheckBoxSkin.mxml | 136 ++++++ .../src/skins/ComboBoxButtonSkin.mxml | 108 +++++ .../flatspark/src/skins/ComboBoxSkin.mxml | 127 ++++++ .../src/skins/ComboBoxTextInputSkin.mxml | 218 ++++++++++ .../src/skins/DropDownListButtonSkin.mxml | 109 +++++ .../flatspark/src/skins/DropDownListSkin.mxml | 143 +++++++ .../flatspark/src/skins/HScrollBarSkin.mxml | 87 ++++ .../src/skins/HScrollBarThumbSkin.mxml | 74 ++++ .../src/skins/HScrollBarTrackSkin.mxml | 71 ++++ .../flatspark/src/skins/PanelSkin.mxml | 253 +++++++++++ .../flatspark/src/skins/ProgressBarSkin.mxml | 54 +++ .../flatspark/src/skins/RadioButtonSkin.mxml | 129 ++++++ .../flatspark/src/skins/ScrollerSkin.mxml | 106 +++++ .../src/skins/TextInputIconSkin.mxml | 338 +++++++++++++++ .../flatspark/src/skins/TextInputSkin.mxml | 330 +++++++++++++++ .../src/skins/TitleWindowCloseButtonSkin.mxml | 118 ++++++ .../flatspark/src/skins/TitleWindowSkin.mxml | 256 +++++++++++ .../flatspark/src/skins/VScrollBarSkin.mxml | 87 ++++ .../src/skins/VScrollBarThumbSkin.mxml | 74 ++++ .../src/skins/VScrollBarTrackSkin.mxml | 71 ++++ .../flatspark/src/utils/AwesomeUtils.as | 399 ++++++++++++++++++ .../flatspark/src/utils/ColorUtils.as | 90 ++++ .../flatspark/src/utils/ConfigSkin.as | 44 ++ 47 files changed, 4473 insertions(+) create mode 100644 frameworks/projects/flatspark/defaults.css create mode 100644 frameworks/projects/flatspark/src/assets/fonts/awesome/FontAwesome.otf create mode 100644 frameworks/projects/flatspark/src/assets/fonts/lato/Lato_Black.otf create mode 100644 frameworks/projects/flatspark/src/assets/fonts/lato/Lato_Black_Italic.otf create mode 100644 frameworks/projects/flatspark/src/assets/fonts/lato/Lato_Bold.otf create mode 100644 frameworks/projects/flatspark/src/assets/fonts/lato/Lato_Bold_Italic.otf create mode 100644 frameworks/projects/flatspark/src/assets/fonts/lato/Lato_Hairline.otf create mode 100644 frameworks/projects/flatspark/src/assets/fonts/lato/Lato_Hairline_Italic.otf create mode 100644 frameworks/projects/flatspark/src/assets/fonts/lato/Lato_Light.otf create mode 100644 frameworks/projects/flatspark/src/assets/fonts/lato/Lato_Light_Italic.otf create mode 100644 frameworks/projects/flatspark/src/assets/fonts/lato/Lato_Regular.otf create mode 100644 frameworks/projects/flatspark/src/assets/fonts/lato/Lato_Regular_Italic.otf create mode 100644 frameworks/projects/flatspark/src/components/ButtonIcon.as create mode 100644 frameworks/projects/flatspark/src/components/TextInputIcon.as create mode 100644 frameworks/projects/flatspark/src/enums/BrandColorEnum.as create mode 100644 frameworks/projects/flatspark/src/enums/ButtonColorEnum.as create mode 100644 frameworks/projects/flatspark/src/enums/ButtonSizeEnum.as create mode 100644 frameworks/projects/flatspark/src/enums/ColorSwatchEnum.as create mode 100644 frameworks/projects/flatspark/src/enums/SizeEnum.as create mode 100644 frameworks/projects/flatspark/src/enums/TextInputSizeEnum.as create mode 100644 frameworks/projects/flatspark/src/itemRenderers/DefaultItemRenderer.mxml create mode 100644 frameworks/projects/flatspark/src/skins/AlertSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/ButtonIconSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/ButtonSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/CheckBoxSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/ComboBoxButtonSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/ComboBoxSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/ComboBoxTextInputSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/DropDownListButtonSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/DropDownListSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/HScrollBarSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/HScrollBarThumbSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/HScrollBarTrackSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/PanelSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/ProgressBarSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/RadioButtonSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/ScrollerSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/TextInputIconSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/TextInputSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/TitleWindowCloseButtonSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/TitleWindowSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/VScrollBarSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/VScrollBarThumbSkin.mxml create mode 100644 frameworks/projects/flatspark/src/skins/VScrollBarTrackSkin.mxml create mode 100644 frameworks/projects/flatspark/src/utils/AwesomeUtils.as create mode 100644 frameworks/projects/flatspark/src/utils/ColorUtils.as create mode 100644 frameworks/projects/flatspark/src/utils/ConfigSkin.as diff --git a/frameworks/projects/flatspark/defaults.css b/frameworks/projects/flatspark/defaults.css new file mode 100644 index 0000000000..5c4783b2cb --- /dev/null +++ b/frameworks/projects/flatspark/defaults.css @@ -0,0 +1,97 @@ +/* CSS file */ +@namespace s "library://ns.adobe.com/flex/spark"; +@namespace components "flatSpark.components.*"; +@namespace ns "http://flex.apache.org/experimental/ns"; + + +@font-face { + src: url("flatSpark/assets/fonts/awesome/FontAwesome.otf"); + fontFamily: FontAwesome; + embedAsCFF: true; +} + +@font-face { + src:url("flatSpark/assets/fonts/lato/Lato_Regular.otf"); + fontFamily: Lato; + embedAsCFF: true; +} + +@font-face { + src:url("flatSpark/assets/fonts/lato/Lato_Regular_Italic.otf"); + fontFamily: Lato; + fontStyle: italic; + embedAsCFF: true; +} + +@font-face { + src:url("flatSpark/assets/fonts/lato/Lato_Bold.otf"); + fontFamily: Lato; + fontWeight: bold; + embedAsCFF: true; +} + +@font-face { + src:url("flatSpark/assets/fonts/lato/Lato_Bold_Italic.otf"); + fontFamily: Lato; + fontStyle: italic; + fontWeight: bold; + embedAsCFF: true; +} + +@font-face { + src:url("flatSpark/assets/fonts/lato/Lato_Light.otf"); + fontFamily: LatoLight; + embedAsCFF: true; +} + +@font-face { + src:url("flatSpark/assets/fonts/lato/Lato_Light_Italic.otf"); + fontFamily: LatoLight; + fontStyle: italic; + embedAsCFF: true; +} + +@font-face { + src:url("flatSpark/assets/fonts/lato/Lato_Hairline.otf"); + fontFamily: LatoHairline; + embedAsCFF: true; +} + +@font-face { + src:url("flatSpark/assets/fonts/lato/Lato_Hairline_Italic.otf"); + fontFamily: LatoHairline; + fontStyle: italic; + embedAsCFF: true; +} + +@font-face { + src:url("flatSpark/assets/fonts/lato/Lato_Black.otf"); + fontFamily: LatoBlack; + embedAsCFF: true; +} + +@font-face { + src:url("flatSpark/assets/fonts/lato/Lato_Black_Italic.otf"); + fontFamily: LatoBlack; + fontStyle: italic; + embedAsCFF: true; +} + +global +{ + modal-transparency: 0.98; + modal-transparency-blur: 0; + modal-transparency-color: #2c3e50; +} + +s|Application { + fontFamily: Lato, Helvetica, Arial, sans-serif; +} + +components|ButtonIcon { + skinClass: ClassReference("flatSpark.skins.ButtonIconSkin"); +} + +components|TextInputIcon { + skinClass: ClassReference("flatSpark.skins.TextInputIconSkin"); +} \ No newline at end of file diff --git a/frameworks/projects/flatspark/src/assets/fonts/awesome/FontAwesome.otf b/frameworks/projects/flatspark/src/assets/fonts/awesome/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..8b0f54e47e1d356dcf1496942a50e228e0f1ee14 GIT binary patch literal 62856 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