comparison browser_side/panels.py @ 241:86055ccf69c3

browser_side: added class PopupMenuPanel to manage more complex context menu
author souliane <souliane@mailoo.org>
date Tue, 15 Oct 2013 13:36:51 +0200
parents b911f2b43fd4
children a25aa882e09a
comparison
equal deleted inserted replaced
240:a565ce2facc0 241:86055ccf69c3
30 from pyjamas.ui.TextArea import TextArea 30 from pyjamas.ui.TextArea import TextArea
31 from pyjamas.ui.Label import Label 31 from pyjamas.ui.Label import Label
32 from pyjamas.ui.Button import Button 32 from pyjamas.ui.Button import Button
33 from pyjamas.ui.HTML import HTML 33 from pyjamas.ui.HTML import HTML
34 from pyjamas.ui.Image import Image 34 from pyjamas.ui.Image import Image
35 from pyjamas.ui.PopupPanel import PopupPanel
35 from pyjamas.ui.ClickListener import ClickHandler 36 from pyjamas.ui.ClickListener import ClickHandler
36 from pyjamas.ui.KeyboardListener import KEY_ENTER, KEY_UP, KEY_DOWN 37 from pyjamas.ui.KeyboardListener import KEY_ENTER, KEY_UP, KEY_DOWN
38 from pyjamas.ui.Event import BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT
37 from pyjamas.ui.MouseListener import MouseHandler 39 from pyjamas.ui.MouseListener import MouseHandler
38 from pyjamas.Timer import Timer 40 from pyjamas.Timer import Timer
39 from pyjamas import DOM 41 from pyjamas import DOM
40 from card_game import CardPanel 42 from card_game import CardPanel
41 from radiocol import RadioColPanel 43 from radiocol import RadioColPanel
544 self.selected_entry.removeStyleName('selected_entry') 546 self.selected_entry.removeStyleName('selected_entry')
545 if entry: 547 if entry:
546 entry.addStyleName('selected_entry') 548 entry.addStyleName('selected_entry')
547 self.selected_entry = entry 549 self.selected_entry = entry
548 550
549 def updateValue(self, type, jid, value): 551 def updateValue(self, type_, jid, value):
550 """Update a jid value in entries 552 """Update a jid value in entries
551 @param type: one of 'avatar', 'nick' 553 @param type_: one of 'avatar', 'nick'
552 @param jid: jid concerned 554 @param jid: jid concerned
553 @param value: new value""" 555 @param value: new value"""
554 def updateVPanel(vpanel): 556 def updateVPanel(vpanel):
555 for child in vpanel.children: 557 for child in vpanel.children:
556 if isinstance(child, MicroblogEntry) and child.author == jid: 558 if isinstance(child, MicroblogEntry) and child.author == jid:
557 child.updateAvatar(value) 559 child.updateAvatar(value)
558 elif isinstance(child, VerticalPanel): 560 elif isinstance(child, VerticalPanel):
559 updateVPanel(child) 561 updateVPanel(child)
560 if type == 'avatar': 562 if type_ == 'avatar':
561 updateVPanel(self.vpanel) 563 updateVPanel(self.vpanel)
562 564
563 def setAcceptedGroup(self, group): 565 def setAcceptedGroup(self, group):
564 """Add one or more group(s) which can be displayed in this panel. 566 """Add one or more group(s) which can be displayed in this panel.
565 Prevent from duplicate values and keep the list sorted. 567 Prevent from duplicate values and keep the list sorted.
938 tab_bar_h = 0 940 tab_bar_h = 0
939 else: 941 else:
940 tab_bar_h = _elts.item(0).offsetHeight 942 tab_bar_h = _elts.item(0).offsetHeight
941 ideal_height = Window.getClientHeight() - tab_bar_h 943 ideal_height = Window.getClientHeight() - tab_bar_h
942 self.setHeight("%s%s" % (ideal_height, "px")) 944 self.setHeight("%s%s" % (ideal_height, "px"))
945
946
947 class PopupMenuPanel(PopupPanel):
948 """This implementation of a popup menu (context menu) allow you to assign
949 two special methods which are common to all the items, in order to hide
950 certain items and also easily define their callbacks. The menu can be
951 bound to any of the mouse button (left, middle, right).
952 """
953 def __init__(self, entries, hide=None, callback=None, vertical=True, item_style="popupMenuItem", menu_style=None, **kwargs):
954 """
955 @param entries: a dict of dicts, where each sub-dict is representing
956 one menu item: the sub-dict key can be used as the item text and
957 description, but optional "title" and "desc" entries would be used
958 if they exists. The sub-dicts may be extended later to do
959 more complicated stuff or overwrite the common methods.
960 @param hide: function with 2 args: widget, key as string and
961 returns True if that item should be hidden from the context menu.
962 @param callback: function with 2 args: widget, key as string
963 @param vertical: True or False, to set the direction
964 @param item_style: alternative CSS class for the menu items
965 @param menu_style: supplementary CSS class for the sender widget
966 """
967 PopupPanel.__init__(self, autoHide=True, **kwargs)
968 self._entries = entries
969 self._hide = hide
970 self._callback = callback
971 self.vertical = vertical
972 self.item_style = item_style
973 self.menu_style = menu_style
974 self._senders = {}
975
976 def _show(self, sender):
977 """Popup the menu relative to this sender's position.
978 @param sender: the widget that has been clicked
979 """
980 menu = VerticalPanel() if self.vertical is True else HorizontalPanel()
981 menu.setStyleName("recipientTypeMenu")
982
983 def button_cb(item):
984 """You can not put that method in the loop and rely
985 on _key, because it is overwritten by each step.
986 You can rely on item.key instead, which is copied
987 from _key after the item creation.
988 @param item: the menu item that has been clicked
989 """
990 if self._callback is not None:
991 self._callback(sender=sender, key=item.key)
992 self.hide(autoClosed=True)
993
994 for _key in self._entries.keys():
995 entry = self._entries[_key]
996 if self._hide is not None and self._hide(sender=sender, key=_key) is True:
997 continue
998 title = entry["title"] if "title" in entry.keys() else _key
999 item = Button(title, button_cb)
1000 item.key = _key
1001 item.setStyleName(self.item_style)
1002 item.setTitle(entry["desc"] if "desc" in entry.keys() else title)
1003 menu.add(item)
1004 self.add(menu)
1005 if self.vertical is True:
1006 x = sender.getAbsoluteLeft() + sender.getOffsetWidth()
1007 y = sender.getAbsoluteTop()
1008 else:
1009 x = sender.getAbsoluteLeft()
1010 y = sender.getAbsoluteTop() + sender.getOffsetHeight()
1011 self.setPopupPosition(x, y)
1012 self.show()
1013 if self.menu_style:
1014 sender.addStyleDependentName(self.menu_style)
1015
1016 def _onHide(popup):
1017 if hasattr(self, "menu_style") and self.menu_style is not None:
1018 sender.removeStyleDependentName(self.menu_style)
1019 return PopupPanel.onHideImpl(self, popup)
1020
1021 self.onHideImpl = _onHide
1022
1023 def registerClickSender(self, sender, button=BUTTON_LEFT):
1024 """Bind the menu to the specified sender.
1025 @param sender: the widget to which the menu should be bound
1026 @param: BUTTON_LEFT, BUTTON_MIDDLE or BUTTON_RIGHT
1027 """
1028 self._senders.setdefault(sender, [])
1029 self._senders[sender].append(button)
1030
1031 if button == BUTTON_RIGHT:
1032 # WARNING: to disable the context menu is a bit tricky...
1033 # The following seems to work on Firefox 24.0, but:
1034 # TODO: find a cleaner way to disable the context menu
1035 sender.getElement().setAttribute("oncontextmenu", "return false")
1036
1037 def _onBrowserEvent(event):
1038 button = DOM.eventGetButton(event)
1039 if DOM.eventGetType(event) == "mousedown" and button in self._senders[sender]:
1040 self._show(sender)
1041 return sender.__class__.onBrowserEvent(sender, event)
1042
1043 sender.onBrowserEvent = _onBrowserEvent
1044
1045 def registerMiddleClickSender(self, sender):
1046 self.registerClickSender(sender, BUTTON_MIDDLE)
1047
1048 def registerRightClickSender(self, sender):
1049 self.registerClickSender(sender, BUTTON_RIGHT)