/
MultiButton.py
98 lines (76 loc) · 3.26 KB
/
MultiButton.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import time
from _Framework.ComboElement import EventElement, WrapperElement
from _Framework.Util import lazy_attribute
from _Framework import Task
# WAITING <-----------------------------------------------+<------------+
# | ^ |
# button_down (ignore button_up | |
# | | |
# V | |
# FIRST_BUTTON_DOWN ---> long_tap_delay_passes --> NOTIFY_LONG_TAP |
# | |
# button_up |
# | |
# V |
# MAYBE_DOUBLE_TAP ---> double_tap_delay_passes --> NOTIFY_SINGLE_TAP --+
# |
# button_down
# |
# V
# NOTIFY_DOUBLE_TAP --> ... (back to WAITING)
#
BUTTON_OFF = 0
class MultiButton(WrapperElement):
u"""
Element wrapper that provides a facade with three events:
- single_press
- double_press
- long_press
"""
__subject_events__ = (u'any_press', u'single_press', u'double_press', u'long_press')
DOUBLE_PRESS_MAX_DELAY = 0.2
LONG_TAP_MAX_DELAY = 0.75
def __init__(self, wrapped_control=None, *a, **k):
super(MultiButton, self).__init__(wrapped_control=wrapped_control, *a, **k)
self.register_wrapped()
self.request_listen_nested_control_elements()
self._long_press_task = self._tasks.add(Task.sequence(
Task.wait(self.LONG_TAP_MAX_DELAY),
Task.run(self.notify_long_press)
)).kill()
self._double_press_task = self._tasks.add(Task.sequence(
Task.wait(self.DOUBLE_PRESS_MAX_DELAY),
Task.run(self.notify_single_press)
)).kill()
def on_nested_control_element_value(self, value, control):
button_down = (not control.is_momentary() or value)
button_up = not button_down
waiting_for_long_tap = not self._long_press_task.is_killed
waiting_for_double_tap = not self._double_press_task.is_killed
if button_down:
self.notify_any_press()
# Waiting for first activity
if not (waiting_for_long_tap or waiting_for_double_tap):
if button_down:
self._long_press_task.restart()
# We have a long-tap candidate
elif waiting_for_long_tap and button_up:
# Button released before it was a long-tap
self._long_press_task.kill()
self._double_press_task.restart()
elif waiting_for_double_tap and button_down:
self._double_press_task.kill()
self.notify_double_press()
super(MultiButton, self).on_nested_control_element_value(value, control)
@lazy_attribute
def any_press(self):
return EventElement(self, 'any_press')
@lazy_attribute
def single_press(self):
return EventElement(self, 'single_press')
@lazy_attribute
def double_press(self):
return EventElement(self, 'double_press')
@lazy_attribute
def long_press(self):
return EventElement(self, 'long_press')