Canvas event handling: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
(Add keyboard events)
(20 intermediate revisions by 4 users not shown)
Line 1: Line 1:
[[Category:Canvas]]
{{infobox subsystem
|name        = Canvas Event Handling
|started    = 11/2012 (Available since [[Changelog 2.10|FlightGear 2.10]])
|description = DOM like event handling
|developers  = {{usr|TheTom}}
|folders =
* {{flightgear file|src/Canvas/gui_mgr.hxx}}
* {{simgear file|simgear/canvas}}
}}
{{Template:Canvas Navigation}}
{{Template:Canvas Navigation}}


The Canvas event handling system is heavily inspired by the [http://www.w3.org/TR/DOM-Level-3-Events/ DOM Event Model]. If you have already worked with events in HTML and JavaScript most concepts of the Canvas event system should be already familiar to you.
The Canvas event handling system closely follows the W3C [http://www.w3.org/TR/DOM-Level-3-Events/ DOM Event Model]. If you have already used events in JavaScript and HTML most concepts of the Canvas event system should be already familiar to you. The most notable difference is the missing capture phase, but it is usually not used anyhow.
 
Listeners - simple Nasal functions - can be attached to every element inside the Canvas and the Canvas itself. Once a certain action - like moving the mouse or pressing a button - occurs the associated listeners are called. We can use this for example to detect whether the mouse has moved over an element or if a certain element has been clicked. For this you should understand how [[Using_Nasal_functions#Function_closures|closures work]].


= Listen for events =
= Listen for events =
Line 8: Line 18:
To receive events callback function can be added to elements on a Canvas as well as to the Canvas itself:
To receive events callback function can be added to elements on a Canvas as well as to the Canvas itself:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
canvas.addEventListener("<type>", <func>);
canvas.addEventListener("<type>", <func>);
canvas_element.addEventListener("<type>", <func>);
canvas_element.addEventListener("<type>", <func>);
</syntaxhighlight>
</syntaxhighlight>


For each placement of a Canvas handling events can be enabled or disabled. A Canvas placed in a PUI widget or as standalone GUI window receives events by default, whereas Canvases placed onto the aircraft model or in the scenery do not receive any events by default.
For each placement of a Canvas handling events can be enabled or disabled. A Canvas placed in a PUI (old GUI) widget or as standalone GUI window receives events by default, whereas Canvases placed onto the aircraft model or in the scenery do not receive any events by default.
 
{{Note|When using the old [[Howto:Adding_a_canvas_to_a_GUI_dialog|PUI/XML GUI]] with a '''<nowiki><canvas></nowiki>'''-widget placement, PUI does not trigger any mouseover/hover (mousemove) events. Mouse clicks/wheel/drag are working as expected. For all other placements like on standalone Canvas windows and [[Howto:Add_a_2D_canvas_instrument_to_your_aircraft|3D models]] there is no such limitation. If you find that your code doesn't work as expected, make sure to verify that your layers (canvas groups) use matching z-index ordering, or overlapping symbols may prevent event handlers from being triggered/called.}}


For standalone GUI windows setting '''capture-events''' to ''0'' or ''1'' enables or disables handling of events respectively. For a Canvas placed onto a 3d model, setting ''capture-events'' inside the placement can be used to activate event handling:
For standalone GUI windows setting '''capture-events''' to ''0'' or ''1'' enables or disables handling of events respectively. For a Canvas placed onto a 3d model, setting ''capture-events'' inside the placement can be used to activate event handling:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
var dlg = canvas.Window.new([152,74]);
var dlg = canvas.Window.new([152,74]);


Line 27: Line 39:
my_canvas.addPlacement({"node": "PFD-Screen", "capture-events": 1});
my_canvas.addPlacement({"node": "PFD-Screen", "capture-events": 1});
</syntaxhighlight>
</syntaxhighlight>
= Event flow =
Events always are targeted at a specific element inside the Canvas. Before any event handler is called the propagation path for the event is determined. It consists of the event target itself and all its ancestor elements (Groups) up to and including the Canvas. Afterwards - during the ''Target Phase'' - all listeners registered on the event target are called. Finally - during the ''Bubbling Phase'' - the event bubbles up the tree, following the propagation path determined in the first step, and all listeners attached to the according elements are called.
[[File:Canvas Eventflow.png|thumb|540px|center|Event flow of Canvas Events similar to W3C DOM Event flow <ref name="dom-event-flow">DOM Level 3 event flow. http://www.w3.org/TR/DOM-Level-3-Events/#event-flow</ref>.]]


= Event classes =
= Event classes =


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
var Event = {
var Event = {
   # Name of event type [read-only]
   # Name of event type [read-only]
Line 37: Line 55:
   # Target element [read-only]
   # Target element [read-only]
   target: <target-element>,
   target: <target-element>,
  # Element the currently called listener is attached to [read-only]
  currentTarget: <target-element>,


   # Stop further propagation of event (stop
   # Stop further propagation of event (stop
Line 42: Line 63:
   stopPropagation: func()
   stopPropagation: func()
};
};
# Since FG 3.1.0
var DeviceEvent = {
  parents: [Event],
  # State of all keyboard modifiers at the time the event has
  # been triggered [read-only]
  modifiers: <modifier-mask>,
  # [read-only]
  ctrlKey: <was-ctrl-down>,
  # [read-only]
  shiftKey: <was-shift-down>,
  # [read-only]
  altKey: <was-alt-down>,
  # [read-only]
  metaKey: <was-meta-down>
}
# Since FG 3.1.0
var KeyboardEvent = {
  parents: [DeviceEvent],
  # [read-only]
  key: <key-string>,
  # Location of the key on the keyboard [read-only]
  #
  # https://www.w3.org/TR/DOM-Level-3-Events/#events-keyboard-key-location
  #
  #  0 standard location
  #  1 left
  #  2 right
  #  3 numpad
  location: <key-location>,
  # [read-only]
  repeat: <is-repeat>,
  # [read-only]
  charCode: <code>,
  # [read-only]
  keyCode: <code>
}


var MouseEvent = {
var MouseEvent = {
  # [FG >= 3.1.0]
  parents: [DeviceEvent],
  # [FG < 3.1.0]
   parents: [Event],
   parents: [Event],


Line 53: Line 126:
   clientX: <client-x>,
   clientX: <client-x>,
   clientY: <client-y>,
   clientY: <client-y>,
  # Position in local/element coordinates [read-only]
  localX: <local-x>,
  localY: <local-y>,


   # Distance to position of previous event [read-only]
   # Distance to position of previous event [read-only]
Line 60: Line 137:
   # Current click count (number of clicks within a certain
   # Current click count (number of clicks within a certain
   # time limit. max. 3) [read-only]
   # time limit. max. 3) [read-only]
   click_count: <click-count>
   click_count: <click-count>,
 
  # Button which triggered this event [read-only, FG >= 3.1.0]
  #
  #  0: primary button (usually the left button)
  #  1: auxiliary button (usually the middle button/mouse wheel)
  #  2: secondary button (usually the right button)
  button: <button>,
 
  # State of all mouse buttons at the time the event has been
  # triggered [read-only, FG >= 3.1.0]
  buttons: <active-button-mask>
};
};
</syntaxhighlight>
</syntaxhighlight>
== Middle-mouse button emulation ==
2-button mice can be setup to emulate a middle-mouse button click by pressing the left and right button simultaneously. On Ubuntu you can use [https://apps.ubuntu.com/cat/applications/gpointing-device-settings gpointing-device-settings] to enable "middle button emulation"
<ref>https://wiki.ubuntu.com/X/Quirks#A2-button_Mice</ref>
<ref>http://askubuntu.com/questions/140828/how-can-i-paste-text-by-pressing-the-left-and-right-mouse-buttons-at-the-same-ti</ref>.


= Event types =
= Event types =
{| class="wikitable"
{| class="wikitable"
|-
|-
! Type !! Description !! DOM equivalent event !! Notes
! Type !! Description !! DOM equivalent event !! Bubbles <ref name="dom-event-flow"/>
|-
| ''mousedown'' || Mouse button pressed || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mousedown mousedown] || {{tick}}
|-
| ''mouseup'' || Mouse button released || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseup mouseup] || {{tick}}
|-
| ''click'' || ''mousedown'' + ''mouseup'' have been triggered for this element without moving more than a certain maximum distance || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-click click] || {{tick}}
|-
|-
| ''mousedown'' || Mouse button pressed || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mousedown mousedown] ||
| ''dblclick'' || Two ''click'' events have been triggered for this element without moving more than a certain maximum distance and time limit || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-dblclick dblclick] || {{tick}}
|-
|-
| ''mouseup'' || Mouse button released || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseup mouseup] ||
| ''drag'' || The mouse has been moved with a button down. After dragging has started above an element, all consecutive ''drag'' events are sent to this element even if the mouse leaves its area || || {{tick}}
|-
|-
| ''click'' || ''mousedown'' + ''mouseup'' have been triggered for this element without moving more than a certain maximum distance || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-click click] ||
| ''wheel'' || Mouse wheel rotated (see deltaY for direction) || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-wheel wheel] || {{tick}}
|-
|-
| ''dblclick'' || Two ''click'' events have been triggered for this element without moving more than a certain maximum distance and time limit || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-dblclick dblclick] ||
| ''mousemove'' || Mouse has moved while beeing inside the area of the target element. || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mousemove mousemove] || {{tick}}
|-
|-
| ''drag'' || The mouse has been moved with a button down. After dragging has started above an element, all consecutive ''drag'' events are sent to this element even if the mouse leaves its area || ||
| ''mouseover'' || Mouse has entered a child or the element itself. ''mouseover'' is also triggered if the mouse moves from one child element to another.  || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseover mouseover] || {{tick}}
|-
|-
| ''wheel'' || Mouse wheel rotated (see deltaY for direction) || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-wheel wheel] ||
| ''mouseout'' || Mouse has left a child or the element itself. ''mouseout'' is also triggered if the mouse moves from one child element to another. || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseout mouseout] || {{tick}}
<!--|-
| ''mousemove'' || TODO || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mousemove mousemove] ||-->
|-
|-
| ''mouseover'' || TODO || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseover mouseover] ||
| ''mouseenter'' || Mouse has entered a child or the element itself. In contrary to ''mouseover'', ''mouseenter'' is not triggered if the mouse moves from one child element to another, but only the first time the element or one of its children is entered. || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseenter mouseenter] || {{cross}}
|-
|-
| ''mouseout'' || TODO || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseout mouseout] ||
| ''mouseleave'' || Mouse has left the element and all of its children. In contrary to ''mouseout'', ''mouseleave'' is not triggered if the mouse moves from one child element to another, but only if the mouse moves outside the element and all its children. || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseleave mouseleave] || {{cross}}
<!--|-
| ''mouseenter'' || TODO || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseenter mouseenter] || not triggered yet-->
|-
|-
| ''mouseleave'' || TODO || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseleave mouseleave] ||
| ''keydown'' || A key was pressed down. || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-keydown keydown] || {{tick}}
|-
| ''keyup'' || A key was released. || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-keyup keyup] || {{tick}}
|-
| ''keypress'' || A key creating a printable character was pressed. || [http://www.w3.org/TR/DOM-Level-3-Events/#event-type-keypress keypress] || {{tick}}
|}
|}


