changeset 69:a9c6b089070d

xmlui: improvments to prepare parameters: - added missing implementation: JidsListWidget (just a simple ListWidget for now), IntWidget, TabsContainer - _xmluiOnChange implemented for StringWidget, PasswordWidget, IntWidget and BoolWidget - added post treatment handling, as it is needed to adjust TabsContainer height - a "save" button is added on params
author Goffi <goffi@goffi.org>
date Sun, 18 Dec 2016 10:24:29 +0100
parents 4a1e1012337e
children 46b5f3ecf6a1
files src/cagou/core/xmlui.py src/cagou/kv/xmlui.kv
diffstat 2 files changed, 106 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/src/cagou/core/xmlui.py	Sat Dec 17 14:34:45 2016 +0100
+++ b/src/cagou/core/xmlui.py	Sun Dec 18 10:24:29 2016 +0100
@@ -24,6 +24,7 @@
 from sat_frontends.tools import xmlui
 from kivy.uix.scrollview import ScrollView
 from kivy.uix.gridlayout import GridLayout
+from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelItem
 from kivy.uix.textinput import TextInput
 from kivy.uix.label import Label
 from kivy.uix.button import Button
@@ -38,6 +39,27 @@
 ## Widgets ##
 
 
+class TextInputOnChange(object):
+
+    def __init__(self):
+        self._xmlui_onchange_cb = None
+        self._got_focus = False
+
+    def _xmluiOnChange(self, callback):
+        self._xmlui_onchange_cb = callback
+
+    def on_focus(self, instance, focus):
+        # we need to wait for first focus, else initial value
+        # will trigger a on_text
+        if not self._got_focus and focus:
+            self._got_focus = True
+
+    def on_text(self, instance, new_text):
+        log.debug("on_text: %s" % new_text)
+        if self._xmlui_onchange_cb is not None and self._got_focus:
+            self._xmlui_onchange_cb(self)
+
+
 class EmptyWidget(xmlui.EmptyWidget, Widget):
 
     def __init__(self, _xmlui_parent):
@@ -58,10 +80,11 @@
     pass
 
 
-class StringWidget(xmlui.StringWidget, TextInput):
+class StringWidget(xmlui.StringWidget, TextInput, TextInputOnChange):
 
     def __init__(self, xmlui_parent, value, read_only=False):
         TextInput.__init__(self, text=value, multiline=False)
+        TextInputOnChange.__init__(self)
         self.readonly = read_only
 
     def _xmluiSetValue(self, value):
@@ -194,11 +217,19 @@
         self._on_change = callback
 
 
-class PasswordWidget(xmlui.PasswordWidget, TextInput):
+class JidsListWidget(ListWidget):
+    # TODO: real list dedicated to jids
+
+    def __init__(self, _xmlui_parent, jids, flags):
+        ListWidget.__init__(self, _xmlui_parent, [(j,j) for j in jids], [], flags)
+
+
+class PasswordWidget(xmlui.PasswordWidget, TextInput, TextInputOnChange):
 
     def __init__(self, _xmlui_parent, value, read_only=False):
         TextInput.__init__(self, password=True, multiline=False,
             text=value, readonly=read_only, size=(100,25), size_hint=(1,None))
+        TextInputOnChange.__init__(self)
 
     def _xmluiSetValue(self, value):
         self.text = value
@@ -220,6 +251,24 @@
     def _xmluiGetValue(self):
         return self.active
 
+    def _xmluiOnChange(self, callback):
+        self.bind(active=lambda instance, value: callback(instance))
+
+
+class IntWidget(xmlui.IntWidget, TextInput, TextInputOnChange):
+
+    def __init__(self, _xmlui_parent, value, read_only=False):
+        TextInput.__init__(self, text=value, input_filter='int', multiline=False)
+        TextInputOnChange.__init__(self)
+        if read_only:
+            self.disabled = True
+
+    def _xmluiSetValue(self, value):
+        self.text = value
+
+    def _xmluiGetValue(self):
+        return self.text
+
 
 ## Containers ##
 
@@ -227,6 +276,7 @@
 class VerticalContainer(xmlui.VerticalContainer, GridLayout):
 
     def __init__(self, xmlui_parent):
