changeset 286:4772ba26623f

xmlui: many improvments: - use a white background to be coherent with the rest of the UI - cancel button is added on "window" XMLUIs - use color for submit/save buttons - VerticalContainer is not a ScrollView anymore, XMLUIPanel now has the main ScrollView - when a TabsContainer is used as main container, main XMLUIPanel ScrollView touch events are disabled, to improve user experience (Kivy doesn't support well ScrollView inside an other ScrollView) - use BoxLayout instead of GridLayout in AdvancedList*, as they use one row/col only - fixed DividerWidget line location - added margins in XMLUI panels - renamed Settings main class to CagouSettings, to avoid conflict with Kivy's Settings class (which is causing troubles with kv)
author Goffi <goffi@goffi.org>
date Wed, 27 Mar 2019 22:22:51 +0100
parents 3f7e227aab00
children 5d96bcd3bfec
files cagou/core/cagou_main.py cagou/core/xmlui.py cagou/kv/xmlui.kv cagou/plugins/plugin_wid_settings.py
diffstat 4 files changed, 132 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/cagou/core/cagou_main.py	Wed Mar 27 22:22:51 2019 +0100
+++ b/cagou/core/cagou_main.py	Wed Mar 27 22:22:51 2019 +0100
@@ -77,10 +77,12 @@
     notification = None
     log.warning(_(u"Can't import plyer, some features disabled"))
 
+
+## platform specific settings ##
+
 if kivy_utils.platform == "android":
     import socket
 
-
     # FIXME: move to separate android module
     # sys.platform is "linux" on android by default
     # so we change it to allow backend to detect android
@@ -93,6 +95,8 @@
     STATE_STOPPED = "stopped"
 
 
+## General Configuration ##
+
 # we want white background by default
 Window.clearcolor = (1, 1, 1, 1)
 # we don't want multi-touch emulation with mouse
--- a/cagou/core/xmlui.py	Wed Mar 27 22:22:51 2019 +0100
+++ b/cagou/core/xmlui.py	Wed Mar 27 22:22:51 2019 +0100
@@ -257,15 +257,14 @@
 ## Containers ##
 
 
-class VerticalContainer(xmlui.VerticalContainer, ScrollView):
-    layout = properties.ObjectProperty(None)
+class VerticalContainer(xmlui.VerticalContainer, BoxLayout):
 
     def __init__(self, xmlui_parent):
         self.xmlui_parent = xmlui_parent
-        ScrollView.__init__(self)
+        BoxLayout.__init__(self)
 
     def _xmluiAppend(self, widget):
-        self.layout.add_widget(widget)
+        self.add_widget(widget)
 
 
 class PairsContainer(xmlui.PairsContainer, GridLayout):
@@ -301,7 +300,7 @@
         return tab
 
 
-class AdvancedListRow(GridLayout):
+class AdvancedListRow(BoxLayout):
     global_index = 0
     index = properties.ObjectProperty()
     selected = properties.BooleanProperty(False)
@@ -325,11 +324,11 @@
         return super(AdvancedListRow, self).on_touch_down(touch)
 
 
-class AdvancedListContainer(xmlui.AdvancedListContainer, GridLayout):
+class AdvancedListContainer(xmlui.AdvancedListContainer, BoxLayout):
 
     def __init__(self, xmlui_parent, columns, selectable='no'):
         self.xmlui_parent = xmlui_parent
-        GridLayout.__init__(self)
+        BoxLayout.__init__(self)
         self._columns = columns
         self.selectable = selectable != 'no'
         self._current_row = None
@@ -376,6 +375,7 @@
         """ Call callback with widget as only argument """
         self._xmlui_select_cb = callback
 
+
 ## Dialogs ##
 
 
@@ -498,15 +498,30 @@
 class FormButton(Button):
     pass
 
+class SubmitButton(FormButton):
+    pass
 
-class XMLUIPanel(xmlui.XMLUIPanel, BoxLayout):
+class CancelButton(FormButton):
+    pass
+
+class SaveButton(FormButton):
+    pass
+
+
+class XMLUIPanel(xmlui.XMLUIPanel, ScrollView):
     widget_factory = WidgetFactory()
+    layout = properties.ObjectProperty()
 
     def __init__(self, host, parsed_xml, title=None, flags=None, callback=None,
                  ignore=None, whitelist=None, profile=C.PROF_KEY_NONE):
-        BoxLayout.__init__(self)
+        ScrollView.__init__(self)
         self.close_cb = None
         self._post_treats = []  # list of callback to call after UI is constructed
+
+        # used to workaround touch issues when a ScrollView is used inside this
+        # one. This happens notably when a TabsContainer is used as main container
+        # (this is the case with settings).
+        self._skip_scroll_events = False
         xmlui.XMLUIPanel.__init__(self,
                                   host,
                                   parsed_xml,
@@ -516,11 +531,30 @@
                                   ignore=ignore,
                                   whitelist=whitelist,
                                   profile=profile)
+        self.bind(height=self.onHeight)
+
+    def on_touch_down(self, touch, after=False):
+        if self._skip_scroll_events:
+            return super(ScrollView, self).on_touch_down(touch)
+        else:
+            return super(XMLUIPanel, self).on_touch_down(touch)
+
+    def on_touch_up(self, touch, after=False):
+        if self._skip_scroll_events:
+            return super(ScrollView, self).on_touch_up(touch)
+        else:
+            return super(XMLUIPanel, self).on_touch_up(touch)
+
+    def on_touch_move(self, touch, after=False):
+        if self._skip_scroll_events:
+            return super(ScrollView, self).on_touch_move(touch)
+        else:
+            return super(XMLUIPanel, self).on_touch_move(touch)
 
     def setCloseCb(self, close_cb):
         self.close_cb = close_cb
 
-    def _xmluiClose(self):
+    def _xmluiClose(self, *__):
         if self.close_cb is not None:
             self.close_cb(self)
         else:
@@ -545,20 +579,34 @@
     def 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)
