|
|
|
# Introduction to ATHENs text editor
|
|
|
|
|
|
|
|
This page gives an introduction into the core functionality of the text widget which is at the heart of ATHEN. This is a custom implementation which is built upon a SWT Canvas. This page is especially interesting for you if you want to know how to create powerful widgets on your own.
|
|
|
|
|
|
|
|
## The Concept
|
|
|
|
|
|
|
|
Starting at a blank page, the goal is to create a text editor, that is able to display text, wrap it into new lines if wanted and display annotations using different stylings. This all should be based around the UIMA **CAS** (Common Analysis Subject)
|
|
|
|
|
|
|
|
This page explains the code that is required to understand this. If you want to check it yourself, you need to navigate to the project
|
|
|
|
```
|
|
|
|
de.uniwue.mk.athen.textwidget
|
|
|
|
```
|
|
|
|
|
|
|
|
and into the package
|
|
|
|
```
|
|
|
|
de.uniwue.mk.athen.textwidget.widget
|
|
|
|
```
|
|
|
|
In there you can find the main class which displays the text widget, it is called *ATHENEditorWidget*
|
|
|
|
|
|
|
|
<hr><hr>
|
|
|
|
|
|
|
|
After opening this class you can immediately study its signature
|
|
|
|
|
|
|
|
```java
|
|
|
|
public class ATHENEditorWidget extends Canvas {
|
|
|
|
```
|
|
|
|
The class extends the SWT Canvas Class. A Canvas represents a primitive widget, as opposed to a composite which is also a widget, but created of more than a single widget internally (so a completely new widget extends Canvas while a collection of Buttons extends Composite!)
|
|
|
|
There is no real difference in the usage of the class whether its extends Composite or Canvas.
|
|
|
|
```java
|
|
|
|
public class ATHENEditorWidget extends Canvas {
|
|
|
|
```
|
|
|
|
|
|
|
|
The idea is now that whenever we have text to display, this widget draws the section that is currently visible, alongside its annotations.
|
|
|
|
|
|
|
|
|
|
|
|
### The drawing process
|
|
|
|
|
|
|
|
The operation which is most crucial is the painting which is invoked many times in the lifetime of this object. The first decision that is to make is that whether the redrawing happens Event-based or in a timered loop (like in a game).
|
|
|
|
|
|
|
|
This editor is fully event based, since time does not make sense in this scenario. (The letters do not vanish after a given amount of time, etc...)
|
|
|
|
|
|
|
|
### What do we need to take care of
|
|
|
|
|
|
|
|
Writing such a component is not extremely hard but also requires some thinking. By no means i expect the code which im going to show here to be perfect, but i dont think it is extremely poor either. When writing a widget one needs to think of everything this widget should be capable of, we can list all those things as tasks:
|
|
|
|
|
|
|
|
* changing the display based on the input
|
|
|
|
* Displaying a mouse cursor in the correct shape as long as it is located over the editor
|
|
|
|
* Displaying the text in different Fonts (that is the type, its size and styling)
|
|
|
|
* Allowing the user to wrap the editor lines, based on the current size of the widget
|
|
|
|
* Managing horizontal and vertical scrollbars if required
|
|
|
|
* Managing the caret and displaceing it whenever a click happens to a different location
|
|
|
|
|
|
|
|
|
|
|
|
The next sections iterate this task list in a reasonable manner:
|
|
|
|
|
|
|
|
### The constructor
|
|
|
|
|
|
|
|
The constructor is yet again very simple,
|
|
|
|
* it merely delegates to the super class
|
|
|
|
|
|
|
|
```java
|
|
|
|
public ATHENEditorWidget(Composite parent, int style) {
|
|
|
|
super(parent, style);
|
|
|
|
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Nothing special in this case
|
|
|
|
|
|
|
|
### Changing the display based on the input
|
|
|
|
|
|
|
|
The widget starts its job, once we get some input from the user. The first decision we need to make is the form, in which the user should provide us with data. This widget makes use of a so called ```CASEditorInput```. This class is a simple POJO class:
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
public class CASEditorInput {
|
|
|
|
|
|
|
|
private CAS cas;
|
|
|
|
|
|
|
|
private File editorInputFile;
|
|
|
|
|
|
|
|
public CASEditorInput(CAS cas, File in) {
|
|
|
|
|
|
|
|
this.cas = cas;
|
|
|
|
this.editorInputFile = in;
|
|
|
|
}
|
|
|
|
|
|
|
|
public CAS getCas() {
|
|
|
|
return cas;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setCas(CAS cas) {
|
|
|
|
this.cas = cas;
|
|
|
|
}
|
|
|
|
|
|
|
|
public File getEditorInputFile() {
|
|
|
|
return editorInputFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setEditorInputFile(File editorInputFile) {
|
|
|
|
this.editorInputFile = editorInputFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
```
|
|
|
|
Objects of the class CASEditorInput serve as an input to the widget. This object is provided in the *setInput()* method of our widget
|
|
|
|
|
|
|
|
```java
|
|
|
|
public void setInput(CASEditorInput input) {
|
|
|
|
this.currentInput = input;
|
|
|
|
|
|
|
|
initLayout();
|
|
|
|
initData();
|
|
|
|
initEventHandler();
|
|
|
|
|
|
|
|
//refresh the widget
|
|
|
|
updateScrollbar();
|
|
|
|
drawWidget();
|
|
|
|
|
|
|
|
//and start the cursor
|
|
|
|
startCursor();
|
|
|
|
|
|
|
|
}
|
|
|
|
```
|
|
|
|
This method is yet again very simple in itself. At first it saves the input, then it initializes its layout in the method *initLayout()* which only assigns a filllayout to this class (in fact it propably does not even matter whcih layout as long as there is one, since we do not position any child UI objects)
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
//very simple in this case
|
|
|
|
private void initLayout() {
|
|
|
|
this.setLayout(new FillLayout());
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|