Mouse and keyboard
In this section, we discuss ways in which a user can interact with dynamic elements, providing different types of input.
UI Elements
A core package wljs-inputs provides a set of basic UI elements used for creating buttons, sliders, text-fields and etc.
If you need a custom element, you can create it right from the notebook using WLX or Javascript cell types. Please see the guide Emitting events
The following elements are available from out of the box:
- InputButton
- InputRange
- InputText
- InputSelect
- InputTable
- InputFile
- InputCheckbox
- InputEditor
- InputJoystick
and for grouping elements
Examples
Each standard input element is EventObject
, to which you can assign any handler function. You don't necessarily need to assign it to a variable, i.e. (see InputButton)
EventHandler[InputButton["Click"], Beep]
is fine as well as
btn = InputButton["Click"];
EventHandler[btn, Beep];
btn
Here is some other examples
EventHandler[InputRange[0,1,0.1], Function[value, radius = value]]
% // EventFire; (* just to initialize `radius` *)
Graphics[{LightBlue, Disk[], Pink, Disk[{0,0}, radius // Offload]}]
Apply EventFire
on any EventObject
to manually fire an event with a default value to initialize your variables (if needed).
You can also add a label to a InputRange
InputRange[0,1,0.1, "Label"->"Radius"]
and initial value as a third argument
InputRange[0,1,0.1, 0.7, "Label"->"Radius"]
Here is an example on InputSelect
angle = 45 Degree;
EventHandler[InputSelect[{Pi/2 -> "90", Pi/4 -> "45", 0 -> "0"}, Pi/4], Function[value, angle = value]]
Graphics[{Rotate[Rectangle[{0,0}, {1,1}], angle // Offload]}]
Here is a simple text-input
text = "Example";
EventHandler[InputText[], Function[value, text = value]]
Graphics[Table[{
Hue[i/10., 1.,1.], Rotate[Text[Style[text // Offload, FontSize->RandomInteger[{12,24}]], RandomReal[{-1,1}, 2]], RandomChoice[{Pi, Pi/4, Pi/2, 0}]]
}, {i, 10}]]
Grouping input elements
If you need to make just visually, then consider to use Grid, [[Row]] or Column, i.e.
slider = InputRange[0,1,0.1];
button = InputButton[];
{slider, button} // Column
There is another way of grouping on the level of events using InputGroup
slider = InputRange[0,1,0.1];
button = InputButton[];
joined = InputGroup[<|"Button"->button, "Slider"->slider|>, "Label"->"Group"];
EventHandler[joined, Print]
It merges an association (as in example above) or list of EventObjects
into a new one. You do not need to assign separate EventHandler
for each, instead you need only one joined
. It fires an event keeping the original form of used association or list
<|"Slider"->0.5, "Button"->True|>
Join different events
One can also merge event objects underneath of UI elements using Join
. Let us have a look at the simples example
button = InputButton[]
slider = InputRange[0,1,0.1]
EventHandler[Join[button, slider], Function[data,
Print[data]
]];
As a result you will get something like this
True
or
0.5
depending which element it generated. In order to resolve this issue, one can utilize patterns (or topics see EventObject
)
button = InputButton["Topic"->"Button"]
slider = InputRange[0,1,0.1, "Topic"->"Slider"]
EventHandler[Join[button, slider], {type_ :> Function[data,
Print[type<>":"<>ToString[data]]
]}];
or capture them individually
button = InputButton["Topic"->"Button"]
slider = InputRange[0,1,0.1, "Topic"->"Slider"]
EventHandler[Join[button, slider], {
"Button" -> Beep,
"Slider" -> Print
}];
A slider will print a message, while a button will make beep sound.
Chaining events
Most of GUI elements do support chaining, when each of them reuse the same EventObject
. It comes as a first argument
ev = EventObject[];
InputButton[ev, "Topic"->"Button"]
InputRange[ev, 0,1,0.1, "Topic"->"Slider"]
EventHandler[ev, {
"Button" -> Beep,
"Slider" -> Print
}];
In such case, there is no need in creating new events and joining them. In the end this approach leaves less footprint as well as less overhead to the system.
2D Graphics
Some of the primitives as well as entire canvas support EventHandler
method. Let us start with a canvas - Graphics`Canvas[]
Canvas
You can attach event handlers to Graphics`Canvas
object, which represents the given SVG container of your 2D graph inside Graphics.
It has some benefits compared to Primitives, namely "mousemove"
or "click"
will be captured even if there are some objects on the front. The following patterns (topics of EventHandler
) are supported
"keydown"
will capture the focus of the window once a user click on it"capturekeydown"
will capture the focus of the window and prevent page scrolling"mousemove"
will capture a mouse position"click"
captures clicks and sends the position (no alt key pressed)"altclick"
captures clicks (with held alt key)
For example
pt = {};
Graphics[{
PointSize[0.05], Blue, Opacity[0.5],
Point[pt // Offload],
EventHandler[Graphics`Canvas[], {
"mousemove" -> Function[xy, pt = Append[pt, xy]]
}]
}]
Primitives
For some of graphics primitives it is possible to attach EventHandler
as well. The following symbols are supported
which can accept the following pattern to capture events
"drag"
make a primitive draggable and sends coordinates"dragall"
the same as previous, but submits events, when dragging was initiated and finished as well"click"
sends coordinates, where a click was captured (no held Alt key)"altclick"
the same as previous, but with held Alt key"mousedown"
captured event on press and sends the coordinates"mouseup"
captures event on release"mousemove"
captures mouse position"mouseover"
captures mouse position once it is entered the element's area"zoom"
captures mouse wheel
For example, combining "zoom"
and "drag"
, one perform a manual fitting of some gaussian curves
try to move a mouse wheel on a red dot and then drag it
In principle using a large white rectangle with mousemove
pattern, once can build a simple mouse follower
pt = {0,0};
Graphics[{
White,
EventHandler[
Rectangle[{-2,-2}, {2,2}],
{"mousemove"->Function[xy, pt = xy]}
],
PointSize[0.05], Cyan,
Point[pt // Offload]
}]
a mouse follower
3D Graphics
For now event listeners in Graphics3D are quite limited.
Primitives
The following primitives support EventHandler
methods
which can be used with patterns
"transform"
makes an object draggable and sends an association with"position"
field
For example it comes handy while working with dynamic lighting system
point = {1,1,1};
Graphics3D[{Shadows[True],
Polygon[{{-5, 5, -1}, {5, 5, -1}, {5, -5, -1}, {-5, -5, -1}}], White,
Cuboid[{-1, -1, -1}, {1, 1, 1}], Shadows[False],
PointLight[Red, {1.5075, 4.1557, 2.6129}, 100],
Shadows[True], SpotLight[Cyan, point // Offload]
EventHandler[Sphere[point, 0.1], {
"transform" -> Function[assoc, point = assoc["position"]]
}]
}, "Lighting" -> None]