Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Scout/HowTo/4.0/Add a custom GUI component"

< Scout‎ | HowTo‎ | 4.0
(Storing to and loading from XML files)
(Replaced content with "The Scout documentation has been moved to https://eclipsescout.github.io/.")
 
Line 1: Line 1:
{{ScoutPage|cat=HowTo 4.0}}
+
The Scout documentation has been moved to https://eclipsescout.github.io/.
 
+
If the out-of-the-box GUI components provided by Scout don’t fit your needs, you can easily create and add custom GUI components. This how-to describes how this can be done.
+
 
+
When we talk about custom GUI components in this how-to we actually describe the creation of custom form fields. For this we need to implement a custom UI model component and acutal representations for all necessary UI technolgies, typically this should include representations for all supported UI technologies: SWT, Swing, and RAP. The model component will be located in a client plugin of the application and the concrete implementations for the UI technolgies will end up in the technology specific UI plugin.
+
 
+
As an example we will create the AbstractDrawLineField used in the Scout form below. In this field, a user can draw a line from a start point to an end point.
+
 
+
[[Image:DrawLineSwing.png]]
+
 
+
== Creating the Scout Component ==
+
 
+
Every Scout component representing a form field extends from <code>AbstractFormField</code>. For this, you first have to create a new class that is derived from AbstractFormField. Instead of directly inheriting from AbstractFormField it is recommended to extend <code>AbstractCustomField</code>, an AbstractFormField that implements the marker interface <code>ICustomField</code>. This makes it easier to distinguish your custom components from controls provided by the Scout framework.
+
 
+
In the client plugin, first create a new package <code>com.bsiag.minicrm.client.ui.form.fields.ext</code> containing the following artefacts:
+
 
+
* <code>IDrawLineField</code>, extending IFormField
+
* <code>IDrawLineUiFacade</code>
+
* <code>AbstractDrawLineField</code>, extending AbstractCustomField and implementing IDrawLineField
+
 
+
=== Properties  ===
+
 
+
Now you have to think about what your custom field should provide. Scout fields typically consist of properties implemented by <code>getConfiguredXY</code> methods and behaviour implemented in the <code>execXY</code> methods.
+
 
+
If a field property is reflected by some visible aspects of the GUI component, we can take advantage of the fact that every Scout form field is also an IPropertyObserver.
+
 
+
Please notice that every form field is also an IPropertyObserver. So if you think that someone else could be interested in your property, then you should make use of the protected field m_propertySupport. This is especially necessary if the property defines behaviour of the GUI control. If someone dynamically changes the property on your field, the GUI control has to be informed about it otherwise it would not be reflected on the GUI.
+
 
+
The example AbstractDrawLineField has two properties, the start and the end point. Thus we add now the following code to IDrawLineField:  
+
 
+
<source lang="java">
+
public static final String PROP_START = "startPoint";
+
public static final String PROP_END = "endPoint";
+
 
+
Point getStart();
+
void setStart(Point p);
+
 
+
Point getEnd();
+
void setEnd(Point p);
+
 
+
IDrawLineUiFacade getUiFacade();
+
</source>
+
 
+
This interface is now already finished. The getUiFacade method will be needed to receive events from the gui control. In the AbstractDrawLineField, we implement now these methods with the usage of m_propertySupport
+
 
+
<source lang="java">
+
  private IDrawLineUiFacade m_uiFacade;
+
 
+
  @Override
+
  public Point getEnd() {
+
    return (Point) propertySupport.getProperty(PROP_END);
+
  }
+
 
+
  @Override
+
  public void setEnd(Point p) {
+
    propertySupport.setProperty(PROP_END, p);
+
  }
+
 
+
  @Override
+
  public Point getStart() {
+
    return (Point) propertySupport.getProperty(PROP_START);
+
  }
+
 
+
  @Override
+
  public void setStart(Point p) {
+
    propertySupport.setProperty(PROP_START, p);
+
  }
+
 
+
  @Override
+
  public IDrawLineUiFacade getUiFacade() {
+
    return m_uiFacade;
+
  }
+
</source>
+
 
+
=== Default Values  ===
+
 
+
Another important thing about properties is the way they are initialized. For each property there should be a corresponding <code>getConfiguredXY</code>-function to set the initial value of the property. This initial value is read in the method <code>initConfig</code>.
+
 
+
For the AbstractDrawLineField we need four getConfigured methods, two for each point. The annotations are needed for the Scout SDK only.
+
 
