Java: Swing: Text components and Document: Views: Views, elements and properties
Document elements are displayed in text components by Views. This discussion focuses on where and how this mapping takes place and how the view implementations take properties from the Document and the element's attribute set.
Some short comments about the HTML package are included here. The RTF package doesn't introduce new views or mappings.
There are only few views that actually paint, the others are concerned with grouping and layouting children views. More specifically, the background is almost never painted, even when set.
take foreground and font from text component
no other properties (no TabSet), but takes tabSize from Document property
if for StyledDocument takes font and foreground from Document. takes background from Document only if it was not inherited by the resolving attribute set
else takes font, foreground from text component, no background (assumes it is already filled properly by update(), or the component is transparent)
supports underline, strike-through, super/subscript even for non-StyledDocuments
if "i18n" is set, it uses GlyphPainter2, which requires AbstractDocument.
requires StyledDocument
same behaviour as GlyphView (main purpose is caching: inaccessible font, background, foreground)
not really useful for subclassing (use GlyphView and re-implement the caching, see this (generic) example)
ComponentView doesn't need to paint itself because the component is really part of the container hierarchy and thus painted automatically.
IconView invokes paintIcon() on the icon, as expected.
CSS boxes (background/background image, border, margin) are handled/painted by StyleSheet.BoxPainter. It is used by ParagraphView, ListView, and TableView.
HRuleView either paints a filled Rectangle (without setting any color) or a standard beveled border.
ImageView: paints image, border, gray loading rectangle/icon
ListView: uses StyleSheet.ListPainter, which implements the whole list, numbering etc. logic
Several views are subclasses of ComponentView.
Every text component (component that has TextUI) has an "editor kit" assigned to it by the UI. With JEditorPane/JTextPane this becomes a property of the component itself, with JTextField and JTextArea it is managed by the UI alone (probably once shared instance).
This editor kit gives access to a view factory, which creates views for elements (possibly only for those elements contained in documents compatible with the editor kit).
It is up the LookAndFeel class(es) to listen for changes of the "document" property (or other properties that could affect representation of elements) and (re)create the Views, using the given view factory (which can be replaced by something custom for JEditorPane/JTextPane and it provided by the TextUI itself for JTextField/JTextArea), or (maybe) fall back to some default if the editor kit hasn't got a view factory (it is unclear whether this is allowed, but DefaultEditorKit does it).
Understanding the behaviour the BasicTextXXXUI classes is complicated because much is handled in BasicTextUI, both for the editor-kit having and the editor-kit less components.
BasicTextUI provides a RootView class that serves as root view, not representing an element itself. Its getViewFactory method returns the editor kit's view factory, or the BasicTextUI itself (which implements ViewFactory), if it is null (it always is for JTextField / JTextArea). The BasicTextUI's implementation by default doesn't create views, but returns null. BasicTextFieldUI / BasicTextAreaUI override create() and create the elements as listed below.
To exchange the mappings for JEditorPane/JTextPane, replace the editor kit. For JTextField / JTextArea the mappings can only be done differently by replacing the UI classes.
The root view of a JTextComponent can be accessed by textComponent.getUI().getRootView(textComponent). This implies that the whole view hierarchy is lost and must be recreated when the LAF changes.
The following mappings usually happen on element name (string comparison) if there is any choice at all.
BasicTextField/AreaUI inspect the document property "i18n" to see if bidirectional layout must be supported. As this may be dynamically changing (and document properties cannot be observed), BasicTextUI tests changes for this property every time its DocumentListener's insertUpdate() has been called and then recreates the views [1]. BasicTextField/AreaUI also test for this change in the component's "font" property change handling.
BasicTextUI also sets the RUN_DIRECTION property on the Document and recreates views if the component orientation changed[2].
without i18n:
Element | View |
|---|---|
root element | FieldView |
with i18n:
Element name | View |
|---|---|
paragraph | subclass of ParagraphView |
content | GlyphView |
any other | FieldView (not really intented?) |
Views are recreated on "lineWrap" or "wrapStyleWord" property change.
without i18n:
Element | View |
|---|---|
root element, with line wrap | WrappedPlainView |
root element, no line wrap | PlainView |
with i18n:
Element name | View |
|---|---|
paragraph | BoxView |
content | subclass of ParagraphView, content is displayed by GlyphView |
any other | null (usually problematic) |
similar to JTextArea, always uses WrappedPlainView
WrappedPlainView and PlainView (and thus FieldView) require the root element of the Document to have children (the "paragraphs"). This is not documented anywhere(?). That means that JTextField/JTextArea (with Basic LAF or non-changed subclasses) will not display valid Documents properly (those whose root element is a leaf). A method of Utilities (getParagraphElement) also assumes this (even a still more special) structure for non-StyledDocuments.
With the i18n property set, the problems are different but even worse:
Text areas are now only capable to display elements with the names "paragraph" and "content", and text fields incorrectly use FieldViews also for any leafs that aren't called "content".
GlyphView with i18n uses GlyphPainter2, and that requires the Document to be a subclass of AbstractDocument.
"Accidently" present style attributes "underlined", "strike-through", "superscript", "subscript" are suddenly honored even for non-StyledDocuments
User interaction still leads to calls of the Utilities method that fails with one-element documents.
Element name | View |
|---|---|
section | BoxView |
paragraph | ParagraphView |
content | LabelView |
component | ComponentView |
icon | IconView |
any other | LabelView |
HTMLEditorKit bases its views on the NameAttribute (which is HTML.Tag and not a String). It falls back to StyledEditorKit behaviour if not found.
[1] BasicTextUI does this although it has no code specific on the "i18n" property. This may lead to unnecessary recreation of the view hierarchies. BasicTextUI assumes that a) any EditorKit will create its views depending on whether i18n is true and b) that it is not capable of handling changes alone, as it can well be, since it is may explicitly register on the component (and thus track changes of the Document).
[2] I don't think that's clean. A Document should be displayable in multiple text components, even with multiple orientations, at the same time. The paint/layout methods should instead fall back to the component's orientation only if the property isn't set on the model.
(C) 2001-2009 Christian Kaufhold (swing@chka.de)