+            self.layout.add_widget(Title(text=self.xmlui_title))
+        if isinstance(self.main_cont, TabsContainer):
+            # cf. comments above
+            self._skip_scroll_events = True
+        self.layout.add_widget(self.main_cont)
         if self.type == 'form':
-            submit_btn = FormButton(text=_(u"Submit"))
+            submit_btn = SubmitButton()
             submit_btn.bind(on_press=self.onFormSubmitted)
-            self.add_widget(submit_btn)
+            self.layout.add_widget(submit_btn)
             if not 'NO_CANCEL' in self.flags:
-                cancel_btn = FormButton(text=_(u"Cancel"))
+                cancel_btn = CancelButton(text=_(u"Cancel"))
                 cancel_btn.bind(on_press=self.onFormCancelled)
-                self.add_widget(cancel_btn)
+                self.layout.add_widget(cancel_btn)
         elif self.type == 'param':
-            self.save_btn = FormButton(text=_(u"Save"), disabled=True)
+            self.save_btn = SaveButton(text=_(u"Save"), disabled=True)
             self.save_btn.bind(on_press=self._saveButtonCb)
-            self.add_widget(self.save_btn)
+            self.layout.add_widget(self.save_btn)
+        elif self.type == 'window':
+            cancel_btn = CancelButton(text=_(u"Cancel"))
+            cancel_btn.bind(on_press=self._xmluiClose)
+            self.layout.add_widget(cancel_btn)
+
+    def onHeight(self, __, height):
+        if isinstance(self.main_cont, TabsContainer):
+            # when the main
+            other_children_height = sum([c.height for c in self.layout.children
+                                         if c is not self.main_cont])
+            self.main_cont.height = height - other_children_height
 
     def show(self, *args, **kwargs):
         if not self.user_action and not kwargs.get("force", False):
--- a/cagou/kv/xmlui.kv	Wed Mar 27 22:22:51 2019 +0100
+++ b/cagou/kv/xmlui.kv	Wed Mar 27 22:22:51 2019 +0100
@@ -17,20 +17,16 @@
 #:set common_height 30
 #:set button_height 50
 
-<Title>:
-    color: 1, 1, 1, 1
 
 <EmptyWidget,StringWidget,PasswordWidget,JidInputWidget>:
     size_hint: 1, None
     height: dp(common_height)
-    color: 1, 1, 1, 1
 
 
 <TextWidget,LabelWidget,JidWidget>:
     size_hint: 1, 1
     size_hint_min_y: max(dp(common_height), self.texture_size[1])
     text_size: self.width, None
-    color: 1, 1, 1, 1
 
 
 <StringWidget,PasswordWidget,IntWidget>:
@@ -54,13 +50,13 @@
 
 <DividerWidget>:
     size_hint: 1, None
-    height: dp(20)
+    height: dp(12)
     canvas.before:
         Color:
-            rgba: 1, 1, 1, 0.8
+            rgba: 0, 0, 0, 1
         Line
