Mercurial > libervia-desktop-kivy
comparison cagou/core/common.py @ 404:f7476818f9fb
core (common): JidSelector + behaviors various improvments:
- renamed *Behaviour => *Behavior to be consistent with Kivy + moved to new
"core.behaviors" modules
- use a dedicated property in ContactItem for notification counter (which is now named
"badge")
- in JidSelector, well-known strings now create use a dedicated layout, add separator
(except if new `add_separators` property is set to False), and are added to attribute of
the same name
- a new `item_class` property is now used to indicate the class to instanciate for items
(by default it's a ContactItem)
- FilterBahavior.do_filter now expect the parent layout instead of directly the children,
this is to allow a FilterBahavior to manage several children layout at once (used with
JidSelector)
- core.utils has been removed, as the behavior there has been moved to core.behaviors
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 12 Feb 2020 20:02:58 +0100 |
parents | 54f6a47cc60a |
children | 84ff5c917064 |
comparison
equal
deleted
inserted
replaced
403:b0af45a92055 | 404:f7476818f9fb |
---|---|
25 from kivy.uix.widget import Widget | 25 from kivy.uix.widget import Widget |
26 from kivy.uix.image import Image | 26 from kivy.uix.image import Image |
27 from kivy.uix.label import Label | 27 from kivy.uix.label import Label |
28 from kivy.uix.behaviors import ButtonBehavior | 28 from kivy.uix.behaviors import ButtonBehavior |
29 from kivy.uix.behaviors import ToggleButtonBehavior | 29 from kivy.uix.behaviors import ToggleButtonBehavior |
30 from kivy.uix.stacklayout import StackLayout | |
30 from kivy.uix.boxlayout import BoxLayout | 31 from kivy.uix.boxlayout import BoxLayout |
31 from kivy.uix.scrollview import ScrollView | 32 from kivy.uix.scrollview import ScrollView |
32 from kivy.event import EventDispatcher | 33 from kivy.event import EventDispatcher |
33 from kivy.metrics import dp | 34 from kivy.metrics import dp |
34 from kivy import properties | 35 from kivy import properties |
35 from sat_frontends.quick_frontend import quick_chat | 36 from sat_frontends.quick_frontend import quick_chat |
36 from cagou.core.constants import Const as C | 37 from .constants import Const as C |
38 from .common_widgets import CategorySeparator | |
37 from cagou import G | 39 from cagou import G |
38 | 40 |
39 log = logging.getLogger(__name__) | 41 log = logging.getLogger(__name__) |
40 | 42 |
41 UNKNOWN_SYMBOL = 'Unknown symbol name' | 43 UNKNOWN_SYMBOL = 'Unknown symbol name' |
61 avatar. | 63 avatar. |
62 """ | 64 """ |
63 base_width = dp(150) | 65 base_width = dp(150) |
64 avatar_layout = properties.ObjectProperty() | 66 avatar_layout = properties.ObjectProperty() |
65 avatar = properties.ObjectProperty() | 67 avatar = properties.ObjectProperty() |
68 badge = properties.ObjectProperty(allownone=True) | |
69 badge_text = properties.StringProperty('') | |
66 profile = properties.StringProperty() | 70 profile = properties.StringProperty() |
67 data = properties.DictProperty() | 71 data = properties.DictProperty() |
68 jid = properties.StringProperty('') | 72 jid = properties.StringProperty('') |
69 | 73 |
70 def on_kv_post(self, __): | 74 def on_badge_text(self, wid, text): |
71 if self.data and self.data.get('notifs'): | 75 if text: |
72 notif = NotifLabel( | 76 if self.badge is not None: |
73 pos_hint={"right": 0.8, "y": 0}, | 77 self.badge.text = text |
74 text=str(len(self.data['notifs'])) | 78 else: |
75 ) | 79 self.badge = NotifLabel( |
76 self.avatar_layout.add_widget(notif) | 80 pos_hint={"right": 0.8, "y": 0}, |
81 text=text, | |
82 ) | |
83 self.avatar_layout.add_widget(self.badge) | |
84 else: | |
85 if self.badge is not None: | |
86 self.avatar_layout.remove_widget(self.badge) | |
87 self.badge = None | |
77 | 88 |
78 | 89 |
79 class ContactButton(ButtonBehavior, ContactItem): | 90 class ContactButton(ButtonBehavior, ContactItem): |
80 pass | 91 pass |
81 | 92 |
172 else: | 183 else: |
173 icon_wid = ActionSymbol(symbol=symbol) | 184 icon_wid = ActionSymbol(symbol=symbol) |
174 self.add_widget(icon_wid) | 185 self.add_widget(icon_wid) |
175 | 186 |
176 | 187 |
188 class JidSelectorCategoryLayout(StackLayout): | |
189 pass | |
190 | |
191 | |
177 class JidSelector(ScrollView, EventDispatcher): | 192 class JidSelector(ScrollView, EventDispatcher): |
178 layout = properties.ObjectProperty(None) | 193 layout = properties.ObjectProperty(None) |
194 # if item_class is changed, the properties must be the same as for ContactButton | |
195 # and ordering must be supported | |
196 item_class = properties.ObjectProperty(ContactButton) | |
197 add_separators = properties.ObjectProperty(True) | |
179 # list of item to show, can be: | 198 # list of item to show, can be: |
180 # - a well-known string like: | 199 # - a well-known string which can be: |
181 # * "roster": to show all roster jids | 200 # * "roster": all roster jids |
182 # * "opened_chats": to show jids of all opened chat widgets | 201 # * "opened_chats": all opened chat widgets |
202 # * "bookmarks": MUC bookmarks | |
203 # A layout will be created each time and stored in the attribute of the same | |
204 # name. | |
205 # If add_separators is True, a CategorySeparator will be added on top of each | |
206 # layout. | |
183 # - a kivy Widget, which will be added to the layout (notable useful with | 207 # - a kivy Widget, which will be added to the layout (notable useful with |
184 # common_widgets.CategorySeparator) | 208 # common_widgets.CategorySeparator) |
185 # - a callable, which must return an iterable of kwargs for ContactButton | 209 # - a callable, which must return an iterable of kwargs for ContactButton |
186 to_show = properties.ListProperty(['roster']) | 210 to_show = properties.ListProperty(['roster']) |
187 # if True, update() is called automatically when widget is created | 211 # if True, update() is called automatically when widget is created |
188 # if False, you'll have to call update() at least once manually | 212 # if False, you'll have to call update() at least once manually |
189 implicit_update = properties.ObjectProperty(True) | 213 implicit_update = properties.ObjectProperty(True) |
190 | 214 |
191 def __init__(self, **kwargs): | 215 def __init__(self, **kwargs): |
192 self.register_event_type('on_select') | 216 self.register_event_type('on_select') |
217 # list of layouts containing items | |
218 self.items_layouts = [] | |
219 # jid to list of ContactButton instances map | |
220 self.items_map = {} | |
193 super().__init__(**kwargs) | 221 super().__init__(**kwargs) |
194 | 222 |
195 def on_kv_post(self, wid): | 223 def on_kv_post(self, wid): |
196 if self.implicit_update: | 224 if self.implicit_update: |
197 self.update() | 225 self.update() |
207 G.host.addListener("contactsFilled", self.onContactsFilled) | 235 G.host.addListener("contactsFilled", self.onContactsFilled) |
208 | 236 |
209 def onContactsFilled(self, profile): | 237 def onContactsFilled(self, profile): |
210 log.debug("onContactsFilled event received") | 238 log.debug("onContactsFilled event received") |
211 self.update() | 239 self.update() |
240 | |
241 | |
242 def _createItem(self, **kwargs): | |
243 item = self.item_class(**kwargs) | |
244 jid = kwargs['jid'] | |
245 self.items_map.setdefault(jid, []).append(item) | |
246 return item | |
212 | 247 |
213 def update(self): | 248 def update(self): |
214 log.debug("starting update") | 249 log.debug("starting update") |
215 self.layout.clear_widgets() | 250 self.layout.clear_widgets() |
216 for item in self.to_show: | 251 for item in self.to_show: |
226 elif isinstance(item, Widget): | 261 elif isinstance(item, Widget): |
227 self.layout.add_widget(item) | 262 self.layout.add_widget(item) |
228 elif callable(item): | 263 elif callable(item): |
229 items_kwargs = item() | 264 items_kwargs = item() |
230 for item_kwargs in items_kwargs: | 265 for item_kwargs in items_kwargs: |
231 item = ContactButton(**item_kwargs) | 266 item = self._createItem(**items_kwargs) |
232 item.bind(on_press=partial(self.dispatch, 'on_select')) | 267 item.bind(on_press=partial(self.dispatch, 'on_select')) |
233 self.layout.add_widget(item) | 268 self.layout.add_widget(item) |
234 else: | 269 else: |
235 log.error(f"unmanaged to_show item type: {item!r}") | 270 log.error(f"unmanaged to_show item type: {item!r}") |
236 | 271 |
272 def addCategoryLayout(self, label=None): | |
273 category_layout = JidSelectorCategoryLayout() | |
274 | |
275 if label and self.add_separators: | |
276 category_layout.add_widget(CategorySeparator(text=label)) | |
277 | |
278 self.layout.add_widget(category_layout) | |
279 self.items_layouts.append(category_layout) | |
280 return category_layout | |
281 | |
282 def getItemFromWid(self, wid): | |
283 """create JidSelector item from QuickChat widget""" | |
284 contact_list = G.host.contact_lists[wid.profile] | |
285 data=contact_list.getItem(wid.target) | |
286 try: | |
287 item = self._createItem( | |
288 jid=wid.target, | |
289 data=data, | |
290 profile=wid.profile, | |
291 ) | |
292 except Exception as e: | |
293 log.warning(f"Can't add contact {wid.target}: {e}") | |
294 return | |
295 notifs = list(G.host.getNotifs(wid.target, profile=wid.profile)) | |
296 if notifs: | |
297 item.badge_text = str(len(notifs)) | |
298 item.bind(on_press=partial(self.dispatch, 'on_select')) | |
299 return item | |
300 | |
237 def addOpenedChatsItems(self): | 301 def addOpenedChatsItems(self): |
238 opened_chats = G.host.widgets.getWidgets( | 302 self.opened_chats = category_layout = self.addCategoryLayout(_("Opened chats")) |
303 widgets = sorted(G.host.widgets.getWidgets( | |
239 quick_chat.QuickChat, | 304 quick_chat.QuickChat, |
240 profiles = G.host.profiles) | 305 profiles = G.host.profiles, |
241 | 306 with_duplicates=False)) |
242 for wid in opened_chats: | 307 |
243 contact_list = G.host.contact_lists[wid.profile] | 308 for wid in widgets: |
244 data=contact_list.getItem(wid.target) | 309 item = self.getItemFromWid(wid) |
245 notifs = list(G.host.getNotifs(wid.target, profile=wid.profile)) | 310 if item is None: |
246 if notifs: | |
247 # we shallow copy the dict to have the notification displayed only with | |
248 # opened chats (otherwise, the counter would appear on each other | |
249 # instance of ContactButton for this entity, i.e. in roster too). | |
250 data = data.copy() | |
251 data['notifs'] = notifs | |
252 try: | |
253 item = ContactButton( | |
254 jid=wid.target, | |
255 data=data, | |
256 profile=wid.profile, | |
257 ) | |
258 except Exception as e: | |
259 log.warning(f"Can't add contact {wid.target}: {e}") | |
260 continue | 311 continue |
261 item.bind(on_press=partial(self.dispatch, 'on_select')) | 312 category_layout.add_widget(item) |
262 self.layout.add_widget(item) | |
263 | 313 |
264 def addRosterItems(self): | 314 def addRosterItems(self): |
315 self.roster = category_layout = self.addCategoryLayout(_("Your contacts")) | |
265 for profile in G.host.profiles: | 316 for profile in G.host.profiles: |
266 contact_list = G.host.contact_lists[profile] | 317 contact_list = G.host.contact_lists[profile] |
267 for entity_jid in sorted(contact_list.roster): | 318 for entity_jid in sorted(contact_list.roster): |
268 item = ContactButton( | 319 item = self._createItem( |
269 jid=entity_jid, | 320 jid=entity_jid, |
270 data=contact_list.getItem(entity_jid), | 321 data=contact_list.getItem(entity_jid), |
271 profile=profile, | 322 profile=profile, |
272 ) | 323 ) |
273 item.bind(on_press=partial(self.dispatch, 'on_select')) | 324 item.bind(on_press=partial(self.dispatch, 'on_select')) |
274 self.layout.add_widget(item) | 325 category_layout.add_widget(item) |
275 | 326 |
276 def addBookmarksItems(self): | 327 def addBookmarksItems(self): |
328 self.bookmarks = category_layout = self.addCategoryLayout(_("Your chat rooms")) | |
277 for profile in G.host.profiles: | 329 for profile in G.host.profiles: |
278 profile_manager = G.host.profiles[profile] | 330 profile_manager = G.host.profiles[profile] |
279 try: | 331 try: |
280 bookmarks = profile_manager._bookmarks | 332 bookmarks = profile_manager._bookmarks |
281 except AttributeError: | 333 except AttributeError: |
286 for entity_jid in bookmarks: | 338 for entity_jid in bookmarks: |
287 try: | 339 try: |
288 cache = contact_list.getItem(entity_jid) | 340 cache = contact_list.getItem(entity_jid) |
289 except KeyError: | 341 except KeyError: |
290 cache = {} | 342 cache = {} |
291 item = ContactButton( | 343 item = self._createItem( |
292 jid=entity_jid, | 344 jid=entity_jid, |
293 data=cache, | 345 data=cache, |
294 profile=profile, | 346 profile=profile, |
295 ) | 347 ) |
296 item.bind(on_press=partial(self.dispatch, 'on_select')) | 348 item.bind(on_press=partial(self.dispatch, 'on_select')) |
297 self.layout.add_widget(item) | 349 category_layout.add_widget(item) |