This is an automated email from the ASF dual-hosted git repository.

carlosrovira pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git


The following commit(s) were added to refs/heads/develop by this push:
     new 2ff66f3  jewel: Add VirtualList for performant lists that need to 
recycle renderers
2ff66f3 is described below

commit 2ff66f3ba682c35034f1ab2700efbf6d6f82e07e
Author: Carlos Rovira <[email protected]>
AuthorDate: Thu Feb 6 18:14:44 2020 +0100

    jewel: Add VirtualList for performant lists that need to recycle renderers
---
 .../projects/Jewel/src/main/resources/defaults.css |   6 +
 .../Jewel/src/main/resources/jewel-manifest.xml    |   4 +
 .../royale/org/apache/royale/jewel/VirtualList.as  |  53 +++
 ...tualDataItemRendererFactoryForCollectionView.as | 253 +++++++++++
 .../beads/layouts/VirtualListVerticalLayout.as     | 464 +++++++++++++++++++++
 .../royale/jewel/beads/views/VirtualListView.as    | 137 ++++++
 .../Jewel/src/main/sass/components/_list.sass      |   8 +-
 7 files changed, 924 insertions(+), 1 deletion(-)

diff --git a/frameworks/projects/Jewel/src/main/resources/defaults.css 
b/frameworks/projects/Jewel/src/main/resources/defaults.css
index d83c90d..6c77471 100644
--- a/frameworks/projects/Jewel/src/main/resources/defaults.css
+++ b/frameworks/projects/Jewel/src/main/resources/defaults.css
@@ -3183,6 +3183,12 @@ j|ListItemRenderer {
     IContentView: 
ClassReference("org.apache.royale.jewel.supportClasses.list.DataGroup");
   }
 }