+
<source lang="java">
+
@ConfigProperty(ConfigProperty.INTEGER)
+
@Order(500)
+
protected int getConfiguredStartX(){
+
  return 0;
+
}
+
 
+
@ConfigProperty(ConfigProperty.INTEGER)
+
@Order(510)
+
protected int getConfiguredStartY(){
+
  return 0;
+
}
+
 
+
@ConfigProperty(ConfigProperty.INTEGER)
+
@Order(520)
+
protected int getConfiguredEndX(){
+
  return 0;
+
}
+
 
+
@ConfigProperty(ConfigProperty.INTEGER)
+
@Order(530)
+
protected int getConfiguredEndY(){
+
  return 0;
+
}
+
 
+
@Override
+
protected void initConfig(){
+
  super.initConfig();
+
  setStart(new Point(getConfiguredStartX(), getConfiguredStartY()));
+
  setEnd(new Point(getConfiguredEndX(), getConfiguredEndY()));
+
}
+
</source>
+
 
+
=== Receiving GUI Events  ===
+
 
+
Now we have to think about which GUI events we want to propagate to the custom Scout component. In our example, we have two important events. Setting a new start and setting a new end point. These events are handled by uiFacade. Add to the facade interface these two methods: void setStartFromUI(Point p); void setEndFromUI(Point p); Now add this inner Class to AbstractDrawLineField: <source lang="java">
+
private class P_UiFacade implements IDrawLineUiFacade{
+
  public void setEndFromUI(Point p){
+
    setEnd(p);
+
  }
+
 
+
  public void setStartFromUI(Point p){
+
    setStart(p);
+
  }
+
}
+
</source> And add this line of code as the first statement in the initConfig method: <source lang="java">
+
m_uiFacade = new P_UiFacade();
+
</source>
+
 
+
== Creating the GUI Control  ==
+
 
+
After creating the model implementation of the custom field, the corresponding GUI component has to be created. Every GUI component should provide implementations for all UI technologies.
+
 
+
* SWT compoments extend SwtScoutFieldComposite
+
* SWT compoments extend SwingScoutFieldComposite
+
* RAP components extend RwtScoutFieldComposite
+
 
+
Accordingly, we will create Swing, SWT and RAP implementations for our AbstractDrawLineField.
+
 
+
=== Implementation  ===
+
 
+
Important methods for Scout GUI components are <code>initializeSwt</code>, <code>initializeSwing</code>, <code>attachScout</code> and <code>handleScoutPropertyChange</code>. The actual GUI composite is created in the initializeSwt / initializeSwing / initializeUi method. Properties of the Scout field are normally handled in the method attachScout respectively in the method handleScoutPropertyChange. To fire events which should be handled in the Scout control and which normally leads to an execution of an execXY method the UIFacade of a Scout field should be used. For the Swt/Swing/RwtScoutDrawLineField we have a mouse listener that propagates the point changes from the GUI to the Scout field. When then the Scout field adjusts the point properties, these changes are handled in the handleScoutPropertyChange method and there the line in the GUI will be updated.
+
 
+
==== Swing Implementation  ====
+
 
+
Create a new package com.bsiag.minicrm.ui.swing.form.fields.ext in the ui.swing plugin. Create in this package a new class SwingScoutDrawLineField that extends SwingScoutFieldComposite&lt;IDrawLineField&gt;.
+
 
