Mercurial > libervia-backend
comparison frontends/src/tools/xmlui.py @ 977:d2e612a45e76
tools, frontends (xmlui): add Widget.setInternalCallback:
- to add UI callback not communication with the bridge
- a list of predefined actions can be used to copy, move, select values
- extra data can be passed within the 'internal_data' element
TODO: frontend side operation to retrieve the data from node to data structure should be generic
author | souliane <souliane@mailoo.org> |
---|---|
date | Thu, 03 Apr 2014 14:56:16 +0200 |
parents | b37b1d183ac3 |
children | 7a39ae3950f7 |
comparison
equal
deleted
inserted
replaced
976:68faf7d77a42 | 977:d2e612a45e76 |
---|---|
188 @main_cont.setter | 188 @main_cont.setter |
189 def main_cont(self, value): | 189 def main_cont(self, value): |
190 if self._main_cont is not None: | 190 if self._main_cont is not None: |
191 raise ValueError(_("XMLUI can have only one main container")) | 191 raise ValueError(_("XMLUI can have only one main container")) |
192 self._main_cont = value | 192 self._main_cont = value |
193 | |
194 | 193 |
195 def _parseChilds(self, parent, current_node, wanted = ('container',), data = None): | 194 def _parseChilds(self, parent, current_node, wanted = ('container',), data = None): |
196 """ Recursively parse childNodes of an elemen | 195 """ Recursively parse childNodes of an elemen |
197 @param parent: widget container with '_xmluiAppend' method | 196 @param parent: widget container with '_xmluiAppend' method |
198 @param current_node: element from which childs will be parsed | 197 @param current_node: element from which childs will be parsed |
313 ctrl._xmlui_param_id = (callback_id, [field.getAttribute('name') for field in node.getElementsByTagName("field_back")]) | 312 ctrl._xmlui_param_id = (callback_id, [field.getAttribute('name') for field in node.getElementsByTagName("field_back")]) |
314 else: | 313 else: |
315 print(_("FIXME FIXME FIXME: widget type [%s] is not implemented") % type_) | 314 print(_("FIXME FIXME FIXME: widget type [%s] is not implemented") % type_) |
316 raise NotImplementedError(_("FIXME FIXME FIXME: type [%s] is not implemented") % type_) | 315 raise NotImplementedError(_("FIXME FIXME FIXME: type [%s] is not implemented") % type_) |
317 | 316 |
318 if self.type == 'param' and type_ != 'text': | 317 if self.type == 'param' and type_ not in ('text', 'button'): |
319 try: | 318 try: |
320 ctrl._xmluiOnChange(self.onParamChange) | 319 ctrl._xmluiOnChange(self.onParamChange) |
321 ctrl._param_category = self._current_category | 320 ctrl._param_category = self._current_category |
322 except (AttributeError, TypeError): # XXX: TypeError is here because pyjamas raise a TypeError instead of an AttributeError | 321 except (AttributeError, TypeError): # XXX: TypeError is here because pyjamas raise a TypeError instead of an AttributeError |
323 if not isinstance(ctrl, (EmptyWidget, TextWidget, LabelWidget, JidWidget)): | 322 if not isinstance(ctrl, (EmptyWidget, TextWidget, LabelWidget, JidWidget)): |
324 print(_("No change listener on [%s]") % ctrl) | 323 print(_("No change listener on [%s]") % ctrl) |
324 | |
325 if type_ != 'text': | |
326 callback = node.getAttribute("internal_callback") or None | |
327 if callback: | |
328 fields = [field.getAttribute('name') for field in node.getElementsByTagName("internal_field")] | |
329 data = self.getInternalCallbackData(callback, node) | |
330 ctrl._xmlui_param_internal = (callback, fields, data) | |
331 if type_ == 'button': | |
332 ctrl._xmluiOnClick(self.onChangeInternal) | |
333 else: | |
334 ctrl._xmluiOnChange(self.onChangeInternal) | |
325 | 335 |
326 ctrl._xmlui_name = name | 336 ctrl._xmlui_name = name |
327 parent._xmluiAppend(ctrl) | 337 parent._xmluiAppend(ctrl) |
328 | 338 |
329 else: | 339 else: |
401 Launch the action associated to the button | 411 Launch the action associated to the button |
402 @param button: the button clicked | 412 @param button: the button clicked |
403 | 413 |
404 """ | 414 """ |
405 callback_id, fields = button._xmlui_param_id | 415 callback_id, fields = button._xmlui_param_id |
416 if not callback_id: # the button is probably bound to an internal action | |
417 return | |
406 data = {} | 418 data = {} |
407 for field in fields: | 419 for field in fields: |
408 escaped = self.escape(field) | 420 escaped = self.escape(field) |
409 ctrl = self.ctrl_list[field] | 421 ctrl = self.ctrl_list[field] |
410 if isinstance(ctrl['control'], ListWidget): | 422 if isinstance(ctrl['control'], ListWidget): |
411 data[escaped] = u'\t'.join(ctrl['control']._xmluiGetSelectedValues()) | 423 data[escaped] = u'\t'.join(ctrl['control']._xmluiGetSelectedValues()) |
412 else: | 424 else: |
413 data[escaped] = ctrl['control']._xmluiGetValue() | 425 data[escaped] = ctrl['control']._xmluiGetValue() |
414 self._xmluiLaunchAction(callback_id, data) | 426 self._xmluiLaunchAction(callback_id, data) |
427 | |
428 def onChangeInternal(self, ctrl): | |
429 """ Called when a widget that has been bound to an internal callback is changed. | |
430 | |
431 This is used to perform some UI actions without communicating with the backend. | |
432 See sat.tools.xml_tools.Widget.setInternalCallback for more details. | |
433 @param ctrl: widget modified | |
434 """ | |
435 action, fields, data = ctrl._xmlui_param_internal | |
436 if action not in ('copy', 'move', 'groups_of_contact'): | |
437 raise NotImplementedError(_("FIXME: XMLUI internal action [%s] is not implemented") % action) | |
438 | |
439 def copy_move(source, target): | |
440 """Depending of 'action' value, copy or move from source to target.""" | |
441 if isinstance(target, ListWidget): | |
442 if isinstance(source, ListWidget): | |
443 values = source._xmluiGetSelectedValues() | |
444 else: | |
445 values = [source._xmluiGetValue()] | |
446 if action == 'move': | |
447 source._xmluiSetValue('') | |
448 values = [value for value in values if value] | |
449 if values: | |
450 target._xmluiAddValues(values, select=True) | |
451 else: | |
452 if isinstance(source, ListWidget): | |
453 value = u', '.join(source._xmluiGetSelectedValues()) | |
454 else: | |
455 value = source._xmluiGetValue() | |
456 if action == 'move': | |
457 source._xmluiSetValue('') | |
458 target._xmluiSetValue(value) | |
459 | |
460 def groups_of_contact(source, target): | |
461 """Select in target the groups of the contact which is selected in source.""" | |
462 assert(isinstance(source, ListWidget)) | |
463 assert(isinstance(target, ListWidget)) | |
464 try: | |
465 contact_jid_s = source._xmluiGetSelectedValues()[0] | |
466 except IndexError: | |
467 return | |
468 target._xmluiSelectValues(data[contact_jid_s]) | |
469 pass | |
470 | |
471 source = None | |
472 for field in fields: | |
473 widget = self.ctrl_list[field]['control'] | |
474 if not source: | |
475 source = widget | |
476 continue | |
477 if action in ('copy', 'move'): | |
478 copy_move(source, widget) | |
479 elif action == 'groups_of_contact': | |
480 groups_of_contact(source, widget) | |
481 source = None | |
482 | |
483 def getInternalCallbackData(self, action, node): | |
484 """Retrieve from node the data needed to perform given action. | |
485 | |
486 TODO: it would be better to not have a specific way to retrieve | |
487 data for each action, but instead to have a generic method to | |
488 extract any kind of data structure from the 'internal_data' element. | |
489 | |
490 @param action (string): a value from the one that can be passed to the | |
491 'callback' parameter of sat.tools.xml_tools.Widget.setInternalCallback | |
492 @param node (DOM Element): the node of the widget that triggers the callback | |
493 """ | |
494 try: # data is stored in the first 'internal_data' element of the node | |
495 data_elts = node.getElementsByTagName('internal_data')[0].childNodes | |
496 except IndexError: | |
497 return None | |
498 data = {} | |
499 if action == 'groups_of_contact': # return a dict(key: string, value: list[string]) | |
500 for elt in data_elts: | |
501 jid_s = elt.getAttribute('name') | |
502 data[jid_s] = [] | |
503 for value_elt in elt.childNodes: | |
504 data[jid_s].append(value_elt.getAttribute('name')) | |
505 return data | |
415 | 506 |
416 def onFormSubmitted(self, ignore=None): | 507 def onFormSubmitted(self, ignore=None): |
417 """ An XMLUI form has been submited | 508 """ An XMLUI form has been submited |
418 call the submit action associated with this form | 509 call the submit action associated with this form |
419 | 510 |