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 "Riena/Navigation"

m (method name changed)
m (method name changed)
Line 138: Line 138:
 
nodeId = new NavigationNodeId("org.eclipse.riena.example.customerDetail");
 
nodeId = new NavigationNodeId("org.eclipse.riena.example.customerDetail");
 
ISubModuleNode subModule = new SubModuleNode(nodeId, "SubModule 1.1");
 
ISubModuleNode subModule = new SubModuleNode(nodeId, "SubModule 1.1");
subModule.setIcon(createIconPath(ExampleIcons.ICON_FILE));
+
subModule.setIcon(getIconPath(ExampleIcons.ICON_FILE));
 
WorkareaManager.getInstance().registerDefinition(subModule,  
 
WorkareaManager.getInstance().registerDefinition(subModule,  
 
     CustomerDetailSubModuleController.class, CustomerDetailSubModuleView.ID);
 
     CustomerDetailSubModuleController.class, CustomerDetailSubModuleView.ID);

Revision as of 06:38, 22 January 2009

Riena Project > Riena Navigation

Navigation Concepts

The goal of the Riena navigation concept is to make navigation as easy and comfortable as possible for the enterprise user.

Subapplications

The Riena application ist made up from possibly multiple subapplications identifyable by the handles in the top area of the application window.

Riena Cient SubApplications.PNG

This example shows a Riena client application with 2 subapplications called 'Navigation' and 'Playground'. The 'Navigation' subapplication is currently selected.

Module Groups, Modules and Submodules

Each subapplication may contain multiple module groups, each module group may contain multiple modules and each module may contain multiple submodules. Submodules may be organized in a hierarchical way, ie a submodul may have zero (probably the most common case), one or more submodules as children.

Riena Client Navigation.PNG The picture to the left shows 3 module groups, the first one containing two modules (named 'Module 1' and 'M2 (not closable)'). The second and third module group each contain one module only (named 'Module 1.2.1' and 'Navigate', resp). The modules named 'M2 (not closable)' Module 1.2.1' cannot be closed by the user indicated by the absence of the 'X' button in the right corner of the module widget.

Note that only the first module within the first module group is expanded, ie its submodules are visible within the navigation tree. This is because that module is currently selected. We can also see that the first submodule (named 'SubModule 1.1') is the parent of another submodule (named 'SubModule 1.1.1') and has a sibling (named 'SubModule 1.2') within the same module. Selecting a subapplication, module group or module automatically activates the first submodule within that context. This implies the there is no work area associated with modules, module groups or subapplications - only submodules can have an associated work area (ie view and optionally a controller)

The Riena navigation therefore can be thought of as a tree where the root is the application node and the leafs are the submodule nodes.

Navigation Node Common Properties

There are some properties that can be defined for all navigation nodes:

  • id - The id is made up from two parts, a type and an instance part. The type part identifies the kind of dialog and is equal for all nodes representing the same kind of dialog, ie they are required to use the same view id and controller class (in case a controller is used). The instance part is used in conjunction with the type part to uniquely identify instances of the same dialog.
    If the customer overview dialog for example has type 'com.acme.customer.overview' the dialog instance for cutomer with the unique customer id '1234567' would be identified by NavigationNodeId('com.acme.customer.overview', '1234567')).
  • children - The children of the node. The type of children depends on the parent:
    • subapplications can only have module group nodes
    • module groups can only have module nodes
    • modules and submodules can only have submodule nodes

Navigation nodes except module group nodes may also have a label and/or an icon.

Programmatic Creation

All navigation structures may easily be created programmatically. If the application class configured in extension org.eclipse.core.runtime.applications extends org.eclipse.riena.navigation.ui.application.AbstractApplication the method createModel() would be sent within the start(IApplicationContext context) method. The default implementation just returns an application node without any children:

public static final String DEFAULT_APPLICATION_TYPEID = "application";
 
protected IApplicationNode createModel() {
	NavigationNodeId applicationNodeId = new NavigationNodeId(DEFAULT_APPLICATION_TYPEID);
	IApplicationNode applicationModel = new ApplicationNode(applicationNodeId);
	return applicationModel;
}

