Mercurial > libervia-web
diff 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 |
line wrap: on
line diff
--- a/browser_side/panels.py Tue Oct 15 13:39:21 2013 +0200 +++ b/browser_side/panels.py Tue Oct 15 13:36:51 2013 +0200 @@ -32,8 +32,10 @@ from pyjamas.ui.Button import Button from pyjamas.ui.HTML import HTML from pyjamas.ui.Image import Image +from pyjamas.ui.PopupPanel import PopupPanel from pyjamas.ui.ClickListener import ClickHandler from pyjamas.ui.KeyboardListener import KEY_ENTER, KEY_UP, KEY_DOWN +from pyjamas.ui.Event import BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT from pyjamas.ui.MouseListener import MouseHandler from pyjamas.Timer import Timer from pyjamas import DOM @@ -546,9 +548,9 @@ entry.addStyleName('selected_entry') self.selected_entry = entry - def updateValue(self, type, jid, value): + def updateValue(self, type_, jid, value): """Update a jid value in entries - @param type: one of 'avatar', 'nick' + @param type_: one of 'avatar', 'nick' @param jid: jid concerned @param value: new value""" def updateVPanel(vpanel): @@ -557,7 +559,7 @@ child.updateAvatar(value) elif isinstance(child, VerticalPanel): updateVPanel(child) - if type == 'avatar': + if type_ == 'avatar': updateVPanel(self.vpanel) def setAcceptedGroup(self, group): @@ -940,3 +942,108 @@ tab_bar_h = _elts.item(0).offsetHeight ideal_height = Window.getClientHeight() - tab_bar_h self.setHeight("%s%s" % (ideal_height, "px")) + + +class PopupMenuPanel(PopupPanel): + """This implementation of a popup menu (context menu) allow you to assign + two special methods which are common to all the items, in order to hide + certain items and also easily define their callbacks. The menu can be + bound to any of the mouse button (left, middle, right). + """ + def __init__(self, entries, hide=None, callback=None, vertical=True, item_style="popupMenuItem", menu_style=None, **kwargs): + """ + @param entries: a dict of dicts, where each sub-dict is representing + one menu item: the sub-dict key can be used as the item text and + description, but optional "title" and "desc" entries would be used + if they exists. The sub-dicts may be extended later to do + more complicated stuff or overwrite the common methods. + @param hide: function with 2 args: widget, key as string and + returns True if that item should be hidden from the context menu. + @param callback: function with 2 args: widget, key as string + @param vertical: True or False, to set the direction + @param item_style: alternative CSS class for the menu items + @param menu_style: supplementary CSS class for the sender widget + """ + PopupPanel.__init__(self, autoHide=True, **kwargs) + self._entries = entries + self._hide = hide + self._callback = callback + self.vertical = vertical + self.item_style = item_style + self.menu_style = menu_style + self._senders = {} + + def _show(self, sender): + """Popup the menu relative to this sender's position. + @param sender: the widget that has been clicked + """ + menu = VerticalPanel() if self.vertical is True else HorizontalPanel() + menu.setStyleName("recipientTypeMenu") + + def button_cb(item): + """You can not put that method in the loop and rely + on _key, because it is overwritten by each step. + You can rely on item.key instead, which is copied + from _key after the item creation. + @param item: the menu item that has been clicked + """ + if self._callback is not None: + self._callback(sender=sender, key=item.key) + self.hide(autoClosed=True) + + for _key in self._entries.keys(): + entry = self._entries[_key] + if self._hide is not None and self._hide(sender=sender, key=_key) is True: + continue + title = entry["title"] if "title" in entry.keys() else _key + item = Button(title, button_cb) + item.key = _key + item.setStyleName(self.item_style) + item.setTitle(entry["desc"] if "desc" in entry.keys() else title) + menu.add(item) + self.add(menu) + if self.vertical is True: + x = sender.getAbsoluteLeft() + sender.getOffsetWidth() + y = sender.getAbsoluteTop() + else: + x = sender.getAbsoluteLeft() + y = sender.getAbsoluteTop() + sender.getOffsetHeight() + self.setPopupPosition(x, y) + self.show() + if self.menu_style: + sender.addStyleDependentName(self.menu_style) + + def _onHide(popup): + if hasattr(self, "menu_style") and self.menu_style is not None: + sender.removeStyleDependentName(self.menu_style) + return PopupPanel.onHideImpl(self, popup) + + self.onHideImpl = _onHide + + def registerClickSender(self, sender, button=BUTTON_LEFT): + """Bind the menu to the specified sender. + @param sender: the widget to which the menu should be bound + @param: BUTTON_LEFT, BUTTON_MIDDLE or BUTTON_RIGHT + """ + self._senders.setdefault(sender, []) + self._senders[sender].append(button) + + if button == BUTTON_RIGHT: + # WARNING: to disable the context menu is a bit tricky... + # The following seems to work on Firefox 24.0, but: + # TODO: find a cleaner way to disable the context menu + sender.getElement().setAttribute("oncontextmenu", "return false") + + def _onBrowserEvent(event): + button = DOM.eventGetButton(event) + if DOM.eventGetType(event) == "mousedown" and button in self._senders[sender]: + self._show(sender) + return sender.__class__.onBrowserEvent(sender, event) + + sender.onBrowserEvent = _onBrowserEvent + + def registerMiddleClickSender(self, sender): + self.registerClickSender(sender, BUTTON_MIDDLE) + + def registerRightClickSender(self, sender): + self.registerClickSender(sender, BUTTON_RIGHT)