Mercurial > libervia-web
comparison src/browser/sat_browser/list_manager.py @ 739:4545d48dee60
browser_side: improve ListPanel and TitlePanel
author | souliane <souliane@mailoo.org> |
---|---|
date | Thu, 19 Nov 2015 16:42:39 +0100 |
parents | fe3c2357a8c9 |
children | b6510fd9ae15 |
comparison
equal
deleted
inserted
replaced
738:caad07bdb659 | 739:4545d48dee60 |
---|---|
16 | 16 |
17 # You should have received a copy of the GNU Affero General Public License | 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/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 from sat.core.log import getLogger | 20 from sat.core.log import getLogger |
21 from pyjamas.ui.DragHandler import DragHandler | |
22 log = getLogger(__name__) | 21 log = getLogger(__name__) |
22 from sat.core.i18n import _ | |
23 | 23 |
24 from pyjamas.ui.ClickListener import ClickHandler | 24 from pyjamas.ui.ClickListener import ClickHandler |
25 from pyjamas.ui.FocusListener import FocusHandler | 25 from pyjamas.ui.FocusListener import FocusHandler |
26 from pyjamas.ui.ChangeListener import ChangeHandler | 26 from pyjamas.ui.ChangeListener import ChangeHandler |
27 from pyjamas.ui.DragHandler import DragHandler | |
27 from pyjamas.ui.KeyboardListener import KeyboardHandler, KEY_ENTER | 28 from pyjamas.ui.KeyboardListener import KeyboardHandler, KEY_ENTER |
28 from pyjamas.ui.DragWidget import DragWidget | 29 from pyjamas.ui.DragWidget import DragWidget |
29 from pyjamas.ui.ListBox import ListBox | 30 from pyjamas.ui.ListBox import ListBox |
30 from pyjamas.ui.Button import Button | 31 from pyjamas.ui.Button import Button |
31 from pyjamas.ui.FlowPanel import FlowPanel | 32 from pyjamas.ui.FlowPanel import FlowPanel |
44 | 45 |
45 | 46 |
46 class ListItem(HorizontalPanel): | 47 class ListItem(HorizontalPanel): |
47 """This class implements a list item with auto-completion and a delete button.""" | 48 """This class implements a list item with auto-completion and a delete button.""" |
48 | 49 |
49 STYLE = {"listItem-box": "listItem-box", | 50 STYLE = {"listItem": "listItem", |
51 "listItem-box": "listItem-box", | |
50 "listItem-box-invalid": "listItem-box-invalid", | 52 "listItem-box-invalid": "listItem-box-invalid", |
51 "listItem-button": "listItem-button", | 53 "listItem-button": "listItem-button", |
52 } | 54 } |
53 | 55 |
54 VALID = 1 | 56 VALID = 1 |
61 @param listener (ListItemHandler): handler for the UI events | 63 @param listener (ListItemHandler): handler for the UI events |
62 @param taglist (quick_list_manager.QuickTagList): list manager | 64 @param taglist (quick_list_manager.QuickTagList): list manager |
63 @param validate (callable): method returning a bool to validate the entry | 65 @param validate (callable): method returning a bool to validate the entry |
64 """ | 66 """ |
65 HorizontalPanel.__init__(self) | 67 HorizontalPanel.__init__(self) |
68 self.addStyleName(self.STYLE["listItem"]) | |
66 | 69 |
67 self.box = AutoCompleteTextBox(StyleName=self.STYLE["listItem-box"]) | 70 self.box = AutoCompleteTextBox(StyleName=self.STYLE["listItem-box"]) |
68 self.remove_btn = Button('<span>x</span>', Visible=False) | 71 self.remove_btn = Button('<span>x</span>', Visible=False) |
69 self.remove_btn.setStyleName(self.STYLE["listItem-button"]) | 72 self.remove_btn.setStyleName(self.STYLE["listItem-button"]) |
70 self.add(self.box) | 73 self.add(self.box) |
105 if self.validate: # if None, the state is always valid | 108 if self.validate: # if None, the state is always valid |
106 self.last_validity = self.validate(self.text) | 109 self.last_validity = self.validate(self.text) |
107 | 110 |
108 if self.last_validity == self.VALID: | 111 if self.last_validity == self.VALID: |
109 self.box.removeStyleName(self.STYLE["listItem-box-invalid"]) | 112 self.box.removeStyleName(self.STYLE["listItem-box-invalid"]) |
113 self.box.setVisibleLength(max(len(self.text), 10)) | |
110 elif self.last_validity == self.INVALID: | 114 elif self.last_validity == self.INVALID: |
111 self.box.addStyleName(self.STYLE["listItem-box-invalid"]) | 115 self.box.addStyleName(self.STYLE["listItem-box-invalid"]) |
112 elif self.last_validity == self.DUPLICATE: | 116 elif self.last_validity == self.DUPLICATE: |
113 self.remove_btn.click() # this may do more stuff then self.remove() | 117 self.remove_btn.click() # this may do more stuff then self.remove() |
114 return | 118 return |
115 | 119 |
116 if self.taglist and self.text: | 120 if self.taglist and self.text: |
117 self.taglist.tag([self.text]) | 121 self.taglist.tag([self.text]) |
118 self.last_checked_value = self.text | 122 self.last_checked_value = self.text |
123 self.box.setSelectionRange(len(self.text), 0) | |
119 self.remove_btn.setVisible(len(self.text) > 0) | 124 self.remove_btn.setVisible(len(self.text) > 0) |
120 | 125 |
121 def setFocus(self, focused): | 126 def setFocus(self, focused): |
122 self.box.setFocus(focused) | 127 self.box.setFocus(focused) |
123 | 128 |
144 self.addDragListener(listener) | 149 self.addDragListener(listener) |
145 | 150 |
146 | 151 |
147 def onDragStart(self, event): | 152 def onDragStart(self, event): |
148 """The user starts dragging the item.""" | 153 """The user starts dragging the item.""" |
149 self.box.setSelectionRange(len(self.text), 0) | |
150 | |
151 dt = event.dataTransfer | 154 dt = event.dataTransfer |
152 dt.setData('text/plain', "%s\n%s" % (self.text, "CONTACT_TEXTBOX")) | 155 dt.setData('text/plain', "%s\n%s" % (self.text, "CONTACT_TEXTBOX")) |
153 dt.setDragImage(self.box.getElement(), 15, 15) | 156 dt.setDragImage(self.box.getElement(), 15, 15) |
154 | 157 |
155 | 158 |
156 class ListItemHandler(ClickHandler, FocusHandler, KeyboardHandler, ChangeHandler): | 159 class ListItemHandler(ClickHandler, FocusHandler, KeyboardHandler, ChangeHandler): |
157 """Implements basic handlers for the ListItem events.""" | 160 """Implements basic handlers for the ListItem events.""" |
158 | 161 |
159 last_item = None # the last item is an empty text box for user input | 162 last_item = None # the last item is an empty text box for user input |
160 | 163 |
161 def __init__(self, manager, key): | 164 def __init__(self, taglist): |
165 """ | |
166 | |
167 @param taglist (quick_list_manager.QuickTagList): list manager | |
168 """ | |
162 ClickHandler.__init__(self) | 169 ClickHandler.__init__(self) |
163 FocusHandler.__init__(self) | 170 FocusHandler.__init__(self) |
164 ChangeHandler.__init__(self) | 171 ChangeHandler.__init__(self) |
165 KeyboardHandler.__init__(self) | 172 KeyboardHandler.__init__(self) |
166 self.manager = manager | 173 self.taglist = taglist |
167 self.key = key | |
168 | 174 |
169 def addItem(self, item): | 175 def addItem(self, item): |
170 raise NotImplementedError | 176 raise NotImplementedError |
171 | 177 |
172 def removeItem(self, item): | 178 def removeItem(self, item): |
187 | 193 |
188 def onFocus(self, sender): | 194 def onFocus(self, sender): |
189 """The text box has the focus.""" | 195 """The text box has the focus.""" |
190 #log.debug("onFocus sender type: %s" % type(sender)) | 196 #log.debug("onFocus sender type: %s" % type(sender)) |
191 assert isinstance(sender, AutoCompleteTextBox) | 197 assert isinstance(sender, AutoCompleteTextBox) |
192 sender.setCompletionItems(self.manager.untagged) | 198 sender.setCompletionItems(self.taglist.untagged) |
193 | 199 |
194 def onKeyUp(self, sender, keycode, modifiers): | 200 def onKeyUp(self, sender, keycode, modifiers): |
195 """The text box is being modified - or ENTER key has been pressed.""" | 201 """The text box is being modified - or ENTER key has been pressed.""" |
196 # this is called after onChange when you press ENTER, and now we get the final value | 202 # this is called after onChange when you press ENTER, and now we get the final value |
197 #log.debug("onKeyUp sender type: %s" % type(sender)) | 203 #log.debug("onKeyUp sender type: %s" % type(sender)) |
211 item = textbox.getParent() | 217 item = textbox.getParent() |
212 if item.text == item.last_checked_value: | 218 if item.text == item.last_checked_value: |
213 # this method has already been called (by self.onChange) and there's nothing new | 219 # this method has already been called (by self.onChange) and there's nothing new |
214 return | 220 return |
215 item.refresh() | 221 item.refresh() |
216 item.box.setSelectionRange(len(item.text), 0) | |
217 if item == self.last_item and item.last_validity == ListItem.VALID and item.text: | 222 if item == self.last_item and item.last_validity == ListItem.VALID and item.text: |
218 self.addItem() | 223 self.addItem() |
219 | 224 |
220 class DraggableListItemHandler(ListItemHandler, DragHandler): | 225 class DraggableListItemHandler(ListItemHandler, DragHandler): |
221 """Implements basic handlers for the DraggableListItem events.""" | 226 """Implements basic handlers for the DraggableListItem events.""" |
222 | 227 |
223 def __init__(self, manager, key): | 228 def __init__(self, manager): |
224 ListItemHandler.__init__(self, manager, key) | 229 """ |
230 | |
231 @param manager (ListManager): list manager | |
232 """ | |
233 ListItemHandler.__init__(self, manager) | |
225 DragHandler.__init__(self) | 234 DragHandler.__init__(self) |
235 | |
236 @property | |
237 def manager(self): | |
238 return self.taglist | |
226 | 239 |
227 def onDragStart(self, event): | 240 def onDragStart(self, event): |
228 """The user starts dragging the item.""" | 241 """The user starts dragging the item.""" |
229 self.manager.drop_target = None | 242 self.manager.drop_target = None |
230 | 243 |
244 # XXX: beware that pyjamas.ui.FlowPanel is not fully implemented: | 257 # XXX: beware that pyjamas.ui.FlowPanel is not fully implemented: |
245 # - it can not be used with pyjamas.ui.Label | 258 # - it can not be used with pyjamas.ui.Label |
246 # - FlowPanel.insert doesn't work | 259 # - FlowPanel.insert doesn't work |
247 | 260 |
248 STYLE = {"listPanel": "listPanel"} | 261 STYLE = {"listPanel": "listPanel"} |
249 | 262 ACCEPT_NEW_ENTRY = False |
250 def __init__(self, manager, key, items): | 263 |
264 def __init__(self, manager, items=None): | |
251 """Initialization with a button for the list name (key) and a DraggableListItem. | 265 """Initialization with a button for the list name (key) and a DraggableListItem. |
252 | 266 |
253 @param manager (ListManager) | 267 @param manager (ListManager): list manager |
254 @param key (unicode): list name | 268 @param items (list): items to be set |
255 @param items (list): items to append | |
256 """ | 269 """ |
257 FlowPanel.__init__(self) | 270 FlowPanel.__init__(self) |
258 DraggableListItemHandler.__init__(self, manager, key) | 271 DraggableListItemHandler.__init__(self, manager) |
259 libervia_widget.DropCell.__init__(self, None) | 272 libervia_widget.DropCell.__init__(self, None) |
260 self.addStyleName(self.STYLE["listPanel"]) | 273 self.addStyleName(self.STYLE["listPanel"]) |
261 self.manager = manager | 274 self.manager = manager |
262 items.sort() | 275 self.resetItems(items) |
263 self.addItem() | |
264 for item in items: | |
265 self.addItem(unicode(item)) | |
266 | 276 |
267 # FIXME: dirty magic strings '@' and '@@' | 277 # FIXME: dirty magic strings '@' and '@@' |
268 self.drop_keys = {"GROUP": lambda host, item_s: self.addItem("@%s" % item_s), | 278 self.drop_keys = {"GROUP": lambda host, item_s: self.addItem("@%s" % item_s), |
269 "CONTACT": lambda host, item_s: self.addItem(item_s), | 279 "CONTACT": lambda host, item_s: self.addItem(item_s), |
270 "CONTACT_TITLE": lambda host, item_s: self.addItem('@@'), | 280 "CONTACT_TITLE": lambda host, item_s: self.addItem('@@'), |
307 def count(list_, item): # XXX: list.count in not implemented by pyjamas | 317 def count(list_, item): # XXX: list.count in not implemented by pyjamas |
308 return len([elt for elt in list_ if elt == item]) | 318 return len([elt for elt in list_ if elt == item]) |
309 | 319 |
310 if count(self.getItems(), text) > 1: | 320 if count(self.getItems(), text) > 1: |
311 return ListItem.DUPLICATE # item already exists in this list so we suggest its deletion | 321 return ListItem.DUPLICATE # item already exists in this list so we suggest its deletion |
322 if self.ACCEPT_NEW_ENTRY: | |
323 return ListItem.VALID | |
312 return ListItem.VALID if text in self.manager.items or not text else ListItem.INVALID | 324 return ListItem.VALID if text in self.manager.items or not text else ListItem.INVALID |
313 | 325 |
314 def addItem(self, text=""): | 326 def addItem(self, text=""): |
315 """Add an item. | 327 """Add an item. |
316 | 328 |
344 """ | 356 """ |
345 if item == self.last_item: | 357 if item == self.last_item: |
346 self.addItem("") | 358 self.addItem("") |
347 item.remove() # this also updates the taglist | 359 item.remove() # this also updates the taglist |
348 | 360 |
361 def resetItems(self, items): | |
362 """Reset the items. | |
363 | |
364 @param items (list): items to be set | |
365 """ | |
366 for child in self.getChildren(): | |
367 child.remove() | |
368 | |
369 self.addItem() | |
370 if not items: | |
371 return | |
372 | |
373 items.sort() | |
374 for item in items: | |
375 self.addItem(unicode(item)) | |
376 | |
349 | 377 |
350 class ListManager(FlexTable, quick_list_manager.QuickTagList): | 378 class ListManager(FlexTable, quick_list_manager.QuickTagList): |
351 """Implements a table to manage one or several lists of items.""" | 379 """Implements a table to manage one or several lists of items.""" |
352 | 380 |
353 STYLE = {"listManager-button": "group", | 381 STYLE = {"listManager-button": "group", |
378 | 406 |
379 if items is None: | 407 if items is None: |
380 items = [] | 408 items = [] |
381 | 409 |
382 self.lists[key] = {"button": Button(key, Title=key, StyleName=self.STYLE["listManager-button"]), | 410 self.lists[key] = {"button": Button(key, Title=key, StyleName=self.STYLE["listManager-button"]), |
383 "panel": ListPanel(self, key, items)} | 411 "panel": ListPanel(self, items)} |
384 | 412 |
385 y, x = len(self.lists), 0 | 413 y, x = len(self.lists), 0 |
386 self.insertRow(y) | 414 self.insertRow(y) |
387 self.setWidget(y, x, self.lists[key]["button"]) | 415 self.setWidget(y, x, self.lists[key]["button"]) |
388 self.setWidget(y, x + 1, self.lists[key]["panel"]) | 416 self.setWidget(y, x + 1, self.lists[key]["panel"]) |