Views

In the PureWeb SDK, views provide remote client applications with a window into the visible elements of a service. They are a combination of images and keyboard/mouse input:

  • Images: images displayed in views can be almost anything you want; they usually represent all or a portion of the service's interface. You can display as many views simultaneously on the screen as you need, and you can expose different views in different clients. For example, the illustration below shows a medical application with four separate views on the same screen, all independent of each other.


    All the heavy computation for rendering the graphics is done by the service, which contributes to keeping the client very thin. The data used to generate the image remains securely in its source location, alleviating any security concerns. None of the images displayed as views persist on the client after the PureWeb session ends.

    In collaboration scenarios, when multiple clients are connected to a single service, the views on that service are available to all clients. Each client gets a copy of each view and can interact with each view independently.

  • Keyboard/Mouse input: views contain the necessary logic to automatically capture all user keyboard and mouse input events, this is how the SDK handles the interactive visualization of graphics-intensive applications. The SDK also allows you to convert touch-screen input to keyboard events, to support graphics interactivity on mobile devices.

Views are an important part of the user interface for PureWeb clients. But you can also add other native interface elements (buttons, checkboxes, drop-down lists, etc.) to complete this interface. Views and other client interface elements are linked to the service-side application logic using commands and application state.

View APIs

On the service, views are generated using the IRenderedView interface. This interface works in conjunction with the paint functionality of the native development platform to generate the views.

On the client, a simple View object is used to display the views which have been remoted by the service.

The ViewManager interface provides methods for managing views. It contains, in particular, methods such as RenderViewImmediate and RenderViewDeferred for monitoring when a view's content has changed and a new render is required.

Each view is assigned a unique name and registered with the StateManager class, a class that handles much of the behind-the-scene code that makes PureWeb applications work smoothly. When referring to a view, both the service and the client must use that view's unique name.

User Input Events

Client-side views automatically capture all mouse and keyboard input, transmit those to the service, and replay them as method calls in the IRenderedView implementation, using the PostMouseEvent and PostKeyEvent handlers. It is then up to you to provide the content for these handlers in order to map them to input events within the service application. How you accomplish this depends on the framework that the service application uses. Touch-screen input can be handled by sending commands, or by mapping the input to PureWeb API mouse or keyboard events. For instructions and code examples, see User Input Events.

Image Encoding

An important aspect of views is the imaging pipeline. When a user interacts with a view, the service transmits dozens of updated images to the client. The SDK handles the hard part of the imaging pipeline for you, so you don’t have to worry about writing complex code for image encoding, quality, and transmission rate. However, you do have control and can use PureWeb API features to easily fine-tune the image type and quality of the graphics displayed in your views to optimize performance and bandwidth usage.

In addition, the ViewManager interface also provides SetViewInteracting, for sending images of different quality when the user is interacting with the view; this is discussed in more detail in Setting the Image Interactivity Mode.

The IRenderedView Interface

The main interface for implementing views is the service-side IRenderedView interface (depending on the service programming language that you are working with, it may be called RenderedView). This interface makes calls to the native functionality of your service development platform to paint and set the dimensions of the view. It also contains functions that capture keyboard and mouse input from the end user, which makes it easier for you to map these events to the service-side functionality.

Function Description
setClientSize Specifies the desired rendering size of a view in pixels.
getActualSize Returns the actual size of the client view.
renderView Handles the actual rendering of the view. It is called by the PureWeb server when requesting an updated image.
postKeyEvent Communicates incoming keyboard events from the client, making them available to the service application.
postMouseEvent Communicates incoming mouse events from the client, making them available to the service application.

The code example in the section Remoting a Service View, further down, illustrates a basic implementation of this interface.

Generic View Handling Classes

There are two major approaches to implementing IRenderedView:

  • Implement it directly in each class where a view must be rendered. This method may work when the number of views is low, but in most cases, it is not the best option.
  • Create a generic PureWeb view handling class. This is more efficient when there are several views.

Examples of generic classes can be found in the sample applications. In the Java sample service, it is called RemotedPanel.java; in the C# sample service, it is called RemotedControl.cs. See Path to Sample Code.

We encourage developers to use or reference these generic classes in their own projects, and we recommend using the adapter design pattern.

Registering Views

Each view must be given a unique name; this is the name that both the service and client applications must use when referring to the view. This name must then be registered with the StateManager. Views will not attach properly if a unique name is not used.

In order to communicate with the StateManager, views must inherit from the IRenderedView interface. If it is not possible or convenient for view classes to inherit directly from this interface, use an adapter pattern.