+j|VirtualList {
+  IBeadView: 
ClassReference("org.apache.royale.jewel.beads.views.VirtualListView");
+  IBeadLayout: 
ClassReference("org.apache.royale.jewel.beads.layouts.VirtualListVerticalLayout");
+  IDataProviderItemRendererMapper: 
ClassReference("org.apache.royale.jewel.beads.itemRenderers.VirtualDataItemRendererFactoryForCollectionView");
+}
+
 j|Navigation {
   IBeadView: ClassReference("org.apache.royale.jewel.beads.views.ListView");
   IBeadController: 
ClassReference("org.apache.royale.jewel.beads.controllers.ListSingleSelectionMouseController");
diff --git a/frameworks/projects/Jewel/src/main/resources/jewel-manifest.xml 
b/frameworks/projects/Jewel/src/main/resources/jewel-manifest.xml
index 157041d..e57b809 100644
--- a/frameworks/projects/Jewel/src/main/resources/jewel-manifest.xml
+++ b/frameworks/projects/Jewel/src/main/resources/jewel-manifest.xml
@@ -71,6 +71,7 @@
     <component id="ControlBar" class="org.apache.royale.jewel.ControlBar"/>
     <component id="TitleBar" class="org.apache.royale.jewel.TitleBar"/>
     <component id="List" class="org.apache.royale.jewel.List"/>
+    <component id="VirtualList" class="org.apache.royale.jewel.VirtualList"/>
     <component id="DropDownList" class="org.apache.royale.jewel.DropDownList"/>
     <component id="ComboBox" class="org.apache.royale.jewel.ComboBox"/>
     <component id="ComboBoxPopUp" 
class="org.apache.royale.jewel.supportClasses.combobox.ComboBoxPopUp"/>
@@ -101,6 +102,7 @@
     <component id="AssignTabContent" 
class="org.apache.royale.jewel.beads.controls.tabbar.AssignTabContent"/>
     
     <component id="HorizontalListScroll" 
class="org.apache.royale.jewel.beads.controls.list.HorizontalListScroll"/>
+    <component id="VirtualListView" 
class="org.apache.royale.jewel.beads.views.VirtualListView"/>
 
     <component id="ButtonBar" class="org.apache.royale.jewel.ButtonBar"/>
     <component id="IconButtonBar" 
class="org.apache.royale.jewel.IconButtonBar"/>
@@ -182,6 +184,7 @@
     <component id="SimpleVerticalLayout" 
class="org.apache.royale.jewel.beads.layouts.SimpleVerticalLayout"/>
     <component id="HorizontalLayout" 
class="org.apache.royale.jewel.beads.layouts.HorizontalLayout"/>
     <component id="VerticalLayout" 
class="org.apache.royale.jewel.beads.layouts.VerticalLayout"/>
+    <component id="VirtualListVerticalLayout" 
class="org.apache.royale.jewel.beads.layouts.VirtualListVerticalLayout"/>
     <component id="FormLayout" 
class="org.apache.royale.jewel.beads.layouts.FormLayout"/>
     <component id="FormItemLayout" 
class="org.apache.royale.jewel.beads.layouts.FormItemLayout"/>
     <component id="HorizontalCenteredLayout" 
class="org.apache.royale.jewel.beads.layouts.HorizontalCenteredLayout"/>
@@ -206,6 +209,7 @@
 
     <component id="DataItemRendererFactoryForArrayData" 
class="org.apache.royale.jewel.beads.itemRenderers.DataItemRendererFactoryForArrayData"/>
     <component id="DataItemRendererFactoryForCollectionView" 
class="org.apache.royale.jewel.beads.itemRenderers.DataItemRendererFactoryForCollectionView"/>
+    <component id="VirtualDataItemRendererFactoryForCollectionView" 
class="org.apache.royale.jewel.beads.itemRenderers.VirtualDataItemRendererFactoryForCollectionView"/>
     <component id="DropDownListItemRendererFactoryForCollectionView" 
class="org.apache.royale.jewel.beads.itemRenderers.DropDownListItemRendererFactoryForCollectionView"/>
     <component id="TableItemRendererFactoryForCollectionView" 
class="org.apache.royale.jewel.beads.itemRenderers.TableItemRendererFactoryForCollectionView"/>
 
diff --git 
a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/VirtualList.as
 
b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/VirtualList.as
new file mode 100644
index 0000000..f40a0ee
--- /dev/null
+++ 
b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/VirtualList.as
@@ -0,0 +1,53 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 org.apache.royale.jewel
+{
+       /**
+        *  The VirtualList class is a List component used for large data 
providers that need to create and recicle just
+     *  the visible list renderers. The List uses the following bead types:
+        *
+        *  org.apache.royale.core.IBeadModel: the data model, which includes 
the dataProvider, selectedItem, and
+        *  so forth.
+        *  org.apache.royale.core.IBeadView:  the bead that constructs the 
visual parts of the list.
+        *  org.apache.royale.core.IBeadController: the bead that handles input 
and output.
+        *  org.apache.royale.core.IBeadLayout: the bead responsible for the 
size and position of the itemRenderers.
+        *  org.apache.royale.core.IDataProviderItemRendererMapper: the bead 
responsible for creating the itemRenders.
+        *  org.apache.royale.core.IItemRenderer: the class or factory used to 
display an item in the list.
+        *
+        *  @langversion 3.0
+        *  @playerversion Flash 10.2
+        *  @playerversion AIR 2.6
+        *  @productversion Royale 0.9.7
+        */
+       public class VirtualList extends List
+       {
+               /**
+                *  constructor.
+                *
+                *  @langversion 3.0
+                *  @playerversion Flash 10.2
+                *  @playerversion AIR 2.6
+                *  @productversion Royale 0.9.7
+                */
+               public function VirtualList()
+               {
+                       super();
+               }
+       }
+}
diff --git 
a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/itemRenderers/VirtualDataItemRendererFactoryForCollectionView.as
 
b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/itemRenderers/VirtualDataItemRendererFactoryForCollectionView.as
new file mode 100644
index 0000000..d5b6532
--- /dev/null
+++ 
b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/itemRenderers/VirtualDataItemRendererFactoryForCollectionView.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 org.apache.royale.jewel.beads.itemRenderers
+{
+       import org.apache.royale.collections.ArrayList;
+       import org.apache.royale.collections.ICollectionView;
+       import org.apache.royale.core.IBead;
+       import org.apache.royale.core.IDataProviderModel;
+       import org.apache.royale.core.IDataProviderVirtualItemRendererMapper;
+       import org.apache.royale.core.IItemRendererClassFactory;
+       import org.apache.royale.core.IItemRendererParent;
+       import org.apache.royale.core.ISelectableItemRenderer;
+       import org.apache.royale.core.ISelectionModel;
+       import org.apache.royale.core.IStrand;
+       import org.apache.royale.core.IStrandWithModelView;
+       import org.apache.royale.core.UIBase;
+       import org.apache.royale.events.Event;
+       import org.apache.royale.events.EventDispatcher;
+       import org.apache.royale.events.IEventDispatcher;
+       import org.apache.royale.events.ItemRendererEvent;
+       import org.apache.royale.html.beads.IListView;
+       import org.apache.royale.html.supportClasses.DataItemRenderer;
+       import 
org.apache.royale.jewel.supportClasses.list.IListPresentationModel;
+       import org.apache.royale.utils.loadBeadFromValuesManager;
+       import org.apache.royale.utils.sendStrandEvent;
+
+    
[Event(name="itemRendererCreated",type="org.apache.royale.events.ItemRendererEvent")]
+       
+    /**
+     *  The DataItemRendererFactoryForArrayData class reads an
+     *  array of data and creates an item renderer for every
+     *  item in the array.  Other implementations of
+     *  IDataProviderItemRendererMapper map different data 
+     *  structures or manage a virtual set of renderers.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10.2
+     *  @playerversion AIR 2.6
+     *  @productversion Royale 0.9.7
+     */
+       public class VirtualDataItemRendererFactoryForCollectionView extends 
EventDispatcher implements IBead, IDataProviderVirtualItemRendererMapper
+       {
+        /**
+         *  Constructor.
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10.2
+         *  @playerversion AIR 2.6
+         *  @productversion Royale 0.9.7
+         */
+               public function 
VirtualDataItemRendererFactoryForCollectionView(target:Object=null)
+               {
+                       super(target);
+               }
+
+               protected var dataProviderModel:IDataProviderModel;
+               protected var dataFieldProvider:DataFieldProviderBead;
+               
+               protected var labelField:String;
+        protected var dataField:String;
+
+               private var _strand:IStrand;
+        /**
+         *  @copy org.apache.royale.core.IBead#strand
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10.2
+         *  @playerversion AIR 2.6
+         *  @productversion Royale 0.9.7
+         */
+               public function set strand(value:IStrand):void
+               {
+                       _strand = value;
+                       
IEventDispatcher(value).addEventListener("initComplete", initComplete);
+               }
+               
+               /**
+                * @private
+                */
+               private function initComplete(event:Event):void
+               {
+            IEventDispatcher(_strand).removeEventListener("initComplete", 
initComplete);
+
+            var view:IListView = (_strand as IStrandWithModelView).view as 
IListView;
+                       dataGroup = view.dataGroup;
+
+                       dataProviderModel = 
_strand.getBeadByType(IDataProviderModel) as IDataProviderModel;
+                       
dataProviderModel.addEventListener("dataProviderChanged", 
dataProviderChangeHandler);
+                       labelField = dataProviderModel.labelField;
+
+            dataFieldProvider = _strand.getBeadByType(DataFieldProviderBead) 
as DataFieldProviderBead;
+                       if (dataFieldProvider)
+            {
+                dataField = dataFieldProvider.dataField;
+            }
+
+                       // if the host component inherits from 
DataContainerBase, the itemRendererClassFactory will 
+                       // already have been loaded by 
DataContainerBase.addedToParent function.
+                       if(!_itemRendererFactory)
+                       _itemRendererFactory = 
loadBeadFromValuesManager(IItemRendererClassFactory, 
"iItemRendererClassFactory", _strand) as IItemRendererClassFactory;             
            
+                       
+                       dataProviderChangeHandler(null);
+               }
+               
+               private var _itemRendererFactory:IItemRendererClassFactory;
+               
+        /**
+         *  The org.apache.royale.core.IItemRendererClassFactory used 
+         *  to generate instances of item renderers.
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10.2
+         *  @playerversion AIR 2.6
+         *  @productversion Royale 0.9.7
+         */
+               public function get 
itemRendererFactory():IItemRendererClassFactory
+               {
+                       if(!_itemRendererFactory)
+                       _itemRendererFactory = 
loadBeadFromValuesManager(IItemRendererClassFactory, 
"iItemRendererClassFactory", _strand) as IItemRendererClassFactory;
+                       
+                       return _itemRendererFactory;
+               }
+               
+        /**
+         *  @private
+         */
+               public function set 
itemRendererFactory(value:IItemRendererClassFactory):void
+               {
+                       _itemRendererFactory = value;
+               }
+
+        /**
+                *  The org.apache.royale.core.IItemRendererParent that will
+                *  parent the item renderers.
+                *
+                *  @langversion 3.0
+                *  @playerversion Flash 10.2
+                *  @playerversion AIR 2.6
+                *  @productversion Royale 0.9.7
+                */
+               protected var dataGroup:IItemRendererParent;
+               
+        /**
+         *  The org.apache.royale.core.IItemRendererParent that will
+         *  parent the item renderers.
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10.2
+         *  @playerversion AIR 2.6
+         *  @productversion Royale 0.9.7
+         *  @royaleignorecoercion org.apache.royale.core.IStrandWithModelView
+         *  @royaleignorecoercion org.apache.royale.html.beads.IListView
+         */            
+               protected function dataProviderChangeHandler(event:Event):void
+               {
+            if (!dataProviderModel)
+                               return;
+                       var dp:ICollectionView = dataProviderModel.dataProvider 
as ICollectionView;
+                       if (!dp)
+                               return;
+                       
+                       dataGroup.removeAllItemRenderers();
+
+            sendStrandEvent(_strand, "layoutNeeded");
+        }
+        
+        /**
+         *  Free an item renderer for a given index.
+         *
+         *  @langversion 3.0
+         *  @playerversion Flash 10.2
+         *  @playerversion AIR 2.6
+         *  @productversion Royale 0.9.7
+         *  @royaleignorecoercion org.apache.royale.core.IStrandWithModelView
+         *  @royaleignorecoercion org.apache.royale.html.beads.IListView
+         */
+        public function freeItemRendererForIndex(index:int):void
+        {
+            dataGroup.removeItemRenderer(rendererMap[index]);
+            delete rendererMap[index];
+        }
+        
+        protected var rendererMap:Object = {};
+        
+        /**
+         *  Get an item renderer for a given index.
+         *
+         *  @langversion 3.0
+         *  @playerversion Flash 10.2
+         *  @playerversion AIR 2.6
+         *  @productversion Royale 0.9.7
+         *  @royaleignorecoercion org.apache.royale.core.IStrandWithModelView
+         *  @royaleignorecoercion org.apache.royale.html.beads.IListView
+         */
+        public function getItemRendererForIndex(index:int, 
elementIndex:int):ISelectableItemRenderer
+        {
+            var ir:ISelectableItemRenderer = rendererMap[index];
+            if (ir) return ir;
+            
+            var dp:ArrayList = dataProviderModel.dataProvider as ArrayList;
+            
+                       ir = itemRendererFactory.createItemRenderer(dataGroup) 
as ISelectableItemRenderer;
+            var dataItemRenderer:DataItemRenderer = ir as DataItemRenderer;
+
+            dataGroup.addItemRendererAt(ir, elementIndex);
+                       ir.index = index;
+                       ir.labelField = labelField;
+            if (dataItemRenderer)
+            {
+                dataItemRenderer.dataField = dataField;
+            }
+            rendererMap[index] = ir;
+            
+            var presentationModel:IListPresentationModel = 
_strand.getBeadByType(IListPresentationModel) as IListPresentationModel;
+                       if (presentationModel) {
+                               UIBase(ir).height = presentationModel.rowHeight;
+                               
+                               if(ir is IAlignItemRenderer)
+                               {
+                                       (ir as IAlignItemRenderer).align = 
presentationModel.align;
+                               }
+                       }
+                       ir.data = dp.getItemAt(index);
+            
+            // if the item was already selected make the item show it
+            if(index == (dataProviderModel as ISelectionModel).selectedIndex)
+            {
+                ir.selected = true;
+            }
+                               
+                       var itemCreatedEvent:ItemRendererEvent = new 
ItemRendererEvent(ItemRendererEvent.CREATED);
+                       itemCreatedEvent.itemRenderer = ir;
+                       sendStrandEvent(_strand, itemCreatedEvent);
+            return ir;
+               }
+       }
+}
diff --git 
a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/layouts/VirtualListVerticalLayout.as
 
b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/layouts/VirtualListVerticalLayout.as
new file mode 100644
index 0000000..faeae54
--- /dev/null
+++ 
b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/layouts/VirtualListVerticalLayout.as
@@ -0,0 +1,464 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 org.apache.royale.jewel.beads.layouts
+{
+       COMPILE::JS
+       {
+
+       }
+    COMPILE::SWF {
+        import org.apache.royale.geom.Size;
+    }
+       import org.apache.royale.collections.ICollectionView;
+       import org.apache.royale.core.IBeadLayout;
+       import org.apache.royale.core.IDataProviderModel;
+       import org.apache.royale.core.IDataProviderVirtualItemRendererMapper;
+       import org.apache.royale.core.ILayoutView;
+       import org.apache.royale.core.ISelectableItemRenderer;
+       import org.apache.royale.core.IStrand;
+       import org.apache.royale.core.IStrandWithPresentationModel;
+       import org.apache.royale.events.Event;
+       import 
org.apache.royale.jewel.supportClasses.list.IListPresentationModel;
+       import org.apache.royale.core.ISelectionModel;
+        
+       /**
+        *  The VirtualListVerticalLayout class is used for Jewel List that 
wants to use VirtualListView
+     *  and Virtual ItemRenders.
+        *
+        *  @langversion 3.0
+        *  @playerversion Flash 10.2
+        *  @playerversion AIR 2.6
+        *  @productversion Royale 0.9.7
+        */
+       public class VirtualListVerticalLayout extends StyledLayoutBase 
implements IBeadLayout
+       {
+               /**
+                *  Constructor.
+                *
+                *  @langversion 3.0
+                *  @playerversion Flash 10.2
+                *  @playerversion AIR 2.6
+                *  @productversion Royale 0.9.7
+         *  @royaleignorecoercion HTMLDivElement
+                */
+               public function VirtualListVerticalLayout()
+               {
+                       super();
+               }
+
+        protected var dataProviderModel:IDataProviderModel;
+
+        override public function set strand(value:IStrand):void
+        {
+            super.strand = value;
+            dataProviderModel = host.getBeadByType(IDataProviderModel) as 
IDataProviderModel;
+            
+            COMPILE::JS
+            {
+                host.element.addEventListener("scroll", scrollHandler);
+            }
+        }
+
+        // override public function set strand(value:IStrand):void
+        // {
+        //     super.strand = value;
+        //     // dataProviderModel = host.getBeadByType(IDataProviderModel) 
as IDataProviderModel;
+        //     listModel = value.getBeadByType(ISelectionModel) as 
ISelectionModel;
+
+        //     //if the list is composed as part of another component, with a 
shared model (e.g. ComboBox) then it should not be the primary dispatcher
+               //      if (listModel is IJewelSelectionModel && 
!(IJewelSelectionModel(listModel).hasDispatcher)) {
+        //          IJewelSelectionModel(listModel).dispatcher = 
IEventDispatcher(value);
+               //      }
+        //     else {
+               //              
IEventDispatcher(listModel).addEventListener('rollOverIndexChanged', 
modelChangeHandler);
+               //              
IEventDispatcher(listModel).addEventListener('selectionChanged', 
modelChangeHandler);
+        //         
IEventDispatcher(listModel).addEventListener('dataProviderChanged', 
modelChangeHandler);
+        //     }
+        //     COMPILE::JS
+        //     {
+        //         host.element.addEventListener("scroll", scrollHandler);
+        //     }
+        // }
+        // protected function modelChangeHandler(event:Event):void{
+        //     IEventDispatcher(_strand).dispatchEvent(new Event(event.type));
+        // }
+        
+        COMPILE::JS
+        protected var topSpacer:HTMLDivElement;
+        
+        COMPILE::JS
+        protected var bottomSpacer:HTMLDivElement;
+        
+        protected var visibleIndexes:Array = [];
+        
+        protected function scrollHandler(e:Event):void
+        {
+            layout();
+        }
+        
+        private var inLayout:Boolean;
+        
+               /**
+                *  Layout children vertically
+                *
+                *  @langversion 3.0
+                *  @playerversion Flash 10.2
+                *  @playerversion AIR 2.6
+                *  @productversion Royale 0.9.7
+                *  @royaleignorecoercion Array
+                *  @royaleignorecoercion org.apache.royale.core.ILayoutHost
+                *  @royaleignorecoercion 
org.apache.royale.core.WrappedHTMLElement
+                *  @royaleignorecoercion 
org.apache.royale.core.IListPresentationModel
+                *  @royaleignorecoercion 
org.apache.royale.core.IStrandWithPresentationModel
+                */
+               override public function layout():Boolean
+               {
+            if (inLayout) return true;
+            inLayout = true;
+
+            // dataProviderModel = host.getBeadByType(IDataProviderModel) as 
IDataProviderModel;
+            
+                       COMPILE::SWF
+                       {
+                // the strategy for virtualization in SWF is based on the
+                // fact that we can completely control the scrolling metrics
+                // instead of trying to rely on the browsers built-in 
scrolling.
+                // This code puts enough renderers on the screen and then 
dictates
+                // the scrolling metrics.
+                               var contentView:ILayoutView = layoutView;
+
+                               var maxWidth:Number = 0;
+                               var maxHeight:Number = 0;
+                var dp:ICollectionView = dataProviderModel.dataProvider as 
ICollectionView;
+                if (!dp) 
+                {
+                    inLayout = false;
+                    return true;
+                }
+                var presentationModel:IListPresentationModel = (host as 
IStrandWithPresentationModel).presentationModel as IListPresentationModel;
+                               var hostWidthSizedToContent:Boolean = 
host.isWidthSizedToContent();
+                               var hostHeightSizedToContent:Boolean = 
host.isHeightSizedToContent();
+                               var hostWidth:Number = host.width;
+                               var hostHeight:Number = host.height;
+
+                               var data:Object;
+                               var canAdjust:Boolean = false;
+
+                               var paddingMetrics:EdgeData = 
(ValuesManager.valuesImpl as 
IBorderPaddingMarginValuesImpl).getPaddingMetrics(host);
+                               var borderMetrics:EdgeData = 
(ValuesManager.valuesImpl as 
IBorderPaddingMarginValuesImpl).getBorderMetrics(host);
+                               
+                               // adjust the host's usable size by the 
metrics. If hostSizedToContent, then the
+                               // resulting adjusted value may be less than 
zero.
+                               hostWidth -= paddingMetrics.left + 
paddingMetrics.right + borderMetrics.left + borderMetrics.right;
+                               hostHeight -= paddingMetrics.top + 
paddingMetrics.bottom + borderMetrics.top + borderMetrics.bottom;
+
+                               var xpos:Number = borderMetrics.left + 
paddingMetrics.left;
+                var ypos:Number = borderMetrics.top + paddingMetrics.top;
+                
+                var viewport:IScrollingViewport = 
host.getBeadByType(IScrollingViewport) as IScrollingViewport;
+                viewport.addEventListener("verticalScrollPositionChanged", 
scrollHandler);
+                var viewportTop:Number = viewport.verticalScrollPosition;
+                var viewportHeight:Number = hostHeight;
+                var startIndex:int = Math.floor(viewportTop / 
presentationModel.rowHeight);
+                var factory:IDataProviderVirtualItemRendererMapper = 
host.getBeadByType(IDataProviderVirtualItemRendererMapper) as 
IDataProviderVirtualItemRendererMapper;
+                var endIndex:int = Math.ceil((viewportTop + viewportHeight) / 
presentationModel.rowHeight);
+                var freeIndex:int;
+                var firstIndex:int;
+                var lastIndex:int;
+
+                if (visibleIndexes.length)
+                {
+                    if (startIndex < visibleIndexes[0])
+                    {
+                        // see if we can re-use any renderers
+                        freeIndex = visibleIndexes.pop();
+                        while (freeIndex >= endIndex)
+                        {
+                            factory.freeItemRendererForIndex(freeIndex);
+                            if (visibleIndexes.length == 0)
+                                break;
+                            freeIndex = visibleIndexes.pop();
+                        }
+                        if (visibleIndexes.length)
+                            endIndex = visibleIndexes[visibleIndexes.length - 
1];
+                    }
+                    else if (startIndex > visibleIndexes[0])
+                    {
+                        // see if we can re-use any renderers
+                        freeIndex = visibleIndexes.shift();
+                        while (freeIndex < startIndex)
+                        {
+                            factory.freeItemRendererForIndex(freeIndex);
+                            if (visibleIndexes.length == 0)
+                                break;
+                            freeIndex = visibleIndexes.shift();
+                        }
+                    }
+                    else
+                    {
+                        // see if rows got added or removed because height 
changed
+                        lastIndex = visibleIndexes[visibleIndexes.length - 1];
+                        if (lastIndex > endIndex)
+                        {
+                            // see if we can re-use any renderers
+                            freeIndex = visibleIndexes.pop();
+                            while (freeIndex > endIndex)
+                            {
+                                factory.freeItemRendererForIndex(freeIndex);
+                                if (visibleIndexes.length == 0)
+                                    break;
+                                freeIndex = visibleIndexes.pop();
+                            }
+                            inLayout = false;
+                            return true;  // we should be all done if we shrunk
+                        }
+                    }
+                    firstIndex = visibleIndexes[0];
+                    lastIndex = visibleIndexes[visibleIndexes.length - 1];
+                }
+                else
+                {
+                    firstIndex = dp.length;
+                    lastIndex = 0;
+                }
+                for (var i:int = startIndex; i < endIndex; i++)
+                {
+                    if (i >= dp.length) continue; // no more renderers needed
+                    
+                    var ir:ISelectableItemRenderer;
+                    if (i < firstIndex)
+                    {
+                        ir  = factory.getItemRendererForIndex(i, i - 
startIndex);
+                        sizeAndPositionRenderer(ir, xpos, ypos + 
(presentationModel.rowHeight * i), hostWidth, hostHeight);
+                        visibleIndexes.push(i);
+                    }
+                    else if (i > lastIndex)
+                    {
+                        ir  = factory.getItemRendererForIndex(i, i - 
startIndex);
+                        sizeAndPositionRenderer(ir, xpos, ypos + 
(presentationModel.rowHeight * i), hostWidth, hostHeight);
+                        visibleIndexes.push(i);
+                    }
+                }
+                visibleIndexes = visibleIndexes.sort(numberSort);
+
+                var view:VirtualDataContainerView = 
host.getBeadByType(VirtualDataContainerView) as VirtualDataContainerView;
+                view.lastContentSize = new Size(hostWidth, dp.length * 
presentationModel.rowHeight);
+
+                inLayout = false;
+                               return true;
+                       }
+                       COMPILE::JS
+                       {
+                // the strategy for virtualization in JS is to leverage the 
built-in scrollbars
+                // by creating a topSpacer and bottomSpacer that take up the 
area that is offscreen
+                // so the scrollbars have the right metrics, then create 
enough renderers to
+                // show in the visible area.  This code does not recycle 
renderers, but the
+                // factory can.  This code does try to keep renderers on the 
DOM that aren't
+                // going off-screen
+                var contentView:ILayoutView = layoutView;
+                var dp:ICollectionView = dataProviderModel.dataProvider as 
ICollectionView;
+                if (!dp) 
+                {
+                    inLayout = false;
+                    return true;
+                }
+                var presentationModel:IListPresentationModel = (host as 
IStrandWithPresentationModel).presentationModel as IListPresentationModel;
+                var totalHeight:Number = presentationModel.rowHeight * 
dp.length;
+                var viewportTop:Number = 
Math.max(contentView.element.scrollTop, 0);
+                var viewportHeight:Number = contentView.height;
+                var startIndex:int = Math.floor(viewportTop / 
presentationModel.rowHeight);
+                var factory:IDataProviderVirtualItemRendererMapper = 
host.getBeadByType(IDataProviderVirtualItemRendererMapper) as 
IDataProviderVirtualItemRendererMapper;
+                var endIndex:int = Math.ceil((viewportTop + viewportHeight) / 
presentationModel.rowHeight);
+                var freeIndex:int;
+                var firstIndex:int;
+                var lastIndex:int;
+                if (!topSpacer)
+                {
+                    topSpacer = document.createElement("div") as 
HTMLDivElement;
+                    contentView.element.appendChild(topSpacer);
+                }
+                topSpacer.style.height = (startIndex * 
presentationModel.rowHeight).toString() + "px";
+                // trace("starting layout: startIndex = " + startIndex + " 
endIndex = " + endIndex);
+                if (visibleIndexes.length)
+                {
+                    // trace("visibleIndexes: " + visibleIndexes);
+                    if (startIndex < visibleIndexes[0])
+                    {
+                        // trace("startIndex < visibleIndex[0]");
+                        // see if we can re-use any renderers
+                        freeIndex = visibleIndexes.pop();
+                        // trace("freeIndex: " + freeIndex);
+                        while (freeIndex > endIndex)
+                        {
+                            // trace("free: " + freeIndex);
+                            factory.freeItemRendererForIndex(freeIndex);
+                            if (visibleIndexes.length == 0)
+                                break;
+                            freeIndex = visibleIndexes.pop();
+                        }
+                        // we popped it off at the end of loop but if we didn't
+                        // use it, then push it back on
+                        if (freeIndex == endIndex)
+                            visibleIndexes.push(freeIndex);
+                        if (visibleIndexes.length)
+                        {
+                            endIndex = visibleIndexes[visibleIndexes.length - 
1];
+                            // trace("changing endIndex: " + endIndex);
+                        }
+                    }
+                    else if (startIndex > visibleIndexes[0])
+                    {
+                        // trace("startIndex > visibleIndex[0]");
+                        // see if we can re-use any renderers
+                        freeIndex = visibleIndexes.shift();
+                        // trace("freeIndex: " + freeIndex);
+                        while (freeIndex < startIndex)
+                        {
+                            // trace("free: " + freeIndex);
+                            factory.freeItemRendererForIndex(freeIndex);
+                            if (visibleIndexes.length == 0)
+                                break;
+                            freeIndex = visibleIndexes.shift();
+                        }
+                    }
+                    else
+                    {
+                        // trace("startIndex == visibleIndex[0]");
+                        // see if rows got added or removed because height 
changed
+                        lastIndex = visibleIndexes[visibleIndexes.length - 1];
+                        // trace("lastIndex: " + lastIndex);
+                        if (lastIndex > endIndex)
+                        {
+                            // see if we can re-use any renderers
+                            freeIndex = visibleIndexes.pop();
+                            // trace("freeIndex: " + freeIndex);
+                            while (freeIndex > endIndex)
+                            {
+                                // trace("free: " + freeIndex);
+                                factory.freeItemRendererForIndex(freeIndex);
+                                if (visibleIndexes.length == 0)
+                                    break;
+                                freeIndex = visibleIndexes.pop();
+                            }
+                            inLayout = false;
+                            return true;  // we should be all done if we shrunk
+                        }
+                    }
+                    firstIndex = visibleIndexes[0];
+                    lastIndex = visibleIndexes[visibleIndexes.length - 1];
+                    // trace("done freeing: firstIndex = " + firstIndex + " 
lastIndex = " + lastIndex);
+                }
+                else
+                {
+                    firstIndex = dp.length;
+                    lastIndex = 0;
+                    // trace("no freeing: firstIndex = " + firstIndex + " 
lastIndex = " + lastIndex);
+                }
+                for (var i:int = startIndex; i < endIndex; i++)
+                {
+                    if (i >= dp.length) continue; // no more renderers needed
+                    
+                    var ir:ISelectableItemRenderer;
+                    if (i < firstIndex)
+                    {
+                    //    trace("i < firstIndex: creating: i = " + i);
+                       ir  = factory.getItemRendererForIndex(i, i - startIndex 
+ 1);
+                    //    ir.element.style.display = "block";
+                       visibleIndexes.push(i);
+                    }
+                    else if (i > lastIndex)
+                    {
+                        // trace("i > lastIndex: creating: i = " + i);
+                        ir  = factory.getItemRendererForIndex(i, i - 
startIndex + 1);
+                        // ir.element.style.display = "block";
+                        visibleIndexes.push(i);
+                    }
+                }
+                visibleIndexes = visibleIndexes.sort(numberSort);
+                // trace("visibleIndexes: " + visibleIndexes);
+                if (!bottomSpacer)
+                {
+                    bottomSpacer = document.createElement("div") as 
HTMLDivElement;
+                    contentView.element.appendChild(bottomSpacer);
+                }
+                else
+                {
+                    // ensure bottom spacer is at the bottom!
+                    contentView.element.removeChild(bottomSpacer);             
       
+                    contentView.element.appendChild(bottomSpacer);             
       
+                }
+                
+                var numBottomRows:int = dp.length - endIndex;
+                bottomSpacer.style.height = (numBottomRows > 0) ? 
(numBottomRows * presentationModel.rowHeight).toString() + "px" : "0px";  
+                // trace("ENDING LAYOUT: bottom spacer = " + 
bottomSpacer.style.height);
+                inLayout = false;
+                               return true;
+                       }
+               }
+        
+        public function numberSort(a:int, b:int):int
+        {
+            return a - b;
+        }
+
+        COMPILE::SWF
+        protected function sizeAndPositionRenderer(ir:ISelectableItemRenderer, 
xpos:Number, ypos:Number, hostWidth:Number, hostHeight:Number):void
+        {
+            var ilc:ILayoutChild;
+            var positions:Object = childPositions(ir);
+            var margins:Object = childMargins(ir, hostWidth, hostHeight);
+            
+            ilc = ir as ILayoutChild;
+            var child:IUIBase = ir as IUIBase;
+            
+            ypos += margins.top;
+            
+            var childXpos:Number = xpos + margins.left; // default x position
+            
+            var childWidth:Number = child.width;
+            if (ilc != null && !isNaN(ilc.percentWidth)) {
+                childWidth = hostWidth * ilc.percentWidth/100.0;
+                ilc.setWidth(childWidth);
+            }
+            else if (ilc.isWidthSizedToContent() && !margins.auto)
+            {
+                childWidth = hostWidth;
+                ilc.setWidth(childWidth);
+            }
+            if (margins.auto)
+                childXpos = (hostWidth - childWidth) / 2;
+            
+            if (ilc) {
+                ilc.setX(childXpos);
+                ilc.setY(ypos);
+                
+                if (!isNaN(ilc.percentHeight)) {
+                    var newHeight:Number = hostHeight * ilc.percentHeight / 
100;
+                    ilc.setHeight(newHeight);
+                }
+                
+            } else {
+                child.x = childXpos;
+                child.y = ypos;
+            }
+            
+            ypos += child.height + margins.bottom;
+        }
+       }
+}
diff --git 
a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/views/VirtualListView.as
 
