Mercurial > libervia-web
comparison src/browser/sat_browser/base_panel.py @ 648:6d3142b782c3 frontends_multi_profiles
browser_side: classes reorganisation:
- moved widgets in dedicated modules (base, contact, editor, libervia) and a widget module for single classes
- same thing for panels (base, main, contact)
- libervia_widget mix main panels and widget and drag n drop for technical reasons (see comments)
- renamed WebPanel to WebWidget
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 26 Feb 2015 18:10:54 +0100 |
parents | src/browser/sat_browser/base_panels.py@e0021d571eef |
children | 9877607c719a |
comparison
equal
deleted
inserted
replaced
647:e0021d571eef | 648:6d3142b782c3 |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # Libervia: a Salut à Toi frontend | |
5 # Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.org> | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 from sat.core.log import getLogger | |
21 log = getLogger(__name__) | |
22 from sat.core.i18n import _ | |
23 | |
24 from pyjamas.ui.VerticalPanel import VerticalPanel | |
25 from pyjamas.ui.HorizontalPanel import HorizontalPanel | |
26 from pyjamas.ui.ScrollPanel import ScrollPanel | |
27 from pyjamas.ui.Button import Button | |
28 from pyjamas.ui.SimplePanel import SimplePanel | |
29 from pyjamas.ui.PopupPanel import PopupPanel | |
30 from pyjamas.ui.StackPanel import StackPanel | |
31 from pyjamas.ui.TextArea import TextArea | |
32 from pyjamas.ui.Event import BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT | |
33 from pyjamas import DOM | |
34 | |
35 | |
36 ### Menus ### | |
37 | |
38 | |
39 class PopupMenuPanel(PopupPanel): | |
40 """This implementation of a popup menu (context menu) allow you to assign | |
41 two special methods which are common to all the items, in order to hide | |
42 certain items and also easily define their callbacks. The menu can be | |
43 bound to any of the mouse button (left, middle, right). | |
44 """ | |
45 def __init__(self, entries, hide=None, callback=None, vertical=True, style=None, **kwargs): | |
46 """ | |
47 @param entries: a dict of dicts, where each sub-dict is representing | |
48 one menu item: the sub-dict key can be used as the item text and | |
49 description, but optional "title" and "desc" entries would be used | |
50 if they exists. The sub-dicts may be extended later to do | |
51 more complicated stuff or overwrite the common methods. | |
52 @param hide: function with 2 args: widget, key as string and | |
53 returns True if that item should be hidden from the context menu. | |
54 @param callback: function with 2 args: sender, key as string | |
55 @param vertical: True or False, to set the direction | |
56 @param item_style: alternative CSS class for the menu items | |
57 @param menu_style: supplementary CSS class for the sender widget | |
58 """ | |
59 PopupPanel.__init__(self, autoHide=True, **kwargs) | |
60 self._entries = entries | |
61 self._hide = hide | |
62 self._callback = callback | |
63 self.vertical = vertical | |
64 self.style = {"selected": None, "menu": "itemKeyMenu", "item": "popupMenuItem"} | |
65 if isinstance(style, dict): | |
66 self.style.update(style) | |
67 self._senders = {} | |
68 | |
69 def _show(self, sender): | |
70 """Popup the menu relative to this sender's position. | |
71 @param sender: the widget that has been clicked | |
72 """ | |
73 menu = VerticalPanel() if self.vertical is True else HorizontalPanel() | |
74 menu.setStyleName(self.style["menu"]) | |
75 | |
76 def button_cb(item): | |
77 """You can not put that method in the loop and rely | |
78 on _key, because it is overwritten by each step. | |
79 You can rely on item.key instead, which is copied | |
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) | |
86 | |
87 for _key in self._entries.keys(): | |
88 entry = self._entries[_key] | |
89 if self._hide is not None and self._hide(sender=sender, key=_key) is True: | |
90 continue | |
91 title = entry["title"] if "title" in entry.keys() else _key | |
92 item = Button(title, button_cb) | |
93 item.key = _key | |
94 item.setStyleName(self.style["item"]) | |
95 item.setTitle(entry["desc"] if "desc" in entry.keys() else title) | |
96 menu.add(item) | |
97 if len(menu.getChildren()) == 0: | |
98 return | |
99 self.add(menu) | |
100 if self.vertical is True: | |
101 x = sender.getAbsoluteLeft() + sender.getOffsetWidth() | |
102 y = sender.getAbsoluteTop() | |
103 else: | |
104 x = sender.getAbsoluteLeft() | |
105 y = sender.getAbsoluteTop() + sender.getOffsetHeight() | |
106 self.setPopupPosition(x, y) | |
107 self.show() | |
108 if self.style["selected"]: | |
109 sender.addStyleDependentName(self.style["selected"]) | |
110 | |
111 def _onHide(popup): | |
112 if self.style["selected"]: | |
113 sender.removeStyleDependentName(self.style["selected"]) | |
114 return PopupPanel.onHideImpl(self, popup) | |
115 | |
116 self.onHideImpl = _onHide | |
117 | |
118 def registerClickSender(self, sender, button=BUTTON_LEFT): | |
119 """Bind the menu to the specified sender. | |
120 @param sender: the widget to which the menu should be bound | |
121 @param: BUTTON_LEFT, BUTTON_MIDDLE or BUTTON_RIGHT | |
122 """ | |
123 self._senders.setdefault(sender, []) | |
124 self._senders[sender].append(button) | |
125 | |
126 if button == BUTTON_RIGHT: | |
127 # WARNING: to disable the context menu is a bit tricky... | |
128 # The following seems to work on Firefox 24.0, but: | |
129 # TODO: find a cleaner way to disable the context menu | |
130 sender.getElement().setAttribute("oncontextmenu", "return false") | |
131 | |
132 def _onBrowserEvent(event): | |
133 button = DOM.eventGetButton(event) | |
134 if DOM.eventGetType(event) == "mousedown" and button in self._senders[sender]: | |
135 self._show(sender) | |
136 return sender.__class__.onBrowserEvent(sender, event) | |
137 | |
138 sender.onBrowserEvent = _onBrowserEvent | |
139 | |
140 def registerMiddleClickSender(self, sender): | |
141 self.registerClickSender(sender, BUTTON_MIDDLE) | |
142 | |
143 def registerRightClickSender(self, sender): | |
144 self.registerClickSender(sender, BUTTON_RIGHT) | |
145 | |
146 | |
147 ### Generic panels ### | |
148 | |
149 | |
150 class ToggleStackPanel(StackPanel): | |
151 """This is a pyjamas.ui.StackPanel with modified behavior. All sub-panels ca be | |
152 visible at the same time, clicking a sub-panel header will not display it and hide | |
153 the others but only toggle its own visibility. The argument 'visibleStack' is ignored. | |
154 Note that the argument 'visible' has been added to listener's 'onStackChanged' method. | |
155 """ | |
156 | |
157 def __init__(self, **kwargs): | |
158 StackPanel.__init__(self, **kwargs) | |
159 | |
160 def onBrowserEvent(self, event): | |
161 if DOM.eventGetType(event) == "click": | |
162 index = self.getDividerIndex(DOM.eventGetTarget(event)) | |
163 if index != -1: | |
164 self.toggleStack(index) | |
165 | |
166 def add(self, widget, stackText="", asHTML=False, visible=False): | |
167 StackPanel.add(self, widget, stackText, asHTML) | |
168 self.setStackVisible(self.getWidgetCount() - 1, visible) | |
169 | |
170 def toggleStack(self, index): | |
171 if index >= self.getWidgetCount(): | |
172 return | |
173 visible = not self.getWidget(index).getVisible() | |
174 self.setStackVisible(index, visible) | |
175 for listener in self.stackListeners: | |
176 listener.onStackChanged(self, index, visible) | |
177 | |
178 | |
179 class TitlePanel(ToggleStackPanel): | |
180 """A toggle panel to set the message title""" | |
181 def __init__(self): | |
182 ToggleStackPanel.__init__(self, Width="100%") | |
183 self.text_area = TextArea() | |
184 self.add(self.text_area, _("Title")) | |
185 self.addStackChangeListener(self) | |
186 | |
187 def onStackChanged(self, sender, index, visible=None): | |
188 if visible is None: | |
189 visible = sender.getWidget(index).getVisible() | |
190 text = self.text_area.getText() | |
191 suffix = "" if (visible or not text) else (": %s" % text) | |
192 sender.setStackText(index, _("Title") + suffix) | |
193 | |
194 def getText(self): | |
195 return self.text_area.getText() | |
196 | |
197 def setText(self, text): | |
198 self.text_area.setText(text) | |
199 | |
200 | |
201 class ScrollPanelWrapper(SimplePanel): | |
202 """Scroll Panel like component, wich use the full available space | |
203 to work around percent size issue, it use some of the ideas found | |
204 here: http://code.google.com/p/google-web-toolkit/issues/detail?id=316 | |
205 specially in code given at comment #46, thanks to Stefan Bachert""" | |
206 | |
207 def __init__(self, *args, **kwargs): | |
208 SimplePanel.__init__(self) | |
209 self.spanel = ScrollPanel(*args, **kwargs) | |
210 SimplePanel.setWidget(self, self.spanel) | |
211 DOM.setStyleAttribute(self.getElement(), "position", "relative") | |
212 DOM.setStyleAttribute(self.getElement(), "top", "0px") | |
213 DOM.setStyleAttribute(self.getElement(), "left", "0px") | |
214 DOM.setStyleAttribute(self.getElement(), "width", "100%") | |
215 DOM.setStyleAttribute(self.getElement(), "height", "100%") | |
216 DOM.setStyleAttribute(self.spanel.getElement(), "position", "absolute") | |
217 DOM.setStyleAttribute(self.spanel.getElement(), "width", "100%") | |
218 DOM.setStyleAttribute(self.spanel.getElement(), "height", "100%") | |
219 | |
220 def setWidget(self, widget): | |
221 self.spanel.setWidget(widget) | |
222 | |
223 def setScrollPosition(self, position): | |
224 self.spanel.setScrollPosition(position) | |
225 | |
226 def scrollToBottom(self): | |
227 self.setScrollPosition(self.spanel.getElement().scrollHeight) |