Where a view is registered depends on whether or not the IRenderedView interface is implemented in a generalized view handling class.

  • If a generic view handling class is not used, register the view where desired within the view class – that may be the constructor of the class or another initializing function.
  • If a generic view class is used, register the view upon construction of the view handling class – this ensures that the lifespan for this handler class equals the registration period of the view it handles.

In either case, the command to register the view is the same:

C++

CScribbleApp::StateManager().ViewManager().RegisterView("MyView", this);

.Net

Program.StateManager.ViewManager.RegisterView(“MyView”, this);

Java

stateManager.getViewManager().registerView(“MyView”, this);

Remoting a Service View

Since service applications are responsible for generating and processing the views, most of the effort involved in remoting a view comes down to implementing the service-side IRenderedView interface.

Although the view is generated by the service, it is displayed on the client. For this reason, the service-side renderView function, which is actually responsible for rendering the image, makes a call to the native paint functionality of the client development platform that you are working with. In a similar fashion, the API's getActualSize and setClientSize functions simply make use of the client platform's native elements to set the dimensions of the panel in which the view will be displayed.

Views must provide images to the RenderView method of IRenderedView. RenderView writes to the array provided by RenderTarget.RenderTargetImage().ImageBytes(). This passes the image into the PureWeb SDK's imaging pipeline for processing (compression and optimization) so that it can automatically be decoded in the client.

Below is a C# example of an implementation of the IRenderedView interface in its most basic form. The PostKeyEvent and PostMouseEvent functions have been intentionally left blank, as their implementation is discussed in the User Input Events section.

.Net

In this example, the RemotedControl is a reduced form of the RemotedControl included in the service-side API for C#. This is an example of a generic view handling class.

The definitions of GetActualSize and SetClientSize simply make use of C#'s control properties to get and set the dimensions of the panel.

public class RemotedControl : UserControl, IRenderedView
{
   IRemoteRenderer m_remoteRenderer;
    string m_viewName;
    bool m_hasPendingRemoteRender;
    Bitmap m_offscreen;
	
    protected override void OnPaint(PaintEventArgs e)
    {
        if (!m_hasPendingRemoteRender)
        {
            m_hasPendingRemoteRender = true;
            Action action = () =>
            {
                m_remoteRenderer.RenderViewDeferred(m_viewName);				
				m_hasPendingRemoteRender = false;
			};
			this.BeginInvoke(action);
        }
		base.OnPaint(e);
    }

	public virtual void SetClientSize(System.Drawing.Size clientSize)
	{
	    if (this.ClientSize != clientSize)
		{
		    this.ClientSize = clientSize;
			this.Invalidate();
		}
	}

	public virtual System.Drawing.Size GetActualSize()
	{
	    return this.ClientSize;
	}

	public virtual void RenderView(RenderTarget target)
	{
	    var image = target.Image;
		try
		{                
		    m_hasPendingRemoteRender = true;
			if (m_offscreen != null)
			{
			    image.DrawUnscaled(m_offscreen);
			}
		}
		finally
		{
		    m_hasPendingRemoteRender = false;
		}
	}


	public void SetOffScreen(Bitmap b)
	{
		if (b != m_offscreen)
		{
		    m_offscreen = b;
			m_remoteRenderer.RenderViewDeferred(m_viewName);
		}
	}

	public void PostKeyEvent(PureWebKeyboardEventArgs keyEvent)
	{
	//See Mapping Keyboard Input
	}

	public void PostMouseEvent(PureWebMouseEventArgs mouseEvent)
	{
	//See Mapping Mouse Input
	}
}		

Java

In this example, the PWPanel is a reduced form of the RemotedPanel included in the service-side API for Java. This is an example of a generic view handling class.

The definitions of getActualSize and setClientSize simply make use of the JPanel’s setSize()/getSize() functions to get and set the dimensions of the panel.

public class PWPanel extends JPanel implements RenderedView{
    protected final String viewName;

	public PWPanel(String viewName){
		this.viewName = viewName;
	}
	public void renderView(RenderTarget target){
		paintComponent(target.getImage().getBitmap().getGraphics());
	}
	public Dimension getActualSize(){
		return getSize();
	}
	public void setClientSize(Dimension clientSize){
		if (!getSize().equals(clientSize)){
		    setSize(clientSize);
			>invalidate();
		}
	}
	public void postKeyEvent(PureWebKeyboardEventArgs keyEvent){
		//See Mapping Keyboard Input
					}
	public void postMouseEvent(PureWebMouseEventArgs mouseEvent){
		//See Mapping Mouse Input
	}	

For more advanced example, refer to the code for the sample applications below.