b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/views/VirtualListView.as
new file mode 100644
index 0000000..26737a1
--- /dev/null
+++ 
b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/views/VirtualListView.as
@@ -0,0 +1,137 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 org.apache.royale.jewel.beads.views
+{
+       import org.apache.royale.core.IItemRenderer;
+       import org.apache.royale.core.IRollOverModel;
+       import org.apache.royale.core.ISelectableItemRenderer;
+       import org.apache.royale.core.ISelectionModel;
+       import org.apache.royale.events.Event;
+       import org.apache.royale.html.beads.VirtualDataContainerView;
+        
+       /**
+        *  The VirtualListView class creates the visual elements of the 
org.apache.royale.jewel.List
+        *  component in a way that can be recicled to reuse as the user 
scrolls the list getting performance improvements
+        *  when dataproviders with lots of items are passed to the component. 
In This way Royale just create a few 
+        *  item renderers visible for the user, instead of one renderer for 
each item in the data provider. 
+        *   
+        *  A List consists of the area to display the data (in the dataGroup), 
any
+        *  scrollbars, and so forth.
+        *
+        *  @viewbead
+        *  @langversion 3.0
+        *  @playerversion Flash 10.2
+        *  @playerversion AIR 2.6
+        *  @productversion Royale 0.9.7
+        */
+       public class VirtualListView extends VirtualDataContainerView
+       {
+               public function VirtualListView()
+               {
+                       super();
+               }
+
+               protected var listModel:ISelectionModel;
+
+               protected var lastSelectedIndex:int = -1;
+
+               /**
+                * @private
+                * @royaleignorecoercion org.apache.royale.core.ISelectionModel
+                */
+               override protected function handleInitComplete(event:Event):void
+               {
+                       listModel = _strand.getBeadByType(ISelectionModel) as 
ISelectionModel;
+                       listModel.addEventListener("selectionChanged", 
selectionChangeHandler);
+                       listModel.addEventListener("rollOverIndexChanged", 
rollOverIndexChangeHandler);
+                       // listenOnStrand("itemsCreated", itemsCreatedHandler);
+
+                       super.handleInitComplete(event);
+               }
+
+               /**
+                * @private
+                * Ensure the list selects the selectedItem if some is set by 
the user at creation time
+                */
+               // override protected function 
itemsCreatedHandler(event:Event):void
+               // {
+        //     super.itemsCreatedHandler(event);
+               //      if(listModel.selectedIndex != -1)
+               //              selectionChangeHandler(null);
+               // }
+
+        protected var firstElementIndex:int = 1;
+               /**
+                * Retrieve the renderer for a given index
+                */
+        override public function 
getItemRendererForIndex(index:int):IItemRenderer
+        {
+            if (contentView.numElements == 0)
+                return null;
+            
+            var firstIndex:int = (contentView.getElementAt(firstElementIndex) 
as ISelectableItemRenderer).index;
+            
+            if (index < firstIndex) 
+                return null;
+            if (index >= (firstIndex + contentView.numElements))
+                return null;
+            
+            return contentView.getElementAt(index - firstIndex + 
firstElementIndex) as IItemRenderer;            
+        }
+        
+               /**
+                * @private
+                * @royaleignorecoercion 
org.apache.royale.core.ISelectableItemRenderer
+                */
+               protected function selectionChangeHandler(event:Event):void
+               {
+                       var ir:ISelectableItemRenderer = 
dataGroup.getItemRendererForIndex(lastSelectedIndex) as ISelectableItemRenderer;
+                       if(ir)
+                               ir.selected = false;
+                       ir = 
dataGroup.getItemRendererForIndex(listModel.selectedIndex) as 
ISelectableItemRenderer;
+                       if(ir)
+                               ir.selected = true;
+
+                       lastSelectedIndex = listModel.selectedIndex;
+               }
+
+               protected var lastRollOverIndex:int = -1;
+
+               /**
+                * @private
+                * @royaleignorecoercion 
org.apache.royale.core.ISelectableItemRenderer
+                * * @royaleignorecoercion org.apache.royale.core.IRollOverModel
+                */
+               protected function rollOverIndexChangeHandler(event:Event):void
+               {
+                       var ir:ISelectableItemRenderer = 
dataGroup.getItemRendererForIndex(lastRollOverIndex) as ISelectableItemRenderer;
+                       if(ir)
+                               ir.hovered = false;
+                       ir = dataGroup.getItemRendererForIndex((listModel as 
IRollOverModel).rollOverIndex) as ISelectableItemRenderer;
+                       if(ir)
+                               ir.hovered = true;
+                       lastRollOverIndex = (listModel as 
IRollOverModel).rollOverIndex;
+               }
+
+               override protected function 
dataProviderChangeHandler(event:Event):void
+               {
+
+               }
+       }
+}
diff --git a/frameworks/projects/Jewel/src/main/sass/components/_list.sass 
b/frameworks/projects/Jewel/src/main/sass/components/_list.sass
index 1c2be5f..c51ec32 100644
--- a/frameworks/projects/Jewel/src/main/sass/components/_list.sass
+++ b/frameworks/projects/Jewel/src/main/sass/components/_list.sass
@@ -59,4 +59,10 @@ j|ListItemRenderer
         //---iBorderModel: 
ClassReference('org.apache.royale.html.beads.models.SingleLineBorderModel')
         IContentView: 
ClassReference("org.apache.royale.jewel.supportClasses.list.DataGroup")
         //font-size: 11px
-        //font-family: Arial
\ No newline at end of file
+        //font-family: Arial
+
+
+j|VirtualList
+    IBeadView:  
ClassReference("org.apache.royale.jewel.beads.views.VirtualListView")
+    IBeadLayout: 
ClassReference("org.apache.royale.jewel.beads.layouts.VirtualListVerticalLayout")
+    IDataProviderItemRendererMapper: 
ClassReference("org.apache.royale.jewel.beads.itemRenderers.VirtualDataItemRendererFactoryForCollectionView")
\ No newline at end of file

Reply via email to