What to do? There's no SWT-API available to calculate this information. So we need a bunch of custom code to make this work. The following codefragements are only a first rough test case and they need JFace from 3.3M4 and a patch from bug 151295 .
Step 1: Setup the infrastructure
- Event object to inform consumers about the change of visible rows
public class ViewerRowStateChangedEvent extends EventObject {- ViewerRowStateChangeListener for consumers to implement
private static final long serialVersionUID = 1L;
public ArrayList itemsHidden;
public ArrayList itemsVisible;
public ViewerRowStateChangedEvent(Object source) {
super(source);
}
}
public interface ViewerRowStateChangeListener {Step 2: Create an AbstractClass implementing the visible row logic on base of ViewerRow and the new API whichn will hopefully added by bug 151295.
public void itemStateChangedListener(ViewerRowStateChangedEvent event);
}
public abstract class AbstractViewerRowVisibilityStateSupport {We simply listen to events who can change the items shown in the Scrollable make a diff to the last state and inform all consumers about the change.
private ArrayList currentItems = new ArrayList();
private ColumnViewer columnViewer;
private ListenerList listenerList = new ListenerList();
public AbstractViewerRowVisibilityStateSupport(ColumnViewer columnViewer) {
this.columnViewer = columnViewer;
Listener l = new Listener() {
public void handleEvent(Event event) {
ArrayList list = recalculateVisibleItems();
ArrayList itemsVisible = new ArrayList();
Iterator it = list.iterator();
Object obj;
while( it.hasNext() ) {
obj = it.next();
if( ! currentItems.remove(obj) ) {
itemsVisible.add(obj);
}
}
ArrayList hiddenItems = currentItems;
currentItems = list;
if( itemsVisible.size() > 0 || hiddenItems.size() > 0 ) {
if( ! listenerList.isEmpty() ) {
ColumnViewer v;
v = AbstractViewerRowVisibilityStateSupport.this.columnViewer;
ViewerRowStateChangedEvent ev = new ViewerRowStateChangedEvent(v);
ev.itemsHidden = hiddenItems;
ev.itemsVisible = itemsVisible;
Object[] listeners = listenerList.getListeners();
ViewerRowStateChangeListener l;
for( int i = 0; i < listeners.length; i++ ) {
l = (ViewerRowStateChangeListener)listeners[i];
l.itemStateChangedListener(ev);
}
}
}
}
};
addListeners(getControl(),l);
}
protected abstract void addListeners(Scrollable control, Listener l);
protected abstract ViewerRow getTopRow();
protected Scrollable getControl() {
return (Scrollable)columnViewer.getControl();
}
public void addItemStateListener(ViewerRowStateChangeListener listener) {
listenerList.add(listener);
}
private ArrayList recalculateVisibleItems() {
ArrayList list = new ArrayList(100);
ViewerRow topRow = getTopRow();
if( topRow != null ) {
int totalHeight = getControl().getClientArea().height;
int itemHeight = topRow.getBounds().height;
list.add(topRow);
int tmp = topRow.getBounds().x+itemHeight;
// tmp += itemHeight;
// this would be more precise but half rows
// would be marked as non-visible
// run until we reached the end of the client-area
while( tmp < totalHeight ) {
tmp += itemHeight;
topRow = topRow.getNeighbor(ViewerRow.BELOW, false);
if( topRow == null ) {
break;
}
list.add(topRow);
}
}
return list;
}
}
But what are the events how modify the items shown? This is delegated to specialized classes for Table and Tree because those may differ from control to control.
Step 3: Provide specialized implementation for Tree and Table
- An implementation for SWT-Table
public class TableViewerRowVisibilityStateSupport extends- An implementation for SWT-Tree
AbstractViewerRowVisibilityStateSupport {
public TableViewerRowVisibilityStateSupport(TableViewer columnViewer) {
super(columnViewer);
}
protected void addListeners(Scrollable control, Listener l) {
control.getVerticalBar().addListener(SWT.Selection, l);
control.addListener(SWT.Resize, l);
control.addListener(SWT.KeyUp, l);
}
protected ViewerRow getTopRow() {
Table t = (Table)getControl();
int index = t.getTopIndex();
TableItem topItem = t.getItem(index);
if( topItem != null ) {
return (ViewerRow) topItem.getData(ViewerRow.ROWPART_KEY);
}
return null;
}
}
public class TreeViewerRowVisibilityStateSupport extendsAs said this is very rough first draft how this could work. I'll post this got some coments about possible issues this could provoke.
AbstractViewerRowVisibilityStateSupport {
public TreeViewerRowVisibilityStateSupport(TreeViewer columnViewer) {
super(columnViewer);
}
protected void addListeners(Scrollable control, Listener l) {
control.getVerticalBar().addListener(SWT.Selection, l);
control.addListener(SWT.Resize, l);
control.addListener(SWT.MouseUp, l);
control.addListener(SWT.KeyUp, l);
}
protected ViewerRow getTopRow() {
TreeItem topItem = ((Tree)getControl()).getTopItem();
if( topItem != null ) {
return (ViewerRow) topItem.getData(ViewerRow.ROWPART_KEY);
}
return null;
}
}
19 comments:
Hi Tom,
when figuring out how virtual Trees and Tables work in Eclipse I came across the same problem: how can I find out which Tree/Table items are not visible anymore so that I can despose them?
I solved the problem with an SWT.SetData listener in the inputChanged method in my class that implements ILazyTreeContentProvider.
So when I scroll down, I get a notification in the handleEvent method for every TreeItem that gets displayed and via tree.getTopItem() and TreeItem.indexOf() I can track with own code which TreeItems get 'scrolled-out'. I would like to post some more code here to make this more clear, but it's very inconvenient with this little html-formular here to post source code so that it looks good.
It's a little tricky but this way you can keep track of the TreeItems that are visible/non-visible. It works similar for Tables of course.
Is the SetData-Callback also called when you scroll up once more? Nevertheless this only works for tables/trees with the virtual bit set whereas this works for in theory for any table/tree.
Can you give me a concrete example (better yet, a snippet) that shows why you might need this?
Well the reason is fairly simple look at
Bug 36977
I went to the bug report and it contained too much text for me to understand. Can you summarize it?
NOTE: I'm not trying to start an argument or claim you don't need the feature etc. etc.
@Tom: I am clearing the items that get out of sight and I get notified via the SetData-Callback when I scroll up again.
I would prefer your solution from now on as it also works for non-virtual trees/tables.
@Steve: I see the use case where you have really large tables/trees with thousands or even ten-thousands of items, maybe in an RCP application. Someone could need to dispose resources for items that are no longer visible so that the memory-consumption of the RCP application doesn't get too huge.
Ludwig is right in my case I have a table who has potentially different images per row. So it makes sense to me to dispose e.g. the top most image when the user scrolls the table-rows out of view.
The bug report I linked simply request e.g. to use Button(SWT.CHECK) to use in viewers instead of the current work-around of diplaying an image if you want a table who displays a check-box in the e.g. second row.
I understand the virtual case (See https://bugs.eclipse.org/bugs/show_bug.cgi?id=163432).
The non-virtual case seems error prone to me (but it is possible that you have every case).
Is there something SWT could provide like an event similar to the one it provides for virtual tables with SetData? Something like ItemShown/ItemHidden?
Hi,
I had the same problem, and handled it at the SWT level. Details are in the bug 163432 that Steve linked. Basically, I used a cache that limits the number of images in memory. When the cache is full, older images are disposed. But I'm still not happy with this, especially with the fixed size of the cache that should depend of the number of visible items.
Another problem is that I use a custom widget (see : http://sharemedia.free.fr/swtgallery_home.php) and I want to keep the API as close as possible to Tree and Table. This widget let the user define the way items are displayed so I can't suppose they are drawn from top to bottom.
In your code, you are looping on visible items. Does this slow down scrolling ?
I like Tom's idea of an swt event fired when an item toggles between visible and invisible. Another way of doing this could be to have a method that returns only visible items (may be easier to implement).
--
Nicolas
Post a Comment