Working with Embedded Wizard: Assembling the application
In the preceding chapters you have learned how to implement new and adapt existing GUI components. You have seen, that the complete workflow with Embedded Wizard is component-oriented. Accordingly, you have to divide your GUI project in individual GUI components. Then you start with the most simple components and implement them. Having them finished you assemble from the simple components more complex GUI components. These steps you repeat until you have composed the application component itself.
Application component
The application component, as the name reveals, represents the entire GUI application. Its content corresponds to what you see on the display in your device. When you add a view (or another GUI component) to the application component, this view appears on the display implicitly. Considering a GUI application as composed from interleaved, nested view objects, the application component serves as the root of this tree-like structure. Therefore, we tend to call the application component also the root object (please see Understand the view tree and the Owner relationships).
From technical point of view, the application component is an ordinary GUI component enhanced by an additional interface permitting its integration within the main software in your device. Through the interface the application component is feed with user input events received in the main software from touch screen or keyboard driver and it has the necessary access to the display in the device. Being the root of the view tree, the application component manages all actually existing GUI components, provides them with the received user input events and takes care of screen updates.
This is possible, because application components are derived from the Mosaic class Core::Root, which, in turn, descends from the class Core::Group serving as the base class for all GUI components. Thus an application component inherits the functionality the ordinary GUI components support per default. In other words, the application component is just another GUI component.
Add a new empty application component
Every Embedded Wizard project has to contain at least one application component. All example projects and project templates provided with Embedded Wizard do contain already such obligatory component. Thus when you create a new project, you will find there the right application component to start the development.
If necessary, however, you can add to your project more additional application components. This can, e.g. be useful when you intend to develop several variants of your product with different capabilities, supporting different screen resolutions, etc. In this manner you can manage for every variant a separate application component. The steps how to add a new application component are described below:
★First switch to the Composer page for the respective unit member, where you want to add the new component.
★Then, in the Gallery window, ensure that the folder Component Templates is opened.
★Look in the folder for the template named Application.
★With the mouse, select the template and drag it into the Composer.
★Drop the template within the Composer.
★Eventually name the new added component.
Open the application component for editing
In order to edit the application component you have to open the class representing the component. For this purpose:
★First ensure, that the component is selected.
★Press the key ENTER ...
★... or double click on the component with the mouse.
Thereupon the content of the component is revealed in a new Composer page. Similarly to when you edit an ordinary GUI component, you see in the Composer window the Canvas area. It is surrounded by the thick blue border. You can consider this area as the part, which is visible on the display at the runtime of your application. Adding new views to the Canvas area results thus in the views being visible on the display in your device. For a new created application component, the Canvas area appears empty filled only with a checkerboard pattern representing the actual transparent screen background:
Adjust the size of the application component
Before editing the application component, you should first adjust the size of its Canvas area so that it matches the actual resolution of the display used in your final product. For example, if you design a product with a display having 320x240 pixel you should resize the Canvas of the application component to also be 320x240 pixel large.
Please note, that the current screen resolution should also be configured in the attribute ScreenSize of the profile used for prototyping and code generation. Both, the size of Canvas area in the application component and the value of the attribute ScreenSize should correspond to the real resolution of the LCD in your device.
Edit the contents of the application component
Editing the application component is done in the same way as you have already learned in the preceding chapters with ordinary GUI components. In particular you can drag-and-drop views, widgets and other already existing GUI components to the Canvas area of the application component and so compose its appearance. The article Compositing component appearance describes the steps in context of an ordinary GUI component.
If desired you can compose the application component also programmatically by implementing code to create and add instances of views and GUI components to the application component at the runtime just in the moment your application it requires. Similarly, you can enumerate and search for views existing within the application component.
As with ordinary GUI components, the areas right and below the Canvas are intended to accommodate all other non graphical project members necessary for the function of the application. Usually, you implement here the interface allowing other components to exchange information with the application component. In this area you can also implement the functionality to manage the component state.
If desired, you can also add touch handlers and keyboard handlers to your application component. It is very common to do this for the top-level navigation. Here the application component implements all handlers permitting the user to navigate between the main parts of the application. All other handlers are found within the individual GUI components.
Finally, you can manage animation effects within the application component. For example, you can run a fade-in/out animation when the user navigates and switches between the different main parts of the application.
Application component background
When you create a new application component, you see the Canvas area filled with the checkerboard pattern. This pattern indicates, that the affected component is actually empty and thus transparent. Unlike the regular, ordinary GUI components, it doesn't make sense to have transparency in the application component. Except cases where there is another content displayed behind the GUI application (like a separate layer with video or camera image), the application component should be completely opaque.
Just put a Filled Rectangle view to the application component and arrange it so that it covers the complete area of the component. The rectangle will serve as the component background. More sophisticated is to use the Image or the Wallpaper view to determine the background. Which views you select depends finally on the visual effect you want to achieve. If the GUI components embedded within the application component are already fully opaque, you can omit the usage of additional views to determine the application background. Finally, the background is already determined by the embedded components.
In the case, you have additional video or camera contents in separate layers, you will need to configure your hardware, so that the video/camera layer is arranged behind the GUI layer. Leaving a transparent gap within the GUI allows you to display the video or camera contents together with the GUI application.
Select the application component in profile
To be complete, every Embedded Wizard project has to contain at least one application component. This application component has to be selected in the attribute ApplicationClass in the profile used for prototyping and code generation.
Having several profiles and several application components you can manage within one project multiple product variants. For example, one variant for a display with 640x480 resolution and another for a display with 320x240 pixel. Depending on the profile selection, Embedded Wizard uses then the right application component.
Access the root object
As described above, the application component represents the entire screen area. Adding to it a new GUI component results in this component appearing on the screen. It is thus common to manage all top level GUI components like alert panels, etc. as views embedded directly within the application component. Doing this however in context of a component other than the application component itself requires first to obtain access to the instance of the application component, to the root object.
You do this by using the built-in variable rootthis. Let's assume, there is a GUI component actually visible on the screen and this component wants to display an alert panel component with an important warning. Since such critical alert panel should cover all other screen contents, it is the simplest to add it directly to the application component. The following code demonstrates how the original component accesses the root object and adds to it a new instance of the alert panel component:
// Obtain access to the root object. var Core::Root rootObject = rootthis; // Create a new instance of some alert panel component. var Application::AlertPanel alertPanel = new Application::AlertPanel; // Configure the alert component eventually. Specify the message, etc. alertPanel.Caption = "WARNING"; alertPanel.Message = "Do you really want to delete the file?"; // Add the alert component directly to the application component. // Thereupon the alert panel will appear on the screen. rootObject.Add( alertPanel, 0 );
Once you have access to the root object, you can act on it in the same way as you do with an ordinary component. The documentation sections Compose the component programmatically and Enumerate and search views existing within the component describe methods you can use in every GUI component to modify and evaluate its contents. Accordingly, the functionality described in the both sections is also applicable to the application component. Using the methods you can, for example, verify whether there are particular GUI components existing actually within the application component, and if this is the case, you can e.g. remove and hide them:
// Obtain access to the root object. var Core::Root rootObject = rootthis; // By using the method 'FindNextView()' search within the application // component for a first visible view var Core::View view = rootObject.FindNextView( null, Core::ViewState[ Visible ]); // Now repeat until all views have been enumerated. while ( view != null ) { // Try to cast the view to the 'Application::AlertPanel' class. var Application::AlertPanel alertPanel = (Application::AlertPanel)view; // Then search for the next following view view = rootObject.FindNextView( view, Core::ViewState[ Visible ]); // If the typecast was successful (the 'view' is an alert panel), remove // the view from the application if ( alertPanel != null ) rootObject.Remove( alertPanel ); }
The effect of the above code sections is demonstrated in the example below. It implements a simple alert panel you can activate by touching a push button. When you touch the button twice, you get two alert panels. With a second button you can close all actually existing alert panels. Every alert panel has also a close button with it you close the panel individually:
Please note, the example presents eventually features available as of version 12.00
Please note, before Embedded Wizard 12, the established approach to obtain an access to the root object was the method GetRoot(). As long as the method was invoked in context of a GUI component belonging already to the view tree, the approach worked as expected. It was however not very convenient and could lead to errors with objects not belonging to the view tree. In Embedded Wizard 12 (and newer) you can use both: the built-in variable rootthis and the Mosaic method GetRoot.
Modal GUI components
Usually the application component dispatches the incoming user input events to all GUI components existing actually within the view tree. When the user touches the screen, the next available touch handler at the touched screen position is activated. Similarly pressing a key on the keyboard feeds one of the key press handlers lying actually on the focus path. Under normal circumstances there is no restriction how the events are dispatched.
In particular circumstances, however, it is essential to temporarily block the GUI components from being able to react to user inputs until the user has finished the actual interaction. Imagine an alert panel. Such panels appear on the screen to inform the user about an important, eventual critical situation. As long as the user has not closed the alert, the system should ignore all user interactions performed in context of GUI components other than the affected alert panel. Only the alert panel should be able to react to the user inputs.
With this measure, the system prevents the application from entering in an inconsistent state. Let's assume, the user starts the hard-disc format operation. During this operation the GUI shows an alert informing that the operation is actually in progress. In the meantime the user can't interact with the application. It doesn't make sense to allow the user to press buttons which then eventually trigger operations to access the file system and create files while we are actually deleting the hard-disc. In contrary, allowing such operation would end in a file system damage.
Such privileged GUI components, which are exclusively receiving user inputs while the remaining part of the GUI application is blocked are called modal. Having the possibility to make a GUI component modal is a great feature. You can imagine a multi-level menu system, the user can navigate inside, enter one menu level, then enter the next level, and so far. Managing the individual menu levels as GUI components with modal state simplifies the entire implementation and implicitly guarantees, that the user focuses on the actual menu level, means the user can interact with the actual modal menu GUI component only.
The following table provides you an overview of the methods available in the application component to control the modal states:
Method |
Description |
---|---|
Initiates the modal state for the given GUI component aGroup. This forces the application component to limit all user inputs to this component and its sub-views only. |
|
Terminates the modal state for the given GUI component aGroup. This restores the origin flow of user input events. |
|
Returns the current modal GUI component. This is the component the application is actually providing with user input events. |
The following Chora code demonstrates the typical use case for the BeginModal() method. Here we create dynamically a new instance of an alert panel component, configure it with all the contents to display and we add it to the application component. At the end the invocation of BeginModal() method ensures, that the just added alert is the unique receiver for user input events:
// Obtain access to the root object. var Core::Root rootObject = rootthis; // Create a new instance of some alert panel component. var Application::AlertPanel alertPanel = new Application::AlertPanel; // Configure the alert component eventually. Specify the message, etc. alertPanel.Caption = "WARNING"; alertPanel.Message = "Do you really want to delete the file?"; // Add the alert component directly to the application component. // Thereupon the alert panel will appear on the screen. rootObject.Add( alertPanel, 0 ); // And finally make the alert panel modal rootObject.BeginModal( alertPanel );
As soon as the application decides to close the alert panel (e.g. after the user has touched the panel's close button), the modal state for the alert panel is revoked and the panel is removed from the application component. To revoke the modal state we use the method EndModal():
// The affected alert component, which is actually modal var Application::AlertPanel alertPanel = ... // Obtain access to the root object. var Core::Root rootObject = rootthis; // Terminate the modal state for the given alert component rootObject.EndModal( alertPanel ); // ... and remove the alert panel from the view tree so it disappears rootObject.Remove( alertPanel );
If you call BeginModal() while there is already another modal component active, the system will put this old component on an internal modal stack and redirect all user input events to the new component. From now, all events are processed by the new modal component while the previous modal component does not receive any events. Calling EndModal() restores the previous situation again. Here the system sees, that there is a component remembered on the modal stack, and restores its modal state.
In principle, you can call BeginModal() as often as you need. Usually you do this, however, when the user enters a new interaction level, e.g. a new menu level. When the user wants to leave the actual level again you call the method EndModal(). This restores the preceding state automatically. Under normal circumstances you don't need to worry about the mechanisms behind it.
Sometimes it is useful to know which component is actually modal. The application component provides for this purpose the method GetModalGroup(). Let's assume, in your application the user can interact with nested alert panels. This means, pressing a button within one panel opens another panel where the user again can press buttons entering so further panels. At fin, when the user activates the button in the last panel all previously opened panels should disappear at once. The following code implements this application case. It uses the method GetModalGroup() to access the actual modal component and close it. Then it repeats with the next modal component until al components are closed:
// Obtain access to the application component var Core::Root rootObject = rootthis; // Get the actual modal component var Core::Group modalGroup = rootObject.GetModalGroup(); // Repeat until all modal components are closed. while ( modalGroup != null ) { // End the modal state for the actual modal component and remove it from // the application component so it disappears. rootObject.EndModal( modalGroup ); rootObject.Remove( modalGroup ); // Get the component which is modal now. modalGroup = rootObject.GetModalGroup(); }
The following example project demonstrates the usage of the above described methods BeginModal(), EndModal() and GetModalGroup(). In this example, the user navigates between nested alert panels asking the user to confirm the deletion of a an imaginary file. With the last alert panel, the delete operation is executed and all alert panels disappear at once:
Please note, the example presents eventually features available as of version 12.00
Switching between dialogs (managing multiple screens)
Except very primitive cases, most GUI applications consist of more than one screen: it means, they contain several components, the user switches forth and back during the dialog with the GUI application. Embedded Wizard provides sophisticated functions how to implement such multiple screen transitions including animations, modal popups nested on top of each other and much more. The chapter Managing dialogs (multiple screens) explains the related technique aspects in detail.
TIP
If your application case requires only few top-level full screen dialogs to be switched forth and back and the associated transitions can occur instantly (without animations) you can skip to the end of the above mentioned chapter and read the section Simplified approach to handle top-level dialogs (switching screens).