diff src/cagou/core/cagou_main.py @ 29:8b5827c43155

notes first draft: Implementation of XMLUI notes. There is a new header on top of root widget which display notifications, and notes are shown for a couple of seconds. A blue Cagou head appear when there are notes, and user can display 10 last when clicking on it. This header will probably not be present on platforms such as Android, because there is already a system-wide notifications handler which can be used instead (saving visual space).
author Goffi <goffi@goffi.org>
date Sun, 21 Aug 2016 15:15:25 +0200
parents 9f9532eb835f
children 4f9e701d76b4
line wrap: on
line diff
--- a/src/cagou/core/cagou_main.py	Sun Aug 21 15:02:18 2016 +0200
+++ b/src/cagou/core/cagou_main.py	Sun Aug 21 15:15:25 2016 +0200
@@ -32,11 +32,17 @@
 kivy.support.install_gobject_iteration()
 from kivy.app import App
 from kivy.lang import Builder
+from kivy import properties
 import xmlui
 from profile_manager import ProfileManager
 from widgets_handler import WidgetsHandler
+from kivy.clock import Clock
+from kivy.uix.label import Label
 from kivy.uix.boxlayout import BoxLayout
+from kivy.uix.screenmanager import ScreenManager, Screen
+from kivy.uix.dropdown import DropDown
 from cagou_widget import CagouWidget
+from .common import IconButton
 from importlib import import_module
 import os.path
 import glob
@@ -44,24 +50,118 @@
 import cagou.kv
 
 
+class NotifIcon(IconButton):
+
+    def __init__(self, callback, callback_args):
+        self._callback = callback
+        self._callback_args = callback_args
+        super(NotifIcon, self).__init__()
+
+    def on_release(self):
+        self.parent.remove_widget(self)
+        self._callback(*self._callback_args)
+
+
+class Note(Label):
+    title = properties.StringProperty()
+    message = properties.StringProperty()
+    level = properties.OptionProperty(C.XMLUI_DATA_LVL_DEFAULT, options=list(C.XMLUI_DATA_LVLS))
+
+
+class NoteDrop(Note):
+    pass
+
+
+class NotesDrop(DropDown):
+    clear_btn = properties.ObjectProperty()
+
+    def __init__(self, notes):
+        super(NotesDrop, self).__init__()
+        self.notes = notes
+
+    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))
+        self.add_widget(self.clear_btn)
+        super(NotesDrop, self).open(widget)
+
+
+class RootHeadWidget(BoxLayout):
+    """Notifications widget"""
+    manager = properties.ObjectProperty()
+    notes = properties.ListProperty()
+
+    def __init__(self):
+        super(RootHeadWidget, self).__init__()
+        self.notes_last = None
+        self.notes_event = None
+        self.notes_drop = NotesDrop(self.notes) # auto_with=False, width=100)
+
+    def addNotif(self, callback, *args):
+        icon = NotifIcon(callback, args)
+        self.add_widget(icon)
+
+    def addNote(self, title, message, level):
+        note = Note(title=title, message=message, level=level)
+        self.notes.append(note)
+        if len(self.notes) > 10:
+            del self.notes[:-10]
+        if self.notes_event is None:
+            self.notes_event = Clock.schedule_interval(self._displayNextNote, 5)
+            self._displayNextNote()
+
+    def _displayNextNote(self, dummy=None):
+        screen = Screen()
+        try:
+            idx = self.notes.index(self.notes_last) + 1
+        except ValueError:
+            idx = 0
+        try:
+            note = self.notes_last = self.notes[idx]
+        except IndexError:
+            self.notes_event.cancel()
+            self.notes_event = None
+        else:
+            screen.add_widget(note)
+        self.manager.switch_to(screen)
+
+
 class CagouRootWidget(BoxLayout):
 
-    def __init__(self, widgets):
+    def __init__(self, main_widget):
         super(CagouRootWidget, self).__init__(orientation=("vertical"))
-        for wid in widgets:
-            self.add_widget(wid)
+        # header
+        self._head_widget = RootHeadWidget()
+        self.add_widget(self._head_widget)
 
-    def change_widgets(self, widgets):
-        self.clear_widgets()
-        for wid in widgets:
-            self.add_widget(wid)
+        # body
+        self._manager = ScreenManager()
+        main_screen = Screen(name='main')
+        main_screen.add_widget(main_widget)
+        self._manager.add_widget(main_screen)
+        self.add_widget(self._manager)
+        self.change_widget(main_widget)
+
+    def change_widget(self, main_widget, screen="main"):
+        """change main widget"""
+        main_screen = self._manager.get_screen(screen)
+        main_screen.clear_widgets()
+        main_screen.add_widget(main_widget)
+
+    def newAction(self, handler, action_data, id_, security_limit, profile):
+        """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)
 
 
 class CagouApp(App):
     """Kivy App for Cagou"""
 
     def build(self):
-        return CagouRootWidget([ProfileManager()])
+        return CagouRootWidget(ProfileManager())
 
     def expand(self, path):
         """expand path and replace known values
@@ -79,6 +179,7 @@
         super(Cagou, self).__init__(create_bridge=DBusBridgeFrontend, xmlui=xmlui)
         self._import_kv()
         self.app = CagouApp()
+        self.app.host = self
         self.media_dir = self.app.media_dir = self.bridge.getConfig("", "media_dir")
         self.app.default_avatar = os.path.join(self.media_dir, "misc/default_avatar.png")
         self._plg_wids = []  # widget plugins
@@ -92,6 +193,8 @@
         main_cls = plugin_info['main']
         return self.widgets.getOrCreateWidget(main_cls, target, on_new_widget=None, profiles=iter(self.profiles))
 
+    ## plugins & kv import ##
+
     def _import_kv(self):
         """import all kv files in cagou.kv"""
         path = os.path.dirname(cagou.kv.__file__)
@@ -180,11 +283,7 @@
                 continue
             yield plugin_data
 
-    def plugging_profiles(self):
-        self.app.root.change_widgets([WidgetsHandler()])
-
-    def setPresenceStatus(self, show='', status=None, profile=C.PROF_KEY_NONE):
-        log.info(u"Profile presence status set to {show}/{status}".format(show=show, status=status))
+    ## widgets handling
 
     def switchWidget(self, old, new):
         """Replace old widget by new one
@@ -208,3 +307,26 @@
             idx = parent.children.index(to_change)
             parent.remove_widget(to_change)
             parent.add_widget(new, index=idx)
+
+    ## misc ##
+
+    def plugging_profiles(self):
+        self.app.root.change_widget(WidgetsHandler())
+
+    def setPresenceStatus(self, show='', status=None, profile=C.PROF_KEY_NONE):
+        log.info(u"Profile presence status set to {show}/{status}".format(show=show, status=status))
+
+    def addNote(self, title, message, level):
+        """add a note (message which disappear) to root widget's header"""
+        self.app.root.addNote(title, message, level)
+
+    ## signals handling ##
+
+    def actionNewHandler(self, action_data, id_, security_limit, profile):
+        handler = super(Cagou, self).actionNewHandler
+        # FIXME: temporarily deactivated
+        # if 'xmlui' in action_data:
+        #     self.app.root.newAction(handler, action_data, id_, security_limit, profile)
+        # else:
+        #     handler(action_data, id_, security_limit, profile)
+        handler(action_data, id_, security_limit, profile)