comparison cagou/core/menu.py @ 222:a676cb07c1cb

core (menu): TouchMenuBehaviour: moved code showing ModernMenu on item from file sharing plugin to a generic behaviour, so it can be re-used elsewhere.
author Goffi <goffi@goffi.org>
date Tue, 26 Jun 2018 20:26:21 +0200
parents e42e0c45d384
children 15e47bbb192c
comparison
equal deleted inserted replaced
221:e1a385a791cc 222:a676cb07c1cb
26 from kivy.uix.boxlayout import BoxLayout 26 from kivy.uix.boxlayout import BoxLayout
27 from kivy.uix.label import Label 27 from kivy.uix.label import Label
28 from kivy.uix.popup import Popup 28 from kivy.uix.popup import Popup
29 from cagou.core.utils import FilterBehavior 29 from cagou.core.utils import FilterBehavior
30 from kivy import properties 30 from kivy import properties
31 from kivy.garden import contextmenu 31 from kivy.garden import contextmenu, modernmenu
32 from sat_frontends.quick_frontend import quick_menus 32 from sat_frontends.quick_frontend import quick_menus
33 from kivy.core.window import Window 33 from kivy.core.window import Window
34 from kivy.animation import Animation 34 from kivy.animation import Animation
35 from kivy.metrics import dp 35 from kivy.metrics import dp
36 from kivy.clock import Clock
36 from cagou import G 37 from cagou import G
38 from functools import partial
37 import webbrowser 39 import webbrowser
38 40
39 ABOUT_TITLE = _(u"About {}".format(C.APP_NAME)) 41 ABOUT_TITLE = _(u"About {}".format(C.APP_NAME))
40 ABOUT_CONTENT = _(u"""Cagou (Salut à Toi) v{version} 42 ABOUT_CONTENT = _(u"""Cagou (Salut à Toi) v{version}
41 43
76 self.parent.hide() 78 self.parent.hide()
77 selected = G.host.selected_widget 79 selected = G.host.selected_widget
78 profile = None 80 profile = None
79 if selected is not None: 81 if selected is not None:
80 try: 82 try:
81 profile = selected.profile 83 # FIXME: handle multi-profiles
84 profile = next(iter(selected.profiles))
82 except AttributeError: 85 except AttributeError:
83 pass 86 pass
84 87
85 if profile is None: 88 if profile is None:
86 try: 89 try:
127 self.add_widget(main_menu) 130 self.add_widget(main_menu)
128 caller = main_menu 131 caller = main_menu
129 else: 132 else:
130 context_menu = contextmenu.ContextMenu() 133 context_menu = contextmenu.ContextMenu()
131 caller.add_widget(context_menu) 134 caller.add_widget(context_menu)
132 # FIXME: next line is needed after parent is set to avoid a display bug in contextmenu 135 # FIXME: next line is needed after parent is set to avoid
136 # a display bug in contextmenu
133 # TODO: fix this upstream 137 # TODO: fix this upstream
134 context_menu._on_visible(False) 138 context_menu._on_visible(False)
135 139
136 caller = context_menu 140 caller = context_menu
137 141
264 class TransferMenu(SideMenu): 268 class TransferMenu(SideMenu):
265 """transfer menu which handle display and callbacks""" 269 """transfer menu which handle display and callbacks"""
266 # callback will be called with path to file to transfer 270 # callback will be called with path to file to transfer
267 # profiles if set will be sent to transfer widget, may be used to get specific files 271 # profiles if set will be sent to transfer widget, may be used to get specific files
268 profiles = properties.ObjectProperty() 272 profiles = properties.ObjectProperty()
269 transfer_txt = _(u"Beware! The file will be sent to your server and stay unencrypted there\nServer admin(s) can see the file, and they choose how, when and if it will be deleted") 273 transfer_txt = _(u"Beware! The file will be sent to your server and stay unencrypted "
270 send_txt = _(u"The file will be sent unencrypted directly to your contact (without transiting by the server), except in some cases") 274 u"there\nServer admin(s) can see the file, and they choose how, "
275 u"when and if it will be deleted")
276 send_txt = _(u"The file will be sent unencrypted directly to your contact "
277 u"(without transiting by the server), except in some cases")
271 items_layout = properties.ObjectProperty() 278 items_layout = properties.ObjectProperty()
272 size_hint_close = (1, 0) 279 size_hint_close = (1, 0)
273 size_hint_open = (1, 0.5) 280 size_hint_open = (1, 0.5)
274 281
275 def __init__(self, **kwargs): 282 def __init__(self, **kwargs):
293 if not external: 300 if not external:
294 self._closeUI(wid) 301 self._closeUI(wid)
295 self.callback( 302 self.callback(
296 file_path, 303 file_path,
297 cleaning_cb, 304 cleaning_cb,
298 transfer_type = C.TRANSFER_UPLOAD if self.ids['upload_btn'].state == "down" else C.TRANSFER_SEND) 305 transfer_type = (C.TRANSFER_UPLOAD
299 wid = plug_info['factory'](plug_info, onTransferCb, self.cancel_cb, self.profiles) 306 if self.ids['upload_btn'].state == "down" else C.TRANSFER_SEND))
307 wid = plug_info['factory'](plug_info,
308 onTransferCb,
309 self.cancel_cb,
310 self.profiles)
300 if not external: 311 if not external:
301 G.host.showExtraUI(wid) 312 G.host.showExtraUI(wid)
302 313
303 314
304 class EntitiesSelectorMenu(SideMenu, FilterBehavior): 315 class EntitiesSelectorMenu(SideMenu, FilterBehavior):
334 self.do_filter(self.layout.children, 345 self.do_filter(self.layout.children,
335 text, 346 text,
336 lambda c: c.jid, 347 lambda c: c.jid,
337 width_cb=lambda c: c.width, 348 width_cb=lambda c: c.width,
338 height_cb=lambda c: dp(70)) 349 height_cb=lambda c: dp(70))
350
351
352 class TouchMenu(modernmenu.ModernMenu):
353 pass
354
355
356 class TouchMenuItemBehaviour(object):
357 """Class to use on every item where a menu may appear
358
359 main_wid attribute must be set to the class inheriting from TouchMenuBehaviour
360 do_item_action is the method called on simple click
361 getMenuChoices must return a list of menus for long press
362 menus there are dict as expected by ModernMenu
363 (translated text, index and callback)
364 """
365 main_wid = properties.ObjectProperty()
366 click_timeout = properties.NumericProperty(0.4)
367
368 def on_touch_down(self, touch):
369 if not self.collide_point(*touch.pos):
370 return
371 t = partial(self.open_menu, touch)
372 touch.ud['menu_timeout'] = t
373 Clock.schedule_once(t, self.click_timeout)
374 return super(TouchMenuItemBehaviour, self).on_touch_down(touch)
375
376 def do_item_action(self, touch):
377 pass
378
379 def on_touch_up(self, touch):
380 if touch.ud.get('menu_timeout'):
381 Clock.unschedule(touch.ud['menu_timeout'])
382 if self.collide_point(*touch.pos) and self.main_wid.menu is None:
383 self.do_item_action(touch)
384 return super(TouchMenuItemBehaviour, self).on_touch_up(touch)
385
386 def open_menu(self, touch, dt):
387 self.main_wid.open_menu(self, touch)
388 del touch.ud['menu_timeout']
389
390 def getMenuChoices(self):
391 """return choice adapted to selected item
392
393 @return (list[dict]): choices ad expected by ModernMenu
394 """
395 return []
396
397
398 class TouchMenuBehaviour(object):
399 """Class to handle a menu appearing on long press on items
400
401 classes using this behaviour need to have a float_layout property
402 pointing the main FloatLayout.
403 """
404 float_layout = properties.ObjectProperty()
405
406 def __init__(self, *args, **kwargs):
407 super(TouchMenuBehaviour, self).__init__(*args, **kwargs)
408 self.menu = None
409 self.menu_item = None
410
411 ## menu methods ##
412
413 def clean_fl_children(self, layout, children):
414 """insure that self.menu and self.menu_item are None when menu is dimissed"""
415 if self.menu is not None and self.menu not in children:
416 self.menu = self.menu_item = None
417
418 def clear_menu(self):
419 """remove menu if there is one"""
420 if self.menu is not None:
421 self.menu.dismiss()
422 self.menu = None
423 self.menu_item = None
424
425 def open_menu(self, item, touch):
426 """open menu for item
427
428 @param item(PathWidget): item when the menu has been requested
429 @param touch(kivy.input.MotionEvent): touch data
430 """
431 if self.menu_item == item:
432 return
433 self.clear_menu()
434 pos = self.to_widget(*touch.pos)
435 choices = item.getMenuChoices()
436 if not choices:
437 return
438 self.menu = TouchMenu(choices=choices,
439 center=pos,
440 size_hint=(None, None))
441 self.float_layout.add_widget(self.menu)
442 self.menu.start_display(touch)
443 self.menu_item = item
444
445 def on_float_layout(self, wid, float_layout):
446 float_layout.bind(children=self.clean_fl_children)