changeset 197:c7d15ef4bfa8

core (menu): new EntitiesSelectorMenu: TransferMenu base has been moved to a "SlideMenu" base class, which is used to create a new "EntitiesSelectorMenu". This later widget allows user to select entities from her roster, and the selected jids are then sent back to user.
author Goffi <goffi@goffi.org>
date Wed, 23 May 2018 21:27:28 +0200
parents 519b3a29743c
children 60b63c3e63a1
files cagou/core/menu.py cagou/kv/menu.kv
diffstat 2 files changed, 119 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/cagou/core/menu.py	Wed May 23 21:25:08 2018 +0200
+++ b/cagou/core/menu.py	Wed May 23 21:27:28 2018 +0200
@@ -22,14 +22,17 @@
 from sat.core import log as logging
 log = logging.getLogger(__name__)
 from cagou.core.constants import Const as C
+from cagou.core.common import JidToggle
 from kivy.uix.boxlayout import BoxLayout
 from kivy.uix.label import Label
 from kivy.uix.popup import Popup
+from cagou.core.utils import FilterBehavior
 from kivy import properties
 from kivy.garden import contextmenu
 from sat_frontends.quick_frontend import quick_menus
 from kivy.core.window import Window
 from kivy.animation import Animation
+from kivy.metrics import dp
 from cagou import G
 import webbrowser
 
@@ -175,42 +178,35 @@
             return True
 
 
-class TransferMenu(BoxLayout):
-    """transfer menu which handle display and callbacks"""
-    # callback will be called with path to file to transfer
+class SideMenu(BoxLayout):
+    base_size_hint_close = (0, 1)
+    base_size_hint_open = (0.4, 1)
+    bg_color = properties.ListProperty([0, 0, 0, 1])
+    # callback will be called with arguments relevant to menu
     callback = properties.ObjectProperty()
+    # call do_callback even when menu is cancelled
+    callback_on_close = properties.BooleanProperty(False)
     # cancel callback need to remove the widget for UI
     # will be called with the widget to remove as argument
     cancel_cb = properties.ObjectProperty()
-    # profiles if set will be sent to transfer widget, may be used to get specific files
-    profiles = properties.ObjectProperty()
-    transfer_txt = _(u"Beware! The file will be sent to your server and stay unencrypted there\nServer admin(s) can see the file, and they choose how, when and if it will be deleted")
-    send_txt = _(u"The file will be sent unencrypted directly to your contact (without transiting by the server), except in some cases")
-    items_layout = properties.ObjectProperty()
 
     def __init__(self, **kwargs):
-        super(TransferMenu, self).__init__(**kwargs)
+        super(SideMenu, self).__init__(**kwargs)
         if self.cancel_cb is None:
-            self.cancel_cb = self.onTransferCancelled
-        if self.profiles is None:
-            self.profiles = iter(G.host.profiles)
-        for plug_info in G.host.getPluggedWidgets(type_=C.PLUG_TYPE_TRANSFER):
-            item = TransferItem(
-                plug_info = plug_info
-                )
-            self.items_layout.add_widget(item)
+            self.cancel_cb = self.onMenuCancelled
 
     def show(self, caller_wid=None):
-        self.size_hint_y = 0
         Window.bind(on_keyboard=self.key_input)
         G.host.app.root.add_widget(self)
-        Animation(size_hint_y=0.5, d=0.3, t='out_back').start(self)
+        Animation(size_hint=self.base_size_hint_open, d=0.3, t='out_back').start(self)
 
     def hide(self):
         Window.unbind(on_keyboard=self.key_input)
-        anim = Animation(size_hint_y=0, d=0.2)
+        anim = Animation(size_hint=self.base_size_hint_close, d=0.2)
         anim.bind(on_complete=lambda anim, menu: self.parent.remove_widget(self))
         anim.start(self)
+        if self.callback_on_close:
+            self.do_callback()
 
     def on_touch_down(self, touch):
         # we remove the menu if we click outside
@@ -219,7 +215,7 @@
         if not self.collide_point(*touch.pos):
             self.hide()
         else:
-            return super(TransferMenu, self).on_touch_down(touch)
+            return super(SideMenu, self).on_touch_down(touch)
         return True
 
     def key_input(self, window, key, scancode, codepoint, modifier):
@@ -227,13 +223,38 @@
             self.hide()
             return True
 
