# HG changeset patch # User Goffi # Date 1553721771 -3600 # Node ID 4772ba26623fcb5b9587cdbb684c0fad117ff0e8 # Parent 3f7e227aab00c3ce650aa5804126a94675bb0e70 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) diff -r 3f7e227aab00 -r 4772ba26623f cagou/core/cagou_main.py --- 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 diff -r 3f7e227aab00 -r 4772ba26623f cagou/core/xmlui.py --- 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): diff -r 3f7e227aab00 -r 4772ba26623f cagou/kv/xmlui.kv --- 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 -: - 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 diff -r 3f7e227aab00 -r 4772ba26623f cagou/plugins/plugin_wid_settings.py --- 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)