+
Paste the following code in the SwingScoutDrawLineField class: <source lang="java">
+
public class SwingScoutDrawLineField extends SwingScoutFieldComposite<IDrawLineField> {
+
private P_MouseListener m_mouseListener;
+
 
+
@Override
+
protected void initializeSwing(){
+
  m_mouseListener = new P_MouseListener();
+
  JPanelEx container=new JPanelEx(new BorderLayoutEx());
+
  container.setOpaque(false);
+
  JStatusLabelEx label=new JStatusLabelEx();
+
  container.add(label,BorderLayoutEx.CENTER);
+
 
+
  MyComponent field = new MyComponent();
+
  SwingUtility.installDefaultFocusHandling(field);
+
  container.add(field);
+
 
+
  field.addMouseMotionListener(m_mouseListener);
+
  field.addMouseListener(m_mouseListener);
+
 
+
  setSwingContainer(container);
+
  setSwingLabel(label);
+
  setSwingField(field);
+
  //layout
+
  getSwingContainer().setLayout(new LogicalGridLayout(getSwingEnvironment(),1, 0));
+
}
+
 
+
@Override
+
protected void attachScout(){
+
super.attachScout();
+
  updateLineFromScout();
+
}
+
 
+
protected void updateLineFromScout(){
+
  getSwingField().repaint();
+
}
+
 
+
protected void handleSwingStartPointChanged(final Point location){
+
  RunnableWithData t=new RunnableWithData(){
+
    @Override
+
    public void run(){
+
      getScoutObject().getUiFacade().setEndFromUI(location);
+
      getScoutObject().getUiFacade().setStartFromUI(location);
+
    }
+
  };
+
  getSwingEnvironment().invokeScoutLater(t, 2345);
+
}
+
 
+
protected void handleSwingEndPointChanged(final Point location){
+
  RunnableWithData t=new RunnableWithData(){
+
    @Override
+
    public void run(){
+
      getScoutObject().getUiFacade().setEndFromUI(location);
+
    }
+
  };
+
  getSwingEnvironment().invokeScoutLater(t, 2345);
+
}
+
 
+
@Override
+
protected void handleScoutPropertyChange(String name, Object newValue){
+
  if(name.equals(IDrawLineField.PROP_START) || name.equals(IDrawLineField.PROP_END)){
+
    updateLineFromScout();
+
  }
+
  super.handleScoutPropertyChange(name, newValue);
+
}
+
 
+
private class MyComponent extends JComponent{
+
  private static final long serialVersionUID=1L;
+
 
+
  @Override
+
  public void paint(Graphics g){
+
    super.paint(g);
+
    g.setColor(getBackground());
+
    g.fillRect(0, 0, getWidth(), getHeight());
+
    g.setColor(getForeground());
+
    if(getScoutObject().getStart() != null && getScoutObject().getEnd() != null){
+
      g.drawLine(getScoutObject().getStart().x, getScoutObject().getStart().y, getScoutObject().getEnd().x, getScoutObject().getEnd().y);
+
    }
+
  }
+
}
+
 
+
private class P_MouseListener extends MouseAdapter{
+
  private boolean m_mouseDown = false;
+
  @Override
+
  public void mousePressed(MouseEvent e){
+
    m_mouseDown = true;
+
    handleSwingStartPointChanged(e.getPoint());
+
  }
+
  @Override
+
  public void mouseReleased(MouseEvent e){
+
    m_mouseDown = false;
+
    handleSwingEndPointChanged(e.getPoint());
+
  }
+
  @Override
+
  public void mouseDragged(MouseEvent e){
+
    if(m_mouseDown){
+
      handleSwingEndPointChanged(e.getPoint());
+
    }
+
  }
+
}
+
}
+
</source>
+
 
+
==== SWT Implementation  ====
+
 
+
Create a new package com.bsiag.minicrm.ui.swt.form.fields.ext in the ui.swt plugin. Create in this package a new class SwtScoutDrawLineField that extends SwtScoutFieldComposite&lt;IDrawLineField&gt;.
+
 