= Example Code =
= Example Code =
For the latest examples, please refer to [https://gitorious.org/~tomprogs/fg/toms-fgdata/commits/canvas-gui-demo Tom's canvas-gui-demo branch on gitorious]. This is also where you can find the following code in $FG_ROOT/Nasal/canvas/gui.nas (note that this example makes use of advanced Nasal concepts, such as anonymous functions, method chaining and lots of embedded/inline code):
(Note that this example makes use of advanced Nasal concepts, such as anonymous functions, method chaining and lots of embedded/inline code):
<syntaxhighlight lang="php"># Canvas GUI demo
<syntaxhighlight lang="nasal">
# Canvas GUI demo
#
#
#  Shows an icon in the top-right corner which upon click opens a simple window
#  Shows an icon in the top-right corner which upon click opens a simple window
#
#
(func {
var init_gui = setlistener("/nasal/std/loaded", func() {
  removelistener(init_gui);
  var dlg = canvas.Window.new([32,32]);
  dlg.setInt("y", 4)
    .setInt("right", 4);
var my_canvas = dlg.createCanvas()
                    .setColorBackground(0,0,0,0);
var root = my_canvas.createGroup();
  canvas.parsesvg(root, "gui/dialogs/images/icon-aircraft.svg");


  my_canvas.addEventListener("mouseover", func(event)
var dlg = canvas.Window.new([32,32]);
{
dlg.setInt("tf/t[1]", 4)
  debug.dump( props.wrapNode(event.target._node_ghost) );
  .setInt("right", 4);
});
var my_canvas = dlg.createCanvas()
  my_canvas.addEventListener("click", func
                  .set("background", "rgba(0,0,0,0)");
  {
var root = my_canvas.createGroup();
var dlg = canvas.Window.new([400,300]);
canvas.parsesvg(root, "gui/dialogs/images/icon-aircraft.svg");
var my_canvas = dlg.createCanvas()
 
                  .setColorBackground(0,0,0,0);
my_canvas.addEventListener("click", func
var root = my_canvas.createGroup();
{
root.addEventListener("click", func(e) { printf("click: screen(%.1f|%.1f) client(%.1f|%.1f) click count = %d", e.screenX, e.screenY, e.clientX, e.clientY, e.click_count); });
  var dlg = canvas.Window.new([400,300], "dialog");
root.addEventListener("dblclick", func(e) { printf("dblclick: screen(%.1f|%.1f) client(%.1f|%.1f)", e.screenX, e.screenY, e.clientX, e.clientY); });
  var my_canvas = dlg.createCanvas()
root.addEventListener("wheel", func(e) { printf("wheel: screen(%.1f|%.1f) client(%.1f|%.1f) %.1f", e.screenX, e.screenY, e.clientX, e.clientY, e.deltaY); });
                    .set("background", "#f2f1f0");
var title_bar = root.createChild("group");
  var root = my_canvas.createGroup();
title_bar.addEventListener("drag", func(e) { dlg.move(e.deltaX, e.deltaY); });
  root.addEventListener("click", func(e) {
var x = 0;
    printf( "click: screen(%.1f|%.1f) client(%.1f|%.1f) click count = %d",
var y = 0;
            e.screenX, e.screenY,
var rx = 8;
            e.clientX, e.clientY,
var ry = 8;
            e.click_count );
var w = 400;
var h = 20;
title_bar.createChild("path")
    .moveTo(x + w - rx, y)
    .arcSmallCWTo(rx, ry, 0, x + w, y + ry)
    .vertTo(y + h)
    .horizTo(x)
    .vertTo(y + ry)
    .arcSmallCWTo(rx, ry, 0, x + rx, y)
    .close()
    .setColorFill(0.25,0.24,0.22)
    .setStrokeLineWidth(0);
y = 20;
h = 280;
root.createChild("path")
    .moveTo(x + w, y)
    .vertTo(y + h)
    .horizTo(x)
    .vertTo(y)
    .setColorFill(1,1,1)
    .setColor(0,0,0);
x = 8;
y = 5;
w = 10;
h = 10;
title_bar.createChild("path", "icon-close")
    .moveTo(x, y)
    .lineTo(x + w, y + h)
    .moveTo(x + w, y)
    .lineTo(x, y + h)
    .setColor(1,0,0)
    .setStrokeLineWidth(3)
    .addEventListener("click", func dlg.del());
title_bar.createChild("text", "dialog-caption")
    .setText("Aircraft Help")
    .setTranslation(x + w + 8, 4)
    .setAlignment("left-top")
    .setFontSize(14)
    .setFont("LiberationFonts/LiberationSans-Bold.ttf")
    .setColor(1,1,1);
var text = root.createChild("text")
    .setText("This could be used for building an 'Aircraft Help' dialog.\nYou can also use it to play around with the new Canvas system :). β")
    .setTranslation(10, 30)
    .setAlignment("left-top")
    .setFontSize(14)
    .setFont("LiberationFonts/LiberationSans-Regular.ttf")
    .set("max-width", 380)
    .setColor(0,0,0);
var text_move =
  root.createChild("text")
    .setText("Mouse moved over text...")
    .set("character-size", 15)
    .set("font", "LiberationFonts/LiberationSans-Bold.ttf")
    .set("alignment", "left-center")
    .setTranslation(20, 200)
    .set("fill", "#ff0000")
    .hide();
var visible_count = 0;
text.addEventListener("mouseover", func text_move.show());
text.addEventListener("mouseout", func text_move.hide());
text.addEventListener("click", func canvas.Dialog.new([240,135]).draw());
   });
   });
  root.addEventListener("dblclick", func(e) {
    printf( "dblclick: screen(%.1f|%.1f) client(%.1f|%.1f)",
            e.screenX, e.screenY,
            e.clientX, e.clientY );
  });
  root.addEventListener("wheel", func(e) {
    printf( "wheel: screen(%.1f|%.1f) client(%.1f|%.1f) delta = %.1f",
            e.screenX, e.screenY,
            e.clientX, e.clientY,
            e.deltaY );
  });
  var text =
    root.createChild("text")
        .setText( "This could be used for building an 'Aircraft Help' dialog.\n"
                ~ "You can also #use it to play around with the new Canvas system :). β" )
        .setTranslation(10, 30)
        .set("alignment", "left-top")
        .set("character-size", 14)
        .set("font", "LiberationFonts/LiberationSans-Regular.ttf")
        .set("max-width", 380)
        .set("fill", "black");
  var text_move =
    root.createChild("text")
        .setText("Mouse moved over text...")
        .setTranslation(20, 200)
        .set("alignment", "left-center")
        .set("character-size", 15)
        .set("font", "LiberationFonts/LiberationSans-Bold.ttf")
        .set("fill", "#ff0000")
        .hide();
  var visible_count = 0;
  text.addEventListener("mouseover", func text_move.show());
  text.addEventListener("mouseout", func text_move.hide());
});
});
})();
</syntaxhighlight>
 
<references/>


</syntaxhighlight>
[[Category:Canvas GUI]]

Revision as of 23:13, 15 February 2018

Canvas Event Handling
Started in 11/2012 (Available since FlightGear 2.10)
Description DOM like event handling
Contributor(s) TheTom
Folders


The Canvas event handling system closely follows the W3C DOM Event Model. If you have already used events in JavaScript and HTML most concepts of the Canvas event system should be already familiar to you. The most notable difference is the missing capture phase, but it is usually not used anyhow.

Listeners - simple Nasal functions - can be attached to every element inside the Canvas and the Canvas itself. Once a certain action - like moving the mouse or pressing a button - occurs the associated listeners are called. We can use this for example to detect whether the mouse has moved over an element or if a certain element has been clicked. For this you should understand how closures work.

Listen for events

To receive events callback function can be added to elements on a Canvas as well as to the Canvas itself:

canvas.addEventListener("<type>", <func>);
canvas_element.addEventListener("<type>", <func>);

For each placement of a Canvas handling events can be enabled or disabled. A Canvas placed in a PUI (old GUI) widget or as standalone GUI window receives events by default, whereas Canvases placed onto the aircraft model or in the scenery do not receive any events by default.

Note  When using the old PUI/XML GUI with a <canvas>-widget placement, PUI does not trigger any mouseover/hover (mousemove) events. Mouse clicks/wheel/drag are working as expected. For all other placements like on standalone Canvas windows and 3D models there is no such limitation. If you find that your code doesn't work as expected, make sure to verify that your layers (canvas groups) use matching z-index ordering, or overlapping symbols may prevent event handlers from being triggered/called.

For standalone GUI windows setting capture-events to 0 or 1 enables or disables handling of events respectively. For a Canvas placed onto a 3d model, setting capture-events inside the placement can be used to activate event handling:

var dlg = canvas.Window.new([152,74]);

# Disable event handling for this window. Events will pass through
# and can reach any window or also object covered by the window.
dlg.setBool("capture-events", 0);

# Place the canvas onto the PFD and enable receiving events
my_canvas.addPlacement({"node": "PFD-Screen", "capture-events": 1});

Event flow

Events always are targeted at a specific element inside the Canvas. Before any event handler is called the propagation path for the event is determined. It consists of the event target itself and all its ancestor elements (Groups) up to and including the Canvas. Afterwards - during the Target Phase - all listeners registered on the event target are called. Finally - during the Bubbling Phase - the event bubbles up the tree, following the propagation path determined in the first step, and all listeners attached to the according elements are called.

Event flow of Canvas Events similar to W3C DOM Event flow [1].

Event classes

var Event = {
  # Name of event type [read-only]
  type: <typename>,

  # Target element [read-only]
  target: <target-element>,

  # Element the currently called listener is attached to [read-only]
  currentTarget: <target-element>,

  # Stop further propagation of event (stop
  # bubbling up to its parents)
  stopPropagation: func()
};

# Since FG 3.1.0
var DeviceEvent = {
  parents: [Event],

  # State of all keyboard modifiers at the time the event has
  # been triggered [read-only]
  modifiers: <modifier-mask>,

  # [read-only]
  ctrlKey: <was-ctrl-down>,

  # [read-only]
  shiftKey: <was-shift-down>,

  # [read-only]
  altKey: <was-alt-down>,

  # [read-only]
  metaKey: <was-meta-down>
}

# Since FG 3.1.0
var KeyboardEvent = {
  parents: [DeviceEvent],

  # [read-only]
  key: <key-string>,

  # Location of the key on the keyboard [read-only]
  #
  # https://www.w3.org/TR/DOM-Level-3-Events/#events-keyboard-key-location
  #
  #  0 standard location
  #  1 left
  #  2 right
  #  3 numpad
  location: <key-location>,

  # [read-only]
  repeat: <is-repeat>,

  # [read-only]
  charCode: <code>,

  # [read-only]
  keyCode: <code>
}

var MouseEvent = {
  # [FG >= 3.1.0]
  parents: [DeviceEvent],

  # [FG < 3.1.0]
  parents: [Event],

  # Position in screen coordinates [read-only]
  screenX: <screen-x>,
  screenY: <screen-y>,

  # Position in window/canvas coordinates [read-only]
  clientX: <client-x>,
  clientY: <client-y>,

  # Position in local/element coordinates [read-only]
  localX: <local-x>,
  localY: <local-y>,

  # Distance to position of previous event [read-only]
  deltaX: <delta-x>,
  deltaY: <delta-y>,

  # Current click count (number of clicks within a certain
  # time limit. max. 3) [read-only]
  click_count: <click-count>,

  # Button which triggered this event [read-only, FG >= 3.1.0]
  #
  #  0: primary button (usually the left button)
  #  1: auxiliary button (usually the middle button/mouse wheel)
  #  2: secondary button (usually the right button)
  button: <button>,

  # State of all mouse buttons at the time the event has been
  # triggered [read-only, FG >= 3.1.0]
  buttons: <active-button-mask>
};

Middle-mouse button emulation

2-button mice can be setup to emulate a middle-mouse button click by pressing the left and right button simultaneously. On Ubuntu you can use gpointing-device-settings to enable "middle button emulation" [2] [3].

Event types

Type Description DOM equivalent event Bubbles [1]
mousedown Mouse button pressed mousedown Tick icon
mouseup Mouse button released mouseup Tick icon
click mousedown + mouseup have been triggered for this element without moving more than a certain maximum distance click Tick icon
dblclick Two click events have been triggered for this element without moving more than a certain maximum distance and time limit dblclick Tick icon
drag The mouse has been moved with a button down. After dragging has started above an element, all consecutive drag events are sent to this element even if the mouse leaves its area Tick icon
wheel Mouse wheel rotated (see deltaY for direction) wheel Tick icon
mousemove Mouse has moved while beeing inside the area of the target element. mousemove Tick icon
mouseover Mouse has entered a child or the element itself. mouseover is also triggered if the mouse moves from one child element to another. mouseover Tick icon
mouseout Mouse has left a child or the element itself. mouseout is also triggered if the mouse moves from one child element to another. mouseout Tick icon
mouseenter Mouse has entered a child or the element itself. In contrary to mouseover, mouseenter is not triggered if the mouse moves from one child element to another, but only the first time the element or one of its children is entered. mouseenter Cross icon
mouseleave Mouse has left the element and all of its children. In contrary to mouseout, mouseleave is not triggered if the mouse moves from one child element to another, but only if the mouse moves outside the element and all its children. mouseleave Cross icon
keydown A key was pressed down. keydown Tick icon
keyup A key was released. keyup Tick icon
keypress A key creating a printable character was pressed. keypress Tick icon

Example Code

(Note that this example makes use of advanced Nasal concepts, such as anonymous functions, method chaining and lots of embedded/inline code):

# Canvas GUI demo
#
#  Shows an icon in the top-right corner which upon click opens a simple window
#

var dlg = canvas.Window.new([32,32]);
dlg.setInt("tf/t[1]", 4)
   .setInt("right", 4);
var my_canvas = dlg.createCanvas()
                   .set("background", "rgba(0,0,0,0)");
var root = my_canvas.createGroup();
canvas.parsesvg(root, "gui/dialogs/images/icon-aircraft.svg");

my_canvas.addEventListener("click", func
{
  var dlg = canvas.Window.new([400,300], "dialog");
  var my_canvas = dlg.createCanvas()
                     .set("background", "#f2f1f0");
  var root = my_canvas.createGroup();
  root.addEventListener("click", func(e) {
    printf( "click: screen(%.1f|%.1f) client(%.1f|%.1f) click count = %d",
            e.screenX, e.screenY,
            e.clientX, e.clientY,
            e.click_count );
  });
  root.addEventListener("dblclick", func(e) {
    printf( "dblclick: screen(%.1f|%.1f) client(%.1f|%.1f)",
            e.screenX, e.screenY,
            e.clientX, e.clientY );
  });
  root.addEventListener("wheel", func(e) {
    printf( "wheel: screen(%.1f|%.1f) client(%.1f|%.1f) delta = %.1f",
            e.screenX, e.screenY,
            e.clientX, e.clientY,
            e.deltaY );
  });
  var text =
    root.createChild("text")
        .setText( "This could be used for building an 'Aircraft Help' dialog.\n"
                ~ "You can also #use it to play around with the new Canvas system :). β" )
        .setTranslation(10, 30)
        .set("alignment", "left-top")
        .set("character-size", 14)
        .set("font", "LiberationFonts/LiberationSans-Regular.ttf")
        .set("max-width", 380)
        .set("fill", "black");
  var text_move =
    root.createChild("text")
        .setText("Mouse moved over text...")
        .setTranslation(20, 200)
        .set("alignment", "left-center")
        .set("character-size", 15)
        .set("font", "LiberationFonts/LiberationSans-Bold.ttf")
        .set("fill", "#ff0000")
        .hide();
  var visible_count = 0;
  text.addEventListener("mouseover", func text_move.show());
  text.addEventListener("mouseout", func text_move.hide());
});