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