changeset 109:7631325e11f4

chat: added completion of header's input, any entity in cache containing the entered text is shown
author Goffi <goffi@goffi.org>
date Sun, 15 Jan 2017 21:21:29 +0100
parents 953ddf817b8a
children 34dfe0e32064
files src/cagou/plugins/plugin_wid_chat.py
diffstat 1 files changed, 100 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/src/cagou/plugins/plugin_wid_chat.py	Sun Jan 15 21:21:25 2017 +0100
+++ b/src/cagou/plugins/plugin_wid_chat.py	Sun Jan 15 21:21:29 2017 +0100
@@ -32,6 +32,8 @@
 from sat_frontends.tools import jid
 from cagou.core import cagou_widget
 from cagou.core.image import Image
+from cagou.core.common import JidWidget
+from kivy.uix.dropdown import DropDown
 from cagou import G
 
 
@@ -131,6 +133,11 @@
         self.host.addListener('progressFinished', self.onProgressFinished, profiles)
         self._waiting_pids = {}  # waiting progress ids
         self.postInit()
+        # completion attribtues
+        self._hi_comp_data = None
+        self._hi_comp_last = None
+        self._hi_comp_dropdown = DropDown()
+        self._hi_comp_allowed = True
 
     @classmethod
     def factory(cls, plugin_info, target, profiles):
@@ -141,6 +148,99 @@
             target = G.host.profiles[profiles[0]].whoami
         return G.host.widgets.getOrCreateWidget(cls, target, on_new_widget=None, on_existing_widget=C.WIDGET_RECREATE, profiles=profiles)
 
+    ## header ##
+
+    def changeWidget(self, jid_):
+        """change current widget for a new one with given jid
+
+        @param jid_(jid.JID): jid of the widget to create
+        """
+        plugin_info = G.host.getPluginInfo(main=Chat)
+        factory = plugin_info['factory']
+        G.host.switchWidget(self, factory(plugin_info, jid_, profiles=[self.profile]))
+        self.header_input.text = ''
+
+    def onHeaderInput(self):
+        text = self.header_input.text.strip()
+        try:
+            if text.count(u'@') != 1 or text.count(u' '):
+                raise ValueError
+            jid_ = jid.JID(text)
+        except ValueError:
+            log.info(u"entered text is not a jid")
+            return
+
+        def discoCb(disco):
+            # TODO: check if plugin XEP-0045 is activated
+            if "conference" in [i[0] for i in disco[1]]:
+                G.host.bridge.mucJoin(unicode(jid_), "", "", self.profile, callback=self._mucJoinCb, errback=self._mucJoinEb)
+            else:
+                self.changeWidget(jid_)
+
+        def discoEb(failure):
+            log.warning(u"Disco failure, ignore this text: {}".format(failure))
+
+        G.host.bridge.discoInfos(jid_.domain, self.profile, callback=discoCb, errback=discoEb)
+
+    def onHeaderInputCompleted(self, input_wid, completed_text):
+        self._hi_comp_allowed = False
+        input_wid.text = completed_text
+        self._hi_comp_allowed = True
+        self._hi_comp_dropdown.dismiss()
+        self.onHeaderInput()
+
+    def onHeaderInputComplete(self, wid, text):
+        if not self._hi_comp_allowed:
+            return
+        text = text.lstrip()
+        if not text:
+            self._hi_comp_data = None
+            self._hi_comp_last = None
+            return
+
+        profile = list(self.profiles)[0]
+
+        if self._hi_comp_data is None:
+            # first completion, we build the initial list
+            comp_data = self._hi_comp_data = []
+            self._hi_comp_last = ''
+            for jid_, jid_data in G.host.contact_lists[profile].all_iter:
+                comp_data.append((jid_, jid_data))
+            comp_data.sort(key=lambda datum: datum[0])
+        else:
+            comp_data = self._hi_comp_data
+
+        # XXX: dropdown is rebuilt each time backspace is pressed or if the text is changed,
+        #      it works OK, but some optimisation may be done here
+        dropdown = self._hi_comp_dropdown
+
+        if not text.startswith(self._hi_comp_last) or not self._hi_comp_last:
+            # text has changed or backspace has been pressed, we restart
+            dropdown.clear_widgets()
+
+            for jid_, jid_data in comp_data:
+                nick = jid_data.get(u'nick', u'')
+                if text in jid_.bare or text in nick.lower():
+                    btn = JidWidget(
+                        jid = jid_.bare,
+                        profile = profile,
+                        size_hint = (0.5, None),
+                        nick = nick,
+                        on_release=lambda dummy, txt=jid_.bare: self.onHeaderInputCompleted(wid, txt)
+                        )
+                    dropdown.add_widget(btn)
+        else:
+            # more chars, we continue completion by removing unwanted widgets
+            to_remove = []
+            for c in dropdown.children[0].children:
+                if text not in c.jid and text not in (c.nick or ''):
+                    to_remove.append(c)
+            for c in to_remove:
+                dropdown.remove_widget(c)
+
+        dropdown.open(wid)
+        self._hi_comp_last = text
+
     def messageDataConverter(self, idx, mess_id):
         return {"mess_data": self.messages[mess_id]}
 
@@ -238,38 +338,6 @@
     def _mucJoinEb(self, failure):
         log.warning(u"Can't join room: {}".format(failure))
 
-    def changeWidget(self, jid_):
-        """change current widget for a new one with given jid
-
-        @param jid_(jid.JID): jid of the widget to create
-        """
-        plugin_info = G.host.getPluginInfo(main=Chat)
-        factory = plugin_info['factory']
-        G.host.switchWidget(self, factory(plugin_info, jid_, profiles=[self.profile]))
-        self.header_input.text = ''
-
-    def onHeaderInput(self):
-        text = self.header_input.text.strip()
-        try:
-            if text.count(u'@') != 1 or text.count(u' '):
-                raise ValueError
-            jid_ = jid.JID(text)
-        except ValueError:
-            log.info(u"entered text is not a jid")
-            return
-
-        def discoCb(disco):
-            # TODO: check if plugin XEP-0045 is activated
-            if "conference" in [i[0] for i in disco[1]]:
-                G.host.bridge.mucJoin(unicode(jid_), "", "", self.profile, callback=self._mucJoinCb, errback=self._mucJoinEb)
-            else:
-                self.changeWidget(jid_)
-
-        def discoEb(failure):
-            log.warning(u"Disco failure, ignore this text: {}".format(failure))
-
-        G.host.bridge.discoInfos(jid_.domain, self.profile, callback=discoCb, errback=discoEb)
-
     def _onDelete(self):
         self.host.removeListener('progressFinished', self.onProgressFinished)
         self.host.removeListener('progressError', self.onProgressError)