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
This commit is contained in:
parent
35d66de820
commit
38eb876e94
2 changed files with 141 additions and 3 deletions
|
|
@ -191,7 +191,6 @@ class XMLUI(object):
|
|||
raise ValueError(_("XMLUI can have only one main container"))
|
||||
self._main_cont = value
|
||||
|
||||
|
||||
def _parseChilds(self, parent, current_node, wanted = ('container',), data = None):
|
||||
""" Recursively parse childNodes of an elemen
|
||||
@param parent: widget container with '_xmluiAppend' method
|
||||
|
|
@ -315,14 +314,25 @@ class XMLUI(object):
|
|||
print(_("FIXME FIXME FIXME: widget type [%s] is not implemented") % type_)
|
||||
raise NotImplementedError(_("FIXME FIXME FIXME: type [%s] is not implemented") % type_)
|
||||
|
||||
if self.type == 'param' and type_ != 'text':
|
||||
if self.type == 'param' and type_ not in ('text', 'button'):
|
||||
try:
|
||||
ctrl._xmluiOnChange(self.onParamChange)
|
||||
ctrl._param_category = self._current_category
|
||||
except (AttributeError, TypeError): # XXX: TypeError is here because pyjamas raise a TypeError instead of an AttributeError
|
||||
except (AttributeError, TypeError): # XXX: TypeError is here because pyjamas raise a TypeError instead of an AttributeError
|
||||
if not isinstance(ctrl, (EmptyWidget, TextWidget, LabelWidget, JidWidget)):
|
||||
print(_("No change listener on [%s]") % ctrl)
|
||||
|
||||
if type_ != 'text':
|
||||
callback = node.getAttribute("internal_callback") or None
|
||||
if callback:
|
||||
fields = [field.getAttribute('name') for field in node.getElementsByTagName("internal_field")]
|
||||
data = self.getInternalCallbackData(callback, node)
|
||||
ctrl._xmlui_param_internal = (callback, fields, data)
|
||||
if type_ == 'button':
|
||||
ctrl._xmluiOnClick(self.onChangeInternal)
|
||||
else:
|
||||
ctrl._xmluiOnChange(self.onChangeInternal)
|
||||
|
||||
ctrl._xmlui_name = name
|
||||
parent._xmluiAppend(ctrl)
|
||||
|
||||
|
|
@ -403,6 +413,8 @@ class XMLUI(object):
|
|||
|
||||
"""
|
||||
callback_id, fields = button._xmlui_param_id
|
||||
if not callback_id: # the button is probably bound to an internal action
|
||||
return
|
||||
data = {}
|
||||
for field in fields:
|
||||
escaped = self.escape(field)
|
||||
|
|
@ -413,6 +425,85 @@ class XMLUI(object):
|
|||
data[escaped] = ctrl['control']._xmluiGetValue()
|
||||
self._xmluiLaunchAction(callback_id, data)
|
||||
|
||||
def onChangeInternal(self, ctrl):
|
||||
""" Called when a widget that has been bound to an internal callback is changed.
|
||||
|
||||
This is used to perform some UI actions without communicating with the backend.
|
||||
See sat.tools.xml_tools.Widget.setInternalCallback for more details.
|
||||
@param ctrl: widget modified
|
||||
"""
|
||||
action, fields, data = ctrl._xmlui_param_internal
|
||||
if action not in ('copy', 'move', 'groups_of_contact'):
|
||||
raise NotImplementedError(_("FIXME: XMLUI internal action [%s] is not implemented") % action)
|
||||
|
||||
def copy_move(source, target):
|
||||
"""Depending of 'action' value, copy or move from source to target."""
|
||||
if isinstance(target, ListWidget):
|
||||
if isinstance(source, ListWidget):
|
||||
values = source._xmluiGetSelectedValues()
|
||||
else:
|
||||
values = [source._xmluiGetValue()]
|
||||
if action == 'move':
|
||||
source._xmluiSetValue('')
|
||||
values = [value for value in values if value]
|
||||
if values:
|
||||
target._xmluiAddValues(values, select=True)
|
||||
else:
|
||||
if isinstance(source, ListWidget):
|
||||
value = u', '.join(source._xmluiGetSelectedValues())
|
||||
else:
|
||||
value = source._xmluiGetValue()
|
||||
if action == 'move':
|
||||
source._xmluiSetValue('')
|
||||
target._xmluiSetValue(value)
|
||||
|
||||
def groups_of_contact(source, target):
|
||||
"""Select in target the groups of the contact which is selected in source."""
|
||||
assert(isinstance(source, ListWidget))
|
||||
assert(isinstance(target, ListWidget))
|
||||
try:
|
||||
contact_jid_s = source._xmluiGetSelectedValues()[0]
|
||||
except IndexError:
|
||||
return
|
||||
target._xmluiSelectValues(data[contact_jid_s])
|
||||
pass
|
||||
|
||||
source = None
|
||||
for field in fields:
|
||||
widget = self.ctrl_list[field]['control']
|
||||
if not source:
|
||||
source = widget
|
||||
continue
|
||||
if action in ('copy', 'move'):
|
||||
copy_move(source, widget)
|
||||
elif action == 'groups_of_contact':
|
||||
groups_of_contact(source, widget)
|
||||
source = None
|
||||
|
||||
def getInternalCallbackData(self, action, node):
|
||||
"""Retrieve from node the data needed to perform given action.
|
||||
|
||||
TODO: it would be better to not have a specific way to retrieve
|
||||
data for each action, but instead to have a generic method to
|
||||
extract any kind of data structure from the 'internal_data' element.
|
||||
|
||||
@param action (string): a value from the one that can be passed to the
|
||||
'callback' parameter of sat.tools.xml_tools.Widget.setInternalCallback
|
||||
@param node (DOM Element): the node of the widget that triggers the callback
|
||||
"""
|
||||
try: # data is stored in the first 'internal_data' element of the node
|
||||
data_elts = node.getElementsByTagName('internal_data')[0].childNodes
|
||||
except IndexError:
|
||||
return None
|
||||
data = {}
|
||||
if action == 'groups_of_contact': # return a dict(key: string, value: list[string])
|
||||
for elt in data_elts:
|
||||
jid_s = elt.getAttribute('name')
|
||||
data[jid_s] = []
|
||||
for value_elt in elt.childNodes:
|
||||
data[jid_s].append(value_elt.getAttribute('name'))
|
||||
return data
|
||||
|
||||
def onFormSubmitted(self, ignore=None):
|
||||
""" An XMLUI form has been submited
|
||||
call the submit action associated with this form
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from wokkel import data_form
|
|||
from twisted.words.xish import domish
|
||||
from sat.core import exceptions
|
||||
|
||||
|
||||
"""This library help manage XML used in SàT (parameters, registration, etc) """
|
||||
|
||||
SAT_FORM_PREFIX = "SAT_FORM_"
|
||||
|
|
@ -299,6 +300,26 @@ class FieldBackElement(Element):
|
|||
self.elem.setAttribute('name', name)
|
||||
|
||||
|
||||
class InternalFieldElement(Element):
|
||||
""" Used by internal callbacks to indicate which fields are manipulated """
|
||||
type = 'internal_field'
|
||||
|
||||
def __init__(self, parent, name):
|
||||
super(InternalFieldElement, self).__init__(parent.xmlui, parent)
|
||||
self.elem.setAttribute('name', name)
|
||||
|
||||
|
||||
class InternalDataElement(Element):
|
||||
""" Used by internal callbacks to retrieve extra data """
|
||||
type = 'internal_data'
|
||||
|
||||
def __init__(self, parent, children):
|
||||
super(InternalDataElement, self).__init__(parent.xmlui, parent)
|
||||
assert(isinstance(children, list))
|
||||
for child in children:
|
||||
self.elem.childNodes.append(child)
|
||||
|
||||
|
||||
class OptionElement(Element):
|
||||
"""" Used by ListWidget to specify options """
|
||||
type = 'option'
|
||||
|
|
@ -510,6 +531,32 @@ class Widget(Element):
|
|||
self.elem.setAttribute('name', name)
|
||||
self.elem.setAttribute('type', self.type)
|
||||
|
||||
def setInternalCallback(self, callback, fields, data_elts=None):
|
||||
"""Set an internal UI callback when the widget value is changed.
|
||||
|
||||
The internal callbacks are NO callback ids, they are strings from
|
||||
a predefined set of actions that are running in the scope of XMLUI.
|
||||
|
||||
@param callback (string): a value from:
|
||||
- 'copy': process the widgets given in 'fields' two by two, by
|
||||
copying the values of one widget to the other. Target widgets
|
||||
of type List do not accept the empty value.
|
||||
- 'move': same than copy but moves the values if the source widget
|
||||
is not a List.
|
||||
- 'groups_of_contact': process the widgets two by two, assume A is
|
||||
is a list of JID and B a list of groups, select in B the groups
|
||||
to which the JID selected in A belongs.
|
||||
- more operation to be added when necessary...
|
||||
@param fields (list): a list of widget names (string)
|
||||
@param data_elts (list[Element]): extra data elements
|
||||
"""
|
||||
self.elem.setAttribute('internal_callback', callback)
|
||||
if fields:
|
||||
for field in fields:
|
||||
InternalFieldElement(self, field)
|
||||
if data_elts:
|
||||
InternalDataElement(self, data_elts)
|
||||
|
||||
|
||||
class InputWidget(Widget):
|
||||
pass
|
||||
|
|
|
|||
Loading…
Reference in a new issue