Differences

This shows you the differences between two versions of the page.

Link to this comparison view

customui [2011/12/28 15:40]
srdjan.lukovic [Implementation Procedure]
customui [2011/12/28 16:24] (current)
srdjan.lukovic [Architecture]
Line 20: Line 20:
 Figure 1 depicts the described architecture of the SOLoist GUI subsystem. Figure 1 depicts the described architecture of the SOLoist GUI subsystem.
  
-{{ :guienvironment.jpg |Figure 1. SOLoist GUI runtime environment}}+{{:guienvironment.jpg?400|Figure 1. SOLoist GUI runtime environment}}
  
 ====== Implementation Procedure ====== ====== Implementation Procedure ======
Line 30: Line 30:
     - Derive it from the top-level SOLoist UI class ''GUIComponent''. Note that by extending the ''GUIComponent'' class, the derived class inherits several attributes and pins. The desired behavior for events on those pins and attributes should be provided later on when implementing the behavior of the component under construction. For example, one of these attributes is the attribute ''enabled''; the implemented behavior should ensure that the widget is disabled when this attribute is set to false.     - Derive it from the top-level SOLoist UI class ''GUIComponent''. Note that by extending the ''GUIComponent'' class, the derived class inherits several attributes and pins. The desired behavior for events on those pins and attributes should be provided later on when implementing the behavior of the component under construction. For example, one of these attributes is the attribute ''enabled''; the implemented behavior should ensure that the widget is disabled when this attribute is set to false.
     - If the UI component is to be configurable in terms of its appearance or functionality, a number of UML attributes should be modeled for the configuration.     - If the UI component is to be configurable in terms of its appearance or functionality, a number of UML attributes should be modeled for the configuration.
-    - Each UI component communicates with other components via pins and wires. For each pin, a UML attribute stereotyped with ''<<input>>'' or ''<<output>>'' depending on its purpose. The type of this attribute is irrelevant and can simply be ''Text''. +    - Each UI component communicates with other components via pins and wires. For each pin, a UML attribute stereotyped with ''<<input>>'' or ''<<output>>'' depending on its purpose. The type of this attribute is irrelevant and can simply be ''Text''.\\ **Figure 2** shows the example ''GUILabelComponent'' class with the abovementioned steps conducted. The attribute ''text'' is meant for holding the initial label’s text. The attribute ''textSize'' holds the value that defines the size of the label’s text in, let’s say, pixels. Furthermore, there is an input pin ''newText'' which is meant to accept a new arbitrary text so that ''GUILabelComponent'' can change it dynamically. Finally, the UI component is able to inform its surrounding UI components that its value (text) has changed, and the output pin ''textChanged'' is used for that.\\ {{:guilabelcomponent.jpg?nolink&|}}
-Figure 2 shows the example ''GUILabelComponent'' class with the abovementioned steps conducted. The attribute ''text'' is meant for holding the initial label’s text. The attribute ''textSize'' holds the value that defines the size of the label’s text in, let’s say, pixels. Furthermore, there is an input pin ''newText'' which is meant to accept a new arbitrary text so that ''GUILabelComponent'' can change it dynamically. Finally, the UI component is able to inform its surrounding UI components that its value (text) has changed, and the output pin ''textChanged'' is used for that. +
   - **Create the corresponding //info// class**. The info class carries all necessary configuration data from the component to its representation on the client. Its objects are created on the server, then they are populated with values from the UI component attributes, then they are serialized, and finally sent to the client (web browser). This class is a plain Java serializable class that is serialized by GWT. The Java project should be configured so that this class gets GWT-compiled into JavaScript by the GWT compiler. To create an info class:   - **Create the corresponding //info// class**. The info class carries all necessary configuration data from the component to its representation on the client. Its objects are created on the server, then they are populated with values from the UI component attributes, then they are serialized, and finally sent to the client (web browser). This class is a plain Java serializable class that is serialized by GWT. The Java project should be configured so that this class gets GWT-compiled into JavaScript by the GWT compiler. To create an info class:
     - Provide it with a name that corresponds to the name of the UI component, for example ''LabelInfo''.     - Provide it with a name that corresponds to the name of the UI component, for example ''LabelInfo''.
     - Make it extend the ''ComponentInfo'' class. By this, it inherits fields that serve as a carrier for attribute values of the ''GUIComponent'' class.     - Make it extend the ''ComponentInfo'' class. By this, it inherits fields that serve as a carrier for attribute values of the ''GUIComponent'' class.
     - For each attribute of the corresponding component class, a corresponding Java field should be created in the info class.     - For each attribute of the corresponding component class, a corresponding Java field should be created in the info class.
