changeset 250:ff1efdeff53f

core: notifs can now have a custom icon and be clickable: - new host.doAction method, to open a specific widget/target (chat only for now) - when adding a notif, symbol can now be specified - an action can be linked to a notification - notifs design improvments - plugins contact list and chat use the new doAction/notif action - if None is given as old widget in SwitchWidget, the new getWidgetToSwitch method is used to select one to switch.
author Goffi <goffi@goffi.org>
date Sat, 26 Jan 2019 20:24:48 +0100
parents 5d69e4cab925
children 1f579baf787a
files cagou/core/cagou_main.py cagou/kv/root_widget.kv cagou/plugins/plugin_wid_chat.py cagou/plugins/plugin_wid_contact_list.py
diffstat 4 files changed, 98 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/cagou/core/cagou_main.py	Sat Jan 26 20:24:48 2019 +0100
+++ b/cagou/core/cagou_main.py	Sat Jan 26 20:24:48 2019 +0100
@@ -54,6 +54,7 @@
 from kivy.uix.screenmanager import (ScreenManager, Screen,
                                     FallOutTransition, RiseInTransition)
 from kivy.uix.dropdown import DropDown
+from kivy.uix.behaviors import ButtonBehavior
 from kivy.core.window import Window
 from kivy.animation import Animation
 from kivy.metrics import dp
@@ -104,13 +105,21 @@
     message = properties.StringProperty()
     level = properties.OptionProperty(C.XMLUI_DATA_LVL_DEFAULT,
                                       options=list(C.XMLUI_DATA_LVLS))
+    symbol = properties.StringProperty()
+    action = properties.ObjectProperty()
 
 
-class NoteDrop(BoxLayout):
+class NoteDrop(ButtonBehavior, BoxLayout):
     title = properties.StringProperty()
     message = properties.StringProperty()
     level = properties.OptionProperty(C.XMLUI_DATA_LVL_DEFAULT,
                                       options=list(C.XMLUI_DATA_LVLS))
+    symbol = properties.StringProperty()
+    action = properties.ObjectProperty()
+
+    def on_press(self):
+        if self.action is not None:
+            self.parent.parent.select(self.action)
 
 
 class NotesDrop(DropDown):
@@ -123,10 +132,24 @@
     def open(self, widget):
         self.clear_widgets()
         for n in self.notes:
-            self.add_widget(NoteDrop(title=n.title, message=n.message, level=n.level))
+            kwargs = {
+                u'title': n.title,
+                u'message': n.message,
+                u'level': n.level
+            }
+            if n.symbol is not None:
+                kwargs[u'symbol'] = n.symbol
+            if n.action is not None:
+                kwargs[u'action'] = n.action
+            self.add_widget(NoteDrop(title=n.title, message=n.message, level=n.level,
+                                     symbol=n.symbol, action=n.action))
         self.add_widget(self.clear_btn)
         super(NotesDrop, self).open(widget)
 
+    def on_select(self, action_kwargs):
+        app = App.get_running_app()
+        app.host.doAction(**action_kwargs)
+
 
 class RootHeadWidget(BoxLayout):
     """Notifications widget"""
@@ -149,8 +172,17 @@
         """
         self.notifs_icon.addNotif(callback, *args, **kwargs)
 
-    def addNote(self, title, message, level):
-        note = Note(title=title, message=message, level=level)
+    def addNote(self, title, message, level, symbol, action):
+        kwargs = {
+            u'title': title,
+            u'message': message,
+            u'level': level
+        }
+        if symbol is not None:
+            kwargs[u'symbol'] = symbol
+        if action is not None:
+            kwargs[u'action'] = action
+        note = Note(**kwargs)
         self.notes.append(note)
         if self.notes_event is None:
             self.notes_event = Clock.schedule_interval(self._displayNextNote, 5)
@@ -237,8 +269,8 @@
         """Add a notification for an action"""
         self.head_widget.addNotif(handler, action_data, id_, security_limit, profile)
 
-    def addNote(self, title, message, level):
-        self.head_widget.addNote(title, message, level)
+    def addNote(self, title, message, level, symbol, action):
+        self.head_widget.addNote(title, message, level, symbol, action)
 
     def addNotifUI(self, ui):
         self.head_widget.addNotifUI(ui)
@@ -662,9 +694,12 @@
     def switchWidget(self, old, new):
         """Replace old widget by new one
 