  • C#:
    [installed_location]\SDK\Samples\Scribble\ScribbleAppCsharp\RemotedControl.cs
  • Java:
    [installed_location]\SDK\Samples\Java\src\server\pureweb\samples\RemotedPanel.java

Displaying a Remoted View in a Client

Displaying a service-side view in a client application is a simple operation: you use your client application's native functionality to create a container for the view, then use the View object, available in the client-side API, to add the view where it should be displayed.

Remember that the client merely displays the image that has been remoted by the service. It does not generate or compute the image, and that image does not persist once the client disconnects.

Note also that the view is attached only after the client has an active connection. Typically, a listener for the CONNECTED_CHANGED event will be included alongside the rest of the client initialization code. For a list of other events that your client can listen to, see Useful PureWeb Events.

It is important, when writing the client-side code for displaying views, to use the view's unique name as registered in the state manager, otherwise, the view will not attach to the client.

In the examples below, a view called ScribbleView is added to the client application.

HTML5

<div id="MyViewDiv" class="purewebview" style="width: 100px; height:100px;"></div>

<script type=’text/javascript’>
    pureweb.listen(pureweb.getClient(),
	pureweb.client.WebClient.EventType.CONNECTED_CHANGED, attachViewOnConnect);
	
	var attachViewOnConnect = function(event)
	    {if (event.target.isConnected()) {
		    myView= new pureweb.client.View({id: MyViewDiv', 'ScribbleView': 'MyView'});
		}
	};
</script>			

iOS

Drag and drop a UIView onto your view controller, and change the class to PWView.

Then, in your view controllers, use the viewDidLoad method to attach to the framework and specify the view name.

- (void)viewDidLoad
{
    //connect the pureweb view, this is done just by setting the name property on the view
	self.scribbleView.framework = [PWFramework sharedInstance];
	self.scribbleView.viewName = @"ScribbleView";
}				

Android

The code below inflates a layout, creates the ScribbleView and adds it to the layout.

LayoutInflater li = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
LinearLayout layout = (LinearLayout) li.inflate(R.layout.main, null);
view = new View(ScribbleAppActivity.this, framework);
view.setViewName("ScribbleView");
layout.addView(view);
setContentView(layout);				

Flex

<ui:View id="view" width="100%" height="100%" horizontalCenter="0" viewName="ScribbleView"/>

Updating Images in Views

When it is time to render a new image, views call either the RenderViewImmediate and the RenderViewDeferred methods of the ViewManager interface.

As their name indicates, the main difference in these methods is timing. In one case, the call to the service's RenderView interface is immediate, in the other instance, it is deferred.

Using the deferred method is the most common, as it is less resource-intensive: the service does not need to spend time churning image bits that may not be needed by the client right away. The downside to this method is that you do not control when RenderView does get called, this is handled automatically by the PureWeb SDK.

Deferring the call to RenderView is a service-side decision which does not impact the displaying of views in the client application. Image updates are seamless to the end users.

C++

In the Scribble sample application, the ScribbleView.cpp file contains the code that implements the CScribbleView class. This class contains the business logic of the ScribbleApp service application.

Below is an example of a view update call using RenderViewDeferred:

CScribbleApp::StateManager().ViewManager().RenderViewDeferred("ScribbleView");

And below is an example using RenderViewImmediate:

CScribbleApp::StateManager().ViewManager().RenderViewImmediate("ScribbleView");

.Net

In the Scribble sample application, the RemotedControl.cs file contains the RemotedControl class, a user control that implements remote rendering through the IRenderedView interface.

Below is an example of a view update call using RenderViewDeferred:

m_remoteRenderer.RenderViewDeferred(m_viewName);

And below is an example using RenderViewImmediate:

m_remoteRenderer.RenderViewImmediate(m_viewName);

Java

In the Scribble sample application, the RemotedPanel.java file contains RemotedPanel, which extends the Java Swing class JPanel, and implements the RenderedView interface. RemotedPanel acts as a base class for service application panel classes, supplying updated images to the StateManager to send back to the client application. This is accomplished through the implementation of the renderView method of the RenderedView interface.

The remoteRender method of the RemotedPanel class notifies the remoteRenderer— which is a reference to the ViewManager through the RemoteRenderer interface that it implements—that the image has changed.

Below is an example of a view update call using RenderViewDeferred:

remoteRenderer.renderViewDeferred(viewName);

And below is an example using RenderViewImmediate:

remoteRenderer.renderViewImmediate(viewName);