Table of Contents
This chapter explains how to incorporate mouse and keyboard input into an OpenLaszlo application. It covers the basic concepts as well as more advanced topics such as tracking the mouse across multiple views while the mouse is down, and how to implement the "right click" context menu in OpenLaszlo applications.
Interaction with a view via the mouse is fundamental to all applications. These sections will explore the basic concepts as well some more advanced topics.
Interacting with the example below displays the five basic mouse events that can be
recognized by a view: onmouseover
, onmouseout
,
onmousedown
, onmouseup
, and
onclick
Although onmouseup
and onclick
are similar,
onmouseup
is sent whenever the user lets up on the mouse, while
onclick
is only sent when the user lets up on the mouse while the
cursor is still contained within the boundary of the view.
Example 31.1. The basic mouse events
<canvas
height
="40
" width
="100%
">
<view
bgcolor
="red
" onmouseover
="txt.setAttribute('text', 'onmouseover')
" onmouseout
="txt.setAttribute('text', 'onmouseout')
" onmousedown
="txt.setAttribute('text', 'onmousedown')
" onmouseup
="txt.setAttribute('text', 'onmouseup')
" onclick
="txt.setAttribute('text', 'onmouseup, onclick')
" x
="10
" y
="10
" width
="20
" height
="20
"/>
<text
id
="txt
" x
="40
" y
="11
" width
="150
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
A view will only respond to mouse events when its attribute
clickable="true"
. Views, by default, are not clickable. If,
however, any of the mouse events are included in a view's tag (like above) then
clickable
will be set to true automatically. In the example below,
there are no mouse events declared within the tag itself, only in its methods. In this
case it becomes necessary to explicitly define clickable="true"
for
the view.
Example 31.2. Making views clickable
<canvas
height
="40
" width
="100%
">
<view
bgcolor
="red
" clickable
="true
" x
="10
" y
="10
" width
="20
" height
="20
">
<handler
name
="onmousedown
">
this.setAttribute('width', 30);
this.setAttribute('height', 30);
this.setAttribute('x', 5);
this.setAttribute('y', 5);
</handler
>
<handler
name
="onmouseup
">
this.setAttribute('width', 20);
this.setAttribute('height', 20);
this.setAttribute('x', 10);
this.setAttribute('y', 10);
</handler
>
</view
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Setting clickregion
to a vector-based SWF turns the SWF shape
into a clickable hotspot.
Example 31.3. Setting a clickregion
<canvas
width
="100%
" height
="300
">
<resource
name
="waitcursor
" src
="resources/lzwaitcursor_rsc.swf
"/>
<view
height
="150
" width
="100
" bgcolor
="red
" clickable
="true
" resource
="waitcursor
" stretches
="both
" clickregion
="waitcursor
" onclick
="message.addText('\nclick')
"/>
<text
id
="message
" y
="140
" multiline
="true
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2001-2008,2010 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
The cursor automatically changes to a hand when it is over a clickable view, but if
a custom cursor is desired, then this can be accomplished by defining a custom resource
for the cursor and using it with lz.Cursor
service as shown below.
Example 31.4. Changing the cursor
<canvas
height
="30
" width
="100%
">
<resource
name
="waitcursor
" src
="resources/lzwaitcursor_rsc.swf
"/>
<button
onclick
="setCursor()
" text
="Click me to change the cursor for 1/2 second
">
<method
name
="setCursor
" args
="cursor=null
">
lz.Cursor.setCursorGlobal('waitcursor');
// call lz.Cursor.restoreCursor() after 1/2 second
var del= new LzDelegate(lz.Cursor, 'restoreCursor');
lz.Timer.addTimer(del, 500);
</method
>
</button
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
By setting lz.Cursor.showHandCursor()
to true or false, you
show or hide the hand cursor for all clickable views. This means that you can, for
example, prevent clicks from traveling through windows, without the hand cursor
appearing.
Here is an example showing the hand cursor turned on and off.
Example 31.5. Enabling and disabling the hand cursor
<canvas
width
="100%
" height
="110
">
<view
width
="100
" height
="100
" bgcolor
="green
">
<handler
name
="onclick
">
lz.Cursor.showHandCursor(true);
</handler
>
</view
>
<view
x
="110
" width
="100
" height
="100
" bgcolor
="yellow
">
<handler
name
="onmouseover
">
lz.Cursor.showHandCursor(true);
</handler
>
<handler
name
="onmouseout
">
lz.Cursor.showHandCursor(false);
</handler
>
</view
>
<view
x
="220
" width
="100
" height
="100
" bgcolor
="red
">
<handler
name
="onclick
">
lz.Cursor.showHandCursor(false);
</handler
>
</view
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
With OpenLaszlo 4, you can set custom cursors in DHTML and SWF. If you plan to use custom cursors across DHTML and SWF, be sure you're using appropriate cursor IDs for DHTML (see http://www.quirksmode.org/css/cursor.html for more info), and make sure you've included resources named after those IDs for SWF.
Note | |
---|---|
Cursor IDs with hypens work fine in DHTML but are not valid SWF names. For example, you should change the cursor ID col-resize, to colResize. |
Note that global cursors currently only work for clickable items in DHTML.
Example 31.6. Using custom cursors
<canvas
width
="100%
" height
="260
">
<resource
name
="crosshair
" src
="resources/cursor_crosshair.png
"/>
<resource
name
="wait
" src
="resources/cursor_wait.png
"/>
<simplelayout
spacing
="10
"/>
<button
onclick
="lz.Cursor.showHandCursor(true)
">Change default cursor for all buttons to hand
(on by default)
</button
>
<button
onclick
="lz.Cursor.showHandCursor(false)
">Change default cursor for all buttons to
default
</button
>
<button
onclick
="lz.Cursor.setCursorGlobal('crosshair')
">Turn on crosshairs
</button
>
<button
onclick
="lz.Cursor.unlock()
">Turn off crosshairs
</button
>
<view
bgcolor
="red
" showhandcursor
="false
" clickable
="true
">
<text
>showhandcursor is false
</text
>
</view
>
<view
bgcolor
="green
" showhandcursor
="true
" clickable
="true
">
<text
>showhandcursor is true
</text
>
</view
>
<view
bgcolor
="yellow
" cursor
="wait
" clickable
="true
">
<text
>cursor is 'wait'
</text
>
</view
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2001-2009 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Creating a custom button that changes its images with onmouseup, onmouseover, and
onmousedown is a common practice. Here is an example using the <basebutton>
tag:
Example 31.7. Creating a custom button using basebutton
<canvas
height
="150
" width
="100%
">
<!-- first create the multi-frame resource and give it a name -->
<resource
name
="mybutton_rsrc
">
<!-- first frame MUST be the mouseup state of the button -->
<frame
src
="resources/button-up.png
"/>
<!-- second frame MUST be the mouseover state of the button -->
<frame
src
="resources/button-over.png
"/>
<!-- third frame MUST be the mousedown state of the button -->
<frame
src
="resources/button-down.png
"/>
</resource
>
<!-- Second, assign the resource to a basebutton tag -->
<basebutton
resource
="mybutton_rsrc
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Dragging a view can be accomplished with the use of a dragstate
.
When a dragstate
is applied, views can be dragged freely or
constrained to a boundary as well as an axis. The example below shows the use of a
dragstate
with its max
, min
,
and axis
attributes.
Example 31.8. Dragging a view
<canvas
height
="120
" width
="100%
">
<simplelayout
spacing
="3
"/>
<text
>dragging within a bounded area
</text
>
<view
bgcolor
="yellow
" height
="40
" width
="160
">
<view
bgcolor
="red
" width
="20
" height
="20
" onmousedown
="this.dragger.apply()
" onmouseup
="this.dragger.remove()
">
<dragstate
name
="dragger
" drag_min_x
="0
" drag_max_x
="$once{parent.width - this.width}
" drag_min_y
="0
" drag_max_y
="$once{parent.height - this.height}
" drag_axis
="both
"/>
</view
>
</view
>
<text
>dragging along the x-axis
</text
>
<view
bgcolor
="yellow
" height
="40
" width
="160
">
<view
bgcolor
="red
" width
="20
" height
="20
" onmousedown
="this.dragger.apply()
" onmouseup
="this.dragger.remove()
">
<dragstate
name
="dragger
" drag_min_x
="0
" drag_max_x
="$once{parent.width - this.width}
" drag_min_y
="0
" drag_max_y
="$once{parent.height - this.height}
" drag_axis
="x
"/>
</view
>
</view
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
This simple program shows how to do basic mouse tracking in a view:
Example 31.9. Simple mouse tracking
<canvas
height
="500
" width
="100%
" debug
="true
">
<debug
y
="300
"/>
<view
width
="300
" height
="300
" bgcolor
="red
" clickable
="true
">
<attribute
name
="moustracker_del
" value
="$once{ new LzDelegate( this, 'trackmouse' )}
"/>
<handler
name
="onmousedown
">
moustracker_del.register(lz.Idle,'onidle');
</handler
>
<handler
name
="onmouseup
">
moustracker_del.unregisterAll();
</handler
>
<method
name
="trackmouse
" args
="v
">
Debug.debug("%s %d %d", "mousex, mousey:", this.getMouse('x'),this.getMouse('y'));
</method
>
</view
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
This generates a lot of points, but you can decide how much data to filter or not in the trackmouse method.
Once the mouse is down in relation to a specific view,
onmouseover
and onmouseout
events are only sent to
that view. This makes it difficult to track the mouse over other views. This section
explains how to accomplish this, first by using a base component called
<basetrackgroup>
, and then with the low level APIs
that basetrackgroup is built upon.
In the example below, notice that the onmouseover
and
onmouseout
events change the size of the view while the
onmousetrackover
and onmousetrackout
events
change the color of the view. As you drag the mouse, the first view will continue to
change its size and color while the other views will only change their color. Again,
this is because the basic onmouseover
and
onmouseout
events are only being sent to the first view that was
clicked. However, onmousetrackover
and
onmousetrackout
are sent to all views while the mouse is down.
Example 31.10. Using basetrackgroup
<canvas
height
="160
" width
="100%
">
<class
name
="myTrackableView
" bgcolor
="red
" onmouseover
="setAttribute('width', 70)
" onmouseout
="setAttribute('width', 60)
" onmouseup
="setAttribute('bgcolor', 0xFF0000)
" width
="60
" height
="30
">
<!-- Use methods for mouse-tracking events -->
<handler
name
="onmousetrackover
">
setAttribute('bgcolor', 0x0000FF); //blue
</handler
>
<handler
name
="onmousetrackout
">
setAttribute('bgcolor', 0xFF0000); //red
</handler
>
<handler
name
="onmousetrackup
">
setAttribute('bgcolor', 0xFF0000); //red
</handler
>
</class
>
<text
>Click on any red view and drag the mouse.
</text
>
<basetrackgroup
bgcolor
="yellow
" x
="20
" y
="40
">
<myTrackableView
/>
<myTrackableView
/>
<myTrackableView
/>
<simplelayout
axis
="y
" spacing
="5
"/>
</basetrackgroup
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
The default canvas context (right click) menu has a "view source" item, available in Flash player Version 7 and later. By default this menu includes an "About OpenLaszlo" entry, which links to the OpenLaszlo website, and an option to view source.
For an application framitz.lzx
that is deployed proxied (using the
OpenLaszlo Server), clicking on View Source will fetch
framitz.lzx?lzt=source
in a new browser window. If you don't wish to
make source visible, set allowRequestSOURCE=false
in
WEB-INF/lps/config/lps.properties
.
Starting with Flash 8, you can control the content of the menu using the class lz.contextmenu
, which you instantiate using new
lz.contextmenu()
. The method lz.contextmenu.makeMenuItem ()
instantiates an
lz.contextmenuitem
.
To make a new menu, first you create the menu, then you create a menu item, and then
you add the menu item to the menu. The data type of the menu item is
lz.contextmenuitem
; you supply the text that you want to appear in
the menu (the word 'Delete' may not be used as context menu text) and a delegate that
specifies how you want that menu item to be handled. lz.contextmenu.addItem()
adds an item to that menu.
So, to create a new menu you would do:
Example 31.11. Creating a context menu
<canvas
height
="200
" width
="100%
">
<view
width
="350
" height
="100
" bgcolor
="#cccccc
">
<text
text
="Right click in the gray area (but not on this text)
"/>
<handler
name
="oninit
">
var cmenu = new lz.contextmenu(); // create the menu
var item1 = cmenu.makeMenuItem('hello', new LzDelegate(this, "handlerightclick")); // create the menu item, and set up an LzDelegate as a callback
cmenu.addItem(item1); //add the item to the menu
this.setAttribute("contextmenu", cmenu);
</handler
>
<method
name
="handlerightclick
" args
="val
">
message.addText("\nhello world");
</method
>
</view
>
<text
id
="message
" y
="100
" multiline
="true
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
There is no method to clear the menu (Settings and About Flash Player are always there), but you can erase one with something like
var cmenu = new lz.contextmenu(); canvas.setDefaultContextMenu(cmenu)
That would give you an empty context menu.
SOLO deployed applications, say, framitz.lzx.swf
,
will attempt to fetch "framitz.lzx.swf.zip"
.This assumes that a copy of
the source will be placed there if you desire to make your source visible. If you don't
wish to share code of your SOLO application, use the method above.
In OpenLaszlo applications, the keyboard input can be accepted by the view which has
the keyboard "focus". The lz.Focus
service allows you to set and control the
focus. The opposite of focus is "blur", as discussed below.
A focusable view is a view whose focusable
attribute is true (the
default value). When the user clicks on a focusable view, the focus is set to that view.
If the view is different from the previously focused view, an onblur
is sent to the previously focused view, and an onfocus
is sent to the newly focused view.
To prevent a view from taking keyboard input, you can set its
focusable
attribute to "false".
Focus trap: A view's focus trap is its most direct
ancestor whose focustrap
attribute is true, or the
canvas. A view's focus group is the set of focusable views that are
descendants of the focus trap, and are not descendants of any more immediate ancestor
whose focustrap
attribute is also true.
The effect of this definition is that tabbing within the children of a view that has
a true focustrap
, such as a window, will tab
to another child of the same view, but won't tab to a view within another window.
The Contacts demo demonstrates the use of keyboard focus. Double-click on a name to open the details window. Click in a text, and then press tab and shift-tab to move to other text fields within the same focus group.
When the tab key is pressed, the focus moves to the next focusable view within the current focus group. If there is no next view, the first view within the current focus group is selected. Similarly, shift-tab selects the previous focusable view. If there is no previous view, the last view within the current focus group is selected.
A view can override the getNextSelection()
and getPrevSelection()
methods to change this
default behavior. The tab and shift tab keys select the view returned by calling getNextSelection()
and getPrevSelection()
, respectively, on the view
that currently has the keyboard focus.
One way of integrating keyboard input into your application is to have a focusable view capture pressed key codes. Only one view can be focusable at any one time. In this example, we're making that view focused oninit (although in a larger application, you could bring focus to that view using the tab key, or you could write an onclick method to tell it to focus).
The example below illustrates use of <lz.Focus>
to make a view
able to accept keyboard input.
Note | |
---|---|
The actual character that corresponds to a particular key depends on the operating
system and locale, so the character returned by String.fromCharCode()
may be different on your system.
|
Example 31.12. Reading key values
<canvas
width
="100%
" height
="200
" debug
="true
">
<debug
x
="110
" y
="15
"/>
<!-- NOTE: Even though the view below takes focus oninit,
in some browsers you might still need to click on the
application itself, to take the focus away from the
browser itself and to the Flash Player. -->
<view
width
="100
" height
="100
" bgcolor
="0x333399
" focusable
="true
" oninit
="lz.Focus.setFocus(this)
">
<handler
name
="oninit
">
this.keyCodes = new Array()
this.keyCodes[65] = "A for Apple";
this.keyCodes[76] = "L for Laszlo";
this.keyCodes[79] = "O for Optometrist";
this.keyCodes[83] = "S for Sammy";
this.keyCodes[90] = "Z for Zebra";
</handler
>
<handler
name
="onkeydown
" args
="akeycode
">
// respond here
Debug.debug("Key pressed: %w", akeycode);
if ( this.keyCodes[akeycode] != undefined ) {
Debug.debug("%s", this.keyCodes[akeycode] );
} else {
Debug.debug("You pressed: %w", String.fromCharCode(akeycode));
}
</handler
>
</view
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Copyright © 2002-2010 Laszlo Systems, Inc. All Rights Reserved. Unauthorized use, duplication or distribution is strictly prohibited. This is the proprietary information of Laszlo Systems, Inc. Use is subject to license terms.