-    - Override the ''createDefaultController()'' method and return ''null'' from its body only for the moment. Later on, when the controller class is created, this method should return the instance of that controller class. Do not forget to do that. +    - Override the ''createDefaultController()'' method and return ''null'' from its body only for the moment. Later on, when the controller class is created, this method should return the instance of that controller class. Do not forget to do that.\\ For example, create the ''LabelInfo'' class like this: <code java>
- +
-For example, create the ''LabelInfo'' class like this: +
-<code java>+
 package org.example.soloist.client.common.info; package org.example.soloist.client.common.info;
- 
 import rs.sol.soloist.client.guiruntime.controller.Controller; import rs.sol.soloist.client.guiruntime.controller.Controller;
  
Line 48: Line 42:
  
    public String text;    public String text;
-    
    public int textSize;    public int textSize;
-   +
    @Override    @Override
    public Controller createDefaultController(){    public Controller createDefaultController(){
Line 57: Line 50:
 } }
 </code> </code>
- +  -**Perform serialization.** Override two serialization methods in the component class. These are:
-  - **Perform serialization.** Override two serialization methods in the component class. These are:+
     - ''protected abstract ComponentInfo createSpecificInfo();''     - ''protected abstract ComponentInfo createSpecificInfo();''
-    - ''protected void fillInfo(ComponentInfo info);'' +    - ''protected void fillInfo(ComponentInfo info);''\\ The former should instantiate the object of the corresponding info class. The latter should populate its fields with values from the attributes of the component object.\\ For the ''GUILabelComponent'' example: <code java>
-The former should instantiate the object of the corresponding info class. The latter should populate its fields with values from the attributes of the component object. +
-For the ''GUILabelComponent'' example: +
-<code java>+
 @Override @Override
 protected ComponentInfo createSpecificInfo() { protected ComponentInfo createSpecificInfo() {
Line 77: Line 66:
 } }
 </code> </code>
- 
   - **Create the client-side ''Widget'' interface.** The **Widget** interface is just a facade hiding the implementation of the widget class from its clients (which are typically objects of **Controller** classes). In order to create a widget interface:   - **Create the client-side ''Widget'' interface.** The **Widget** interface is just a facade hiding the implementation of the widget class from its clients (which are typically objects of **Controller** classes). In order to create a widget interface:
     - Provide it with an appropriate name corresponding to the UI component class.     - Provide it with an appropriate name corresponding to the UI component class.
     - Make it extend ''rs.sol.soloist.client.guiruntime.view.Widget'' interface.     - Make it extend ''rs.sol.soloist.client.guiruntime.view.Widget'' interface.
-    - Create additional arbitrary methods. +    - Create additional arbitrary methods.\\ For ''GUILabelComponent'', the ''LabelWidget'' interface looks like this: <code java>
-For ''GUILabelComponent'', the ''LabelWidget'' interface looks like this: +
-<code java>+
 public interface LabelWidget extends Widget { public interface LabelWidget extends Widget {
    void setText(String text);    void setText(String text);
Line 89: Line 75:
 } }
 </code> </code>
