http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/productsView/ProductCatalogPanel.mxml ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/productsView/ProductCatalogPanel.mxml b/FlexStore/mx/src/productsView/ProductCatalogPanel.mxml new file mode 100644 index 0000000..63754ba --- /dev/null +++ b/FlexStore/mx/src/productsView/ProductCatalogPanel.mxml @@ -0,0 +1,534 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +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. + +--> +<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" + xmlns:productsView="productsView.*" + layout="vertical" + currentState="browse" + verticalScrollPolicy="off" + horizontalScrollPolicy="off" + styleName="catalogPanel"> + + <mx:Metadata> + [Event(name="purchase", type="samples.flexstore.ProductThumbEvent")] + [Event(name="compare", type="samples.flexstore.ProductThumbEvent")] + [Event(name="details", type="samples.flexstore.ProductThumbEvent")] + </mx:Metadata> + + <mx:Script> + <![CDATA[ + import flash.utils.Dictionary; + + import mx.collections.ArrayCollection; + import mx.collections.IViewCursor; + import mx.core.DragSource; + import mx.core.IUIComponent; + import mx.effects.Effect; + import mx.effects.Fade; + import mx.effects.Move; + import mx.events.EffectEvent; + import mx.events.DragEvent; + import mx.managers.DragManager; + + import samples.flexstore.Product; + import samples.flexstore.ProductFilter; + import samples.flexstore.ProductFilterEvent; + import samples.flexstore.ProductThumbEvent; + + + private var accepted:Dictionary = new Dictionary(); + private var thumbnails:Array; + private var filterCount:int; + private var thumbnailState:String = 'browse'; //either 'browse' or 'compare' + [Bindable] + private var titleButtons:CatalogTitleButtons; //the buttons that also allow the panel to switch sides + + override protected function createChildren():void + { + super.createChildren(); + titleButtons = new CatalogTitleButtons(); + titleBar.addChild(titleButtons); + + } + + override protected function layoutChrome(unscaledWidth:Number, unscaledHeight:Number):void + { + super.layoutChrome(unscaledWidth, unscaledHeight); + //when adding to a UIComponent (not a Container) need explicit width/height + titleButtons.width = unscaledWidth / 2; //make it big so as we add cart items we can stretch) + titleButtons.height = titleButtons.measuredHeight; + //this placement algorithm is pretty hacky, there are better ways that probably + //involve copying more of the Panel layoutChrome method and supporting methods + titleButtons.move(statusTextField.x - titleButtons.width, titleTextField.y); + } + + + [Bindable] + public var cartCount:int; + + //----------------------------- + // catalog + //----------------------------- + + private var _catalog:ArrayCollection; + + [Bindable] + public function set catalog(c:ArrayCollection):void + { + _catalog = c; + createThumbnails(); + } + + public function get catalog():ArrayCollection + { + return _catalog; + } + + //---------------------------------------------------------------------- + // methods + //---------------------------------------------------------------------- + + private function createThumbnails():void + { + var i:int; //variables are hoisted up in scope so declare here to avoid warning + if (thumbnails != null) + { + for (i=0; i < thumbnails.length; i++) + { + thumbContent.removeChild(thumbnails[i]); + } + } + + var row:int = 0; + var col:int = -1; + var n:int = catalog.length; + thumbnails = new Array(n); + filterCount = n; + + for (i=0; i < n; i++) + { + var thumb:ProductCatalogThumbnail = new ProductCatalogThumbnail(); + thumbnails[i] = thumb; + thumbnails[i].showInAutomationHierarchy = true; + thumb.product = catalog.getItemAt(i) as Product; + accepted[thumb.product] = true; + thumbContent.addChild(thumb); + thumb.addEventListener(ProductThumbEvent.PURCHASE, productThumbEventHandler); + thumb.addEventListener(ProductThumbEvent.COMPARE, productThumbEventHandler); + thumb.addEventListener(ProductThumbEvent.DETAILS, productThumbEventHandler); + thumb.addEventListener(DragEvent.DRAG_START,thumbDragStartHandler); + } + + layoutCatalog(); + } + + private function thumbDragStartHandler(event:MouseEvent):void + { + if (DragManager.isDragging == false) + { + var thumb:ProductCatalogThumbnail = event.target as ProductCatalogThumbnail; + var ds:DragSource = new DragSource(); + ds.addData(thumb.product, "product"); + + var di:ProductCatalogThumbnail = new ProductCatalogThumbnail(); + di.product = thumb.product; + + //the offset logic is honestly not the most intuitive but + //it's necessary to get the dragProxy positioned correctly + DragManager.doDrag(thumbContent, ds, event, di, -thumb.x, + -thumb.y + thumbContent.verticalScrollPosition, + 0.5, false); + } + } + + public function filter(productFilter:ProductFilter, live:Boolean):void + { + currentState = "browse"; + thumbnailState = "browse"; + var count:int=0; + var n:int = thumbnails.length; + var fadeOut:Fade = new Fade(); + fadeOut.alphaFrom = 1; + fadeOut.alphaTo = .1; + var targets:Array = []; + for (var i:int = 0; i < n; i++) + { + var thumb:ProductCatalogThumbnail = thumbnails[i]; + var product:Product = thumb.product; + if (productFilter.accept(product)) + { + accepted[product] = true; + thumb.alpha = 1; + count++; + } + else + { + accepted[product] = false; + if (live) + { + thumb.alpha = .1; + } + else if (thumb.alpha == 1) //only fade if we hadn't started + { + targets.push(thumb); + } + } + } + productFilter.count = count; + filterCount = count; + + if (targets.length > 0) + { + fadeOut.targets = targets; + fadeOut.play(); + fadeOut.addEventListener(EffectEvent.EFFECT_END, + function(event:EffectEvent):void + { + layoutCatalog(); + }); + } + else if (!live) + { + layoutCatalog(); + } + } + + private function layoutCatalog():Effect + { + var tileWidth:Number; + var tileHeight:Number; + var numCols:int; + + if (filterCount > 9 || currentState == "compare") + { + numCols = 4; + tileWidth = ProductCatalogThumbnail.COL_WIDTH_4; + tileWidth = currentState == "compare" + ? ProductCatalogThumbnail.COMPARE_WIDTH + : ProductCatalogThumbnail.COL_WIDTH_4 + tileHeight = currentState == "compare" + ? height - 4 + : ProductCatalogThumbnail.COL_HEIGHT_4; + } + else if (filterCount > 4) + { + numCols = 3; + tileWidth = ProductCatalogThumbnail.COL_WIDTH_3; + tileHeight = ProductCatalogThumbnail.COL_HEIGHT_3; + } + else if (filterCount <= 9) + { + numCols = 2; + tileWidth = ProductCatalogThumbnail.COL_WIDTH_2; + tileHeight = ProductCatalogThumbnail.COL_HEIGHT_2; + } + else + { + } + + var row:int = 0; + var col:int = -1; + + var move:Move = null; + + var n:int = catalog.length; + for (var i:int = 0 ; i < n ; i++) + { + var product:Product = catalog.getItemAt(i) as Product; + var thumb:ProductCatalogThumbnail = thumbnails[i]; + if (accepted[product]) + { + thumb.currentState = "" + numCols + "cols"; + col++; + if (col > numCols - 1) + { + row++; + col = 0; + } + + var xTo:Number = col * (tileWidth + ProductCatalogThumbnail.HORIZONTAL_GAP); + var yTo:Number = row * (tileHeight + ProductCatalogThumbnail.VERTICAL_GAP); + + // If the thumbnail is already visible + // animate it to its new position. + if (thumb.visible) + { + // Animate only if the position is different + // from its current position. + if (thumb.x != xTo || thumb.y != yTo) + { + move = new Move(thumb); + move.xTo = xTo; + move.yTo = yTo; + move.play(); + } + } + + // If the thumbnail was not previously visible, sets its + // x and y coordinates. We'll make it reappear after all + // the visible thumbnails have reached their new position. + else + { + thumb.x = xTo; + thumb.y = yTo; + thumb.includeInLayout = true; + } + } + else + { + thumb.visible = false; + thumb.includeInLayout = false; + } + } + + if (!move) + { + // No visible thumbnails were animated to a new position; + // fade in newly selected thumbnails right away. + fadeInThumbnails(); + } + else + { + //since movement is happening get the scrollbar back to the top + thumbContent.verticalScrollPosition = 0; + // Fade in newly selected thumbnails after the last + // visible thumbnail has moved to its new position. + move.addEventListener(EffectEvent.EFFECT_END, + function(event:EffectEvent):void + { + fadeInThumbnails(); + }); + } + //return the last move to watch + return move; + } + + //return the last effect so we could add effectEnd handler if desired + private function fadeInThumbnails():void + { + var n:int = thumbnails.length; + var effect:Fade = new Fade(); + effect.alphaTo = 1; + var targets:Array = []; + for (var i:int = 0; i < n ; i++) + { + var thumb:ProductCatalogThumbnail = thumbnails[i]; + if (accepted[thumb.product] && !thumb.visible) + { + thumb.alpha = 0; + thumb.visible = true; + targets.push(thumb); + } + } + if (targets.length > 0) + { + effect.targets = targets; + effect.play(); + } + } + + private function showDetails(product:Product):void + { + if (currentState == "details") + { + details.product = product; + return; + } + + var row:int = -1; + + //should be computed using border metrics instead of hard-coding the 20, but... + var xTo:Number = thumbContent.width - ProductCatalogThumbnail.COL_WIDTH_4 - 20; + var yTo:Number; + + var move:Move; + var first:Boolean = true; + var selectedThumb:ProductCatalogThumbnail; + + var n:int = thumbnails.length; + for (var i:int = 0; i < n; i++) + { + var thumb:ProductCatalogThumbnail = thumbnails[i]; + if (thumb.visible) + { + row++; + yTo = row * (ProductCatalogThumbnail.COL_HEIGHT_4 + ProductCatalogThumbnail.VERTICAL_GAP); + + thumb.currentState = "4cols"; + + if (thumb.x != xTo || thumb.y != yTo) + { + move = new Move(thumb); + if (first) + { + move.addEventListener(EffectEvent.EFFECT_END, + function(event:EffectEvent):void + { + details.product = product; + currentState = "details"; + }); + + first = false; + } + move.xTo = xTo; + move.yTo = yTo; + move.play(); + } + + if (thumb.product == product) + { + selectedThumb = thumb; + } + } + } + if (selectedThumb != null) + { + //make sure that the selected thumb is visible in the list on the right + move.addEventListener(EffectEvent.EFFECT_END, + function(e:EffectEvent):void + { + var curpos:int = thumbContent.verticalScrollPosition; + if (selectedThumb.y < curpos) + { + thumbContent.verticalScrollPosition = y; + } + else if (selectedThumb.y + ProductCatalogThumbnail.COL_HEIGHT_4 > curpos + thumbContent.height) + { + //this logic doesn't seem to be perfect but it will do + var diff:int = selectedThumb.y - (curpos + thumbContent.height) + thumbContent.verticalScrollPosition += diff + ProductCatalogThumbnail.COL_HEIGHT_4 + ProductCatalogThumbnail.VERTICAL_GAP; + } + }); + } + + } + + private function productThumbEventHandler(event:ProductThumbEvent):void + { + if (event.type == ProductThumbEvent.DETAILS) + { + showDetails(event.product); + } + else if (event.type == ProductThumbEvent.BROWSE) + { + if (thumbnailState == "browse") + { + currentState = "browse"; + layoutCatalog(); + } + else + { + compare(); + } + } + else + { + dispatchEvent(event); + } + } + + public function compare(toCompare:Array=null):void + { + currentState = "compare"; + thumbnailState = "compare"; + if (toCompare != null) + { + var n:int = thumbnails.length; + for (var i:int = 0; i < n; i++) + { + accepted[thumbnails[i].product] = false; + } + for (i=0; i < toCompare.length; i++) + { + accepted[toCompare[i]] = true; + } + } + var lastEffect:Effect = layoutCatalog(); + if (lastEffect != null) + { + lastEffect.addEventListener(EffectEvent.EFFECT_END, + function (event:EffectEvent):void + { + setCompareState(); + }); + } + else + { + setCompareState(); + } + } + + private function setCompareState():void + { + //avoid an issue if the user clicks quickly where we move into + //compare state even though we're no longer in compare + if (currentState == "compare") + { + var n:int = thumbnails.length; + for (var i:int = 0; i < n; i++) + { + var thumb:ProductCatalogThumbnail = thumbnails[i]; + if (accepted[thumb.product]) + { + thumb.currentState = "compare"; + } + } + + } + } + + ]]> + </mx:Script> + + <mx:Binding source="cartCount" destination="titleButtons.cartCount" /> + <!-- two-way binding between the states of panel title buttons and the product view state --> + <mx:Binding source="ProductsView(parentDocument).currentState" destination="titleButtons.currentState" /> + <mx:Binding destination="ProductsView(parentDocument).currentState" source="titleButtons.currentState" /> + + <mx:Canvas width="100%" height="100%" + verticalScrollPolicy="off" + horizontalScrollPolicy="off" + > + <mx:Canvas id="thumbContent" width="100%" height="100%" + horizontalScrollPolicy="off"/> + <productsView:ProductDetails id="details" + width="{ProductCatalogThumbnail.COL_WIDTH_4 * 3}" + height="100%" + visible="false" + compare="productThumbEventHandler(event)" + purchase="productThumbEventHandler(event)" + browse="productThumbEventHandler(event)" /> + </mx:Canvas> + + <mx:states> + <mx:State name="browse"> + <mx:SetProperty name="title" value="Browse"/> + </mx:State> + + <mx:State name="compare"> + <mx:SetProperty name="title" value="Product Comparison"/> + </mx:State> + + <mx:State name="details"> + <mx:SetProperty name="title" value="Product Details"/> + <mx:SetProperty target="{details}" name="visible" value="true"/> + </mx:State> + </mx:states> + +</mx:Panel>
http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/productsView/ProductCatalogThumbnail.mxml ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/productsView/ProductCatalogThumbnail.mxml b/FlexStore/mx/src/productsView/ProductCatalogThumbnail.mxml new file mode 100644 index 0000000..88a8f0a --- /dev/null +++ b/FlexStore/mx/src/productsView/ProductCatalogThumbnail.mxml @@ -0,0 +1,226 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +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. + +--> +<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" + width="{COL_WIDTH_4}" height="{COL_HEIGHT_4}" + borderStyle="solid" + borderColor="#FFFFFF" + horizontalScrollPolicy="off" verticalScrollPolicy="off" + rollOver="rollOverHandler(event)" + rollOut="rollOutHandler(event)" + mouseDown="mouseDownHandler(event)" + mouseMove="mouseMoveHandler(event)" + mouseUp="mouseUpHandler(event)" + click="clickHandler(event)" + currentState="4cols" + automationName="{product.name}" + dropShadowColor="#000000" + shadowDistance="2" + > + + <mx:Metadata> + [Event(name="purchase", type="samples.flexstore.ProductThumbEvent")] + [Event(name="compare", type="samples.flexstore.ProductThumbEvent")] + [Event(name="details", type="samples.flexstore.ProductThumbEvent")] + </mx:Metadata> + + <mx:Script> + <![CDATA[ + + import mx.events.*; + import mx.core.*; + + import samples.flexstore.Product; + import samples.flexstore.ProductThumbEvent; + + public static const COL_WIDTH_4:int = 162; + public static const COL_HEIGHT_4:int = 122; + public static const COL_WIDTH_3:int = 217; + public static const COL_HEIGHT_3:int = 165; + public static const COL_WIDTH_2:int = 327; + public static const COL_HEIGHT_2:int = 250; + public static const COMPARE_WIDTH:int = 162; + public static const HORIZONTAL_GAP:int = 2; + public static const VERTICAL_GAP:int = 3; + + [Bindable] + public var product:Product; + + private function rollOverHandler(event:MouseEvent):void + { + setStyle("borderColor", "#CCCCCC"); + setStyle("dropShadowEnabled", true); + buttons.visible = true; + } + + private function rollOutHandler(event:MouseEvent):void + { + setStyle("borderColor", "#FFFFFF"); + setStyle("dropShadowEnabled", false); + buttons.visible = false; + } + + private var dragStartPoint:Point; + + public function mouseDownHandler(event:MouseEvent):void + { + if (event.target != purchase && + event.target != compare && + event.target != details) + { + dragStartPoint = new Point(event.stageX, event.stageY); + dragStartPoint = globalToLocal(dragStartPoint); + + } + } + + public function mouseMoveHandler(event:MouseEvent):void + { + if (dragStartPoint != null) + { + var dragEvent:DragEvent = new DragEvent(DragEvent.DRAG_START, true); + dragEvent.localX = dragStartPoint.x; + dragEvent.localY = dragStartPoint.y; + dragEvent.buttonDown = true; + dispatchEvent(dragEvent); + + rollOutHandler(event); + + dragStartPoint = null; + } + } + + public function mouseUpHandler(event:MouseEvent):void + { + if (dragStartPoint != null) + { + dragStartPoint = null; + } + } + + public function clickHandler(event:MouseEvent):void + { + if (event.target != purchase && + event.target != compare && + event.target != details) + { + dispatchEvent(new ProductThumbEvent(ProductThumbEvent.DETAILS, product)); + } + } + + ]]> + </mx:Script> + + <mx:CurrencyFormatter currencySymbol="$" id="cf" precision="2"/> + + <mx:VBox id="vb" width="100%" height="100%" + paddingLeft="6" paddingTop="4" paddingRight="8" paddingBottom="4" + verticalGap="0"> + + <mx:Label text="{product.name}" fontWeight="bold"/> + + <mx:HBox width="100%" paddingTop="0" horizontalGap="4"> + + <mx:Image id="img" height="90" width="45" source="{product.image}" /> + + <mx:VBox id="descr" width="100%" height="100%" verticalGap="0" paddingTop="0"> + <mx:Text text="{product.featureString}" selectable="false" width="80" height="48"/> + <mx:Label text="{cf.format(product.price)}" fontWeight="bold"/> + <mx:Spacer height="4"/> + </mx:VBox> + + </mx:HBox> + + <mx:Spacer height="8"/> + + </mx:VBox> + + <mx:VBox id="buttons" visible="false" verticalGap="4" paddingRight="8" right="8" top="12"> + <mx:Button id="purchase" icon="@Embed('/assets/icon_cart_empty.png')" + click="dispatchEvent(new ProductThumbEvent(ProductThumbEvent.PURCHASE, product))" + width="30" toolTip="Add to cart"/> + <mx:Button id="compare" icon="@Embed('/assets/icon_compare.png')" + click="dispatchEvent(new ProductThumbEvent(ProductThumbEvent.COMPARE, product))" + width="30" toolTip="Add to compare list"/> + <mx:Button id="details" icon="@Embed('/assets/icon_details.png')" + click="dispatchEvent(new ProductThumbEvent(ProductThumbEvent.DETAILS, product))" + width="30" toolTip="Show details"/> + </mx:VBox> + + <mx:states> + + <mx:State name="compare"> + <mx:SetProperty name="height" value="502"/> + <mx:AddChild relativeTo="{vb}"> + <mx:Label text="Years: {product.experience}"/> + </mx:AddChild> + <mx:AddChild relativeTo="{vb}"> + <mx:Label text="BlazeDS: {product.blazeds}"/> + </mx:AddChild> + <mx:AddChild relativeTo="{vb}"> + <mx:Label text="Mobile: {product.mobile}"/> + </mx:AddChild> + <mx:AddChild relativeTo="{vb}"> + <mx:Label text="Video: {product.video}"/> + </mx:AddChild> + <mx:AddChild relativeTo="{vb}"> + <mx:Label text="Highlight: {product.highlight1}"/> + </mx:AddChild> + <mx:AddChild relativeTo="{vb}"> + <mx:Label text="Highlight: {product.highlight2}"/> + </mx:AddChild> + <mx:AddChild relativeTo="{vb}"> + <mx:Label text="Description:"/> + </mx:AddChild> + <mx:AddChild relativeTo="{vb}"> + <mx:Text text="{product.description}" width="100%"/> + </mx:AddChild> + </mx:State> + + <mx:State name="4cols"> + <mx:SetProperty name="width" value="{COL_WIDTH_4}"/> + <mx:SetProperty name="height" value="{COL_HEIGHT_4}"/> + </mx:State> + + <mx:State name="3cols"> + <mx:SetProperty name="width" value="{COL_WIDTH_3}"/> + <mx:SetProperty name="height" value="{COL_HEIGHT_3}"/> + <mx:SetProperty target="{img}" name="width" value="60"/> + <mx:SetProperty target="{img}" name="height" value="120"/> + <mx:AddChild relativeTo="{descr}"> + <mx:Label text="{product.highlight1}" color="#EE8D0C"/> + </mx:AddChild> + <mx:AddChild relativeTo="{descr}"> + <mx:Label text="{product.highlight2}" color="#EE8D0C"/> + </mx:AddChild> + </mx:State> + + <mx:State name="2cols" basedOn="3cols"> + <mx:SetProperty name="width" value="{COL_WIDTH_2}"/> + <mx:SetProperty name="height" value="{COL_HEIGHT_2}"/> + <mx:SetProperty target="{img}" name="width" value="60"/> + <mx:SetProperty target="{img}" name="height" value="120"/> + <mx:AddChild relativeTo="{vb}"> + <mx:Text width="100%" text="{product.description}"/> + </mx:AddChild> + </mx:State> + + </mx:states> + +</mx:Canvas> http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/productsView/ProductDetails.mxml ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/productsView/ProductDetails.mxml b/FlexStore/mx/src/productsView/ProductDetails.mxml new file mode 100644 index 0000000..ac80b11 --- /dev/null +++ b/FlexStore/mx/src/productsView/ProductDetails.mxml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +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. + +--> +<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" + xmlns:productsView="productsView.*" + horizontalScrollPolicy="off" verticalScrollPolicy="off"> + + <mx:Metadata> + [Event(name="purchase", type="samples.flexstore.ProductThumbEvent")] + [Event(name="compare", type="samples.flexstore.ProductThumbEvent")] + [Event(name="browse", type="samples.flexstore.ProductThumbEvent")] + </mx:Metadata> + + <mx:Script> + <![CDATA[ + + import mx.events.*; + + import samples.flexstore.Product; + import samples.flexstore.ProductThumbEvent; + + private var _product:Product; + + [Bindable] + public function get product():Product + { + return _product; + } + + public function set product(p:Product):void + { + _product = p; + tn.selectedIndex = 0; + } + + ]]> + </mx:Script> + + <mx:CurrencyFormatter currencySymbol="$" id="cf" precision="2"/> + + <mx:TabNavigator id="tn" width="100%" height="100%" + left="4" right="8" top="4" bottom="4"> + + <mx:VBox width="100%" height="100%" label="Features" verticalGap="8" + paddingLeft="8" paddingTop="8" paddingRight="8" paddingBottom="8" showEffect="Fade" hideEffect="Fade"> + + <mx:HBox width="100%" horizontalGap="12"> + + <mx:Image id="img" width="101" height="200" source="{product.image}"/> + + <mx:VBox id="descr" width="100%" height="100%" paddingTop="0" verticalGap="4"> + + <mx:Label text="{product.name}" fontSize="11" fontWeight="bold"/> + + <mx:Text text="{product.featureString}" width="80" height="48"/> + + <mx:Label text="{product.highlight1}" color="#EE8D0C"/> + + <mx:Label text="{product.highlight2}" color="#EE8D0C"/> + + <mx:Label text="{cf.format(product.price)}" fontWeight="bold"/> + + </mx:VBox> + </mx:HBox> + + <mx:Text id="descriptionText" width="100%" height="100%" text="{product.description}"/> + + </mx:VBox> + + <productsView:ProductSupport width="100%" height="100%" label="Support" showEffect="Fade" hideEffect="Fade"/> + + </mx:TabNavigator> + + <mx:VBox verticalGap="4" right="16" top="36"> + <mx:Spacer width="100%"/> + <mx:Button icon="@Embed('/assets/icon_cart_empty.png')" click="dispatchEvent(new ProductThumbEvent(ProductThumbEvent.PURCHASE, product))" width="30" toolTip="Add to cart"/> + <mx:Button icon="@Embed('/assets/icon_compare.png')" click="dispatchEvent(new ProductThumbEvent(ProductThumbEvent.COMPARE, product))" width="30" toolTip="Add to compare list"/> + <mx:Button icon="@Embed('/assets/icon_tiles.png')" click="dispatchEvent(new ProductThumbEvent(ProductThumbEvent.BROWSE, product));" width="30" toolTip="Back to thumbnail view"/> + </mx:VBox> + +</mx:Canvas> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/productsView/ProductFilterPanel.mxml ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/productsView/ProductFilterPanel.mxml b/FlexStore/mx/src/productsView/ProductFilterPanel.mxml new file mode 100644 index 0000000..794d0d3 --- /dev/null +++ b/FlexStore/mx/src/productsView/ProductFilterPanel.mxml @@ -0,0 +1,192 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +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. + +--> +<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" + xmlns:productsView="productsView.*" + xmlns:flexstore="samples.flexstore.*" + height="100%" + paddingLeft="4" + paddingRight="12" + paddingTop="12" + paddingBottom="8" + verticalGap="0" + currentState="showingThumbnails"> + + <mx:Metadata> + [Event(name="filter", type="samples.flexstore.ProductFilterEvent")] + [Event("compare")] + </mx:Metadata> + + <mx:Script> + <![CDATA[ + import mx.controls.sliderClasses.Slider; + import mx.controls.Alert; + import samples.flexstore.ProductFilterEvent; + + private var thumbBeingPressed:Boolean; + + private function dispatchFilter():void + { + var event:ProductFilterEvent = + new ProductFilterEvent(filter, thumbBeingPressed); + dispatchEvent(event); + currentState = "showingThumbnails"; + } + + private function sliderValue(values:Array, index:int):Number + { + return values[index]; + } + + private function productRemoved():void + { + if (currentState == "showingComparison") + { + if (productList.items.length == 0) + { + dispatchFilter(); + } + else + { + attemptCompare(); + } + } + } + + private function attemptCompare():void + { + if (productList.items.length > 0) + { + dispatchEvent(new Event("compare")); + currentState = "showingComparison"; + } + else + { + Alert.show("There are no items to compare.", "Compare"); + } + } + + ]]> + </mx:Script> + + <flexstore:ProductFilter id="filter"> + <flexstore:experience>{series.selectedItem}</flexstore:experience> + <flexstore:minPrice>{sliderValue(priceSlider.values, 0)}</flexstore:minPrice> + <flexstore:maxPrice>{sliderValue(priceSlider.values, 1)}</flexstore:maxPrice> + <flexstore:blazeds>{cbBlazeDS.selected}</flexstore:blazeds> + <flexstore:mobile>{cbMobile.selected}</flexstore:mobile> + <flexstore:video>{cbVideo.selected}</flexstore:video> + </flexstore:ProductFilter> + + <mx:CurrencyFormatter currencySymbol="$" id="cf"/> + + <mx:Label text="Find" styleName="sectionHeader"/> + + <mx:HBox width="100%"> + <mx:TextInput styleName="glass" width="100%"/> + <mx:Button styleName="glass" label="Go" click="Alert.show('This feature is not implemented in this sample', 'Find')"/> + </mx:HBox> + + <mx:Spacer height="18"/> + + <mx:HRule width="100%"/> + + <mx:Spacer height="8"/> + + <mx:HBox paddingTop="0" paddingLeft="0" verticalAlign="bottom"> + <mx:Label text="Filter" styleName="sectionHeader"/> + <mx:Label text="({filter.count} items selected)" color="{getStyle('themeColor')}" fontWeight="bold"/> + </mx:HBox> + + <mx:Spacer height="8"/> + + <mx:Label text="Years of Experience"/> + + <mx:ComboBox id="series" styleName="glass" width="140" change="dispatchFilter();"> + <mx:dataProvider> + <mx:Array> + <mx:String>All</mx:String> + <mx:String>3</mx:String> + <mx:String>5</mx:String> + <mx:String>7</mx:String> + <mx:String>9</mx:String> + </mx:Array> + </mx:dataProvider> + </mx:ComboBox> + + <mx:Spacer height="18"/> + + <mx:Label text="Price"/> + + <mx:HSlider id="priceSlider" styleName="glassSlider" minimum="0" maximum="200" tickInterval="10" snapInterval="10" + width="100%" thumbCount="2" values="[0,200]" labels="[$0,$200]" liveDragging="true" dataTipFormatFunction="{cf.format}" + change="dispatchFilter()" + thumbPress="thumbBeingPressed=true" + thumbRelease="thumbBeingPressed=false;dispatchFilter()" + /> + + <mx:Spacer height="18"/> + + <mx:Label text="Required Features"/> + + <mx:Spacer height="4"/> + + <mx:CheckBox id="cbBlazeDS" styleName="glass" label="BlazeDS" click="dispatchFilter();"/> + <mx:Spacer height="4"/> + <mx:CheckBox id="cbMobile" styleName="glass" label="Mobile" click="dispatchFilter()"/> + <mx:Spacer height="4"/> + <mx:CheckBox id="cbVideo" styleName="glass" label="Video" click="dispatchFilter();"/> + + <mx:Spacer height="18"/> + + <mx:HRule width="100%"/> + + <mx:Spacer height="8"/> + + <mx:HBox> + <mx:Label text="Compare" styleName="sectionHeader"/> + <mx:Label text="(Drag items here to compare)" styleName="instructions"/> + </mx:HBox> + + + <mx:Spacer height="4"/> + + <!-- height is maxItems * ProductListItem.HEIGHT + 2px border --> + <productsView:ProductList id="productList" height="{productList.maxItems * ProductListItem.HEIGHT + 2}" width="100%" + newItemStartX="300" newItemStartY="-100" maxItems="4" + removeProduct="productRemoved()"/> + + <mx:Spacer height="8"/> + + <mx:Button id="compareButton" styleName="glass" /> + + <mx:states> + <mx:State name="showingThumbnails"> + <mx:SetProperty target="{compareButton}" name="label" value="Compare Items" /> + <mx:SetStyle target="{compareButton}" name="icon" value="@Embed('/assets/icon_compare.png')" /> + <mx:SetEventHandler target="{compareButton}" name="click" handler="attemptCompare()" /> + </mx:State> + <mx:State name="showingComparison"> + <mx:SetProperty target="{compareButton}" name="label" value="Back to thumbnail view" /> + <mx:SetStyle target="{compareButton}" name="icon" value="@Embed('/assets/icon_tiles.png')" /> + <mx:SetEventHandler target="{compareButton}" name="click" handler="dispatchFilter()" /> + </mx:State> + </mx:states> + +</mx:VBox> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/productsView/ProductList.mxml ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/productsView/ProductList.mxml b/FlexStore/mx/src/productsView/ProductList.mxml new file mode 100644 index 0000000..b0498e7 --- /dev/null +++ b/FlexStore/mx/src/productsView/ProductList.mxml @@ -0,0 +1,210 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +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. + +--> +<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" + borderStyle="solid" + horizontalScrollPolicy="off" + dragEnter="doDragEnter(event)" + dragDrop="doDragDrop(event)" + backgroundAlpha="0" backgroundColor="#FF0000"> <!-- need a background color for drag and drop but can set alpha to 0 --> + + <mx:Metadata> + [Event(name="addProduct", type="samples.flexstore.ProductListEvent")] + [Event(name="duplicateProduct", type="samples.flexstore.ProductListEvent")] + [Event(name="productQtyChange", type="samples.flexstore.ProductListEvent")] + [Event(name="removeProduct", type="samples.flexstore.ProductListEvent")] + </mx:Metadata> + + <mx:Script> + <![CDATA[ + import mx.core.*; + import mx.effects.*; + import mx.events.*; + import mx.managers.DragManager; + import mx.effects.EffectManager; + + import samples.flexstore.Product; + import samples.flexstore.ProductListEvent; + + public var items:Array = []; + + public var newItemStartX:int; + public var newItemStartY:int; + [Bindable] + public var maxItems:int; + public var showQuantity:Boolean; + + private var playingEffects:Dictionary = new Dictionary(true); + + public function addProduct(product:Product):void + { + var index:int = indexOf(product.productId); + var event:ProductListEvent; + var item:ProductListItem; + + if (index != -1) + { + item = items[index] as ProductListItem; + //if we don't keep track of what's playing a double-click can + //cause the list item to keep rising + if (playingEffects[item] == null) + { + var jump:Sequence = new Sequence(); + var m1:Move = new Move(item) + m1.yBy = -5; + var m2:Move = new Move(item) + m2.yBy = 5; + jump.addChild(m1); + jump.addChild(m2); + jump.duration = 150; + playingEffects[item] = jump; + jump.addEventListener(EffectEvent.EFFECT_END, function(event:Event):void + { + delete playingEffects[item]; + }); + jump.play(); + } + event = new ProductListEvent(ProductListEvent.DUPLICATE_PRODUCT); + event.product = item.product; + dispatchEvent(event); + } + else + { + index = items.length; + if (maxItems <= 0 || index < maxItems) + { + item = new ProductListItem(); + if (showQuantity) + { + item.currentState = 'showQuantity'; + } + item.product = product; + item.percentWidth = 100; + item.addEventListener(ProductListEvent.REMOVE_PRODUCT, removeItemHandler); + items[index] = item; + addChild(item); + layoutItems(index, true); + event = new ProductListEvent(ProductListEvent.ADD_PRODUCT); + event.product = product; + dispatchEvent(event); + } + } + } + + public function getProducts():Array + { + var ret:Array = []; + for (var i:int = 0; i < items.length; i++) + { + ret[i] = items[i].product; + } + return ret; + } + + private function removeItemHandler(event:Event):void + { + var item:ProductListItem = event.target as ProductListItem; + var index:int = indexOf(item.product.productId); + items.splice(index, 1); + removeChild(item); + layoutItems(index); + } + + private function layoutItems(startIndex:int, scrollToBottom:Boolean=false):void + { + var n:int = items.length; + var e:Move; + for (var i:int = startIndex; i < n ; i++) + { + var item:ProductListItem = items[i]; + var yTo:Number = i * (item.height); + //still need to prevent items that are already in motion from getting + //jumpy + if (playingEffects[item] == null) + { + e = new Move(item); + if (item.x == 0 && item.y == 0) + { + e.xFrom = newItemStartX; + e.yFrom = newItemStartY; + } + + e.xTo = 0; + e.yTo = yTo; + playingEffects[item] = e; + e.addEventListener(EffectEvent.EFFECT_END, function(event:Event):void + { + delete playingEffects[item]; + }); + e.play(); + } + else + { + playingEffects[item].pause(); + playingEffects[item].yTo = yTo; + playingEffects[item].play(); + } + } + //get the last event and if we should scroll make sure we can validate + //and scroll to maxVPosition + if (scrollToBottom) + { + e.addEventListener(EffectEvent.EFFECT_END, function(event:Event):void + { + validateNow(); + verticalScrollPosition = maxVerticalScrollPosition; + }); + } + } + + private function indexOf(productId:int):int + { + var index:int = -1; + + var n:int = items.length; + for (var i:int = 0; i < items.length; i++) + { + if (items[i].product.productId == productId) + { + index = i; + break; + } + } + + return index; + } + + private function doDragEnter(event:DragEvent):void + { + if (event.dragSource.hasFormat("product")) + { + DragManager.acceptDragDrop(IUIComponent(event.target)); + } + } + + private function doDragDrop(event:DragEvent):void + { + var product:Product = event.dragSource.dataForFormat("product") as Product; + addProduct(product); + } + + ]]> + </mx:Script> + +</mx:Canvas> http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/productsView/ProductListItem.mxml ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/productsView/ProductListItem.mxml b/FlexStore/mx/src/productsView/ProductListItem.mxml new file mode 100644 index 0000000..40aeb20 --- /dev/null +++ b/FlexStore/mx/src/productsView/ProductListItem.mxml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +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. + +--> +<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" + styleName="listItem" + height="{ProductListItem.HEIGHT}" + automationName="{product.name}"> + + <mx:Metadata> + [Event(name="productQtyChange", type="samples.flexstore.ProductListEvent")] + [Event(name="removeProduct", type="samples.flexstore.ProductListEvent")] + </mx:Metadata> + + <mx:Script> + <![CDATA[ + + import samples.flexstore.Product; + import samples.flexstore.ProductListEvent; + + public static const HEIGHT:int = 30; + + [Bindable] + public var product:Product; + + private function qtyChange():void + { + product.qty = int(qty.text); + var event:ProductListEvent = new ProductListEvent(ProductListEvent.PRODUCT_QTY_CHANGE); + event.product = product; + dispatchEvent(event); + } + + private function removeItem():void + { + var event:ProductListEvent = new ProductListEvent(ProductListEvent.REMOVE_PRODUCT); + event.product = product; + dispatchEvent(event); + } + + ]]> + </mx:Script> + + <mx:CurrencyFormatter currencySymbol="$" id="cf" precision="2"/> + + <mx:Button id="removeButton" + width="14" height="14" + icon="@Embed('/assets/trashcan.png')" + toolTip="Remove from cart" + click="removeItem()"/> + + <mx:Image id="productImage" width="12" height="24" source="{product.image}"/> + + <mx:Label id="productName" maxWidth="100" text="{product.name}"/> + + <mx:Spacer width="100%" /> + + <mx:Label id="productPrice" + text="{cf.format(product.price)}" textAlign="right"/> + + <mx:states> + <mx:State name="showQuantity"> + <mx:AddChild> + <mx:TextInput id="qty" width="25" text="{product.qty}" + textAlign="right" maxChars="3" change="qtyChange()" /> + </mx:AddChild> + </mx:State> + </mx:states> + +</mx:HBox> http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/productsView/ProductSupport.mxml ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/productsView/ProductSupport.mxml b/FlexStore/mx/src/productsView/ProductSupport.mxml new file mode 100644 index 0000000..1d9aab9 --- /dev/null +++ b/FlexStore/mx/src/productsView/ProductSupport.mxml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +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. + +--> +<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" + paddingLeft="4" paddingRight="8" paddingBottom="4" + horizontalScrollPolicy="off" verticalScrollPolicy="off"> + + <mx:Script> + <![CDATA[ + + private function toggle():void + { + /* + if (vd.playing) + { + vd.stop(); + list.visible = true; + } + else + { + list.visible = false; + vd.play(); + } + */ + } + + ]]> + </mx:Script> + + <mx:Parallel id="hideList"> + <mx:children> + <mx:Array> + <mx:Resize target="{list}" widthTo="0"/> + <!--<mx:Resize target="{vd}" widthTo="400" heightTo="314"/>--> + </mx:Array> + </mx:children> + </mx:Parallel> + + <mx:Parallel id="showList"> + <mx:children> + <mx:Array> + <mx:Resize target="{list}" widthTo="130"/> + <!--<mx:Resize target="{vd}" widthTo="270" heightTo="217"/>--> + </mx:Array> + </mx:children> + </mx:Parallel> + + <mx:List id="list" width="130" height="100%" selectedIndex="0" + hideEffect="hideList" showEffect="showList"> + <mx:dataProvider> + <mx:Array> + <mx:Object label="Install SIM Card"/> + </mx:Array> + </mx:dataProvider> + </mx:List> + + <mx:Canvas width="100%" verticalScrollPolicy="off" horizontalScrollPolicy="off"> + + <!--<mx:VideoDisplay id="vd" width="270" height="217" source="assets/phone.flv" + autoPlay="false" complete="list.visible=true"/> + + <mx:Button label="{vd.playing ? 'Stop' : 'Play'}" click="toggle()" left="8" bottom="8" includeInLayout="false"> + </mx:Button> + --> + </mx:Canvas> + + +</mx:HBox> http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/samples/flexstore/ButtonBarButtonSkin.as ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/samples/flexstore/ButtonBarButtonSkin.as b/FlexStore/mx/src/samples/flexstore/ButtonBarButtonSkin.as new file mode 100644 index 0000000..5e94b58 --- /dev/null +++ b/FlexStore/mx/src/samples/flexstore/ButtonBarButtonSkin.as @@ -0,0 +1,298 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 samples.flexstore +{ + +import flash.display.GradientType; +import mx.containers.BoxDirection; +import mx.controls.Button; +import mx.controls.ButtonBar; +import mx.skins.Border; +import mx.skins.halo.*; +import mx.styles.StyleManager; +import mx.utils.ColorUtil; + +/** + * Adapted from mx.skins.halo.ButtonBarButtonSkin. + * This version of the ButtonBarButtonSkin is applied for the + * selectedOver, selectedUp, and over states to use the 2nd two + * values of the fillColors for the selected state of the + * button. The over state then uses a computed value from + * the themeColor to show emphasis. The border of the selected + * button also uses a computed value from the themeColor, but + * is partially transparent. + */ +public class ButtonBarButtonSkin extends Border +{ + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private static var cache:Object = {}; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Several colors used for drawing are calculated from the base colors + * of the component (themeColor, borderColor and fillColors). + * Since these calculations can be a bit expensive, + * we calculate once per color set and cache the results. + */ + private static function calcDerivedStyles(themeColor:uint, + fillColor0:uint, + fillColor1:uint):Object + { + var key:String = HaloColors.getCacheKey(themeColor, + fillColor0, fillColor1); + + if (!cache[key]) + { + var o:Object = cache[key] = {}; + + // Cross-component styles. + HaloColors.addHaloColors(o, themeColor, fillColor0, fillColor1); + + // Button-specific styles. + o.innerEdgeColor1 = ColorUtil.adjustBrightness2(fillColor0, -10); + o.innerEdgeColor2 = ColorUtil.adjustBrightness2(fillColor1, -25); + } + + return cache[key]; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * Constructor. + */ + public function ButtonBarButtonSkin() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Overridden properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // measuredWidth + //---------------------------------- + + /** + * @private + */ + override public function get measuredWidth():Number + { + return 50; + } + + //---------------------------------- + // measuredHeight + //---------------------------------- + + /** + * @private + */ + override public function get measuredHeight():Number + { + return 22; + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function updateDisplayList(w:Number, h:Number):void + { + super.updateDisplayList(w, h); + + // User-defined styles. + var borderColor:uint = getStyle("borderColor"); + var cornerRadius:Number = getStyle("cornerRadius"); + var fillAlphas:Array = getStyle("fillAlphas"); + var fillColors:Array = getStyle("fillColors"); + styleManager.getColorNames(fillColors); + var highlightAlphas:Array = getStyle("highlightAlphas"); + var themeColor:uint = getStyle("themeColor"); + + // Derivative styles. + var derStyles:Object = calcDerivedStyles(themeColor, fillColors[0], + fillColors[1]); + + var borderColorDrk1:Number = + ColorUtil.adjustBrightness2(borderColor, -50); + + var themeColorDrk1:Number = + ColorUtil.adjustBrightness2(themeColor, -25); + + var emph:Boolean = false; + + if (parent is Button) + emph = (parent as Button).emphasized; + + var tmp:Number; + + var bar:ButtonBar = parent ? ButtonBar(parent.parent) : null; + var horizontal:Boolean = true; + var pos:int = 0; + + if (bar) + { + if (bar.direction == BoxDirection.VERTICAL) + horizontal = false; + + // first: -1, middle: 0, last: 1 + var index:int = bar.getChildIndex(parent); + pos = (index == 0 ? -1 : (index == bar.numChildren - 1 ? 1 : 0)); + } + + var radius:Object = getCornerRadius(pos, horizontal, cornerRadius); + var cr:Object = getCornerRadius(pos, horizontal, cornerRadius); + var cr1:Object = getCornerRadius(pos, horizontal, cornerRadius - 1); + var cr2:Object = getCornerRadius(pos, horizontal, cornerRadius - 2); + var cr3:Object = getCornerRadius(pos, horizontal, cornerRadius - 3); + + graphics.clear(); + + switch (name) + { + case "selectedUpSkin": + case "selectedOverSkin": + { + var overFillColors:Array; + if (fillColors.length > 2) + overFillColors = [ fillColors[2], fillColors[3] ]; + else + overFillColors = [ fillColors[0], fillColors[1] ]; + + var overFillAlphas:Array; + if (fillAlphas.length > 2) + overFillAlphas = [ fillAlphas[2], fillAlphas[3] ]; + else + overFillAlphas = [ fillAlphas[0], fillAlphas[1] ]; + + // button border/edge + drawRoundRect( + 0, 0, w, h, cr, + [ themeColor, derStyles.themeColDrk1 ], 0.5, + verticalGradientMatrix(0, 0, w , h), + GradientType.LINEAR, null, + { x: 1, y: 1, w: w - 2, h: h - 2, r: cr1 }); + + // button fill + drawRoundRect( + 1, 1, w - 2, h - 2, cr1, + overFillColors, overFillAlphas, + verticalGradientMatrix(0, 0, w - 2, h - 2)); + + // top highlight + if (!(radius is Number)) + { radius.bl = radius.br = 0;} + drawRoundRect( + 1, 1, w - 2, (h - 2) / 2, radius, + [ 0xFFFFFF, 0xFFFFFF ], highlightAlphas, + verticalGradientMatrix(1, 1, w - 2, (h - 2) / 2)); + break; + } + + case "overSkin": + { + // button border/edge + drawRoundRect( + 0, 0, w, h, cr, + [ themeColor, derStyles.themeColDrk1 ], 0.5, + verticalGradientMatrix(0, 0, w, h)); + + // button fill + drawRoundRect( + 1, 1, w - 2, h - 2, cr1, + [ derStyles.fillColorPress1, derStyles.fillColorPress2 ], 1, + verticalGradientMatrix(0, 0, w - 2, h - 2)); + + // top highlight + if (!(radius is Number)) + { radius.bl = radius.br = 0;} + drawRoundRect( + 1, 1, w - 2, (h - 2) / 2, radius, + [ 0xFFFFFF, 0xFFFFFF ], highlightAlphas, + verticalGradientMatrix(1, 1, w - 2, (h - 2) / 2)); + + break; + } + } + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function getCornerRadius(pos:int, horizontal:Boolean, + radius:Number):Object + { + if (pos == 0) + return 0; + + radius = Math.max(0, radius); + + if (horizontal) + { + if (pos == -1) + return { tl: radius, tr: 0, bl: radius, br: 0 }; + else // pos == 1 + return { tl: 0, tr: radius, bl: 0, br: radius }; + } + else + { + if (pos == -1) + return { tl: radius, tr: radius, bl: 0, br: 0 }; + else // pos == 1 + return { tl: 0, tr: 0, bl: radius, br: radius }; + } + } +} + +} http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/samples/flexstore/Product.as ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/samples/flexstore/Product.as b/FlexStore/mx/src/samples/flexstore/Product.as new file mode 100644 index 0000000..040fb8f --- /dev/null +++ b/FlexStore/mx/src/samples/flexstore/Product.as @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 samples.flexstore +{ + +[Bindable] +public class Product +{ + + public var productId:int; + public var name:String; + public var description:String; + public var price:Number; + public var image:String; + public var experience:String; + public var blazeds:Boolean; + public var mobile:Boolean; + public var video:Boolean; + public var highlight1:String; + public var highlight2:String; + public var qty:int; + + public function Product() + { + + } + + public function fill(obj:Object):void + { + for (var i:String in obj) + { + this[i] = obj[i]; + } + } + + [Bindable(event="propertyChange")] + public function get featureString():String + { + var str:String = ""; + if (blazeds) + str += "BlazeDS"; + + if (mobile) + { + if (str.length > 0) + str += "\n"; + str += "Mobile"; + } + + if (video) + { + if (str.length > 0) + str += "\n"; + str += "Video"; + } + + return str; + } + +} + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/samples/flexstore/ProductFilter.as ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/samples/flexstore/ProductFilter.as b/FlexStore/mx/src/samples/flexstore/ProductFilter.as new file mode 100644 index 0000000..d182371 --- /dev/null +++ b/FlexStore/mx/src/samples/flexstore/ProductFilter.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 samples.flexstore +{ + +[Bindable] +public class ProductFilter +{ + public var count:int; + public var experience:String; + public var minPrice:Number; + public var maxPrice:Number; + public var blazeds:Boolean; + public var mobile:Boolean; + public var video:Boolean; + + public function ProductFilter() + { + super(); + } + + public function accept(product:Product):Boolean + { + //price is often the first test so let's fail fast if possible + if (minPrice > product.price || maxPrice < product.price) + return false; + if (experience != "All" && experience > product.experience) + return false; + if (blazeds && !product.blazeds) + return false; + if (mobile && !product.mobile) + return false; + if (video && !product.video) + return false; + + return true; + } +} + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/samples/flexstore/ProductFilterEvent.as ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/samples/flexstore/ProductFilterEvent.as b/FlexStore/mx/src/samples/flexstore/ProductFilterEvent.as new file mode 100644 index 0000000..b13af3e --- /dev/null +++ b/FlexStore/mx/src/samples/flexstore/ProductFilterEvent.as @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 samples.flexstore +{ + +import flash.events.Event; + +public class ProductFilterEvent extends Event +{ + public static const FILTER:String = "filter"; + + public var live:Boolean; + public var filter:ProductFilter; + + public function ProductFilterEvent(filter:ProductFilter, live:Boolean) + { + super(FILTER); + this.filter = filter; + this.live = live; + } +} + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/samples/flexstore/ProductListEvent.as ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/samples/flexstore/ProductListEvent.as b/FlexStore/mx/src/samples/flexstore/ProductListEvent.as new file mode 100644 index 0000000..fb4992f --- /dev/null +++ b/FlexStore/mx/src/samples/flexstore/ProductListEvent.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 samples.flexstore +{ + +import flash.events.Event; + +public class ProductListEvent extends Event +{ + public static const ADD_PRODUCT:String = "addProduct"; + public static const DUPLICATE_PRODUCT:String = "duplicateProduct"; + public static const REMOVE_PRODUCT:String = "removeProduct"; + public static const PRODUCT_QTY_CHANGE:String = "productQtyChange"; + + public var product:Product; + + //making the default bubbles behavior of the event to true since we want + //it to bubble out of the ProductListItem and beyond + public function ProductListEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false) + { + super(type, bubbles, cancelable); + } + +} + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/mx/src/samples/flexstore/ProductThumbEvent.as ---------------------------------------------------------------------- diff --git a/FlexStore/mx/src/samples/flexstore/ProductThumbEvent.as b/FlexStore/mx/src/samples/flexstore/ProductThumbEvent.as new file mode 100644 index 0000000..e967daf --- /dev/null +++ b/FlexStore/mx/src/samples/flexstore/ProductThumbEvent.as @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 samples.flexstore +{ + +import flash.events.Event; + +public class ProductThumbEvent extends Event +{ + public static const PURCHASE:String = "purchase"; + public static const COMPARE:String = "compare"; + public static const DETAILS:String = "details"; + public static const BROWSE:String = "browse"; + + public var product:Product; + + public function ProductThumbEvent(type:String, product:Product) + { + super(type); + this.product = product; + } + + override public function clone():Event + { + return new ProductThumbEvent(type, product); + } +} + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-examples/blob/c652cfba/FlexStore/spark/src/FlexStore.mxml ---------------------------------------------------------------------- diff --git a/FlexStore/spark/src/FlexStore.mxml b/FlexStore/spark/src/FlexStore.mxml new file mode 100644 index 0000000..7a82cb9 --- /dev/null +++ b/FlexStore/spark/src/FlexStore.mxml @@ -0,0 +1,280 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +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. + +--> +<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" + xmlns:s="library://ns.adobe.com/flex/spark" + xmlns="*" + minWidth="990" minHeight="550" + preinitialize="loadStyle()" + creationComplete="startService()" + stateChangeComplete="chainStatesIfNeeded()" + pageTitle="FlexStore"> + + <fx:Script> + <![CDATA[ + import mx.collections.IViewCursor; + import mx.collections.ArrayCollection; + import samples.flexstore.Product; + import mx.rpc.events.ResultEvent; + import mx.events.StyleEvent; + import mx.styles.StyleManager; + import mx.managers.LayoutManager; + + [Bindable] + private var catalog:ArrayCollection; + + private var currentTheme:String = "beige"; + + private function toggleTheme():void + { + if (currentTheme == "beige") + { + currentTheme = "blue"; + } + else + { + currentTheme = "beige"; + } + + loadStyle(); + } + + private function startService():void + { + productService.send(); + } + + private function loadStyle():void + { + var eventDispatcher:IEventDispatcher = + styleManager.loadStyleDeclarations(currentTheme + ".swf"); + eventDispatcher.addEventListener(StyleEvent.COMPLETE, completeHandler); + } + + private function completeHandler(event:StyleEvent):void + { + image.source = acb.getStyle("storeLogo"); + homeView.updateMapImage(); + super.initialized = true; + callLater(prebake); + } + + private function productServiceResultHandler(event:ResultEvent):void + { + //HTTPService returns an ArrayCollection for nested arrays + var products:ArrayCollection = event.result.catalog.product; + var temp:ArrayCollection = new ArrayCollection(); + var cursor:IViewCursor = products.createCursor(); + while (!cursor.afterLast) + { + var product:Product = new Product(); + product.fill(cursor.current); + temp.addItem(product); + cursor.moveNext(); + } + catalog = temp; + } + + override public function set initialized(value:Boolean):void + { + // Hold off until the Runtime CSS SWF is done loading. + } + + private var stateChain:Array; + + private function headHome():void + { + homeButton.selected = true; + if (currentState == "ProductsState") + { + productsButton.selected = false; + stateChain = ["ProductsWipeUp", "HomeWipeDown", "HomeState"]; + currentState = "ProductsWipeUp"; + } + else if (currentState == "SupportState") + { + supportButton.selected = false; + stateChain = ["SupportWipeUp", "HomeWipeDown", "HomeState"]; + currentState = "SupportWipeUp"; + } + } + + private function headToProducts():void + { + productsButton.selected = true; + if (currentState == "SupportState") + { + supportButton.selected = false; + stateChain = ["SupportWipeUp", "ProductsWipeDown", "ProductsState"]; + currentState = "SupportWipeUp"; + } + if (currentState == "HomeState") + { + homeButton.selected = false; + stateChain = ["HomeWipeUp", "ProductsWipeDown", "ProductsState"]; + currentState = "HomeWipeUp"; + } + } + + private function headToSupport():void + { + supportButton.selected = true; + if (currentState == "ProductsState") + { + productsButton.selected = false; + stateChain = ["ProductsWipeUp", "SupportWipeDown", "SupportState"]; + currentState = "ProductsWipeUp"; + } + if (currentState == "HomeState") + { + homeButton.selected = false; + stateChain = ["HomeWipeUp", "SupportWipeDown", "SupportState"]; + currentState = "HomeWipeUp"; + } + } + + private function prebake():void + { + if (LayoutManager.getInstance().isInvalid()) + { + callLater(prebake); + return; + } + addEventListener("enterFrame", prebake2); + } + + private function prebake2(event:Event):void + { + removeEventListener("enterFrame", prebake2); + trace("prebake2"); + stateChain = ["ProductsPreBake", "HomeState"]; + currentState = "ProductsPreBake"; + } + + private function chainStatesIfNeeded():void + { + if (LayoutManager.getInstance().isInvalid()) + { + callLater(chainStatesIfNeeded); + return; + } + if (stateChain != null) + { + if (currentState == stateChain[0]) + { + addEventListener("enterFrame", nextState); + } + } + } + + private function nextState(event:Event):void + { + removeEventListener("enterFrame", nextState); + stateChain.shift(); + if (stateChain.length) + currentState = stateChain[0]; + else + stateChain == null; + } + ]]> + </fx:Script> + + <fx:Style source="main.css"/> + + <fx:Declarations> + <s:HTTPService id="productService" url="data/catalog.xml" + result="productServiceResultHandler(event)"/> + </fx:Declarations> + + <s:controlBarContent> + <s:HGroup id="acb" width="100%" styleName="storeControlBar"> + <s:Image id="image" + click="toggleTheme()" + toolTip="Change Theme"/> + <s:ToggleButton id="homeButton" + label="Home" + height="100%" + selected="true" + styleName="storeButtonBar" + click="headHome()" /> + <s:ToggleButton id="productsButton" + label="Products" + height="100%" + styleName="storeButtonBar" + click="headToProducts()"/> + <s:ToggleButton id="supportButton" + label="Support" + height="100%" + styleName="storeButtonBar" + click="headToSupport()"/> + </s:HGroup> + </s:controlBarContent> + + <s:states> + <s:State name="HomeState" stateGroups="['Home']" /> + <s:State name="HomeWipeUp" stateGroups="['Home']" /> + <s:State name="HomeWipeDown" stateGroups="['Home']" /> + <s:State name="ProductsPreBake" stateGroups="['Home', 'Products']" /> + <s:State name="ProductsState" stateGroups="['Products']" /> + <s:State name="ProductsWipeUp" stateGroups="['Products']" /> + <s:State name="ProductsWipeDown" stateGroups="['Products']" /> + <s:State name="SupportState" stateGroups="['Support']" /> + <s:State name="SupportWipeUp" stateGroups="['Support']" /> + <s:State name="SupportWipeDown" stateGroups="['Support']" /> + </s:states> + + <s:transitions> + <s:Transition fromState="HomeState" toState="HomeWipeUp"> + <s:Wipe direction="up" target="{homeView}" /> + </s:Transition> + <s:Transition fromState="HomeWipeDown" toState="HomeState"> + <s:Wipe direction="down" target="{homeView}" /> + </s:Transition> + <s:Transition fromState="ProductsState" toState="ProductsWipeUp"> + <s:Wipe direction="up" target="{pView}" /> + </s:Transition> + <s:Transition fromState="ProductsWipeDown" toState="ProductsState"> + <s:Wipe direction="down" target="{pView}" /> + </s:Transition> + <s:Transition fromState="SupportState" toState="SupportWipeUp"> + <s:Wipe direction="up" target="{supportView}" /> + </s:Transition> + <s:Transition fromState="SupportWipeDown" toState="SupportState"> + <s:Wipe direction="down" target="{supportView}" /> + </s:Transition> + </s:transitions> + <s:VGroup width="990" paddingLeft="0" paddingRight="0" horizontalCenter="0" top="12"> + + <HomeView id="homeView" width="100%" height="550" includeIn="Home" + visible.HomeWipeUp="false" + visible.HomeWipeDown="false" + /> + <ProductsView id="pView" includeIn="Products" visible.ProductsPreBake="false" + visible.ProductsWipeUp="false" + visible.ProductsWipeDown="false" + width="100%" height="550" creationComplete="pView.catalog = catalog" + /> + <SupportView id="supportView" includeIn="Support" + visible.SupportWipeUp="false" + visible.SupportWipeDown="false" + width="100%" height="550" + /> + </s:VGroup> + +</s:Application>