+
Paste the following code in the SwtScoutDrawLineField class: <source lang="java">
+
public class SwtScoutDrawLineField extends SwtScoutFieldComposite<IDrawLineField> {
+
  private P_MouseListener m_mouseListener;
+
 
+
  @Override
+
  protected void initializeSwt(Composite parent) {
+
    m_mouseListener = new P_MouseListener();
+
 
+
    Composite container = getEnvironment().getFormToolkit().createComposite(parent);
+
    StatusLabelEx label = getEnvironment().getFormToolkit().createStatusLabel(container, getEnvironment(), getScoutObject());
+
    Canvas canvas = getEnvironment().getFormToolkit().createCanvas(container);
+
 
+
    canvas.addPaintListener(new P_PaintListener());
+
    canvas.addMouseMoveListener(m_mouseListener);
+
    canvas.addMouseListener(m_mouseListener);
+
 
+
    setSwtContainer(container);
+
    setSwtLabel(label);
+
    setSwtField(canvas);
+
 
+
    // layout
+
    getSwtContainer().setLayout(new LogicalGridLayout(1, 0));
+
  }
+
 
+
  @Override
+
  protected void attachScout() {
+
    super.attachScout();
+
    updateLineFromScout();
+
  }
+
 
+
  protected void updateLineFromScout() {
+
    getSwtField().redraw();
+
  }
+
 
+
  protected void handleSwtStartPointChanged(final Point location) {
+
    RunnableWithData t = new RunnableWithData() {
+
      @Override
+
      public void run() {
+
        getScoutObject().getUiFacade().setEndFromUI(location);
+
        getScoutObject().getUiFacade().setStartFromUI(location);
+
      }
+
    };
+
    getEnvironment().invokeScoutLater(t, 2345);
+
  }
+
 
+
  protected void handleSwtEndPointChanged(final Point location) {
+
    RunnableWithData t = new RunnableWithData() {
+
      @Override
+
      public void run() {
+
        getScoutObject().getUiFacade().setEndFromUI(location);
+
      }
+
    };
+
    getEnvironment().invokeScoutLater(t, 2345);
+
  }
+
 
+
  @Override
+
  protected void handleScoutPropertyChange(String name, Object newValue) {
+
    if (name.equals(IDrawLineField.PROP_START) || name.equals(IDrawLineField.PROP_END)) {
+
      updateLineFromScout();
+
    }
+
    super.handleScoutPropertyChange(name, newValue);
+
  }
+
 
+
  private class P_PaintListener implements PaintListener {
+
    private static final long serialVersionUID = 1L;
+
 
+
    @Override
+
    public void paintControl(PaintEvent e) {
+
      if (getScoutObject().getStart() != null && getScoutObject().getEnd() != null) {
+
        e.gc.drawLine(getScoutObject().getStart().x, getScoutObject().getStart().y, getScoutObject().getEnd().x, getScoutObject().getEnd().y);
+
      }
+
    }
+
  }
+
 
+
  private class P_MouseListener implements MouseListener, MouseMoveListener {
+
    private boolean m_mouseDown = false;
+
 
+
    @Override
+
    public void mouseDoubleClick(org.eclipse.swt.events.MouseEvent e) {
+
    }
+
 
+
    @Override
+
    public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
+
      m_mouseDown = true;
+
      handleSwtStartPointChanged(new Point(e.x, e.y));
+
    }
+
 
+
    @Override
+
    public void mouseUp(org.eclipse.swt.events.MouseEvent e) {
+
      m_mouseDown = false;
+
      handleSwtEndPointChanged(new Point(e.x, e.y));
+
    }
+
 
+
    @Override
+
    public void mouseMove(org.eclipse.swt.events.MouseEvent e) {
+
      if (m_mouseDown) {
+
        handleSwtEndPointChanged(new Point(e.x, e.y));
+
      }
+
    }
+
 
+
  }
+
}
+
</source>
+
 
+
==== RAP Implementation  ====
+
 
+
There is a limitation to the RAP implementation as it does not support listening for mouse-move events. This means that the user will draw his line blindly (because we cannot update it as it is being drawn). To help the user, we will indicate the starting point of the current line with a circle if the start and end points of a line are indentical.
+
 
+
Create a new package com.bsiag.minicrm.ui.rap.form.fields.ext in the ui.rap plugin. Create in this package a new class RwtScoutDrawLineField that extends RwtScoutFieldComposite&lt;IDrawLineField&gt;.
+
 