+    def onMenuCancelled(self, wid, cleaning_cb=None):
+        self._closeUI(wid)
+        if cleaning_cb is not None:
+            cleaning_cb()
+
     def _closeUI(self, wid):
         G.host.closeUI()
 
-    def onTransferCancelled(self, wid, cleaning_cb=None):
-        self._closeUI(wid)
-        if cleaning_cb is not None:
-            cleaning_cb()
+    def do_callback(self, *args, **kwargs):
+        log.warning(u"callback not implemented")
+
+
+class TransferMenu(SideMenu):
+    """transfer menu which handle display and callbacks"""
+    # callback will be called with path to file to transfer
+    # profiles if set will be sent to transfer widget, may be used to get specific files
+    profiles = properties.ObjectProperty()
+    transfer_txt = _(u"Beware! The file will be sent to your server and stay unencrypted there\nServer admin(s) can see the file, and they choose how, when and if it will be deleted")
+    send_txt = _(u"The file will be sent unencrypted directly to your contact (without transiting by the server), except in some cases")
+    items_layout = properties.ObjectProperty()
+    base_size_hint_close = (1, 0)
+    base_size_hint_open = (1, 0.5)
+
+    def __init__(self, **kwargs):
+        super(TransferMenu, self).__init__(**kwargs)
+        if self.profiles is None:
+            self.profiles = iter(G.host.profiles)
+        for plug_info in G.host.getPluggedWidgets(type_=C.PLUG_TYPE_TRANSFER):
+            item = TransferItem(
+                plug_info = plug_info
+                )
+            self.items_layout.add_widget(item)
 
     def do_callback(self, plug_info):
         self.parent.remove_widget(self)
@@ -252,3 +273,36 @@
             wid = plug_info['factory'](plug_info, onTransferCb, self.cancel_cb, self.profiles)
             if not external:
                 G.host.showExtraUI(wid)
+
+
+class EntitiesSelectorMenu(SideMenu, FilterBehavior):
+    """allow to select entities from roster"""
+    profiles = properties.ObjectProperty()
+    layout = properties.ObjectProperty()
+    instructions = properties.StringProperty(_(u"Please select entities"))
+    filter_input = properties.ObjectProperty()
+
+    def __init__(self, **kwargs):
+        super(EntitiesSelectorMenu, self).__init__(**kwargs)
+        self.filter_input.bind(text=self.do_filter_input)
+        if self.profiles is None:
+            self.profiles = iter(G.host.profiles)
+        for profile in self.profiles:
+            for jid_, jid_data in G.host.contact_lists[profile].all_iter:
+                jid_wid = JidToggle(
+                    jid=jid_,
+                    profile=profile)
+                self.layout.add_widget(jid_wid)
+
+    def do_callback(self):
+        if self.callback is not None:
+            jids = [c.jid for c in self.layout.children if c.state == 'down']
+            self.callback(jids)
+
+    def do_filter_input(self, filter_input, text):
+        self.layout.spacing = 0 if text else dp(5)
+        self.do_filter(self.layout.children,
+                       text,
+                       lambda c: c.jid,
+                       width_cb=lambda c: c.width,
+                       height_cb=lambda c: dp(70))
--- a/cagou/kv/menu.kv	Wed May 23 21:25:08 2018 +0200
+++ b/cagou/kv/menu.kv	Wed May 23 21:27:28 2018 +0200
@@ -91,3 +91,43 @@
         text_size: self.size
         halign: "center"
         valign: "top"
+
+
+<SideMenu>:
+    orientation: "vertical"
+    size_hint: self.base_size_hint_close
+    canvas.before:
+        Color:
+            rgba: self.bg_color
+        Rectangle:
+            pos: self.pos
+            size: self.size
+
+
+<EntitiesSelectorMenu>:
+    bg_color: 0, 0, 0, 0.9
+    filter_input: filter_input
+    layout: layout
+    callback_on_close: True
+    Label:
+        size_hint: 1, None
+        text_size: root.width, None
+        size: self.texture_size
+        padding: dp(5), dp(5)
+        color: 1, 1, 1, 1
+        text: root.instructions
+        halign: "center"
+    TextInput:
+        id: filter_input
+        size_hint: 1, None
+        height: dp(32)
+        multiline: False
+        hint_text: _(u"enter filter here")
+    ScrollView:
+        size_hint: 1, 1
+        BoxLayout:
+            id: layout
+            orientation: "vertical"
+            size_hint: 1, None
+            height: self.minimum_height
+            spacing: dp(5)