comparison cagou/core/menu.py @ 197:c7d15ef4bfa8

core (menu): new EntitiesSelectorMenu: TransferMenu base has been moved to a "SlideMenu" base class, which is used to create a new "EntitiesSelectorMenu". This later widget allows user to select entities from her roster, and the selected jids are then sent back to user.
author Goffi <goffi@goffi.org>
date Wed, 23 May 2018 21:27:28 +0200
parents c63922860f80
children e42e0c45d384
comparison
equal deleted inserted replaced
196:519b3a29743c 197:c7d15ef4bfa8
20 20
21 from sat.core.i18n import _ 21 from sat.core.i18n import _
22 from sat.core import log as logging 22 from sat.core import log as logging
23 log = logging.getLogger(__name__) 23 log = logging.getLogger(__name__)
24 from cagou.core.constants import Const as C 24 from cagou.core.constants import Const as C
25 from cagou.core.common import JidToggle
25 from kivy.uix.boxlayout import BoxLayout 26 from kivy.uix.boxlayout import BoxLayout
26 from kivy.uix.label import Label 27 from kivy.uix.label import Label
27 from kivy.uix.popup import Popup 28 from kivy.uix.popup import Popup
29 from cagou.core.utils import FilterBehavior
28 from kivy import properties 30 from kivy import properties
29 from kivy.garden import contextmenu 31 from kivy.garden import contextmenu
30 from sat_frontends.quick_frontend import quick_menus 32 from sat_frontends.quick_frontend import quick_menus
31 from kivy.core.window import Window 33 from kivy.core.window import Window
32 from kivy.animation import Animation 34 from kivy.animation import Animation
35 from kivy.metrics import dp
33 from cagou import G 36 from cagou import G
34 import webbrowser 37 import webbrowser
35 38
36 ABOUT_TITLE = _(u"About {}".format(C.APP_NAME)) 39 ABOUT_TITLE = _(u"About {}".format(C.APP_NAME))
37 ABOUT_CONTENT = _(u"""Cagou (Salut à Toi) v{version} 40 ABOUT_CONTENT = _(u"""Cagou (Salut à Toi) v{version}
173 transfer_menu = transfer_menu.parent 176 transfer_menu = transfer_menu.parent
174 transfer_menu.do_callback(self.plug_info) 177 transfer_menu.do_callback(self.plug_info)
175 return True 178 return True
176 179
177 180
178 class TransferMenu(BoxLayout): 181 class SideMenu(BoxLayout):
179 """transfer menu which handle display and callbacks""" 182 base_size_hint_close = (0, 1)
180 # callback will be called with path to file to transfer 183 base_size_hint_open = (0.4, 1)
184 bg_color = properties.ListProperty([0, 0, 0, 1])
185 # callback will be called with arguments relevant to menu
181 callback = properties.ObjectProperty() 186 callback = properties.ObjectProperty()
187 # call do_callback even when menu is cancelled
188 callback_on_close = properties.BooleanProperty(False)
182 # cancel callback need to remove the widget for UI 189 # cancel callback need to remove the widget for UI
183 # will be called with the widget to remove as argument 190 # will be called with the widget to remove as argument
184 cancel_cb = properties.ObjectProperty() 191 cancel_cb = properties.ObjectProperty()
192
193 def __init__(self, **kwargs):
194 super(SideMenu, self).__init__(**kwargs)
195 if self.cancel_cb is None:
196 self.cancel_cb = self.onMenuCancelled
197
198 def show(self, caller_wid=None):
199 Window.bind(on_keyboard=self.key_input)
200 G.host.app.root.add_widget(self)
201 Animation(size_hint=self.base_size_hint_open, d=0.3, t='out_back').start(self)
202
203 def hide(self):
204 Window.unbind(on_keyboard=self.key_input)
205 anim = Animation(size_hint=self.base_size_hint_close, d=0.2)
206 anim.bind(on_complete=lambda anim, menu: self.parent.remove_widget(self))
207 anim.start(self)
208 if self.callback_on_close:
209 self.do_callback()
210
211 def on_touch_down(self, touch):
212 # we remove the menu if we click outside
213 # else we want to handle the event, but not
214 # transmit it to parents
215 if not self.collide_point(*touch.pos):
216 self.hide()
217 else:
218 return super(SideMenu, self).on_touch_down(touch)
219 return True
220
221 def key_input(self, window, key, scancode, codepoint, modifier):
222 if key == 27:
223 self.hide()
224 return True
225
226 def onMenuCancelled(self, wid, cleaning_cb=None):
227 self._closeUI(wid)
228 if cleaning_cb is not None:
229 cleaning_cb()
230
231 def _closeUI(self, wid):
232 G.host.closeUI()
233
234 def do_callback(self, *args, **kwargs):
235 log.warning(u"callback not implemented")
236
237
238 class TransferMenu(SideMenu):
239 """transfer menu which handle display and callbacks"""
240 # callback will be called with path to file to transfer
185 # profiles if set will be sent to transfer widget, may be used to get specific files 241 # profiles if set will be sent to transfer widget, may be used to get specific files
186 profiles = properties.ObjectProperty() 242 profiles = properties.ObjectProperty()
187 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") 243 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")
188 send_txt = _(u"The file will be sent unencrypted directly to your contact (without transiting by the server), except in some cases") 244 send_txt = _(u"The file will be sent unencrypted directly to your contact (without transiting by the server), except in some cases")
189 items_layout = properties.ObjectProperty() 245 items_layout = properties.ObjectProperty()
246 base_size_hint_close = (1, 0)
247 base_size_hint_open = (1, 0.5)
190 248
191 def __init__(self, **kwargs): 249 def __init__(self, **kwargs):
192 super(TransferMenu, self).__init__(**kwargs) 250 super(TransferMenu, self).__init__(**kwargs)
193 if self.cancel_cb is None:
194 self.cancel_cb = self.onTransferCancelled
195 if self.profiles is None: 251 if self.profiles is None:
196 self.profiles = iter(G.host.profiles) 252 self.profiles = iter(G.host.profiles)
197 for plug_info in G.host.getPluggedWidgets(type_=C.PLUG_TYPE_TRANSFER): 253 for plug_info in G.host.getPluggedWidgets(type_=C.PLUG_TYPE_TRANSFER):
198 item = TransferItem( 254 item = TransferItem(
199 plug_info = plug_info 255 plug_info = plug_info
200 ) 256 )
201 self.items_layout.add_widget(item) 257 self.items_layout.add_widget(item)
202
203 def show(self, caller_wid=None):
204 self.size_hint_y = 0
205 Window.bind(on_keyboard=self.key_input)
206 G.host.app.root.add_widget(self)
207 Animation(size_hint_y=0.5, d=0.3, t='out_back').start(self)
208
209 def hide(self):
210 Window.unbind(on_keyboard=self.key_input)
211 anim = Animation(size_hint_y=0, d=0.2)
212 anim.bind(on_complete=lambda anim, menu: self.parent.remove_widget(self))
213 anim.start(self)
214
215 def on_touch_down(self, touch):
216 # we remove the menu if we click outside
217 # else we want to handle the event, but not
218 # transmit it to parents
219 if not self.collide_point(*touch.pos):
220 self.hide()
221 else:
222 return super(TransferMenu, self).on_touch_down(touch)
223 return True
224
225 def key_input(self, window, key, scancode, codepoint, modifier):
226 if key == 27:
227 self.hide()
228 return True
229
230 def _closeUI(self, wid):
231 G.host.closeUI()
232
233 def onTransferCancelled(self, wid, cleaning_cb=None):
234 self._closeUI(wid)
235 if cleaning_cb is not None:
236 cleaning_cb()
237 258
238 def do_callback(self, plug_info): 259 def do_callback(self, plug_info):
239 self.parent.remove_widget(self) 260 self.parent.remove_widget(self)
240 if self.callback is None: 261 if self.callback is None:
241 log.warning(u"TransferMenu callback is not set") 262 log.warning(u"TransferMenu callback is not set")
250 cleaning_cb, 271 cleaning_cb,
251 transfer_type = C.TRANSFER_UPLOAD if self.ids['upload_btn'].state == "down" else C.TRANSFER_SEND) 272 transfer_type = C.TRANSFER_UPLOAD if self.ids['upload_btn'].state == "down" else C.TRANSFER_SEND)
252 wid = plug_info['factory'](plug_info, onTransferCb, self.cancel_cb, self.profiles) 273 wid = plug_info['factory'](plug_info, onTransferCb, self.cancel_cb, self.profiles)
253 if not external: 274 if not external:
254 G.host.showExtraUI(wid) 275 G.host.showExtraUI(wid)
276
277
278 class EntitiesSelectorMenu(SideMenu, FilterBehavior):
279 """allow to select entities from roster"""
280 profiles = properties.ObjectProperty()
281 layout = properties.ObjectProperty()
282 instructions = properties.StringProperty(_(u"Please select entities"))
283 filter_input = properties.ObjectProperty()
284
285 def __init__(self, **kwargs):
286 super(EntitiesSelectorMenu, self).__init__(**kwargs)
287 self.filter_input.bind(text=self.do_filter_input)
288 if self.profiles is None:
289 self.profiles = iter(G.host.profiles)
290 for profile in self.profiles:
291 for jid_, jid_data in G.host.contact_lists[profile].all_iter:
292 jid_wid = JidToggle(
293 jid=jid_,
294 profile=profile)
295 self.layout.add_widget(jid_wid)
296
297 def do_callback(self):
298 if self.callback is not None:
299 jids = [c.jid for c in self.layout.children if c.state == 'down']
300 self.callback(jids)
301
302 def do_filter_input(self, filter_input, text):
303 self.layout.spacing = 0 if text else dp(5)
304 self.do_filter(self.layout.children,
305 text,
306 lambda c: c.jid,
307 width_cb=lambda c: c.width,
308 height_cb=lambda c: dp(70))