+
Paste the following code in the RwtScoutDrawLineField class: <source lang="java">
+
public class RwtScoutDrawLineField extends RwtScoutFieldComposite<IDrawLineField> {
+
  private P_MouseListener m_mouseListener;
+
  private Menu m_contextMenu;
+
 
+
  @Override
+
  protected void initializeUi(Composite parent) {
+
    m_mouseListener = new P_MouseListener();
+
 
+
    Composite container = getUiEnvironment().getFormToolkit().createComposite(parent);
+
    StatusLabelEx label = getUiEnvironment().getFormToolkit().createStatusLabel(container, getScoutObject());
+
    Canvas canvas = getUiEnvironment().getFormToolkit().createCanvas(container);
+
 
+
    canvas.addPaintListener(new P_PaintListener());
+
    // canvas.addMouseTrackListener(m_mouseListener); // this doesn't work for RAP
+
    canvas.addMouseListener(m_mouseListener);
+
 
+
    setUiContainer(container);
+
    setUiLabel(label);
+
    setUiField(canvas);
+
 
+
    // layout
+
    getUiContainer().setLayout(new LogicalGridLayout(1, 0));
+
  }
+
 
+
  @Override
+
  protected void attachScout() {
+
    super.attachScout();
+
    updateLineFromScout();
+
  }
+
 
+
  protected void updateLineFromScout() {
+
    getUiField().redraw();
+
  }
+
 
+
  protected void handleSwtStartPointChanged(final Point location) {
+
    RunnableWithData t = new RunnableWithData() {
+
      @Override
+
      public void run() {
+
        getScoutObject().getUiFacade().setEndFromUI(location);
+
        getScoutObject().getUiFacade().setStartFromUI(location);
+
      }
+
    };
+
    getUiEnvironment().invokeScoutLater(t, 2345);
+
  }
+
 
+
  protected void handleSwtEndPointChanged(final Point location) {
+
    RunnableWithData t = new RunnableWithData() {
+
      @Override
+
      public void run() {
+
        getScoutObject().getUiFacade().setEndFromUI(location);
+
      }
+
    };
+
    getUiEnvironment().invokeScoutLater(t, 2345);
+
  }
+
 
+
  @Override
+
  protected void handleScoutPropertyChange(String name, Object newValue) {
+
    if ((name.equals(IDrawLineField.PROP_START)) || (name.equals(IDrawLineField.PROP_END))) {
+
      updateLineFromScout();
+
    }
+
    super.handleScoutPropertyChange(name, newValue);
+
  }
+
 
+
  private class P_PaintListener implements PaintListener {
+
    private static final long serialVersionUID = 1L;
+
 
+
    @Override
+
    public void paintControl(PaintEvent e) {
+
      if (getScoutObject().getStart() != null && getScoutObject().getEnd() != null) {
+
        if (getScoutObject().getStart().equals(getScoutObject().getEnd())) {
+
          // special case for RAP: mark start of line
+
          e.gc.drawOval(getScoutObject().getStart().getX(), getScoutObject().getStart().getY(), 10, 10);
+
        } else {
+
          e.gc.drawLine(getScoutObject().getStart().getX(), getScoutObject().getStart().getY(), getScoutObject().getEnd().getX(), getScoutObject().getEnd().getY());
+
        }
+
      }
+
    }
+
  }
+
 
+
  private class P_MouseListener implements MouseListener, MouseMoveListener {
+
    private static final long serialVersionUID = 1L;
+
    private boolean m_mouseDown = false;
+
 
+
    @Override
+
    public void mouseDoubleClick(org.eclipse.swt.events.MouseEvent e) {
+
    }
+
 
+
    @Override
+
    public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
+
      if (e != null && e.button == 1) {
+
        m_mouseDown = true;
+
        handleSwtStartPointChanged(new Point(e.x, e.y));
+
      }
+
    }
+
 
+
    @Override
+
    public void mouseUp(org.eclipse.swt.events.MouseEvent e) {
+
      if (e != null && e.button == 1) {
+
        m_mouseDown = false;
+
        handleSwtEndPointChanged(new Point(e.x, e.y));
+
      }
+
    }
+
 
+
    @Override
+
    public void mouseMove(org.eclipse.swt.events.MouseEvent e) {
+
      if (m_mouseDown) {
+
        handleSwtEndPointChanged(new Point(e.x, e.y));
+
      }
+
    }
+
 
+
  }
+
}
+
</source>
+
 
+
<br>
+
 
+
== Map the Controls  ==
+
 
+
To link the custom GUI components to the UI model component Scout uses Eclipse extension points.
+
For a SWT field we need the extension point org.eclipse.scout.rt.ui.swt.formfields and for a Swing we use org.eclipse.scout.rt.ui.swing.formfields. In this extension point we have to specify which model class maps to which UI class. As model class we use usually the interface and not the concrete class.
+
 
+
=== Swing extension  ===
+
 
+
For our example, open the plugin.xml from the ui.swing plugin. Go to the Extensions tab on the bottom of the manifest editor and click Add…. Uncheck the checkbox at the bottom and select the org.eclipse.scout.rt.ui.swing.formfields extension point before you click Finish. Right-click the newly created extension in your manifest editor and select New &gt; formField. Now enter in the name text box “Draw Line Field” and select as modelClass “com.bsiag.minicrm.client.ui.form.fields.ext.IDrawLineField”. Now we have to specify the UI class. Right-click on the formField extension and select New &gt; uiClass. There we select the class “com.bsiag.minicrm.ui.swing.form.fields.ext.SwingScoutDrawLineField”.
+
 
+
=== SWT extension  ===
+
 
+
Open the plugin.xml from the ui.swt plugin. Go to the Extensions tab on the bottom of the manifest editor and click Add…. Uncheck the checkbox at the bottom and select the org.eclipse.scout.rt.ui.swt.formfields extension point before you click Finish. Right-click the newly created extension in your manifest editor and select New &gt; formField. Now enter in the name text box “Draw Line Field” and select as modelClass “com.bsiag.minicrm.client.ui.form.fields.ext.IDrawLineField”. Now we have to specify the ui class. Right-click on the formField extension and select New &gt; uiClass. There we select the class “com.bsiag.minicrm.ui.swt.form.fields.ext.SwtScoutDrawLineField”.
+
 
