1************************
2Mouse and keyboard input
3************************
4
5Input handling in glooey is built on the pyglet event framework.  You can read
6all about this framework `here`__, but the basic idea is that events are
7triggered when input occurs, and your code can react to these events however it
8likes.  Glooey works the same way, but events are triggered by every widget
9that's affected by the input in question.  Another way to say this is that
10every widget is an `EventDispatcher`.  So while pyglet lets you react when the
11mouse is clicked, glooey lets you react when the mouse is clicked on a
12particular widget.
13
14__ https://pyglet.readthedocs.io/en/latest/programming_guide/events.html
15
16The following sections will describe the user-input-related events that widgets
17can trigger, but the first thing to know is how to react to an event.  You do
18this by attaching a handler (which is a function with the same name and
19signature as the event) to a widget.  There are two prominent ways to do this.
20The first is to use the `~Widget.event()` decorator::
21
22   @widget.event
23   def on_click(widget):
24      print(f"{widget} was clicked!")
25
26The second is to use the `~Widget.push_handlers()` method::
27
28   def on_click(widget):
29      print(f"{widget} was clicked!")
30   widget.push_handlers(on_click)
31
32Handlers that have been pushed can also be popped, which is sometimes useful::
33
34   widget.pop_handlers()
35
36Mouse input
37===========
38Every widget can dispatch the following events in response to mouse input:
39
40``on_click(widget)``
41   Triggered when the user clicks on a widget.  This is pretty similar to
42   ``on_mouse_release()``, but there are a few differences.  First, a widget
43   cannot be clicked if it's not "enabled", i.e. via `~Widget.enable()` and
44   `~Widget.disable()`.  (You can think of a disabled widget as being
45   "greyed-out", although it's up to the widget itself to actually change it's
46   appearance when it's been disabled.)  Second, a widget cannot be clicked if
47   the click didn't begin within the widget.  In other words, if you were to
48   drag the mouse over a widget and then release it, that would not be a click.
49
50``on_double_click(widget)``
51   Triggered when the user clicks the same widget twice within 500 ms.  The
52   second click triggers both ``on_click()`` and ``on_double_click()`` events.
53
54``on_rollover(widget, current_state, previous_state)``
55   Triggered whenever a widget's rollover state changes.  There are three
56   possible states: 'base' (the mouse is not interacting with the widget),
57   'over' (the mouse is currently hovering over the widget), and 'down' (the
58   mouse is in the middle of clicking on the widget).
59
60``on_mouse_press(x, y, button, modifiers)``
61   Triggered when the mouse is pressed while over a widget.
62
63``on_mouse_release(x, y, button, modifiers)``
64   Triggered when the mouse is released while over a widget.
65
66``on_mouse_hold(dt)``
67   Triggered 60 times a second whenever the mouse is being pressed on a widget
68   (if the press began on the widget).  The ``dt`` argument gives the amount of
69   time that passed between triggers.
70
71``on_mouse_motion(x, y, dx, dy)``
72   Triggered when the mouse moves around inside a widget.
73
74``on_mouse_enter(x, y)``
75   Triggered when the mouse moves into a widget.  Note that pyglet only uses
76   this event to indicate when the mouse enters the window, so in this sense
77   glooey treats each widget like its own window.
78
79``on_mouse_leave(x, y)``
80   Triggered when the mouse moves out of a widget.  Note that pyglet only uses
81   this event to indicate when the mouse leaves the window, so in this sense
82   glooey treats each widget like its own window.
83
84``on_mouse_drag(x, y, dx, dy, buttons, modifiers)``
85   Triggered when the mouse is dragged around inside a widget.
86
87``on_mouse_drag_enter(x, y)``
88   Triggered when the mouse is dragged into a widget.  Note that pyglet only
89   uses this event to indicate when the mouse leaves the window, so in this
90   sense glooey treats each widget like its own window.
91
92``on_mouse_drag_leave(x, y)``
93   Triggered when the mouse is dragged out of a widget.  Note that pyglet only
94   uses this event to indicate when the mouse leaves the window, so in this
95   sense glooey treats each widget like its own window.
96
97``on_mouse_scroll(x, y, scroll_x, scroll_y)``
98   Triggered when the scroll wheel is turned while the mouse is over a widget.
99
100In addition to the above events that can be triggered by any widget, the
101following events can be triggered only by certain widgets:
102
103``on_toggle(widget)``
104   Triggered by Checkbox and RadioButton when the state of the button is
105   changed.  This can happen programmatically, but usually happens because the
106   user clicked on it.
107
108``on_mouse_pan(direction, dt)``
109   Triggered by PanningGui when the mouse is at the edge of the screen.  The
110   Viewport widget can listen for these events and use them to pan around a
111   larger widget, so together these widgets are useful for making fullscreen
112   games.
113
114Keyboard input
115==============
116This section should really be empty; glooey doesn't affect how keyboard events
117are handled at all.  If you want to react to keyboard input (e.g. to make a
118hotkey), just attach a ``on_key_press`` and/or ``on_key_release`` handlers to
119the window just like you would in any pyglet program.  Certain widgets (like
120`Form`) do interact with the keyboard, but they do so by directly interacting
121with the window themselves, and they don't re-dispatch keyboard related events.
122
123