+        self.xmlui_parent = xmlui_parent
         GridLayout.__init__(self)
 
     def _xmluiAppend(self, widget):
@@ -236,12 +286,46 @@
 class PairsContainer(xmlui.PairsContainer, GridLayout):
 
     def __init__(self, xmlui_parent):
+        self.xmlui_parent = xmlui_parent
         GridLayout.__init__(self)
 
     def _xmluiAppend(self, widget):
         self.add_widget(widget)
 
 
+class TabsPanelContainer(TabbedPanelItem):
+
+    def _xmluiAppend(self, widget):
+        self.add_widget(widget)
+
+
+class TabsContainer(xmlui.TabsContainer, TabbedPanel):
+
+    def __init__(self, xmlui_parent):
+        self.xmlui_parent = xmlui_parent
+        xmlui_panel = xmlui_parent
+        while not isinstance(xmlui_panel, XMLUIPanel):
+            xmlui_panel = xmlui_panel.xmlui_parent
+        xmlui_panel.addPostTreat(self._postTreat)
+        TabbedPanel.__init__(self, do_default_tab=False)
+
+    def _xmluiAddTab(self, label, selected):
+        tab = TabsPanelContainer(text=label)
+        self.add_widget(tab)
+        return tab
+
+    def _postTreat(self):
+        """bind minimum height of tabs' content so self.height is adapted"""
+        # we need to do this in postTreat because contents exists after UI construction
+        for t in self.tab_list:
+            t.content.bind(minimum_height=self._updateHeight)
+
+    def _updateHeight(self, instance, height):
+        """Called after UI is constructed (so height can be calculated)"""
+        # needed because TabbedPanel doesn't have a minimum_height property
+        self.height = max([t.content.minimum_height for t in self.tab_list]) + self.tab_height + 5
+
+
 class AdvancedListRow(GridLayout):
     global_index = 0
     index = properties.ObjectProperty()
@@ -268,7 +352,8 @@
 
 class AdvancedListContainer(xmlui.AdvancedListContainer, GridLayout):
 
-    def __init__(self, _xmlui_parent, columns, selectable='no'):
+    def __init__(self, xmlui_parent, columns, selectable='no'):
+        self.xmlui_parent = xmlui_parent
         GridLayout.__init__(self)
         self._columns = columns
         self.selectable = selectable != 'no'
@@ -365,6 +450,7 @@
         ScrollView.__init__(self)
         self.close_cb = None
         self._grid = XMLUIPanelGrid()
+        self._post_treats = []  # list of callback to call after UI is constructed
         ScrollView.add_widget(self, self._grid)
         xmlui.XMLUIPanel.__init__(self, host, parsed_xml, title, flags, callback, profile)
 
@@ -380,8 +466,16 @@
         else:
             G.host.closeUI()
 
+    def addPostTreat(self, callback):
+        self._post_treats.append(callback)
+
+    def _postTreatCb(self):
+        for cb in self._post_treats:
+            cb()
+        del self._post_treats
+
     def constructUI(self, parsed_dom):
-        xmlui.XMLUIPanel.constructUI(self, parsed_dom)
+        xmlui.XMLUIPanel.constructUI(self, parsed_dom, self._postTreatCb)
         if self.xmlui_title:
             self.add_widget(Title(text=self.xmlui_title))
         self.add_widget(self.main_cont)
@@ -393,6 +487,10 @@
                 cancel_btn = FormButton(text=_(u"Cancel"))
                 cancel_btn.bind(on_press=self.onFormCancelled)
                 self.add_widget(cancel_btn)
+        elif self.type == 'param':
+            save_btn = FormButton(text=_(u"Save"))
+            save_btn.bind(on_press=self.onSaveParams)
+            self.add_widget(save_btn)
         self.add_widget(Widget())  # to have elements on the top
 
     def show(self, *args, **kwargs):
--- a/src/cagou/kv/xmlui.kv	Sat Dec 17 14:34:45 2016 +0100
+++ b/src/cagou/kv/xmlui.kv	Sun Dec 18 10:24:29 2016 +0100
@@ -78,6 +78,10 @@
     size_hint: 1, None
     height: self.minimum_height
 
+<TabsContainer>:
+    size_hint: 1, None
+    height: 100
+
 <FormButton>:
     size_hint: 1, None
     height: dp(button_height)