+
=== RAP extension  ===
+
 
+
Open the plugin.xml from the ui.rap plugin. Go to the Extensions tab on the bottom of the manifest editor and click Add…. Uncheck the checkbox at the bottom and select the org.eclipse.scout.rt.ui.rap.formfields extension point before you click Finish. Right-click the newly created extension in your manifest editor and select New &gt; formField. Now enter in the name text box “Draw Line Field” and select as modelClass “com.bsiag.minicrm.client.ui.form.fields.ext.IDrawLineField”. Now we have to specify the ui class. Right-click on the formField extension and select New &gt; uiClass. There we select the class “com.bsiag.minicrm.ui.rap.form.fields.ext.RwtScoutDrawLineField”.
+
 
+
== Test the DrawLineField  ==
+
 
+
You can now create a new Form where you use the DrawLineField. If you set the grid height to about a value of eight and disable the label you get a big field.
+
 
+
<br>
+
 
+
== Making the DrawLineField persistent  ==
+
 
+
So far, the line in our custom control can be pre-configured in the SDK and manually re-drawn using the mouse.
+
 
+
However, it is not yet possible to persistently store and load the line points from/to the database. This chapter shows how the custom field needs to be modified to allow persistence.
+
 
+
=== Data structures for the FormData  ===
+
 
+
*Create a class '''org.eclipse.minicrm.shared.data.form.fields.ext.LinePoint''':
+
<source lang="java">package org.eclipse.minicrm.shared.data.form.fields.ext;
+
 
+
import java.awt.Point;
+
import java.io.Serializable;
+
 
+
public class LinePoint implements Serializable {
+
  private static final long serialVersionUID = 1L;
+
  private int x;
+
  private int y;
+
 
+
  public LinePoint(int x, int y) {
+
    this.x = x;
+
    this.y = y;
+
  }
+
 
+
  public LinePoint(Point p) {
+
    this.x = p.x;
+
    this.y = p.y;
+
  }
+
 
+
  public void setX(int x) {
+
    this.x = x;
+
  }
+
 
+
  public void setY(int y) {
+
    this.y = y;
+
  }
+
 
+
  public int getX() {
+
    return x;
+
  }
+
 
+
  public int getY() {
+
    return y;
+
  }
+
 
+
  public Point getPoint() {
+
    return new Point(x, y);
+
  }
+
 
+
  @Override
+
  public int hashCode() {
+
    final int prime = 31;
+
    int result = 1;
+
    result = prime * result + x;
+
    result = prime * result + y;
+
    return result;
+
  }
+
 
+
  @Override
+
  public boolean equals(Object obj) {
+
    if (this == obj) return true;
+
    if (obj == null) return false;
+
    if (getClass() != obj.getClass()) return false;
+
    LinePoint other = (LinePoint) obj;
+
    if (x != other.x) return false;
+
    if (y != other.y) return false;
+
    return true;
+
  }
+
}
+
</source>
+
This class is only needed because Point does not have setters and getters for X and Y.
+
 
