comparison 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
comparison
equal deleted inserted replaced
28:9f9532eb835f 29:8b5827c43155
30 kivy.require('1.9.1') 30 kivy.require('1.9.1')
31 import kivy.support 31 import kivy.support
32 kivy.support.install_gobject_iteration() 32 kivy.support.install_gobject_iteration()
33 from kivy.app import App 33 from kivy.app import App
34 from kivy.lang import Builder 34 from kivy.lang import Builder
35 from kivy import properties
35 import xmlui 36 import xmlui
36 from profile_manager import ProfileManager 37 from profile_manager import ProfileManager
37 from widgets_handler import WidgetsHandler 38 from widgets_handler import WidgetsHandler
39 from kivy.clock import Clock
40 from kivy.uix.label import Label
38 from kivy.uix.boxlayout import BoxLayout 41 from kivy.uix.boxlayout import BoxLayout
42 from kivy.uix.screenmanager import ScreenManager, Screen
43 from kivy.uix.dropdown import DropDown
39 from cagou_widget import CagouWidget 44 from cagou_widget import CagouWidget
45 from .common import IconButton
40 from importlib import import_module 46 from importlib import import_module
41 import os.path 47 import os.path
42 import glob 48 import glob
43 import cagou.plugins 49 import cagou.plugins
44 import cagou.kv 50 import cagou.kv
45 51
46 52
53 class NotifIcon(IconButton):
54
55 def __init__(self, callback, callback_args):
56 self._callback = callback
57 self._callback_args = callback_args
58 super(NotifIcon, self).__init__()
59
60 def on_release(self):
61 self.parent.remove_widget(self)
62 self._callback(*self._callback_args)
63
64
65 class Note(Label):
66 title = properties.StringProperty()
67 message = properties.StringProperty()
68 level = properties.OptionProperty(C.XMLUI_DATA_LVL_DEFAULT, options=list(C.XMLUI_DATA_LVLS))
69
70
71 class NoteDrop(Note):
72 pass
73
74
75 class NotesDrop(DropDown):
76 clear_btn = properties.ObjectProperty()
77
78 def __init__(self, notes):
79 super(NotesDrop, self).__init__()
80 self.notes = notes
81
82 def open(self, widget):
83 self.clear_widgets()
84 for n in self.notes:
85 self.add_widget(NoteDrop(title=n.title, message=n.message, level=n.level))
86 self.add_widget(self.clear_btn)
87 super(NotesDrop, self).open(widget)
88
89
90 class RootHeadWidget(BoxLayout):
91 """Notifications widget"""
92 manager = properties.ObjectProperty()
93 notes = properties.ListProperty()
94
95 def __init__(self):
96 super(RootHeadWidget, self).__init__()
97 self.notes_last = None
98 self.notes_event = None
99 self.notes_drop = NotesDrop(self.notes) # auto_with=False, width=100)
100
101 def addNotif(self, callback, *args):
102 icon = NotifIcon(callback, args)
103 self.add_widget(icon)
104
105 def addNote(self, title, message, level):
106 note = Note(title=title, message=message, level=level)
107 self.notes.append(note)
108 if len(self.notes) > 10:
109 del self.notes[:-10]
110 if self.notes_event is None:
111 self.notes_event = Clock.schedule_interval(self._displayNextNote, 5)
112 self._displayNextNote()
113
114 def _displayNextNote(self, dummy=None):
115 screen = Screen()
116 try:
117 idx = self.notes.index(self.notes_last) + 1
118 except ValueError:
119 idx = 0
120 try:
121 note = self.notes_last = self.notes[idx]
122 except IndexError:
123 self.notes_event.cancel()
124 self.notes_event = None
125 else:
126 screen.add_widget(note)
127 self.manager.switch_to(screen)
128
129
47 class CagouRootWidget(BoxLayout): 130 class CagouRootWidget(BoxLayout):
48 131
49 def __init__(self, widgets): 132 def __init__(self, main_widget):
50 super(CagouRootWidget, self).__init__(orientation=("vertical")) 133 super(CagouRootWidget, self).__init__(orientation=("vertical"))
51 for wid in widgets: 134 # header
52 self.add_widget(wid) 135 self._head_widget = RootHeadWidget()
53 136 self.add_widget(self._head_widget)
54 def change_widgets(self, widgets): 137
55 self.clear_widgets() 138 # body
56 for wid in widgets: 139 self._manager = ScreenManager()
57 self.add_widget(wid) 140 main_screen = Screen(name='main')
141 main_screen.add_widget(main_widget)
142 self._manager.add_widget(main_screen)
143 self.add_widget(self._manager)
144 self.change_widget(main_widget)
145
146 def change_widget(self, main_widget, screen="main"):
147 """change main widget"""
148 main_screen = self._manager.get_screen(screen)
149 main_screen.clear_widgets()
150 main_screen.add_widget(main_widget)
151
152 def newAction(self, handler, action_data, id_, security_limit, profile):
153 """Add a notification for an action"""
154 self._head_widget.addNotif(handler, action_data, id_, security_limit, profile)
155
156 def addNote(self, title, message, level):
157 self._head_widget.addNote(title, message, level)
58 158
59 159
60 class CagouApp(App): 160 class CagouApp(App):
61 """Kivy App for Cagou""" 161 """Kivy App for Cagou"""
62 162
63 def build(self): 163 def build(self):
64 return CagouRootWidget([ProfileManager()]) 164 return CagouRootWidget(ProfileManager())
65 165
66 def expand(self, path): 166 def expand(self, path):
67 """expand path and replace known values 167 """expand path and replace known values
68 168
69 useful in kv. Values which can be used: 169 useful in kv. Values which can be used:
77 177
78 def __init__(self): 178 def __init__(self):
79 super(Cagou, self).__init__(create_bridge=DBusBridgeFrontend, xmlui=xmlui) 179 super(Cagou, self).__init__(create_bridge=DBusBridgeFrontend, xmlui=xmlui)
80 self._import_kv() 180 self._import_kv()
81 self.app = CagouApp() 181 self.app = CagouApp()
182 self.app.host = self
82 self.media_dir = self.app.media_dir = self.bridge.getConfig("", "media_dir") 183 self.media_dir = self.app.media_dir = self.bridge.getConfig("", "media_dir")
83 self.app.default_avatar = os.path.join(self.media_dir, "misc/default_avatar.png") 184 self.app.default_avatar = os.path.join(self.media_dir, "misc/default_avatar.png")
84 self._plg_wids = [] # widget plugins 185 self._plg_wids = [] # widget plugins
85 self._import_plugins() 186 self._import_plugins()
86 187
89 190
90 def _defaultFactory(self, plugin_info, target, profiles): 191 def _defaultFactory(self, plugin_info, target, profiles):
91 """factory used to create widget instance when PLUGIN_INFO["factory"] is not set""" 192 """factory used to create widget instance when PLUGIN_INFO["factory"] is not set"""
92 main_cls = plugin_info['main'] 193 main_cls = plugin_info['main']
93 return self.widgets.getOrCreateWidget(main_cls, target, on_new_widget=None, profiles=iter(self.profiles)) 194 return self.widgets.getOrCreateWidget(main_cls, target, on_new_widget=None, profiles=iter(self.profiles))
195
196 ## plugins & kv import ##
94 197
95 def _import_kv(self): 198 def _import_kv(self):
96 """import all kv files in cagou.kv""" 199 """import all kv files in cagou.kv"""
97 path = os.path.dirname(cagou.kv.__file__) 200 path = os.path.dirname(cagou.kv.__file__)
98 for kv_path in glob.glob(os.path.join(path, "*.kv")): 201 for kv_path in glob.glob(os.path.join(path, "*.kv")):
178 for plugin_data in self._plg_wids: 281 for plugin_data in self._plg_wids:
179 if plugin_data['main'] == except_cls: 282 if plugin_data['main'] == except_cls:
180 continue 283 continue
181 yield plugin_data 284 yield plugin_data
182 285
183 def plugging_profiles(self): 286 ## widgets handling
184 self.app.root.change_widgets([WidgetsHandler()])
185
186 def setPresenceStatus(self, show='', status=None, profile=C.PROF_KEY_NONE):
187 log.info(u"Profile presence status set to {show}/{status}".format(show=show, status=status))
188 287
189 def switchWidget(self, old, new): 288 def switchWidget(self, old, new):
190 """Replace old widget by new one 289 """Replace old widget by new one
191 290
192 old(CagouWidget): CagouWidget instance or a child 291 old(CagouWidget): CagouWidget instance or a child
206 else: 305 else:
207 parent = to_change.parent 306 parent = to_change.parent
208 idx = parent.children.index(to_change) 307 idx = parent.children.index(to_change)
209 parent.remove_widget(to_change) 308 parent.remove_widget(to_change)
210 parent.add_widget(new, index=idx) 309 parent.add_widget(new, index=idx)
310
311 ## misc ##
312
313 def plugging_profiles(self):
314 self.app.root.change_widget(WidgetsHandler())
315
316 def setPresenceStatus(self, show='', status=None, profile=C.PROF_KEY_NONE):
317 log.info(u"Profile presence status set to {show}/{status}".format(show=show, status=status))
318
319 def addNote(self, title, message, level):
320 """add a note (message which disappear) to root widget's header"""
321 self.app.root.addNote(title, message, level)
322
323 ## signals handling ##
324
325 def actionNewHandler(self, action_data, id_, security_limit, profile):
326 handler = super(Cagou, self).actionNewHandler
327 # FIXME: temporarily deactivated
328 # if 'xmlui' in action_data:
329 # self.app.root.newAction(handler, action_data, id_, security_limit, profile)
330 # else:
331 # handler(action_data, id_, security_limit, profile)
332 handler(action_data, id_, security_limit, profile)