Mercurial > libervia-web
comparison 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 |
comparison
equal
deleted
inserted
replaced
734:26046f13e93b | 735:e4ae8e2b0afd |
---|---|
35 | 35 |
36 ### Menus ### | 36 ### Menus ### |
37 | 37 |
38 | 38 |
39 class PopupMenuPanel(PopupPanel): | 39 class PopupMenuPanel(PopupPanel): |
40 """This implementation of a popup menu (context menu) allow you to assign | 40 """Popup menu (contextual menu) with common callbacks for all the items. |
41 two special methods which are common to all the items, in order to hide | 41 |
42 certain items and also easily define their callbacks. The menu can be | 42 This implementation of a popup menu allow you to assign two special methods which |
43 bound to any of the mouse button (left, middle, right). | 43 are common to all the items, in order to hide certain items and define their callbacks. |
44 callbacks. The menu can be bound to any button of the mouse (left, middle, right). | |
44 """ | 45 """ |
46 | |
45 def __init__(self, entries, hide=None, callback=None, vertical=True, style=None, **kwargs): | 47 def __init__(self, entries, hide=None, callback=None, vertical=True, style=None, **kwargs): |
46 """ | 48 """ |
47 @param entries: a dict of dicts, where each sub-dict is representing | 49 @param entries (dict{unicode: dict{unicode: unicode}: |
48 one menu item: the sub-dict key can be used as the item text and | 50 - menu item keys |
49 description, but optional "title" and "desc" entries would be used | 51 - values: dict{unicode: unicode}: |
50 if they exists. The sub-dicts may be extended later to do | 52 - item data lile "title", "desc"... |
51 more complicated stuff or overwrite the common methods. | 53 - value |
52 @param hide: function with 2 args: widget, key as string and | 54 @param hide (callable): function of signature Widget, unicode: bool |
53 returns True if that item should be hidden from the context menu. | 55 which takes the sender and the item key, and returns True if that |
54 @param callback: function with 2 args: sender, key as string | 56 item has to be hidden from the context menu. |
55 @param vertical: True or False, to set the direction | 57 @param callback (callbable): function of signature Widget, unicode: None |
56 @param item_style: alternative CSS class for the menu items | 58 which takes the sender and the item key. |
57 @param menu_style: supplementary CSS class for the sender widget | 59 @param vertical (bool): set the direction vertical or horizontal |
60 @param item_style (unicode): alternative CSS class for the menu items | |
61 @param menu_style (unicode): supplementary CSS class for the sender widget | |
58 """ | 62 """ |
59 PopupPanel.__init__(self, autoHide=True, **kwargs) | 63 PopupPanel.__init__(self, autoHide=True, **kwargs) |
60 self._entries = entries | 64 self.entries = entries |
61 self._hide = hide | 65 self.hideMenu = hide |
62 self._callback = callback | 66 self.callback = callback |
63 self.vertical = vertical | 67 self.vertical = vertical |
64 self.style = {"selected": None, "menu": "itemKeyMenu", "item": "popupMenuItem"} | 68 self.style = {"selected": None, "menu": "itemKeyMenu", "item": "popupMenuItem"} |
65 if isinstance(style, dict): | 69 if isinstance(style, dict): |
66 self.style.update(style) | 70 self.style.update(style) |
67 self._senders = {} | 71 self.senders = {} |
68 | 72 |
69 def _show(self, sender): | 73 def showMenu(self, sender): |
70 """Popup the menu relative to this sender's position. | 74 """Popup the menu on the screen, where it fits to the sender's position. |
71 @param sender: the widget that has been clicked | 75 |
76 @param sender (Widget): the widget that has been clicked | |
72 """ | 77 """ |
73 menu = VerticalPanel() if self.vertical is True else HorizontalPanel() | 78 menu = VerticalPanel() if self.vertical is True else HorizontalPanel() |
74 menu.setStyleName(self.style["menu"]) | 79 menu.setStyleName(self.style["menu"]) |
75 | 80 |
76 def button_cb(item): | 81 def button_cb(item): |
77 """You can not put that method in the loop and rely | 82 # XXX: you can not put that method in the loop and rely on key |
78 on _key, because it is overwritten by each step. | 83 if self.callback is not None: |
79 You can rely on item.key instead, which is copied | 84 self.callback(sender=sender, key=item.key) |
80 from _key after the item creation. | |
81 @param item: the menu item that has been clicked | |
82 """ | |
83 if self._callback is not None: | |
84 self._callback(sender=sender, key=item.key) | |
85 self.hide(autoClosed=True) | 85 self.hide(autoClosed=True) |
86 | 86 |
87 for _key in self._entries.keys(): | 87 for key, entry in self.entries.iteritems(): |
88 entry = self._entries[_key] | 88 if self.hideMenu is not None and self.hideMenu(sender=sender, key=key) is True: |
89 if self._hide is not None and self._hide(sender=sender, key=_key) is True: | |
90 continue | 89 continue |
91 title = entry["title"] if "title" in entry.keys() else _key | 90 title = entry.get("title", key) |
92 item = Button(title, button_cb) | 91 item = Button(title, button_cb, StyleName=self.style["item"]) |
93 item.key = _key | 92 item.key = key # XXX: copy the key because we loop on it and it will change |
94 item.setStyleName(self.style["item"]) | 93 item.setTitle(entry.get("desc", title)) |
95 item.setTitle(entry["desc"] if "desc" in entry.keys() else title) | |
96 menu.add(item) | 94 menu.add(item) |
97 if len(menu.getChildren()) == 0: | 95 |
98 return | 96 if menu.getWidgetCount() == 0: |
97 return # no item to display means no menu at all | |
98 | |
99 self.add(menu) | 99 self.add(menu) |
100 | |
100 if self.vertical is True: | 101 if self.vertical is True: |
101 x = sender.getAbsoluteLeft() + sender.getOffsetWidth() | 102 x = sender.getAbsoluteLeft() + sender.getOffsetWidth() |
102 y = sender.getAbsoluteTop() | 103 y = sender.getAbsoluteTop() |
103 else: | 104 else: |
104 x = sender.getAbsoluteLeft() | 105 x = sender.getAbsoluteLeft() |
105 y = sender.getAbsoluteTop() + sender.getOffsetHeight() | 106 y = sender.getAbsoluteTop() + sender.getOffsetHeight() |
107 | |
106 self.setPopupPosition(x, y) | 108 self.setPopupPosition(x, y) |
107 self.show() | 109 self.show() |
110 | |
108 if self.style["selected"]: | 111 if self.style["selected"]: |
109 sender.addStyleDependentName(self.style["selected"]) | 112 sender.addStyleDependentName(self.style["selected"]) |
110 | 113 |
111 def _onHide(popup): | 114 def onHide(popup): |
112 if self.style["selected"]: | 115 if self.style["selected"]: |
113 sender.removeStyleDependentName(self.style["selected"]) | 116 sender.removeStyleDependentName(self.style["selected"]) |
114 return PopupPanel.onHideImpl(self, popup) | 117 return PopupPanel.onHideImpl(self, popup) |
115 | 118 |
116 self.onHideImpl = _onHide | 119 self.onHideImpl = onHide |
117 | 120 |
118 def registerClickSender(self, sender, button=BUTTON_LEFT): | 121 def registerClickSender(self, sender, button=BUTTON_LEFT): |
119 """Bind the menu to the specified sender. | 122 """Bind the menu to the specified sender. |
120 @param sender: the widget to which the menu should be bound | 123 |
121 @param: BUTTON_LEFT, BUTTON_MIDDLE or BUTTON_RIGHT | 124 @param sender (Widget): bind the menu to this widget |
122 """ | 125 @param (int): BUTTON_LEFT, BUTTON_MIDDLE or BUTTON_RIGHT |
123 self._senders.setdefault(sender, []) | 126 """ |
124 self._senders[sender].append(button) | 127 self.senders.setdefault(sender, []) |
128 self.senders[sender].append(button) | |
125 | 129 |
126 if button == BUTTON_RIGHT: | 130 if button == BUTTON_RIGHT: |
127 # WARNING: to disable the context menu is a bit tricky... | 131 # WARNING: to disable the context menu is a bit tricky... |
128 # The following seems to work on Firefox 24.0, but: | 132 # The following seems to work on Firefox 24.0, but: |
129 # TODO: find a cleaner way to disable the context menu | 133 # TODO: find a cleaner way to disable the context menu |
130 sender.getElement().setAttribute("oncontextmenu", "return false") | 134 sender.getElement().setAttribute("oncontextmenu", "return false") |
131 | 135 |
132 def _onBrowserEvent(event): | 136 def onBrowserEvent(event): |
133 button = DOM.eventGetButton(event) | 137 button = DOM.eventGetButton(event) |
134 if DOM.eventGetType(event) == "mousedown" and button in self._senders[sender]: | 138 if DOM.eventGetType(event) == "mousedown" and button in self.senders[sender]: |
135 self._show(sender) | 139 self.showMenu(sender) |
136 return sender.__class__.onBrowserEvent(sender, event) | 140 return sender.__class__.onBrowserEvent(sender, event) |
137 | 141 |
138 sender.onBrowserEvent = _onBrowserEvent | 142 sender.onBrowserEvent = onBrowserEvent |
139 | 143 |
140 def registerMiddleClickSender(self, sender): | 144 def registerMiddleClickSender(self, sender): |
141 self.registerClickSender(sender, BUTTON_MIDDLE) | 145 self.registerClickSender(sender, BUTTON_MIDDLE) |
142 | 146 |
143 def registerRightClickSender(self, sender): | 147 def registerRightClickSender(self, sender): |