Mercurial > libervia-web
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) |