63 const "elt","$unordered_items[@inkscape:label = $elt_label]"; |
66 const "elt","$unordered_items[@inkscape:label = $elt_label]"; |
64 const "pos","position()"; |
67 const "pos","position()"; |
65 const "item_path", "$items_paths[$pos]"; |
68 const "item_path", "$items_paths[$pos]"; |
66 | [ /* item="«$elt_label»" path="«$item_path»" */ |
69 | [ /* item="«$elt_label»" path="«$item_path»" */ |
67 if "count($elt)=0" error > Missing item labeled «$elt_label» in ForEach widget «$hmi_element/@id» |
70 if "count($elt)=0" error > Missing item labeled «$elt_label» in ForEach widget «$hmi_element/@id» |
|
71 if "count($elt)>1" error > Duplicate item labeled «$elt_label» in ForEach widget «$hmi_element/@id» |
68 foreach "func:refered_elements($elt)[@id = $hmi_elements/@id][not(@id = $elt/@id)]" { |
72 foreach "func:refered_elements($elt)[@id = $hmi_elements/@id][not(@id = $elt/@id)]" { |
69 if "not(func:is_descendant_path(func:widget(@id)/path/@value, $item_path))" |
73 if "not(func:is_descendant_path(func:widget(@id)/path/@value, $item_path))" |
70 error > Widget id="«@id»" label="«@inkscape:label»" is having wrong path. Accroding to ForEach widget ancestor id="«$hmi_element/@id»", path should be descendant of "«$item_path»". |
74 error > Widget id="«@id»" label="«@inkscape:label»" is having wrong path. Accroding to ForEach widget ancestor id="«$hmi_element/@id»", path should be descendant of "«$item_path»". |
71 | hmi_widgets["«@id»"]`if "position()!=last()" > ,` |
75 | hmi_widgets["«@id»"]`if "position()!=last()" > ,` |
72 } |
76 } |
73 | ]`if "position()!=last()" > ,` |
77 | ]`if "position()!=last()" > ,` |
74 } |
78 } |
75 | ] |
79 | ] |
76 | }, |
80 | }, |
77 | item_offset: 0, |
81 | range: «count($hmi_index_items)», |
|
82 | size: «count($unordered_items)», |
|
83 | position: 0, |
78 } |
84 } |
79 |
85 |
80 widget_class("ForEach") |
86 widget_class("ForEach") |
81 || |
87 || |
|
88 items_subscribed = false; |
82 |
89 |
83 unsub_items(){ |
90 unsub_items(){ |
84 for(let item of this.items){ |
91 if(this.items_subscribed){ |
85 for(let widget of item) { |
92 for(let item of this.items){ |
86 widget.unsub(); |
93 for(let widget of item) { |
|
94 widget.unsub(); |
|
95 } |
|
96 } |
|
97 this.items_subscribed = false; |
|
98 } |
|
99 } |
|
100 |
|
101 unsub(){ |
|
102 super.unsub() |
|
103 this.unsub_items(); |
|
104 } |
|
105 |
|
106 sub_items(){ |
|
107 if(!this.items_subscribed){ |
|
108 for(let i = 0; i < this.size; i++) { |
|
109 let item = this.items[i]; |
|
110 let orig_item_index = this.index_pool[i]; |
|
111 let item_index = this.index_pool[i+this.position]; |
|
112 let item_index_offset = item_index - orig_item_index; |
|
113 if(this.relativeness[0]) |
|
114 item_index_offset += this.offset; |
|
115 for(let widget of item) { |
|
116 /* all variables of all widgets in a ForEach are all relative. |
|
117 Really. |
|
118 |
|
119 TODO: allow absolute variables in ForEach widgets |
|
120 */ |
|
121 widget.sub(item_index_offset, widget.indexes.map(_=>true)); |
|
122 } |
87 } |
123 } |
88 } |
124 } |
89 } |
125 } |
90 |
126 |
91 unsub(){ |
127 sub(new_offset, relativeness, container_id){ |
92 this.unsub_items(); |
128 let position_given = this.indexes.length > 2; |
93 this.offset = 0; |
129 |
94 this.relativeness = undefined; |
130 // sub() will call apply_cache() and then dispatch() |
|
131 // undefining position forces dispatch() to call apply_position() |
|
132 if(position_given) |
|
133 this.position = undefined; |
|
134 |
|
135 super.sub(new_offset, relativeness, container_id); |
|
136 |
|
137 // if position isn't given as a variable |
|
138 // dispatch() to call apply_position() aren't called |
|
139 // and items must be subscibed now. |
|
140 if(!position_given) |
|
141 this.sub_items(); |
|
142 |
|
143 // as soon as subribed apply range and size once for all |
|
144 this.apply_hmi_value(1, this.range); |
|
145 this.apply_hmi_value(3, this.size); |
95 } |
146 } |
96 |
147 |
97 sub_items(){ |
148 apply_position(new_position){ |
98 for(let i = 0; i < this.items.length; i++) { |
149 let old_position = this.position; |
99 let item = this.items[i]; |
150 let limited_position = Math.round(Math.max(Math.min(new_position, this.range - this.size), 0)); |
100 let orig_item_index = this.index_pool[i]; |
151 if(this.position == limited_position){ |
101 let item_index = this.index_pool[i+this.item_offset]; |
152 return false; |
102 let item_index_offset = item_index - orig_item_index; |
153 } |
103 if(this.relativeness[0]) |
154 this.unsub_items(); |
104 item_index_offset += this.offset; |
155 this.position = limited_position; |
105 for(let widget of item) { |
156 this.sub_items(); |
106 /* all variables of all widgets in a ForEach are all relative. |
157 request_subscriptions_update(); |
107 Really. |
158 jumps_need_update = true; |
|
159 this.request_animate(); |
|
160 return true; |
|
161 } |
108 |
162 |
109 TODO: allow absolute variables in ForEach widgets |
163 on_click(opstr, evt) { |
110 */ |
164 let new_position = eval(String(this.position)+opstr); |
111 widget.sub(item_index_offset, widget.indexes.map(_=>true)); |
165 if(new_position + this.size > this.range) { |
|
166 if(this.position + this.size == this.range) |
|
167 new_position = 0; |
|
168 else |
|
169 new_position = this.range - this.size; |
|
170 } else if(new_position < 0) { |
|
171 if(this.position == 0) |
|
172 new_position = this.range - this.size; |
|
173 else |
|
174 new_position = 0; |
|
175 } |
|
176 if(this.apply_position(new_position)){ |
|
177 this.apply_hmi_value(2, this.position); |
|
178 } |
|
179 } |
|
180 |
|
181 dispatch(value, oldval, index) { |
|
182 // Only care about position, others are constants |
|
183 if(index == 2){ |
|
184 this.apply_position(value); |
|
185 if(this.position != value){ |
|
186 // widget refused or apply different value, force it back |
|
187 this.apply_hmi_value(2, this.position); |
112 } |
188 } |
113 } |
189 } |
114 } |
190 } |
115 |
191 |
116 sub(new_offset=0, relativeness=[]){ |
|
117 this.offset = new_offset; |
|
118 this.relativeness = relativeness; |
|
119 this.sub_items(); |
|
120 } |
|
121 |
|
122 apply_cache() { |
|
123 this.items.forEach(item=>item.forEach(widget=>widget.apply_cache())); |
|
124 } |
|
125 |
|
126 on_click(opstr, evt) { |
|
127 let new_item_offset = eval(String(this.item_offset)+opstr); |
|
128 if(new_item_offset + this.items.length > this.index_pool.length) { |
|
129 if(this.item_offset + this.items.length == this.index_pool.length) |
|
130 new_item_offset = 0; |
|
131 else |
|
132 new_item_offset = this.index_pool.length - this.items.length; |
|
133 } else if(new_item_offset < 0) { |
|
134 if(this.item_offset == 0) |
|
135 new_item_offset = this.index_pool.length - this.items.length; |
|
136 else |
|
137 new_item_offset = 0; |
|
138 } |
|
139 this.item_offset = new_item_offset; |
|
140 this.unsub_items(); |
|
141 this.sub_items(); |
|
142 update_subscriptions(); |
|
143 this.apply_cache(); |
|
144 jumps_need_update = true; |
|
145 requestHMIAnimation(); |
|
146 } |
|
147 || |
192 || |
148 |
193 |