Overall, this change looks pretty good. My comments inline.
G
On Nov 12, 2009, at 7:38 AM, [email protected] wrote:
Author: noelgrandin
Date: Thu Nov 12 12:38:13 2009
New Revision: 835368
URL: http://svn.apache.org/viewvc?rev=835368&view=rev
Log:
PIVOT-301: Add a "variableItemHeight" style to TerraListViewSkin
Modified:
incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/
TerraListViewSkin.java
Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/
terra/TerraListViewSkin.java
URL:
http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraListViewSkin.java?rev=835368&r1=835367&r2=835368&view=diff
=
=
=
=
=
=
=
=
======================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/
TerraListViewSkin.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/
TerraListViewSkin.java Thu Nov 12 12:38:13 2009
@@ -63,6 +63,10 @@
private Color highlightColor;
private Color highlightBackgroundColor;
private boolean showHighlight;
+ private boolean variableItemHeight;
+ private ArrayList<Integer> variableItemHeightYCache = new
ArrayList<Integer>();
+ /* I can work out all of the heights from the YCache except for
the last one */
+ private int variableItemLastHeight;
I think you can do this all with an array, see below. I suggest
renaming the array to itemHeights. It can be set to null when
variableItemHeight is false.
private Insets checkboxPadding = new Insets(2, 2, 2, 0);
private int highlightedIndex = -1;
@@ -131,12 +135,29 @@
ListView listView = (ListView)getComponent();
List<Object> listData = (List<Object>)listView.getListData();
- preferredHeight = listData.getLength() * getItemHeight();
+ ListView.ItemRenderer itemRenderer =
listView.getItemRenderer();
+
+ if (variableItemHeight) {
+ int clientWidth = width;
+ if (listView.getCheckmarksEnabled()) {
+ clientWidth = Math.max(clientWidth -
(CHECKBOX.getWidth() + (checkboxPadding.left
+ + checkboxPadding.right)), 0);
+ }
+
+ int index = 0;
+ for (Object item : listData) {
+ itemRenderer.render(item, index++, listView, false,
false, false, false);
+ preferredHeight += itemRenderer.getPreferredHeight
(clientWidth);
+ }
+ } else {
+ preferredHeight = listData.getLength() *
getFixedItemHeight();
+ }
return preferredHeight;
}
@Override
+ @SuppressWarnings("unchecked")
public int getBaseline(int width, int height) {
ListView listView = (ListView)getComponent();
@@ -149,8 +170,20 @@
}
ListView.ItemRenderer itemRenderer = listView.getItemRenderer
();
- itemRenderer.render(null, -1, listView, false, false,
false, false);
- baseline = itemRenderer.getBaseline(clientWidth,
getItemHeight());
+ List<Object> listData = (List<Object>)listView.getListData();
+ if (variableItemHeight && listData.getLength()>0) {
There should be spaces around ">" operator. Also - we may want to
return -1 in the case where variableItemHeight is true, but
listData.getLength() returns 0, to indicate that there is no baseline.
+ itemRenderer.render(listData.get(0), 0, listView,
false, false, false, false);
+ int itemHeight = itemRenderer.getPreferredHeight
(clientWidth);
+ if (listView.getCheckmarksEnabled()) {
+ itemHeight = Math.max(CHECKBOX.getHeight() +
(checkboxPadding.top
+ + checkboxPadding.bottom), itemHeight);
+ }
+
+ baseline = itemRenderer.getBaseline(clientWidth,
itemHeight);
+ } else {
+ itemRenderer.render(null, -1, listView, false, false,
false, false);
+ baseline = itemRenderer.getBaseline(clientWidth,
getFixedItemHeight());
+ }
return baseline;
}
@@ -169,14 +202,19 @@
int width = getWidth();
int height = getHeight();
- int itemHeight = getItemHeight();
// Paint the background
if (backgroundColor != null) {
graphics.setPaint(backgroundColor);
graphics.fillRect(0, 0, width, height);
}
+
+ if (variableItemHeight) {
+ paintVariableItemHeight(graphics);
+ return;
+ }
It would be preferable to keep all the drawing code within the
existing paint() method. We don't have any other cases in the codebase
where we delegate to a "special" paint method, and I think it will be
easier to do given some slight modifications - see below.
+ int itemHeight = getFixedItemHeight();
// Paint the list contents
int itemStart = 0;
int itemEnd = listData.getLength() - 1;
@@ -247,6 +285,107 @@
}
}
+ @SuppressWarnings("unchecked")
+ private void paintVariableItemHeight(Graphics2D graphics) {
+ ListView listView = (ListView)getComponent();
+ List<Object> listData = (List<Object>)listView.getListData();
+ ListView.ItemRenderer itemRenderer =
listView.getItemRenderer();
+
+ int width = getWidth();
+
+ // Paint the list contents
+ int itemEnd = listData.getLength() - 1;
+
+ // Ensure that we only paint items that are visible
+ Rectangle clipBounds = graphics.getClipBounds();
+
+ int checkboxHeight = 0;
+ if (listView.getCheckmarksEnabled()) {
+ checkboxHeight = CHECKBOX.getHeight() +
(checkboxPadding.top
+ + checkboxPadding.bottom);
+ }
+
+ // if we haven't initialised the cache, we need to paint
everything
+ boolean computeCache = variableItemHeightYCache.getLength
() != listData.getLength();
+ int itemY = 0;
The layout() method is probably a better place to put the item height
calculation code. This method is guaranteed to be called any time the
component is flagged as invalid. If the component was invalidated by a
call to invalidateComponent() in the skin, it will also be repainted.
This way, you can rely on your item heights being valid by the time
paint() is called, because layout() will have been called first.
+
+ for (int itemIndex = 0; itemIndex <= itemEnd; itemIndex++) {
+ if (!computeCache && clipBounds!=null && itemY >
clipBounds.y + clipBounds.height) {
+ break;
+ }
+ Object item = listData.get(itemIndex);
+ boolean highlighted = (itemIndex == highlightedIndex
+ && listView.getSelectMode() !=
ListView.SelectMode.NONE);
+ boolean selected = listView.isItemSelected(itemIndex);
+ boolean disabled = listView.isItemDisabled(itemIndex);
+
+ int itemWidth = width;
+ int itemX = 0;
+
+ boolean checked = false;
+ if (listView.getCheckmarksEnabled()) {
+ checked = listView.isItemChecked(itemIndex);
+ itemX = CHECKBOX.getWidth() + (checkboxPadding.left
+ + checkboxPadding.right);
+ itemWidth -= itemX;
+ }
+
+ itemRenderer.render(item, itemIndex, listView,
selected, checked, highlighted, disabled);
+ int itemHeight = itemRenderer.getPreferredHeight
(itemWidth);
+ if (listView.getCheckmarksEnabled()) {
+ itemHeight = Math.max(itemHeight, checkboxHeight);
+ }
+
+ boolean paintItem = true;
+ if (clipBounds != null) {
+ paintItem = (itemY + itemHeight) >= clipBounds.y &&
itemY < (clipBounds.y + clipBounds.height);
+ }
+ if (paintItem) {
+ Color itemBackgroundColor = null;
+
+ if (selected) {
+ itemBackgroundColor = (listView.isFocused())
+ ? this.selectionBackgroundColor :
inactiveSelectionBackgroundColor;
+ } else {
+ if (highlighted && showHighlight && !disabled) {
+ itemBackgroundColor =
highlightBackgroundColor;
+ }
+ }
+
+ if (itemBackgroundColor != null) {
+ graphics.setPaint(itemBackgroundColor);
+ graphics.fillRect(0, itemY, width, itemHeight);
+ }
+
+ if (listView.getCheckmarksEnabled()) {
+ int checkboxY = (itemHeight - CHECKBOX.getHeight
()) / 2;
+ Graphics2D checkboxGraphics = (Graphics2D)
graphics.create(checkboxPadding.left,
+ itemY + checkboxY, CHECKBOX.getWidth(),
CHECKBOX.getHeight());
+
+ CHECKBOX.setSelected(checked);
+ CHECKBOX.setEnabled(!disabled && !
listView.isCheckmarkDisabled(itemIndex));
+ CHECKBOX.paint(checkboxGraphics);
+ checkboxGraphics.dispose();
+ }
+
+ // Paint the data
+ Graphics2D rendererGraphics = (Graphics2D)
graphics.create(itemX, itemY,
+ itemWidth, itemHeight);
+
+ itemRenderer.setSize(itemWidth, itemHeight);
+ itemRenderer.paint(rendererGraphics);
+ rendererGraphics.dispose();
+ }
+
+ if (computeCache) {
+ variableItemHeightYCache.add(itemY);
+ variableItemLastHeight = itemHeight;
+ }
+ itemY += itemHeight;
+ }
+ }
+
+
// List view skin methods
@Override
@SuppressWarnings("unchecked")
@@ -257,9 +396,17 @@
ListView listView = (ListView)getComponent();
- int index = (y / getItemHeight());
+ int index;
+ if (variableItemHeight) {
+ index = ArrayList.binarySearch
(variableItemHeightYCache, y);
+ if (index<0) {
+ index = -index-2;
Insert spaces around "<" and "-" operators.
+
+ }
+ } else {
+ index = (y / getFixedItemHeight());
+ }
List<Object> listData = (List<Object>)listView.getListData();
-
if (index >= listData.getLength()) {
index = -1;
}
@@ -269,8 +416,21 @@
@Override
public Bounds getItemBounds(int index) {
- int itemHeight = getItemHeight();
- return new Bounds(0, index * itemHeight, getWidth(),
itemHeight);
+ int itemY;
+ int itemHeight;
+ if (variableItemHeight) {
+ itemY = variableItemHeightYCache.get(index);
+ if (index < variableItemHeightYCache.getLength() - 1) {
+ itemHeight = variableItemHeightYCache.get(index+1)
- itemY;
Insert spaces around "+" operator.
+ } else {
+ itemHeight = variableItemLastHeight;
+ }
+ return new Bounds(0, itemY, getWidth(), itemHeight);
This return call doesn't appear to be necessary.
+ } else {
+ itemHeight = getFixedItemHeight();
+ itemY = index * itemHeight;
+ }
+ return new Bounds(0, itemY, getWidth(), itemHeight);
}
@Override
@@ -285,7 +445,7 @@
return itemIndent;
}
- public int getItemHeight() {
+ private int getFixedItemHeight() {
ListView listView = (ListView)getComponent();
ListView.ItemRenderer itemRenderer = listView.getItemRenderer
();
itemRenderer.render(null, -1, listView, false, false, false,
false);
@@ -568,6 +728,16 @@
setCheckboxPadding(Insets.decode(checkboxPadding));
}
+ public boolean getVariableItemHeight() {
+ return variableItemHeight;
+ }
Rename to isVariableItemHeight() ("getVariableItemHeight()" implies
that the method will return a number, especially since we also have a
"getFixedItemHeight()" method).
+
+ public void setVariableItemHeight(boolean variableItemHeight) {
+ this.variableItemHeight = variableItemHeight;
+ repaintComponent();
+ }
+
+
@Override
public boolean mouseMove(Component component, int x, int y) {
boolean consumed = super.mouseMove(component, x, y);
@@ -617,12 +787,12 @@
ListView listView = (ListView)getComponent();
List<Object> listData = (List<Object>)listView.getListData();
- int itemHeight = getItemHeight();
- int itemIndex = y / itemHeight;
+ int itemIndex = getItemAt(y);
- if (itemIndex < listData.getLength()
+ if (itemIndex != -1
+ && itemIndex < listData.getLength()
&& !listView.isItemDisabled(itemIndex)) {
- int itemY = itemIndex * itemHeight;
+ int itemY = getItemBounds(itemIndex).y;
if (!(listView.getCheckmarksEnabled()
&& x > checkboxPadding.left
@@ -693,12 +863,11 @@
List<Object> listData = (List<Object>)listView.getListData();
- int itemHeight = getItemHeight();
- int itemIndex = y / itemHeight;
+ int itemIndex = getItemAt(y);
if (itemIndex < listData.getLength()
&& !listView.isItemDisabled(itemIndex)) {
- int itemY = itemIndex * itemHeight;
+ int itemY = getItemBounds(itemIndex).y;
if (listView.getCheckmarksEnabled()
&& !listView.isCheckmarkDisabled(itemIndex)
@@ -855,11 +1024,13 @@
// List view events
@Override
public void listDataChanged(ListView listView, List<?>
previousListData) {
+ variableItemHeightYCache.clear();
The calls to clear the item height cache will not be necessary once
the height calculation is moved to layout().
invalidateComponent();
}
@Override
public void itemRendererChanged(ListView listView,
ListView.ItemRenderer previousItemRenderer) {
+ variableItemHeightYCache.clear();
invalidateComponent();
}
@@ -875,6 +1046,7 @@
@Override
public void checkmarksEnabledChanged(ListView listView) {
+ variableItemHeightYCache.clear();
invalidateComponent();
}
@@ -902,27 +1074,36 @@
// List view item events
@Override
public void itemInserted(ListView listView, int index) {
+ variableItemHeightYCache.clear();
invalidateComponent();
}
@Override
public void itemsRemoved(ListView listView, int index, int
count) {
+ variableItemHeightYCache.clear();
invalidateComponent();
}
@Override
public void itemUpdated(ListView listView, int index) {
+ variableItemHeightYCache.clear();
invalidateComponent();
}
@Override
public void itemsCleared(ListView listView) {
+ variableItemHeightYCache.clear();
invalidateComponent();
}
@Override
public void itemsSorted(ListView listView) {
- repaintComponent();
+ variableItemHeightYCache.clear();
+ if (variableItemHeight) {
+ invalidateComponent();
+ } else {
+ repaintComponent();
+ }
}
// List view item state events
@@ -934,18 +1115,26 @@
// List view selection detail events
@Override
public void selectedRangeAdded(ListView listView, int
rangeStart, int rangeEnd) {
- // Repaint the area containing the added selection
- int itemHeight = getItemHeight();
- repaintComponent(0, rangeStart * itemHeight,
- getWidth(), (rangeEnd - rangeStart + 1) * itemHeight);
+ if (variableItemHeight) {
+ repaintComponent();
+ } else {
+ // Repaint the area containing the added selection
+ int itemHeight = getFixedItemHeight();
+ repaintComponent(0, rangeStart * itemHeight,
+ getWidth(), (rangeEnd - rangeStart + 1) *
itemHeight);
+ }
After you move the item height calculation to layout(), you can
probably consolidate both cases into a single case and simply repaint
the affected bounds.
}
@Override
public void selectedRangeRemoved(ListView listView, int
rangeStart, int rangeEnd) {
- // Repaint the area containing the removed selection
- int itemHeight = getItemHeight();
- repaintComponent(0, rangeStart * itemHeight,
- getWidth(), (rangeEnd - rangeStart + 1) * itemHeight);
+ if (variableItemHeight) {
+ repaintComponent();
+ } else {
+ // Repaint the area containing the removed selection
+ int itemHeight = getFixedItemHeight();
+ repaintComponent(0, rangeStart * itemHeight,
+ getWidth(), (rangeEnd - rangeStart + 1) *
itemHeight);
+ }
Same comment as above.
}
@Override