Currently, CompositeTable treats the concepts of focus, selection, and "current" as identical. I would like to separate focus and selection and track them separately, similar to the way that an SWT Table treats selection, with SINGLE and MULTI support and methods to retrieve the selected indices and items. As a next step, I would like to implement CHECK style selections. Ultimately, I would like to use CompositeTable as an ISelectionProvider that can act as a drop-in replacement for a TableViewer and as an ICheckable that can replace CheckboxTableViewer.
There are a number of issues that need to be solved, and I'd like to describe my ideas and get some feedback from the list before I dive in. I'd appreciate any comments you could provide. 0. Fill the architectural role of JFace Viewer. Overall, I propose to build a CompositeTableViewer (CTV) class to fill the architectural role of a JFace Viewer. Unfortunately, due to the statically-typed Item in method signatures, this class cannot derive from Viewer. I will attempt to make it semantically compatible, even if it cannot have true binary compatibility. 1. Emulate the JFace Element-Item mapping. Unlike the native Table and Tree controls (and the other Nebula grids), CompositeTable does not have a concept of an Item to represent each row. CompositeTable is "pure virtual" -- rows are continuously reused, while SWT Table is "lazy" -- rows are created as needed, but once present they are not reused. AFAIK, this was a deliberate choice to avoid memory cost. Unfortunately, much of the JFace Viewer API depends on the existence of a statically-typed and long-lived Item for each row. CompositeTable does have the concept of an index, however. Operations on rows are performed in terms of a row index within the entire collection being displayed. This could be seen as roughly equivalent to SWT operations that are performed in terms of an Item. Therefore, I propose that the selection and check concepts should work in terms of collection indices within CompositeTable. Thus CT can take on the architectural role of the SWT widget within the JFace architecture, despite the lack of an Item concept. All JFace API that accepts an Item parameter would be rewritten to take an Integer parameter instead. AFAIK, JFace does not guarantee that an Item will always map to the same element. A change in sort order will change this mapping. Does anyone know of a reason why an index number could not stand in for an Item within a Viewer implementation? 2. Emulate the SWT Table selection/check API. 2a. Table provides various selection-related methods -- often with multiple overloads: deselect/deselectAll, select/selectAll, setSelection, showSelection, getSelectonIndex/getSelectionIndices, getSelectionCount, isSelected, addSelectionListener/removeSelectionListener. Also, the remove/removeAll() methods interact with the selection state. The check-state methods on TableItem would need to be moved up to CompositeTable, as well: get/setGrayed, get/setChecked. 2b. Some of the CompositeTable APIs use an index relative to the top of the control, instead of relative to the collection being displayed. There seems to be an easy workaround for each case. I propose that the API be standardized to always operate in terms of the entire collection. Operations which need to work in terms of the row Control arrangement should be package-private or protected. 3. Implement visual feedback for selection and checked states. 3a. On one hand, it would be nice if CompositeTable could provide a consistent UI for selection and checked states "for free" like an SWT Table does. On the other hand, the whole purpose of CompositeTable is to support custom rendering of table rows. My instinct is to leave this up to the label provider to render, but provide a mechanism in CompositeTable to track selections and checked states cheaply, outside of the row Control itself. That way, generic ISelectionProvider and ICheckable implementations can be written, even though they do not know how the selection and checked states will be manipulated by users. Client code will need to update the mapping between the CompositeTable's internal state and the visual state of each row, in whatever way is appropriate for the specific row Control. Would it be better for setSelection to automatically refresh any affected row Controls, or should it be the client code's responsibility to refresh if/when it is necessary? 3b. Even if a general solution is impossible, I could provide a default implementation that wraps each row Control in a new Composite. Selection would appear as a border highlight and could be toggled by clicking on a small "tab" or "handle" along one side of the row -- just a blank space that responds to click events. (This might also need some kind of visual affordance, to indicate that it can be clicked.) Checked state would be implemented by a standard SWT.CHECK Button with no label, placed within the "tab". 3c. Client code would enable the default selection/check support by specifying style bits in the constructor. SWT.SINGLE or SWT.MULTI would enable selection, and SWT.CHECK would add checked state support. SWT.LEAD and SWT.TRAIL would control the location of the selection "tab". 3d. In the future, the selection "tab" could be the basis for a new drag and drop row-reordering feature. I don't intend to work on this myself for a long time, since the rest of this proposal will take long enough! 4. Emulate the JFace IContentProvider/ILabelProvider separation of concerns. CTV will manage the mapping from a data element to a row index and from a row index to a row Control. It will adapt existing IStructuredContentProvider and ILazyContentProvider implementations to work with CompositeTable. Custom logic will still be required to push data from the data element into the row Control and vice versa, but this will be separated from the source of the data in the same way that JFace separates IContentProvider from ILabelProvider. 5. Emulate the ILabelProvider API. I would appreciate help on this aspect of the design. In the JFace architecture, an ILabelProvider is responsible for mapping a data element into terms that the SWT Control can use directly. It performs this mapping on an element-by-element basis. 5a. With ILabelProvider and ITableLabelProvider, each portion of the GUI is mapped with separate method calls, such as getText() and getImage(). With IViewerLabelProvider, the entire GUI is mapped in a single method. Since the structure of a CompositeTable row Control is unspecified and varies from instance to instance, it seems more appropriate to use the all-at-once approach and let the provider handle differences in structure by itself. 5b. With JFace IViewerLabelProvider, the Viewer is responsible for the last step of pushing data from the ViewerLabel into the Item. I don't see how this will be possible for CTV, since a row Control is a black box. AFAIK, this separation between the label provider and the SWT Item was intended to allow label decorators to modify the values created by the provider before they are set on the Item. This is a worthwhile optimization, and I would prefer to support the same pattern with CompositeTable. Unfortunately, I can't figure out a way to do it. Even if it was possible to construct an intermediate object similar to ViewerLabel that would work for arbitrary row Controls, the CTV cannot know what to do with it when it is time to actually update the Control. I propose that this aspect of the JFace API is incompatible with CompositeTable's purpose, and so it should not be emulated. The object which will fill the architectural role of ILabelProvider will be responsible for directly modifying the row Control. The concept of a label decorator will be outside the scope of this design. The update method will be passed the row Control directly, rather than an intermediate object. 5c. IBaseLabelProvider is the root of the type hierarchy for all JFace label providers. I intend for the new CTV-specific provider to derive from it. 5d. Name suggestions would be welcome. ICompositeTableLabelProvider? Perhaps the name should reflect the expanded role described in 4b -- ICompositeTableRowRenderer? 6. Support as many other JFace features as possible. Sorting, filtering, and IElementComparer are directly applicable to CompositeTableViewer. DoubleClickListener, OpenListener, drag and drop support, and custom tooltips might or might not make sense. Conclusion: This design would provide two useful new capabilities for CompositeTable: selection and checked states. It would also provide an MVC harness with an API as close to JFace as possible without sacrificing core features. Although it would not be a true drop-in replacement for TableViewer, it would maximize code reuse and leverage developer experience with the JFace architecture. Implementation Notes: I plan to start with the SWT-level features (2 and 3). These features are relatively simple and will be immediately useful, even without JFace-style APIs. Once that is complete, I plan to take the source code for the complete Viewer hierarchy and find-replace from Item to Integer. If I'm lucky, most of the code should translate directly. Since Items cannot be reordered and serve mostly as opaque keys within the JFace implementation, Integer should be semantically equivalent in this context. I believe that the only methods of Item that the Viewer implementation relies on are getData() and setData(). It should be possible to replace these methods with simple array accesses. Then I will inspect to see where this naïve rule breaks down and re-implement whatever is necessary. I expect that the label provider logic will need significant rework, for example. Features with questionable semantics for CTV will be removed or disabled/ignored. Again, your comments would be very much appreciated. If anyone else is interested in helping with this work, that would be appreciated even more! -- Peter Centgraf _______________________________________________ nebula-dev mailing list [email protected] https://dev.eclipse.org/mailman/listinfo/nebula-dev