- 
   - **Create the client-side GWT widget class.** SOLoist relies on GWT in its UI system. Every SOLoist widget is actually a GWT widget. In order to create a SOLoist widget class:   - **Create the client-side GWT widget class.** SOLoist relies on GWT in its UI system. Every SOLoist widget is actually a GWT widget. In order to create a SOLoist widget class:
     - Provide it with a name according to the UI component name.     - Provide it with a name according to the UI component name.
Line 96: Line 81:
     - Implement all methods from the implemented widget interface.     - Implement all methods from the implemented widget interface.
     - Provide it with a reference to the corresponding controller object.     - Provide it with a reference to the corresponding controller object.
-    - Implement the widget-specific functionality. Draw the widget. Inform its controller when necessary by calling its public methods. +    - Implement the widget-specific functionality. Draw the widget. Inform its controller when necessary by calling its public methods.\\ For the ''GUILabelComponent'' example, this looks like this: <code java>
-For the ''GUILabelComponent'' example, this looks like this: +
-<code java>+
 import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Label;
  
Line 141: Line 124:
 } }
 </code> </code>
- +  - **A small hack.** Consider the class ''rs.sol.soloist.client.common.GUIComponentPins''. There is a number of ''String'' constants, one for each pin of each SOLoist built-in UI component. For example: <code java>
-  - **A small hack.** Consider the class ''rs.sol.soloist.client.common.GUIComponentPins''. There is a number of ''String'' constants, one for each pin of each SOLoist built-in UI component. For example: +
-<code java>+
 public static final String ENABLED_COMPONENT_I = "3"; public static final String ENABLED_COMPONENT_I = "3";
 public static final String RESET_SEARCH_RESULT_COMPONENT_I = "c"; public static final String RESET_SEARCH_RESULT_COMPONENT_I = "c";
Line 149: Line 130:
 public static final String RESULT_COMMAND_COMPONENT_O = "12"; public static final String RESULT_COMMAND_COMPONENT_O = "12";
  
-</code> +</code> The values of these constants represent unique identifiers of the pins. The names of these constant fields follow the pattern ''PIN_NAME_COMPONENT_NAME_PIN_DIRECTION''. Furthermore, there are two ''HashMap''s named ''PIN_NAMES'' and ''PIN_IDS''. The former stores the mappings from the fully qualified name to the identifier of each pin of each component, while the latter stores the inverse mapping. You should first create unique ''String'' identifiers for all pins that you created in the component under construction and then put the mappings for them into these two ''HashMap''s on the application startup. The next step shows the use of these constants. Finally, you should make sure that they reside in a class that gets GWT-compiled.
-The values of these constants represent unique identifiers of the pins. The names of these constant fields follow the pattern ''PIN_NAME_COMPONENT_NAME_PIN_DIRECTION''. Furthermore, there are two ''HashMap''s named ''PIN_NAMES'' and ''PIN_IDS''. The former stores the mappings from the fully qualified name to the identifier of each pin of each component, while the latter stores the inverse mapping. You should first create unique ''String'' identifiers for all pins that you created in the component under construction and then put the mappings for them into these two ''HashMap''s on the application startup. The next step shows the use of these constants. Finally, you should make sure that they reside in a class that gets GWT-compiled. +
   - **Create the client-side controller class.** The controller object is responsible for maintaining the UI representation (through a GWT widget), handling events from the widget, reacting on events on input pins, generating events on output pins, and communicating with the UI component object on the server, if necessary. It is strictly a client-side class – controller objects reside in the web browser. Hence, the controller class needs to be GWT-compiled as well.\\ In order to create a controller class:   - **Create the client-side controller class.** The controller object is responsible for maintaining the UI representation (through a GWT widget), handling events from the widget, reacting on events on input pins, generating events on output pins, and communicating with the UI component object on the server, if necessary. It is strictly a client-side class – controller objects reside in the web browser. Hence, the controller class needs to be GWT-compiled as well.\\ In order to create a controller class:
     - Provide it with a name corresponding to the name of the UI component, for example ''LabelController''.     - Provide it with a name corresponding to the name of the UI component, for example ''LabelController''.
     - Make it extend the SOLoist’s root client class: ''ComponentController''.     - Make it extend the SOLoist’s root client class: ''ComponentController''.