All navigation node interfaces extend a common super interface, org.eclipse.riena.navigation.INavigationNode. The implementation classes form a corresponding hierarchy where all non abstract navigation node classes inherit from org.eclipse.riena.navigation.model.NavigationNode:

  Riena INavigationNode Hierarchy.PNG   Riena NavigationNode Hierarchy.PNG


Using Custom createModel() method

Let's now try to create the 'Navigation' subapplication and the first module group from the screenshot above. We start our implementation of the createModel() method with a call to the super implemetantion to receive an IApplicationNode and configure label and application icon:

@Override
protected IApplicationNode createModel() {
	IApplicationNode applicationNode = super.createModel();
	applicationNode.setLabel("Riena Navigation Example");
	applicationNode.setIcon(getIconPath(ExampleIcons.ICON_APPLICATION));
 
// ... everthing described below goes here ...
 
	return applicationModel;

Creating a Subapplication

To be able to create a subapplication we need one more thing: As subapplications in Riena correspond to RCP perspectives we need to contribute the corresponding extension. The view class representing the subapplication perspective in Riena is org.eclipse.riena.navigation.ui.swt.views.SubApplicationView, so we will configure our subapplication as follows (of course the id is not required to be 'subapplication.1'):

<extension point="org.eclipse.ui.perspectives">
   <perspective
         class="org.eclipse.riena.navigation.ui.swt.views.SubApplicationView"
         id="subapplication.1"
         name="subapplication.1">
   </perspective>
</extension>

We can now

  • create a subapplication node with a certain node id
  • register it with the WorkareaManager to assign the perspective we configured
  • finally add it to the application node
// Navigation SubApplication
NavigationNodeId nodeId = new NavigationNodeId("org.eclipse.riena.example.navigation.subapplication");
ISubApplicationNode subApplication = new SubApplicationNode(nodeId, "Navigation");
WorkareaManager.getInstance().registerDefinition(subApplication, "subapplication.1");
applicationNode.addChild(subApplication);

This would bring up the Riena application with one subapplication called 'Navigation' that does not contain any content (please ignore menu and toolbar for now - those are there because I lazily abused the Riena example client and did not remove them): Riena EmptyNavigationSubApp.PNG

Creating a Module Group

Creating a module group is rather easy in as there is no label, icon or work area that could require configuration. As we also do not need a node id here we can simply write:

// create a module group and add it to subapplication
IModuleGroupNode moduleGroup = new ModuleGroupNode(null);
subApplication.addChild(moduleGroup);

This does not change anything in the visualization of our example client as we do not yet have any modules within our module group and empty groups are not displayed. Therefore let's proceed creating our first module...

Creating a Module

// create a module and add it module group
IModuleNode module = new ModuleNode(null, "Module 1");
module.setIcon(getIconPath(ExampleIcons.ICON_APPLICATION));
moduleGroup.addChild(module);

...and add a submodule right away.

Creating Submodules

As the submodule has a presentation in the work area the associated view needs to be configured using the eclipse 'org.eclipse.ui.views' extension point:

<extension point="org.eclipse.ui.views">
   <view allowMultiple="true"
         class="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView"
         id="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView"
         name="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView">
   </view>
</extension>

The association from node to view is done, just like for the subapplication, via the WorkareaManager.

// create "SubModule 1.1" and add it to "Module 1"
nodeId = new NavigationNodeId("org.eclipse.riena.example.customerDetail");
ISubModuleNode subModule = new SubModuleNode(nodeId, "SubModule 1.1");
subModule.setIcon(getIconPath(ExampleIcons.ICON_FILE));
WorkareaManager.getInstance().registerDefinition(subModule, 
     CustomerDetailSubModuleController.class, CustomerDetailSubModuleView.ID);
module.addChild(subModule);

Starting the application would bring up the example appliation like this:

Riena NavigationOneModule.PNG

Note that

  • the first submodule found within the whole application (ie. first submodule within first module within first module group within first subapplication) is automatically selected by default
  • the module cannot be expanded as it has only one submodule (expanding would not make too much sense as with one submodule you do not have too many options to select another submodule)

Final Steps

It should now be easy to add some more submodules

// create "SubModule 1.1.1" and add it to "SubModule 1.1"
nodeId = new NavigationNodeId("org.eclipse.riena.example.customerDetail");
ISubModuleNode subModule2 = new SubModuleNode(nodeId, "SubModule 1.1.1");
WorkareaManager.getInstance().registerDefinition(subModule2, 
     CustomerDetailSubModuleController.class, CustomerDetailSubModuleView.ID);
subModule.addChild(subModule2);
 
// create "SubModule 1.2" and add it to "Module 1"
nodeId = new NavigationNodeId("org.eclipse.riena.example.customerDetail");
subModule = new SubModuleNode(nodeId, "SubModule 1.2");
WorkareaManager.getInstance().registerDefinition(subModule, 
     CustomerDetailSubModuleController.class, CustomerDetailSubModuleView.ID);
module.addChild(subModule);

or create a second module within the same module group. For variation we make the second module non closable.

// create "M2 (not closable)" and add it to the module group
module = new ModuleNode(null, "M2 (not closable)");
module.setIcon(createIconPath(ExampleIcons.ICON_HOMEFOLDER));
module.setClosable(false);
moduleGroup.addChild(module);
 
// create "SubModule 2.1" and add it to "M2 (not closable)"
nodeId = new NavigationNodeId("org.eclipse.riena.example.customerDetail");
subModule = new SubModuleNode(nodeId, "SubModule 2.1");
WorkareaManager.getInstance().registerDefinition(subModule, 
     CustomerDetailSubModuleController.class, CustomerDetailSubModuleView.ID);
module.addChild(subModule);

resulting in our final example application:

Riena NavigationTwoModules.PNG

That's all for the programmatic creation of the navigation tree basics. Now let's have a look how this could be achieved using the declarative way.

Using Extension Points

All navigation structures may also be created in a declarative way using extension points. Just like if using the programmatic approach the application class configured in extension org.eclipse.core.runtime.applications is required to extend org.eclipse.riena.navigation.ui.application.AbstractApplication so the method createModel() will be sent within the start(IApplicationContext context) method. The application class is (except for your view and controller implementations, of course) the only java code that is required to create exactly the same navigation structure as we did before the programmatic way.

Using Custom createModel() method

The implementation of the createModel() method is really only required to set label and icon of the application, we will not add any more code to this method:

@Override
protected IApplicationNode createModel() {
	IApplicationNode applicationNode = super.createModel();
	applicationNode.setLabel("Riena Navigation Example");
	applicationNode.setIcon(getIconPath(ExampleIcons.ICON_APPLICATION));
	return applicationModel;

Creating a Subapplication

To create a subapplication we need to contribute the corresponding perspective extension. This is exactly the same as before:

<extension point="org.eclipse.ui.perspectives">
   <perspective
         class="org.eclipse.riena.navigation.ui.swt.views.SubApplicationView"
         id="subapplication.1"
         name="subapplication.1">
   </perspective>
</extension>

The following xml code shows how to

  • define a subapplication node with a certain node id
  • assign the perspective we configured
  • assign the label for the subapplication handle

Please ignore the assembly element for now, I will explain it later:

<extension point="org.eclipse.riena.navigation.assemblies">
   <assembly
         id="org.eclipse.riena.example.navigation.assembly"
         autostartsequence="100"
         parentTypeId="application">
      <!-- create subapplication -->
      <subapplication 
            label="Navigation"
            typeId="org.eclipse.riena.example.navigation.subapplication"
            view="subapplication.1">
      </subapplication>
   </assembly>
</extension>

This would bring up the same Riena application with one subapplication called 'Navigation' not containing any content like in the first step of the programmatic example: Riena EmptyNavigationSubApp.PNG

Creating a Module Group

Creating a module group just requires adding the modulegroup element to our subapplication element. There are no attributes required but we will assign a name so we can easily identify it within the PDE:

<subapplication 
      label="Navigation"
      typeId="org.eclipse.riena.example.navigation.subapplication"
      view="subapplication.1">
   <!-- create module group and add it subapplication -->
   <modulegroup name="group">
   </modulegroup>
</subapplication>

This does not change anything in the visualization of our example client as we do not yet have any modules within or module group and empty groups are not displayed. Therefore let's again proceed creating our module...

Creating a Module

As you may have already guessed there is also a module element that can be added as a child to the modulegroup element:

<modulegroup name="group">
   <module
         label="Module 1"
         icon="org.eclipse.riena.example.client:/icons/0457_a_a00.png">
   </module>
</modulegroup>

The icon path is prefixed with the bundle name so the generic Riena navigation assembler would be able to find it.

Creating Submodules

As the submodule has a presentation in the work area the associated view needs to be configured using the eclipse 'org.eclipse.ui.views' extension point:

<extension point="org.eclipse.ui.views">
   <view allowMultiple="true"
         class="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView"
         id="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView"
         name="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView">
   </view>
</extension>

The association from node to view and controller is donevia the view and controller attributes where the controller atribute takes the fully qualified name of the controller class.

<module
      label="Module 1"
      icon="org.eclipse.riena.example.client:/icons/0457_a_a00.png">
   <!-- create "SubModule 1.1" and add it to "Module 1" -->
   <submodule
         label="SubModule 1.1"
         icon="org.eclipse.riena.example.client:/icons/file.gif"
         typeId="org.eclipse.riena.example.customerDetail"
         controller="org.eclipse.riena.example.client.controllers.CustomerDetailSubModuleController"
         view="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView">
   </submodule>
</module>

Starting the application would bring up the example appliation looking just like in the programmatic case:

Riena NavigationOneModule.PNG

Final Steps

It should now be easy to add some more submodules

<module
      label="Module 1"
      icon="org.eclipse.riena.example.client:/icons/0457_a_a00.png">
   <!-- create "SubModule 1.1" and add it to "Module 1" -->
   <submodule
         label="SubModule 1.1"
         icon="org.eclipse.riena.example.client:/icons/file.gif"
         typeId="org.eclipse.riena.example.customerDetail"
         controller="org.eclipse.riena.example.client.controllers.CustomerDetailSubModuleController"
         view="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView">
      <!-- create "SubModule 1.1.1" and add it to "SubModule 1.1" -->
      <submodule
            label="SubModule 1.1.1"
            icon="org.eclipse.riena.example.client:/icons/file.gif"
            typeId="org.eclipse.riena.example.customerDetail"
            controller="org.eclipse.riena.example.client.controllers.CustomerDetailSubModuleController"
            view="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView">
      </submodule>
   </submodule>
   <!-- create "SubModule 1.2" and add it to "Module 1" -->
   <submodule
         label="SubModule 1.2"
         icon="org.eclipse.riena.example.client:/icons/file.gif"
         typeId="org.eclipse.riena.example.customerDetail"
         controller="org.eclipse.riena.example.client.controllers.CustomerDetailSubModuleController"
         view="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView">
   </submodule>
</module>

or create a second module within the same module group. Again the second module will not be closable.

// create "M2 (not closable)" and add it to the module group
// create "SubModule 2.1" and add it to "M2 (not closable)"
<modulegroup name="group">
   <module
         label="Module 1"
         icon="org.eclipse.riena.example.client:/icons/0457_a_a00.png">
   <!-- ... code from above... -->
   </module>
   <module
         label="M2 (not closable)"
         icon="org.eclipse.riena.example.client:/icons/HomeFolder.gif"
         unclosable="true">
      <!-- create "SubModule 2.1" and add it to "M2 (not closable)" -->
      <submodule
            label="SubModule 2.1"
            icon="org.eclipse.riena.example.client:/icons/file.gif"
            typeId="org.eclipse.riena.example.customerDetail"
            controller="org.eclipse.riena.example.client.controllers.CustomerDetailSubModuleController"
            view="org.eclipse.riena.example.client.views.CustomerDetailSubModuleView">
      </submodule>
   </module>
</modulegroup>

resulting in the same example application as the programmatic way:

Riena NavigationTwoModules.PNG

This concludes the extension point section of the navigation tree basics.

Navigation Node Assemblers

Assembler Basics

As you have seen in the previous section the extension point contains assembly elements. These elements decribe how a certain part of the Riena navigation tree is going to be 'assembled'. Basically there are two options

  • Custom Assemblers provide the user with the option to create parts of the navigation tree structure in a programmatic way without using any additional xml. A custom assembler would create navigation nodes just the way we did within the createModel() method of the programmatic example. A custom assembler MUST implement the interface org.eclipse.riena.navigation.INavigationAssembler
  • Generic Assemblers are prepared to use the element structure provided within the assembly element. Generic Assemblers MUST implement the org.eclipse.riena.navigation.IGenericNavigationAssembler interface. There is a default implementation of a generic assembler, org.eclipse.riena.navigation.model.GenericNavigationAssembler, that will be used if no assembler is specified.

Navigation Assembler Variables

When defining the content of any string attribute like eg labels, instance ids etc. variables may be used and are automatically replaced by their current value. There are some predefined variables allowing access to

  • navigation node id ${riena.navigation.nodeid}
  • navigation node parameter if supplied by the user ${riena.navigation.parameter}
  • navigation node context ${riena.navigation.context}

If you eg have a module within your application that would provide one or more submodules to edit some cutomer data and navigation nodes for different customers are distinguished by using the customer number as the navigation node instance id, the module definition could look like this:

<module
   label="Customer ${riena.navigation.nodeid:instanceId}"
   typeId="com.acme.customer.edit.module">

The paramenter after the colon is interpreted as a property of the variable ${riena.navigation.nodeid} instance at the time the module node is built. Nested and mapped properties may be accessed using the appropriate string (see the Apache Commons BeanUtils page for more information).

If your application eg would open the customer edit module only on an existing customer object (eg found in a search dialog), you might want provide the customer within the navigation argument when navigating to the customer edit module. If the customer had a name attribute, we could display the name instead of the customer number in the module label like this:

<module
   label="Customer ${riena.navigation.parameter:name}"
   typeId="com.acme.customer.edit.module">

The navigation node context is not the context accessible via NavigationNode.getContext(String) as this would not make too much sense - that context would always be empty as it can only be populated after node creation. The context in question here is the creation context of the node - simply a map that is created upon creation of the top level node created by this navigation nodes assembler. A copy of the context is inherited to the children thus modifications to the context made in different branches are independent from each other. The navigation node creation context was introduced to enable a more advanced node creation concept: loops.

Loops

Imagine the customer from the previous section has several accounts and you would like to display each account within an individual submodule. This cannot be achieved by the static configuration means we have seen so far, we need something more dynamic here:

<module
      label="Customer ${riena.navigation.parameter:name}"
      typeId="com.acme.customer.edit.module">
   <submodule 
      typeId="com.acme.customer.edit.base"
      controller="..."
      view="..."/>
   <foreach element="account" in="${riena.navigation.parameter:accounts}">
      <submodule
         label="Account ${riena.navigation.nodecontext:account.accountNo}"
         typeId="com.acme.customer.edit.account"
         instanceId="${riena.navigation.nodecontext:account.accountNo}"
         controller="..."
         view="..."/>
   </foreach>
</module>

This assumes the customer object we provided as a navigation parameter has an accounts property that returns a list or an array of account objects, each having an accountNo property. For the sake of simplicity it also assumes that the account numbers uniquely identify an account.

But what does the 'foreach' declaration above exactly do? Well, it simply iterates all accounts of the customer object provided as a navigation parameter by the user and

  1. creates a new context derived from the module context for each account and stores the current loop object under the key 'account' in it.
  2. creates a submodule for each account using the unique account number as navigation node instance id (and also showing it in the submodule title label)

Element Reference

Element Reference

Back to the top