|
1 // widget_button.ysl2 |
|
2 |
|
3 widget_desc("Button") { |
|
4 longdesc |
|
5 || |
|
6 Button widget takes one boolean variable path, and reflect current true |
|
7 or false value by showing "active" or "inactive" labeled element |
|
8 respectively. Pressing and releasing button changes variable to true and |
|
9 false respectively. Potential inconsistency caused by quick consecutive |
|
10 presses on the button is mitigated by using a state machine that wait for |
|
11 previous state change to be reflected on variable before applying next one. |
|
12 || |
|
13 |
|
14 shortdesc > Push button reflecting consistently given boolean variable |
|
15 |
|
16 path name="value" accepts="HMI_BOOL" > Boolean variable |
|
17 |
|
18 } |
|
19 |
|
20 // Finite state machine |
|
21 decl fsm(name); |
|
22 decl state(name); |
|
23 decl on_mouse(position); |
|
24 decl on_dispatch(value); |
|
25 decl jump(state); |
|
26 decl show(eltname); |
|
27 decl hmi_value(value); |
|
28 |
|
29 gen_index_xhtml { |
|
30 |
|
31 // State machine to drive HMI_BOOL on a potentially laggy connection |
|
32 const "_button_fsm" fsm { |
|
33 state "init" { |
|
34 on_dispatch "false" jump "released"; |
|
35 on_dispatch "true" jump "pressed"; |
|
36 } |
|
37 |
|
38 state "pressing" { |
|
39 // show "waitactive"; |
|
40 hmi_value "true"; |
|
41 on_dispatch "true" jump "pressed"; |
|
42 on_mouse "up" jump "shortpress"; |
|
43 } |
|
44 state "pressed" { |
|
45 show "active"; |
|
46 on_mouse "up" jump "releasing"; |
|
47 on_dispatch "false" jump "released"; |
|
48 } |
|
49 state "shortpress" { |
|
50 on_dispatch "true" jump "releasing"; |
|
51 on_mouse "down" jump "pressing"; |
|
52 } |
|
53 |
|
54 state "releasing" { |
|
55 // show "waitinactive"; |
|
56 hmi_value "false"; |
|
57 on_dispatch "false" jump "released"; |
|
58 on_mouse "down" jump "shortrelease"; |
|
59 } |
|
60 state "released" { |
|
61 show "inactive"; |
|
62 on_mouse "down" jump "pressing"; |
|
63 on_dispatch "true" jump "pressed"; |
|
64 } |
|
65 state "shortrelease" { |
|
66 on_dispatch "false" jump "pressing"; |
|
67 on_mouse "up" jump "releasing"; |
|
68 } |
|
69 } |
|
70 |
|
71 template "fsm", mode="dispatch_transition" { |
|
72 | switch (this.state) { |
|
73 apply "state", mode="dispatch_transition"; |
|
74 | } |
|
75 } |
|
76 template "state", mode="dispatch_transition" { |
|
77 | case "«@name»": |
|
78 apply "on-dispatch"; |
|
79 | break; |
|
80 } |
|
81 template "on-dispatch" { |
|
82 | if(value == «@value») { |
|
83 apply "jump", mode="transition"; |
|
84 | } |
|
85 } |
|
86 |
|
87 template "fsm", mode="mouse_transition" { |
|
88 param "position"; |
|
89 | switch (this.state) { |
|
90 apply "state", mode="mouse_transition" with "position", "$position"; |
|
91 | } |
|
92 } |
|
93 template "state", mode="mouse_transition" { |
|
94 param "position"; |
|
95 | case "«@name»": |
|
96 apply "on-mouse[@position = $position]"; |
|
97 | break; |
|
98 } |
|
99 template "on-mouse" { |
|
100 // up or down state is already assumed because apply statement filters it |
|
101 apply "jump", mode="transition"; |
|
102 } |
|
103 |
|
104 template "jump", mode="transition" { |
|
105 | this.state = "«@state»"; |
|
106 | this.«@state»_action(); |
|
107 } |
|
108 |
|
109 template "fsm", mode="actions" { |
|
110 apply "state", mode="actions"; |
|
111 } |
|
112 template "state", mode="actions" { |
|
113 | «@name»_action(){ |
|
114 //| console.log("Entering state «@name»"); |
|
115 apply "*", mode="actions"; |
|
116 | } |
|
117 } |
|
118 template "show", mode="actions" { |
|
119 | this.display = "«@eltname»"; |
|
120 | this.request_animate(); |
|
121 } |
|
122 template "hmi-value", mode="actions" { |
|
123 | this.apply_hmi_value(0, «@value»); |
|
124 } |
|
125 |
|
126 } |
|
127 |
|
128 widget_class("Button"){ |
|
129 const "fsm","exsl:node-set($_button_fsm)"; |
|
130 | frequency = 5; |
|
131 |
|
132 | display = "inactive"; |
|
133 | state = "init"; |
|
134 |
|
135 | dispatch(value) { |
|
136 // | console.log("dispatch"+value); |
|
137 apply "$fsm", mode="dispatch_transition"; |
|
138 | } |
|
139 |
|
140 | onmouseup(evt) { |
|
141 | svg_root.removeEventListener("pointerup", this.bound_onmouseup, true); |
|
142 // | console.log("onmouseup"); |
|
143 apply "$fsm", mode="mouse_transition" with "position", "'up'"; |
|
144 | } |
|
145 | onmousedown(evt) { |
|
146 | svg_root.addEventListener("pointerup", this.bound_onmouseup, true); |
|
147 // | console.log("onmousedown"); |
|
148 apply "$fsm", mode="mouse_transition" with "position", "'down'"; |
|
149 | } |
|
150 |
|
151 apply "$fsm", mode="actions"; |
|
152 |
|
153 | animate(){ |
|
154 | if (this.active_elt && this.inactive_elt) { |
|
155 foreach "str:split('active inactive')" { |
|
156 | if(this.display == "«.»") |
|
157 | this.«.»_elt.style.display = ""; |
|
158 | else |
|
159 | this.«.»_elt.style.display = "none"; |
|
160 } |
|
161 | } |
|
162 | } |
|
163 |
|
164 | init() { |
|
165 | this.bound_onmouseup = this.onmouseup.bind(this); |
|
166 | this.element.addEventListener("pointerdown", this.onmousedown.bind(this)); |
|
167 | } |
|
168 } |
|
169 |
|
170 widget_defs("Button") { |
|
171 optional_labels("active inactive"); |
|
172 } |