-    - Create a constructor with the corresponding info as a parameter and call the inherited ''constructor()'' method with the same parameter. For the example of ''GUILabelComponent'':\\ +    - Create a constructor with the corresponding info as a parameter and call the inherited ''constructor()'' method with the same parameter. For the example of ''GUILabelComponent'': <code java>
-<code java>+
 public LabelController(LabelInfo info) { public LabelController(LabelInfo info) {
    constructor(info);    constructor(info);
 } }
 </code> </code>
-    - Implement the inherited abstract method for creating a widget: ''createRepresentative()''. It should return the created widget. You can access the information from the info object (populated on the server) in the ''myInfo'' field of this (controller) class. You should set the widget’s reference to this controller by calling ''setController(this)''. One possible implementation of this method could look like this:\\ +    - Implement the inherited abstract method for creating a widget: ''createRepresentative()''. It should return the created widget. You can access the information from the info object (populated on the server) in the ''myInfo'' field of this (controller) class. You should set the widget’s reference to this controller by calling ''setController(this)''. One possible implementation of this method could look like this: <code java>
-<code java>+
 @Override @Override
 protected Widget createRepresentative() { protected Widget createRepresentative() {
Line 169: Line 146:
    return representative;    return representative;
 } }
-</code> +</code> Note that you can access the created widget through the field ''myRepresentative''. 
-Note that you can access the created widget through the field ''myRepresentative''. +    - Override the ''acceptViaBinding'' method in order to react on events on each of the component’s input pins. Do not forget to react on events received via inherited input pins (from ''GUIComponent'' class). For the ''GUILabelComponent'' example, we could do something like this in order to react on the event on the ''newText'' input pin. In a similar way, you can react on events from all other input pins of this component. In order to send values on output pins, use the method ''sendViaBinding''. <code java>
-    - Override the ''acceptViaBinding'' method in order to react on events on each of the component’s input pins. Do not forget to react on events received via inherited input pins (from ''GUIComponent'' class). For the ''GUILabelComponent'' example, we could do something like this in order to react on the event on the ''newText'' input pin. In a similar way, you can react on events from all other input pins of this component. In order to send values on output pins, use the method ''sendViaBinding''.\\ +
-<code java>+
 @Override @Override
 public boolean acceptViaBinding(String destSlot, List<Descr> message) { public boolean acceptViaBinding(String destSlot, List<Descr> message) {
Line 188: Line 163:
     - In order to communicate with the server:     - In order to communicate with the server:
       - Use one of existing classes from the ''rs.sol.soloist.client.common.requests.Request'' hierarchy (or create one of your own request class) and fill it with request parameters.       - Use one of existing classes from the ''rs.sol.soloist.client.common.requests.Request'' hierarchy (or create one of your own request class) and fill it with request parameters.
-      - Call ''Controller.requestBuffer.addRequest(Request request, ServiceConsumer client, int requestIndex)'' in this way, for example:\\ +      - Call ''Controller.requestBuffer.addRequest(Request request, ServiceConsumer client, int requestIndex)'' in this way, for example: <code java>Controller.requestBuffer.addRequest(myRequest, this, 0);</code> 
-      <code java>Controller.requestBuffer.addRequest(myRequest, this, 0);</code> +      - Override the method in the component class (created in step 1) to handle the request and create a response (a response must be serializable and its class must be GWT-compiled):\\ ''public Object GUIComponent::handle(Request request)''
-      - Override the method in the component class (created in step 1) to handle the request and create a response (a response must be serializable and its class must be GWT-compiled):\\ +
-      ''public Object GUIComponent::handle(Request request)''+
       - Wait for the response on the client by overriding both:       - Wait for the response on the client by overriding both:
         - ''serviceSuccessCallback(Request request, int requestIndex, Serializable result);''         - ''serviceSuccessCallback(Request request, int requestIndex, Serializable result);''
Print/export