diff src/browser/sat_browser/base_panel.py @ 735:e4ae8e2b0afd

browser_side: improve PopupMenuPanel comments + rename a button + better PEP8 compliance
author souliane <souliane@mailoo.org>
date Thu, 19 Nov 2015 11:19:05 +0100
parents 9877607c719a
children 4545d48dee60
line wrap: on
line diff
--- a/src/browser/sat_browser/base_panel.py	Tue Nov 17 19:59:14 2015 +0100
+++ b/src/browser/sat_browser/base_panel.py	Thu Nov 19 11:19:05 2015 +0100
@@ -37,91 +37,95 @@
 
 
 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).
+    """Popup menu (contextual menu) with common callbacks for all the items.
+    
+    This implementation of a popup menu allow you to assign two special methods which
+    are common to all the items, in order to hide certain items and define their callbacks.
+    callbacks. The menu can be bound to any button of the mouse (left, middle, right).
     """
+    
     def __init__(self, entries, hide=None, callback=None, vertical=True, 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: sender, 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
+        @param entries (dict{unicode: dict{unicode: unicode}:
+            - menu item keys 
+            - values: dict{unicode: unicode}:
+                - item data lile "title", "desc"...
+                - value
+        @param hide (callable): function of signature Widget, unicode: bool
+            which takes the sender and the item key, and returns True if that
+            item has to be hidden from the context menu.
+        @param callback (callbable): function of signature Widget, unicode: None
+            which takes the sender and the item key.
+        @param vertical (bool): set the direction vertical or horizontal
+        @param item_style (unicode): alternative CSS class for the menu items
+        @param menu_style (unicode): supplementary CSS class for the sender widget
         """
         PopupPanel.__init__(self, autoHide=True, **kwargs)
-        self._entries = entries
-        self._hide = hide
-        self._callback = callback
+        self.entries = entries
+        self.hideMenu = hide
+        self.callback = callback
         self.vertical = vertical
         self.style = {"selected": None, "menu": "itemKeyMenu", "item": "popupMenuItem"}
         if isinstance(style, dict):
             self.style.update(style)
-        self._senders = {}
+        self.senders = {}
 
-    def _show(self, sender):
-        """Popup the menu relative to this sender's position.
-        @param sender: the widget that has been clicked
+    def showMenu(self, sender):
+        """Popup the menu on the screen, where it fits to the sender's position.
+
+        @param sender (Widget): the widget that has been clicked
         """
         menu = VerticalPanel() if self.vertical is True else HorizontalPanel()
         menu.setStyleName(self.style["menu"])
 
         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)
+            # XXX: you can not put that method in the loop and rely on key
+            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:
+        for key, entry in self.entries.iteritems():
+            if self.hideMenu is not None and self.hideMenu(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.style["item"])
-            item.setTitle(entry["desc"] if "desc" in entry.keys() else title)
+            title = entry.get("title", key)
+            item = Button(title, button_cb, StyleName=self.style["item"])
+            item.key = key  # XXX: copy the key because we loop on it and it will change
+            item.setTitle(entry.get("desc", title))
             menu.add(item)
-        if len(menu.getChildren()) == 0:
-            return
+
+        if menu.getWidgetCount() == 0:
+            return  # no item to display means no menu at all
+        
         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.style["selected"]:
             sender.addStyleDependentName(self.style["selected"])
 
-        def _onHide(popup):
+        def onHide(popup):
             if self.style["selected"]:
                 sender.removeStyleDependentName(self.style["selected"])
             return PopupPanel.onHideImpl(self, popup)
 
-        self.onHideImpl = _onHide
+        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
+
+        @param sender (Widget): bind the menu to this widget
+        @param (int): BUTTON_LEFT, BUTTON_MIDDLE or BUTTON_RIGHT
         """
-        self._senders.setdefault(sender, [])
-        self._senders[sender].append(button)
+        self.senders.setdefault(sender, [])
+        self.senders[sender].append(button)
 
         if button == BUTTON_RIGHT:
             # WARNING: to disable the context menu is a bit tricky...
@@ -129,13 +133,13 @@
             # TODO: find a cleaner way to disable the context menu
             sender.getElement().setAttribute("oncontextmenu", "return false")
 
-        def _onBrowserEvent(event):
+        def onBrowserEvent(event):
             button = DOM.eventGetButton(event)
-            if DOM.eventGetType(event) == "mousedown" and button in self._senders[sender]:
-                self._show(sender)
+            if DOM.eventGetType(event) == "mousedown" and button in self.senders[sender]:
+                self.showMenu(sender)
             return sender.__class__.onBrowserEvent(sender, event)
 
-        sender.onBrowserEvent = _onBrowserEvent
+        sender.onBrowserEvent = onBrowserEvent
 
     def registerMiddleClickSender(self, sender):
         self.registerClickSender(sender, BUTTON_MIDDLE)