-            points: 0, self.y + dp(10), self.width, self.y + dp(10)
-            width: dp(3)
+            points: self.x, self.y + dp(5), self.x + self.width, self.y + dp(5)
+            width: dp(2)
 
 
 <ListWidgetItem>:
@@ -84,15 +80,15 @@
 
 
 <AdvancedListRow>:
+    orientation: "horizontal"
+    size_hint: 1, None
+    height: self.minimum_height
     canvas.before:
         Color:
-            rgba: 1, 1, 1, 0.2 if self.global_index%2 else 0.1
+            rgba: app.c_prim_light if self.global_index%2 else app.c_prim_dark
         Rectangle:
             pos: self.pos
             size: self.size
-    size_hint: 1, None
-    height: self.minimum_height
-    rows: 1
     canvas.after:
         Color:
             rgba: 0, 0, 1, 0.5 if self.selected else 0
@@ -102,23 +98,15 @@
 
 
 <AdvancedListContainer>:
-    cols: 1
     size_hint: 1, None
     height: self.minimum_height
+    orientation: "vertical"
 
 
 <VerticalContainer>:
-    size_hint: 1, 1
-    layout: layout
-    do_scroll_x: False
-    scroll_type: ['bars', 'content']
-    bar_width: dp(6)
-    BoxLayout:
-        id: layout
-        orientation: "vertical"
-        size_hint: 1, None
-        height: self.minimum_height
-
+    orientation: "vertical"
+    size_hint: 1, None
+    height: self.minimum_height
 
 <PairsContainer>:
     cols: 2
@@ -128,7 +116,8 @@
 
 
 <TabsContainer>:
-    size_hint: 1, 1
+    size_hint: 1, None
+    height: dp(200)
 
 <TabsPanelContainer>:
     layout: layout
@@ -136,15 +125,48 @@
         do_scroll_x: False
         scroll_type: ['bars', 'content']
         bar_width: dp(6)
+        canvas.before:
+            Color:
+                rgba: 1, 1, 1, 1
+            Rectangle:
+                pos: self.pos
+                size: self.size
         BoxLayout:
             id: layout
             orientation: "vertical"
             size_hint: 1, None
             height: self.minimum_height
+            canvas.before:
+                Color:
+                    rgba: 1, 1, 1, 1
+                Rectangle:
+                    pos: self.pos
+                    size: self.size
+
 
 <FormButton>:
     size_hint: 1, None
     height: dp(button_height)
+    color: 0, 0, 0, 1
+    bold: True
+
+
+<SubmitButton>:
+    text: _(u"Submit")
+    background_normal: ''
+    background_color: 0.33, 0.67, 0.0, 1
+
+
+<CancelButton>:
+    text: _(u"Cancel")
+    color: 1, 1, 1, 1
+    bold: False
+
+
+<SaveButton>:
+    text: _(u"Save")
+    background_normal: ''
+    background_color: 0.33, 0.67, 0.0, 1
 
 
 <FileDialog>:
@@ -171,11 +193,14 @@
 
 
 <XMLUIPanel>:
-    orientation: "vertical"
     size_hint: 1, 1
-    canvas.before:
-        Color:
-            rgba: 0, 0, 0, 1
-        Rectangle:
-            pos: self.pos
-            size: self.size
+    layout: layout
+    do_scroll_x: False
+    scroll_type: ['bars', 'content']
+    bar_width: dp(6)
+    BoxLayout:
+        id: layout
+        orientation: "vertical"
+        size_hint: 1, None
+        padding: app.MARGIN_LEFT, 0, app.MARGIN_RIGHT, 0
+        height: self.minimum_height
--- a/cagou/plugins/plugin_wid_settings.py	Wed Mar 27 22:22:51 2019 +0100
+++ b/cagou/plugins/plugin_wid_settings.py	Wed Mar 27 22:22:51 2019 +0100
@@ -31,13 +31,15 @@
 
 PLUGIN_INFO = {
     "name": _(u"settings"),
-    "main": "Settings",
+    "main": "CagouSettings",
     "description": _(u"Cagou/SàT settings"),
     "icon_symbol": u"wrench",
 }
 
 
-class Settings(quick_widgets.QuickWidget, cagou_widget.CagouWidget):
+class CagouSettings(quick_widgets.QuickWidget, cagou_widget.CagouWidget):
+    # XXX: this class can't be called "Settings", because Kivy has already a class
+    #      of this name, and the kv there would apply
 
     def __init__(self, host, target, profiles):
         quick_widgets.QuickWidget.__init__(self, G.host, target, profiles)