 |
Home | Changes | Index | Search | Go
Summary of design goals, implications, status, dead ends ... in the process of overhauling SwingX renderers.
Motivation
The base trigger to look into renderers at all was the misbehaving (in interaction with Highlighters) DefaultTableCellRenderer (which is a subtle side-effect of violating the "do-not-subclass-components" rule - todo: explain on separate page!). Additionally, there are (basically core) issues (TODO: add links to swingx issues) with inconsistent enabled/disabled appearance and bidi-compliance across default core renderers. Another driving force is the exciting advent of Painters which need to be supported seamlessly in SwingX rendering/highlighting. Yet another (TODO: formulate) need for consistent String representation of rendered value (for use in PatternHighlighter, -Filter, Searchable)
Goals
Separate concerns on the renderer level to match the separation in the highlighter level: the renderer should be mainly concerned about the content to renderer ("what") while the highlighter is responsible to decide the visuals ("how"). Move most of the concrete per-renderee specifics out of the way: table, list, tree renderers should ideally be pluggable with the same content-controlled components.
TODO: add glue wording ...
- Consistency of renderer usage, appearance and behaviour across collection components. Core defaults vary widely.
- One location for hooking default support for advanced features (like f.i. Painter)
- One location for documenting renderer's contract (cope with null renderee, unfitting values, complete reset)
- Replace misbehaving (in interaction with Highlighters) DefaultTableCellRenderer with a well-behaved default implementation
Collaborators
A
preliminary class diagram: at least the names are very much open to discussion! The colors are meant to reflect the importance to client code - the grey foreground on white background being the least important, very near to an implementation detail.
ComponentProvider
Responsible for providing a rendering component with its content and visuals configured from the passed-in CellContext. This is the working horse, usually delegates the default visual config to DefaultVisuals and handles the content specific formatting/configuration itself (with the help of a pluggable StringValue - not shown).
DefaultXXRenderer:
f.i. DefaultTableRenderer (implements TableCellRenderer) - bridge to core Swing: captures the renderee's state as passed-in through the parameters into a CellContext and delegates the concrete configuration to a ComponentProvider
CellContext
Captures renderee's state at the time of calling renderer.getXXRendererComponent. It's similar to the highlighter-related part of ComponentAdapter, differing in representing an immutable snapshot of the current state. Additionally, responsible for looking up renderee ui specifics.
DefaultVisuals
Implementation detail - the ComponentProvider internally delegates all default visual configuration of the given component to this class. The config is very similar (the same?) for all types of concrete rendering components. It's factored into a dedicated class to emphasize the same-ness - the component provider doesn't need to worry about the details "most of the time". Can create and use a custom class if needed. We could think about inlining it back into the provider (not my preference)
Client code
Typically will interact with the ComponentProvider and subclasses to customize the content appearance. The base class has hooks to plug-in a custom toString converter and configure component's alignment. Subclassing for more elaborate customization (an example is a checkbox showing both the check-icon and text can be found in the renderer package test hierarchy) requires minimal effort.
Code Snippets
- Goal
- use a composed string representation from several fields and use it across all collection views.
- How-To
- create a custom StringValue, instantiate and register the appropriate DefaultXXRenderer with the XXComponent (XX standing for table, list, tree - with the assumption that the listModel/table column/tree nodes all contain items of type Contributor)
- Snippet
- (the full example is in the demo hierarchy)
// configure table, list, tree renderer to use the same string
// representation
StringValue stringValue = new StringValue() {
public String getString(Object value) {
if (!(value instanceof Contributor)) return TO_STRING.getString(value);
Contributor contributor = (Contributor) value;
return contributor.lastName + ", " + contributor.firstName;
}
};
table.setDefaultRenderer(Contributor.class, new DefaultTableRenderer(stringValue));
list.setCellRenderer(new DefaultListRenderer(stringValue));
tree.setCellRenderer(new DefaultTreeRenderer(stringValue));
There's more example code in the renderer package of the test hierarchy (xVisualCheck).
Open Questions
- need set/Selection/Foreground/Background on renderer level?
- Pro: core has some/all dependent on concrete renderer - so we want it here to lower the hurdle
- Contra: the SwingX way is do custom visuals is to use Highlighters - so we want to discourage renderer based visuals
- fold back DefaultVisuals into ComponentProvider?
- Pro: one class less
- Contra: less clear boundary of responsibilities
- Names, names and names ...
- evolve StringValue to full-fledget Format?
- Pro: usable in CellEditors (== same appearance plus controlled conversion from string to object)
- Contra: additional, unused weight for simple read-only rendering
- responsibility clash of background reset with LF provided alternate row coloring
- tooltip support - where exactly? Visual decoration or content?
Links
History
latest first:
- switched JXTable/JXList to use SwingX renderers by default - Announcement in forum
- extensive renaming for shorter base names: ComponentProvider (was: RenderingComponentController), DefaultVisuals (was: RendererController), StringValue (was: ToStringConverter) - tagged before as ??
- removed parametrization of DefaultXXRenderer, removed static methods, added wrapping for ease-of-use of tree renderers
- move RenderingComponentController into center, make RendererController an implementation detail of the component controller, drop renderee parameterization (class diagram)
- separate visual config from content config, introduce RendererController at center stage, let concrete renderers delegate to controller (tagged in cvs as ??, class diagram)
- introduce common AbstractCellRenderer and let all concrete renderers inherit (tagged in cvs as ??)
|