+
*Add the following annotation before the class definition of '''com.bsiag.minicrm.client.ui.form.fields.ext.AbstractDrawLineField''':
+
<source lang="java">@FormData(value = AbstractDrawLineFieldData.class, sdkCommand = SdkCommand.CREATE, defaultSubtypeSdkCommand = DefaultSubtypeSdkCommand.CREATE)
+
public abstract class AbstractDrawLineField extends AbstractCustomField implements IDrawLineField {</source>
+
If this leads to an error about ''DefaultSubtypeSdkCommand ''not being defined, manually import it
+
<source lang="java">import org.eclipse.scout.commons.annotations.FormData.DefaultSubtypeSdkCommand;</source>
+
*Add the following two private members to '''com.bsiag.minicrm.client.ui.form.fields.ext.AbstractDrawLineField''':
+
<source lang="java">  private LinePoint m_start;
+
  private LinePoint m_end;
+
</source>
+
<br>
+
 
+
=== Adjusting the interface and renderers  ===
+
 
+
*In '''com.bsiag.minicrm.client.ui.form.fields.ext.IDrawLineField''' change all occurences of '''Point''' to '''LinePoint''':
+
<source lang="java">  LinePoint getStart();
+
 
+
  void setStart(LinePoint p);
+
 
+
  LinePoint getEnd();
+
 
+
  void setEnd(LinePoint p);
+
</source>
+
*Change the following line in the '''paintControl()''' method in '''com.bsiag.minicrm.ui.swt.form.fields.ext.SwtScoutDrawLineFieldfrom''':
+
<source lang="java">e.gc.drawLine(getScoutObject().getStart().x, getScoutObject().getStart().y, getScoutObject().getEnd().x, getScoutObject().getEnd().y);</source>
+
to
+
<source lang="java">e.gc.drawLine(getScoutObject().getStart().getX(), getScoutObject().getStart().getY(), getScoutObject().getEnd().getX(), getScoutObject().getEnd().getY());</source>
+
*Change the following line in the '''paint()''' method in '''com.bsiag.minicrm.ui.swing.form.fields.ext.SwingScoutDrawLineFieldfrom''':
+
<source lang="java">g.drawLine(getScoutObject().getStart().x, getScoutObject().getStart().y, getScoutObject().getEnd().x, getScoutObject().getEnd().y);</source>
+
to
+
<source lang="java">g.drawLine(getScoutObject().getStart().getX(), getScoutObject().getStart().getY(), getScoutObject().getEnd().getX(), getScoutObject().getEnd().getY());</source>
+
 
+
=== Accessing start and end points from PROP_VALUE  ===
+
 
+
*Adjust the getStart(), getEnd(), setStart() and setEnd() methods in '''com.bsiag.minicrm.client.ui.form.fields.ext.AbstractDrawLineField''' as follows:
+
<source lang="java">  @Override
+
  @FormData
+
  public LinePoint getEnd() {
+
    return m_end;
+
  }
+
 
+
  @Override
+
  @FormData
+
  public void setEnd(LinePoint p) {
+
    m_end = p;
+
    propertySupport.setProperty(PROP_END, p);
+
  }
+
 
+
  @Override
+
  @FormData
+
  public LinePoint getStart() {
+
    return m_start;
+
  }
+
 
+
  @Override
+
  @FormData
+
  public void setStart(LinePoint p) {
+
    m_start = p;
+
    propertySupport.setProperty(PROP_START, p);
+
  }
+
</source>
+
The important change here is the '''@FormData''' annotation and the use of the private members in addition to the property.
+
 
+
=== AbstractDrawLineField  ===
+
 
+
*Adjust the type from '''Point''' to '''LinePoint''' in '''initConfig()''' and the '''UiFacade''':
+
<pre>      @Override
+
      protected void initConfig() {
+
        m_uiFacade = new P_UiFacade();
+
        super.initConfig();
+
        setStart(new LinePoint(getConfiguredStartX(), getConfiguredStartY()));
+
        setEnd(new LinePoint(getConfiguredEndX(), getConfiguredEndY()));
+
      }
+
 
+
      // ---------------------------------------------------------------------------------
+
      private class P_UiFacade implements IDrawLineUiFacade {
+
        @Override
+
        public void setEndFromUI(Point p) {
+
          setEnd(new LinePoint(p));
+
        }
+
 
+
        @Override
+
        public void setStartFromUI(Point p) {
+
          setStart(new LinePoint(p));
+
        }
+
      }
+
</pre>
+
*Add the following new member variables to '''com.bsiag.minicrm.client.ui.form.fields.ext.AbstractDrawLineField''':
+
<pre>      private LinePoint m_initStart;
+
      private LinePoint m_initEnd;
+
</pre>
+
*Add the following constructors to '''com.bsiag.minicrm.client.ui.form.fields.ext.AbstractDrawLineField''':
+
<pre>      public AbstractDrawLineField() {
+
        this(true);
+
      }
+
 
+
      public AbstractDrawLineField(boolean callInitializer) {
+
        super(callInitializer);
+
      }
+
</pre>
+
=== Storing to and loading from XML files  ===
+
 
+
This step is only needed if you want to be able to save and load the current DrawLineField content to an XML file.
+
 
+
*Add the following two methods to '''com.bsiag.minicrm.client.ui.form.fields.ext.AbstractDrawLineField''':
+
<pre>  @Override
+
  public void storeXML(SimpleXmlElement x) throws ProcessingException {
+
    super.storeXML(x);
+
    try {
+
      x.setObjectAttribute(IDrawLineField.PROP_START, getStart());
+
      x.setObjectAttribute(IDrawLineField.PROP_END, getEnd());
+
    }
+
    catch (IOException e) {
+
      if (ScoutLogManager.getLogger(AbstractDrawLineField.class).isInfoEnabled()) {
+
        ScoutLogManager.getLogger(AbstractDrawLineField.class).info("not serializable value in field " + getClass().getName() + "/" + getLabel() + ": " + e);
+
      }
+
    }
+
  }
+
 
+
  @Override
+
  public void loadXML(SimpleXmlElement x) throws ProcessingException {
+
    super.loadXML(x);
+
    try {
+
      LinePoint start = TypeCastUtility.castValue(x.getObjectAttribute(IDrawLineField.PROP_START, null), LinePoint.class);
+
      LinePoint end = TypeCastUtility.castValue(x.getObjectAttribute(IDrawLineField.PROP_END, null), LinePoint.class);
+
      setStart(start);
+
      setEnd(end);
+
    }
+
    catch (Exception e) {
+
      // be lenient, maybe the field was changed
+
      ScoutLogManager.getLogger(AbstractDrawLineField.class).warn(null, e);
+
    }
+
  }</pre>
+
 
+
=== Keeping track of the changed status  ===
+
 
+
This is needed so the form can detect whether any changes were made to the field in case the Cancel button on a form is pressed.
+
 
+
*Add the following methods to '''com.bsiag.minicrm.client.ui.form.fields.ext.AbstractDrawLineField''':
+
<pre>  public void setInitValue(LinePoint initStart, LinePoint initEnd) {
+
    m_initStart = initStart;
+
    m_initEnd = initEnd;
+
  }
+
 
+
  public LinePoint getInitStart() {
+
    return m_initStart;
+
  }
+
 
+
  public LinePoint getInitEnd() {
+
    return m_initEnd;
+
  }
+
 
+
  @Override
+
  protected boolean execIsSaveNeeded() throws ProcessingException {
+
    LinePoint start = getStart();
+
    LinePoint end = getEnd();
+
    LinePoint initStart = getInitStart();
+
    LinePoint initEnd = getInitEnd();
+
 
+
    if (start == null &amp;&amp; initStart == null &amp;&amp; end == null &amp;&amp; initEnd == null) {
+
      return false;
+
    }
+
    else if (start == null || initStart == null || end == null || initEnd == null) {
+
      return true;
+
    }
+
    else {
+
      if (start.equals(initStart) &amp;&amp; end.equals(initEnd)) {
+
        return false;
+
      }
+
      else {
+
        return true;
+
      }
+
    }
+
  }
+
 
+
  @Override
+
  protected void execMarkSaved() throws ProcessingException {
+
    super.execMarkSaved();
+
    setInitValue(getStart(), getEnd());
+
  }
+
</pre>
+
=== Accessing the field in the ProcessService  ===
+
 
+
There are several possibilties to access these fields in the ProcessService:
+
 
+
==== Using IntegerHolders  ====
+
<pre>      IntegerHolder startX = new IntegerHolder();
+
      IntegerHolder startY = new IntegerHolder();
+
      IntegerHolder endX = new IntegerHolder();
+
      IntegerHolder endY = new IntegerHolder();
+
      SQL.selectInto("SELECT 100,100,200,200 FROM DUAL INTO :startX, :startY, :endX, :endY",
+
          new NVPair("startX", startX),
+
          new NVPair("startY", startY),
+
          new NVPair("endX", endX),
+
          new NVPair("endY", endY));
+
      formData.getDrawLine().getStart().setX(startX.getValue());
+
      formData.getDrawLine().getStart().setY(startY.getValue());
+
      formData.getDrawLine().getEnd().setX(endX.getValue());
+
      formData.getDrawLine().getEnd().setY(endY.getValue());
+
</pre>
+
==== Using SQL.selectInto  ====
+
<pre>      SQL.selectInto("SELECT 100, 100,200, 200 FROM DUAL INTO :drawLine.start.x, :drawLine.start.y, :drawLine.end.x, :drawLine.end.y", formData);
+
</pre>
+
<br>
+
 
+
<br>
+
 
+
== Result  ==
+
 
+
=== Swing  ===
+
 
+
[[Image:DrawLineSwing.png]]<br>
+
 
+
=== SWT  ===
+
 
+
[[Image:DrawLineSwt.png]]<br>
+
 
+
=== RAP  ===
+
 
+
On the left you can see the drawn line, on the right you can see the marked starting point which will be shown while the mouse is being dragged.
+
 
+
[[Image:DrawLineRap.png]] [[Image:DrawLineRapPoint.png]]
+

Latest revision as of 07:35, 18 March 2024

The Scout documentation has been moved to https://eclipsescout.github.io/.

Back to the top