-        old(CagouWidget): CagouWidget instance or a child
+        old(CagouWidgetn None): CagouWidget instance or a child
+            None to select automatically widget to switch
         new(CagouWidget): new widget instance
         """
+        if old is None:
+            old = self.getWidgetToSwitch()
         to_change = None
         if isinstance(old, CagouWidget):
             to_change = old
@@ -730,6 +765,42 @@
             w.addTarget(t)
         return w
 
+    def getWidgetToSwitch(self):
+        """Choose best candidate when we need to switch widget and old is not specified
+
+        @return (CagouWidget): widget to switch
+        """
+        if self.selected_widget is not None:
+            return self.selected_widget
+        # no widget is selected we check if we have any default widget
+        default_cls = self.default_wid['main']
+        for w in self.visible_widgets:
+            if isinstance(w, default_cls):
+                return w
+
+        # no default widget found, we return the first widget
+        return next(iter(self.visible_widgets))
+
+    def doAction(self, action, target, profiles):
+        """Launch an action handler by a plugin
+
+        @param action(unicode): action to do, can be:
+            - chat: open a chat widget
+        @param target(unicode): target of the action
+        @param profiles(list[unicode]): profiles to use
+        """
+        try:
+            # FIXME: Q&D way to get chat plugin, should be replaced by a clean method
+            #        in host
+            plg_infos = [p for p in self.getPluggedWidgets()
+                         if action in p['import_name']][0]
+        except IndexError:
+            log.warning(u"No plugin widget found to do {action}".format(action=action))
+        else:
+            factory = plg_infos['factory']
+            self.switchWidget(None,
+                factory(plg_infos, target=target, profiles=profiles))
+
     ## menus ##
 
     def _menusGetCb(self, backend_menus):
@@ -766,9 +837,10 @@
                 message=_(u'error while processing: {msg}')):
         self.addNote(title, message.format(msg=failure_), level=C.XMLUI_DATA_LVL_WARNING)
 
-    def addNote(self, title, message, level=C.XMLUI_DATA_LVL_INFO):
+    def addNote(self, title, message, level=C.XMLUI_DATA_LVL_INFO, symbol=None,
+        action=None):
         """add a note (message which disappear) to root widget's header"""
-        self.app.root.addNote(title, message, level)
+        self.app.root.addNote(title, message, level, symbol, action)
 
     def addNotifUI(self, ui):
         """add a notification with a XMLUI attached
--- a/cagou/kv/root_widget.kv	Sat Jan 26 20:24:48 2019 +0100
+++ b/cagou/kv/root_widget.kv	Sat Jan 26 20:24:48 2019 +0100
@@ -33,7 +33,7 @@
 <NoteDrop>:
     orientation: 'horizontal'
     size_hint: 1, None
-    height: label.height
+    height: max(label.height, dp(45))
     symbol: symbol
     canvas.before:
         BorderImage:
@@ -45,14 +45,14 @@
         width: dp(20)
     Symbol:
         id: symbol
-        size_hint: None, None
+        size_hint: None, 1
         width: dp(30)
-        height: label.height
         padding_y: dp(10)
         valign: 'top'
         haligh: 'right'
-        symbol: root.level
+        symbol: root.symbol or root.level
         color:
+            C.COLOR_PRIM_LIGHT if root.symbol is None else \
             {C.XMLUI_DATA_LVL_INFO: app.c_prim_light,\
             C.XMLUI_DATA_LVL_WARNING: C.COLOR_WARNING,\
             C.XMLUI_DATA_LVL_ERROR: C.COLOR_ERROR}[root.level]
--- a/cagou/plugins/plugin_wid_chat.py	Sat Jan 26 20:24:48 2019 +0100
+++ b/cagou/plugins/plugin_wid_chat.py	Sat Jan 26 20:24:48 2019 +0100
@@ -527,14 +527,24 @@
                 if not is_visible:
                     G.host.addNote(
                         _(u"private message"),
-                        notif_msg
+                        notif_msg,
+                        symbol = u"chat",
+                        action = {
+                            "action": u'chat',
+                            "target": self.target,
+                            "profiles": self.profiles}
                         )
         else:
             if mess_data.mention and not mess_data.history:
                 notif_msg = self._get_notif_msg(mess_data)
                 G.host.addNote(
                     _(u"mention"),
-                    notif_msg
+                    notif_msg,
+                    symbol = u"chat",
+                    action = {
+                        "action": u'chat',
+                        "target": self.target,
+                        "profiles": self.profiles}
                     )
                 if not Window.focus:
                     G.host.desktop_notif(
--- a/cagou/plugins/plugin_wid_contact_list.py	Sat Jan 26 20:24:48 2019 +0100
+++ b/cagou/plugins/plugin_wid_contact_list.py	Sat Jan 26 20:24:48 2019 +0100
@@ -117,18 +117,7 @@
         assert self.profile
         # XXX: for now clicking on an item launch the corresponding Chat widget
         #      behaviour should change in the future
-        try:
-            # FIXME: Q&D way to get chat plugin, should be replaced by a clean method
-            #        in host
-            plg_infos = [p for p in G.host.getPluggedWidgets()
-                         if 'chat' in p['import_name']][0]
-        except IndexError:
-            log.warning(u"No plugin widget found to display chat")
-        else:
-            factory = plg_infos['factory']
-            G.host.switchWidget(self, factory(plg_infos,
-                                              jid.JID(self.jid),
-                                              profiles=[self.profile]))
+        G.host.doAction(u'chat', jid.JID(self.jid), [self.profile])
 
     def getMenuChoices(self):
         choices = []