JVx Reference, CellEditors
Let's talk about CellEditors, and how they are decoupled from the surrounding GUI.
What are they?
While we've already covered large parts of how the GUI layer and the model of JVx works, the CellEditors have been left completely untouched and unmentioned. One might believe that they can be easily explained together with the Editors, however, they are a topic on their own, and a complex one from time to time that is.
The difference between Editors (the UIEditor for the most part) and CellEditors is that the Editors only provide the high-level GUI control, while the CellEditors provide the actual functionality. Let's take a look at a quite simple screen.
We see a window with a table on the left and some editors on the right, simple enough. Now these components we are seeing are UIEditors, not CellEditors. The CellEditors themselves are only added as child components to the Editors, so the Editors are basically just panels which contain the actual CellEditor.
So technically every UIEditor is just another panel which gets the CellEditor added. The CellEditors themselves follow the same pattern as all GUI components in JVx, there is the base interface, an eventual extension of technology components, the implementation and finally the UI object. They are, however, rarely directly used in building the GUI, but mostly only referenced when building the model.
Why are they?
If you want to make GUI editor components, I know of two possible ways from the top of my head to achieve that: You create dedicated editor components for the datatypes that are available, for example a NumberEditor, TextEditor and so forth. Or you create one editor component which acts as a mere container and allows to plug in any wanted behavior for the type you're editing.
We've opted for the second option, because it means that the GUI is actually decoupled from the datatypes (and in extension the data) of the model. If we'd have separate components for each datatype, changing the datatype of a single column would mean that you'd have to touch all editors associated with that column and change that code, maybe with rippling effects on the rest of the GUI. With the CellEditors, one can change the datatype of a column and not worry about the GUI that is associated with that column. The CellEditor is changed on the model once and that change is automatically picked up by all Editors. Which also means that one can define and change defaults very easily and globally.
Of course one can also set the preferred or wanted CellEditor directly on the Editor, instead of using the one defined in the model, should the need arise.
And the table?
The same applies to the Table. Theoretically, every cell of the Table can be viewed as a single Editor, for this context at least. So a single cell behaves the same as an Editor when it comes to how the CellEditors are handled.
How many are there?
JVx comes with a variety of CellEditors out of the box:
- Boolean
- Choice
- Date/Time
- List
- Number
- Text
- HTML
- Multiline
- Password
- Standard
With these nearly all needs can be covered. If there is need for a new one, it can be created and added like any other UI component.
Using CellEditors
As said previously, which CellEditor is used is defined primarily with the model, for example:
-
private void initiliazeModel() throws ModelException
-
{
-
dataBook = new MemDataBook();
-
-
ICellEditor cellEditor = new UITextCellEditor();
-
IDataType dataType = new StringDataType(cellEditor);
-
ColumnDefinition column = new ColumnDefinition("COLUMN", dataType);
-
-
RowDefinition rowDefinition = dataBook.getRowDefinition();
-
rowDefinition.addColumnDefinition(column);
-
-
dataBook.open();
-
}
-
-
private void initializeUI() throws ModelException
-
{
-
editor = new UIEditor(dataBook, "COLUMN");
-
-
add(editor);
-
}
We can see that every column has a datatype and every datatype has a CellEditor. That allows the model to provide the actual editing functionality without changing the GUI code. The Editor, when notifyRepaint() is called, will fetch the CellEditor from the datatype and use it. Additionally, there is a technology dependent default mechanism which allows this system to work even when the UI classes are not used.
Let's do a step by step explanation of what happens:
- The model is created.
- The GUI is created.
- The model invokes notifyRepaint() on all bound controls.
- The Editor gets the CellEditor from the model and adds it to itself.
One moment, instance sharing?
If we revisit at the example code from above, we will notice that the CellEditor instance is set on the model and must then be used by the Editor. That means that a single CellEditor instance is used for all bound Editors. We all know that sharing instances in such a way can be fun, but in this case it is not a problem because CellEditors are only "factories" for the actual editing components.
The ICellEditor interface does actually only specify two methods, whether it is a direct cell editor, and the factory method for creating an ICellEditorHandler. The CellEditorHandler is the manager of the instance of the component that is going to be embedded into the Editor.
- notifyRepaint() is called on the editor.
- The Editor gets the CellEditorHandler from the CellEditor.
- The Editor gets the component from the CellEditorHandler and embeds it.
This mechanism makes sure that no component instances end up shared between different GUI components.
A closer look at the CellEditorHandler
If we take a good look at the CellEditorHandler interface, we see that it contains everything that is required for setting up a component to be able to edit data coming from a DataRow. One method is especially important, the getCellEditorComponent() function. It returns the actual technology component that is to be embedded into the Editor. That means that even though there are implementations for the CellEditors on the UI layer, the actual components which will provide the functionality for editing the data are implemented on the technology layer. A short refresher:
Revisiting our simple screen from above, we'd actually need to represent it as something like this:
Because the embedded components in the Editor are actually on the technology layer.
CellRenderers
There is another small topic we need to discuss, CellRenderers. They follow nearly the same schematics as CellEditors but are used to display values directly, for example values in a table cell. The Table is also the primary component which uses them to display the cell values until the editing is started. For simplicity reasons, most CellEditors implement ICellRenderer directly and provide management of the created component. That is because the reuse of components for barely displaying values is easier does not contain as much error potential.
Conclusion
CellEditors provide an easy mechanic to allow to edit data, and more important, they are decoupled from the GUI code in which they are used in a way which allows the model to change, even dynamically. That enables programmers to create and edit screens and models quickly without the need to check if the GUI and the model fit together, they always do.