Package muntjac :: Package ui :: Module panel
[hide private]
[frames] | no frames]

Source Code for Module muntjac.ui.panel

  1  # Copyright (C) 2012 Vaadin Ltd.  
  2  # Copyright (C) 2012 Richard Lincoln 
  3  #  
  4  # Licensed under the Apache License, Version 2.0 (the "License");  
  5  # you may not use this file except in compliance with the License.  
  6  # You may obtain a copy of the License at  
  7  #  
  8  #     http://www.apache.org/licenses/LICENSE-2.0  
  9  #  
 10  # Unless required by applicable law or agreed to in writing, software  
 11  # distributed under the License is distributed on an "AS IS" BASIS,  
 12  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
 13  # See the License for the specific language governing permissions and  
 14  # limitations under the License. 
 15   
 16  """Defines a simple single component container.""" 
 17   
 18  from warnings import warn 
 19   
 20  from muntjac.terminal.scrollable import IScrollable 
 21  from muntjac.event.action_manager import ActionManager 
 22  from muntjac.event import action 
 23  from muntjac.ui.vertical_layout import VerticalLayout 
 24  from muntjac.ui.abstract_component_container import AbstractComponentContainer 
 25   
 26  from muntjac.ui import component_container 
 27   
 28  from muntjac.ui.component import IFocusable 
 29  from muntjac.ui.layout import ILayout 
 30  from muntjac.event.mouse_events import ClickEvent, IClickListener 
 31   
 32  from muntjac.terminal.gwt.client.mouse_event_details import MouseEventDetails 
 33  from muntjac.terminal.gwt.client.ui.v_panel import VPanel 
 34   
 35   
36 -class Panel(AbstractComponentContainer, IScrollable, 37 component_container.IComponentAttachListener, 38 component_container.IComponentDetachListener, 39 action.INotifier, IFocusable):
40 """Panel - a simple single component container. 41 42 @author: Vaadin Ltd. 43 @author: Richard Lincoln 44 @version: 1.1.2 45 """ 46 47 CLIENT_WIDGET = None #ClientWidget(VPanel, LoadStyle.EAGER) 48 49 _CLICK_EVENT = VPanel.CLICK_EVENT_IDENTIFIER 50 51 # Removes extra decorations from the Panel. 52 # 53 # @deprecated: this style is no longer part of the core framework and this 54 # component, even though most built-in themes implement this 55 # style. Use the constant specified in the theme class file 56 # that you're using, if it provides one, e.g. 57 # L{Reindeer#PANEL_LIGHT} or L{Runo#PANEL_LIGHT} . 58 STYLE_LIGHT = 'light' 59 60
61 - def __init__(self, *args):
62 """Creates a new panel with caption and or content. A VerticalLayout 63 is used as content by default. 64 65 @param args: tuple of the form 66 - (content) 67 1. the content for the panel (HTML/XHTML). 68 - (caption) 69 1. the caption used in the panel (HTML/XHTML). 70 - (caption, content) 71 1. the caption of the panel. 72 2. the content used in the panel (HTML/XHTML). 73 """ 74 super(Panel, self).__init__() 75 76 #: Content of the panel. 77 self._content = None 78 79 #: Scroll X position. 80 self._scrollOffsetX = 0 81 82 #: Scroll Y position. 83 self._scrollOffsetY = 0 84 85 #: Scrolling mode. 86 self._scrollable = False 87 88 #: Keeps track of the Actions added to this component, and manages 89 # the painting and handling as well. 90 self.actionManager = None 91 92 #: By default the Panel is not in the normal document focus flow and 93 # can only be focused by using the focus()-method. Change this to 0 94 # if you want to have the Panel in the normal focus flow. 95 self._tabIndex = -1 96 97 nargs = len(args) 98 if nargs == 0: 99 Panel.__init__(self, None) 100 elif nargs == 1: 101 if isinstance(args[0], basestring): 102 caption, = args 103 Panel.__init__(self, caption, None) 104 self.setCaption(caption) 105 else: 106 content, = args 107 self.setContent(content) 108 self.setWidth(100, self.UNITS_PERCENTAGE) 109 elif nargs == 2: 110 caption, content = args 111 Panel.__init__(self, content) 112 self.setCaption(caption) 113 else: 114 raise ValueError, 'too many arguments'
115 116
117 - def setCaption(self, caption):
118 """Sets the caption of the panel. 119 120 Note that the caption is interpreted as HTML/XHTML and therefore care 121 should be taken not to enable HTML injection and XSS attacks using 122 panel captions. This behavior may change in future versions. 123 124 @see L{AbstractComponent.setCaption} 125 """ 126 super(Panel, self).setCaption(caption)
127 128
129 - def getLayout(self):
130 """Gets the current layout of the panel. 131 132 @return: the Current layout of the panel. 133 @deprecated: A Panel can now contain a IComponentContainer which is not 134 necessarily a ILayout. Use L{getContent} instead. 135 """ 136 warn('Use getContent() instead', DeprecationWarning) 137 138 if isinstance(self._content, ILayout): 139 return self._content 140 elif self._content is None: 141 return None 142 raise ValueError, ('Panel does not contain a ILayout. ' 143 'Use getContent() instead of getLayout().')
144 145
146 - def setLayout(self, newLayout):
147 """Sets the layout of the panel. 148 149 If given layout is null, a VerticalLayout with margins set is used 150 as a default. 151 152 Components from old layout are not moved to new layout by default. 153 Use function in ILayout interface manually. 154 155 @param newLayout: 156 the New layout of the panel. 157 @deprecated: A Panel can now contain a IComponentContainer which is 158 not necessarily a ILayout. Use L{setContent} instead. 159 """ 160 self.setContent(newLayout)
161 162
163 - def getContent(self):
164 """Returns the content of the Panel. 165 """ 166 return self._content
167 168
169 - def setContent(self, newContent):
170 """Set the content of the Panel. If null is given as the new content 171 then a layout is automatically created and set as the content. 172 173 @param newContent: The new content 174 """ 175 # If the content is null we create the default content 176 if newContent is None: 177 newContent = self.createDefaultContent() 178 179 # if newContent is None: 180 # raise ValueError, "Content cannot be null" 181 182 if newContent == self._content: 183 return # don't set the same content twice 184 185 # detach old content if present 186 if self._content is not None: 187 self._content.setParent(None) 188 self._content.removeListener(self, 189 component_container.IComponentAttachListener) 190 self._content.removeListener(self, 191 component_container.IComponentDetachListener) 192 193 # Sets the panel to be parent for the content 194 newContent.setParent(self) 195 196 # Sets the new content 197 self._content = newContent 198 199 # Adds the event listeners for new content 200 newContent.addListener(self, 201 component_container.IComponentAttachListener) 202 newContent.addListener(self, 203 component_container.IComponentDetachListener) 204 205 self._content = newContent
206 207
208 - def createDefaultContent(self):
209 """Create a IComponentContainer which is added by default to 210 the Panel if user does not specify any content. 211 """ 212 layout = VerticalLayout() 213 # Force margins by default 214 layout.setMargin(True) 215 return layout
216 217
218 - def paintContent(self, target):
219 self._content.paint(target) 220 221 target.addVariable(self, 'tabindex', self.getTabIndex()) 222 223 if self.isScrollable(): 224 target.addVariable(self, 'scrollLeft', self.getScrollLeft()) 225 target.addVariable(self, 'scrollTop', self.getScrollTop()) 226 227 if self.actionManager is not None: 228 self.actionManager.paintActions(None, target)
229 230
231 - def requestRepaintAll(self):
232 # Panel has odd structure, delegate to layout 233 self.requestRepaint() 234 if self.getContent() is not None: 235 self.getContent().requestRepaintAll()
236 237
238 - def addComponent(self, c):
239 """Adds the component into this container. 240 241 @param c: the component to be added. 242 @see: L{AbstractComponentContainer.addComponent} 243 """ 244 self._content.addComponent(c)
245 # No repaint request is made as we except the underlying 246 # container to request repaints 247 248
249 - def removeComponent(self, c):
250 """Removes the component from this container. 251 252 @param c: The component to be removed. 253 @see: L{AbstractComponentContainer.removeComponent} 254 """ 255 self._content.removeComponent(c)
256 # No repaint request is made as we except the underlying 257 # container to request repaints 258 259
260 - def getComponentIterator(self):
261 """Gets the component container iterator for going through 262 all the components in the container. 263 264 @return: the iterator of the components inside the container. 265 @see: L{IComponentContainer.getComponentIterator} 266 """ 267 return self._content.getComponentIterator()
268 269
270 - def changeVariables(self, source, variables):
271 """Called when one or more variables handled by the implementing 272 class are changed. 273 274 @see: L{muntjac.terminal.VariableOwner.changeVariables} 275 """ 276 super(Panel, self).changeVariables(source, variables) 277 278 if self._CLICK_EVENT in variables: 279 self.fireClick(variables[self._CLICK_EVENT]) 280 281 # Get new size 282 newWidth = variables.get('width') 283 newHeight = variables.get('height') 284 285 if newWidth is not None and newWidth != self.getWidth(): 286 self.setWidth(newWidth, self.UNITS_PIXELS) 287 288 if newHeight is not None and newHeight != self.getHeight(): 289 self.setHeight(newHeight, self.UNITS_PIXELS) 290 291 # Scrolling 292 newScrollX = variables.get('scrollLeft') 293 newScrollY = variables.get('scrollTop') 294 if newScrollX is not None and newScrollX != self.getScrollLeft(): 295 # set internally, not to fire request repaint 296 self._scrollOffsetX = newScrollX 297 298 if newScrollY is not None and newScrollY != self.getScrollTop(): 299 # set internally, not to fire request repaint 300 self._scrollOffsetY = newScrollY 301 302 # Actions 303 if self.actionManager is not None: 304 self.actionManager.handleActions(variables, self)
305 306
307 - def getScrollLeft(self):
308 return self._scrollOffsetX
309 310
311 - def getScrollOffsetX(self):
312 """@deprecated: use L{getScrollLeft} instead""" 313 warn('use getScrollLeft() instead', DeprecationWarning) 314 return self.getScrollLeft()
315 316
317 - def getScrollTop(self):
318 return self._scrollOffsetY
319 320
321 - def getScrollOffsetY(self):
322 """@deprecated: use L{getScrollTop} instead""" 323 warn('use getScrollTop() instead', DeprecationWarning) 324 return self.getScrollTop()
325 326
327 - def isScrollable(self):
328 return self._scrollable
329 330
331 - def setScrollable(self, isScrollingEnabled):
332 """Sets the panel as programmatically scrollable. 333 334 Panel is by default not scrollable programmatically with 335 L{setScrollLeft} and L{setScrollTop}, so if you use those methods, 336 you need to enable scrolling with this method. Components that 337 extend Panel may have a different default for the programmatic 338 scrollability. 339 340 @see: L{IScrollable.setScrollable} 341 """ 342 if self._scrollable != isScrollingEnabled: 343 self._scrollable = isScrollingEnabled 344 self.requestRepaint()
345 346
347 - def setScrollLeft(self, pixelsScrolled):
348 """Sets the horizontal scroll position. 349 350 Setting the horizontal scroll position with this method requires that 351 programmatic scrolling of the component has been enabled. For Panel it 352 is disabled by default, so you have to call L{setScrollable}. 353 Components extending Panel may have a different default for 354 programmatic scrollability. 355 356 @see: L{IScrollable.setScrollable} 357 @see: L{setScrollable} 358 """ 359 if pixelsScrolled < 0: 360 raise ValueError, 'Scroll offset must be at least 0' 361 362 if self._scrollOffsetX != pixelsScrolled: 363 self._scrollOffsetX = pixelsScrolled 364 self.requestRepaint()
365 366
367 - def setScrollOffsetX(self, pixels):
368 """@deprecated: use setScrollLeft() method instead""" 369 warn('use setScrollLeft() method instead', DeprecationWarning) 370 self.setScrollLeft(pixels)
371 372
373 - def setScrollTop(self, pixelsScrolledDown):
374 """Sets the vertical scroll position. 375 376 Setting the vertical scroll position with this method requires that 377 programmatic scrolling of the component has been enabled. For Panel 378 it is disabled by default, so you have to call L{setScrollable}. 379 Components extending Panel may have a different default for 380 programmatic scrollability. 381 382 @see: L{IScrollable.setScrollTop} 383 @see: L{setScrollable} 384 """ 385 if pixelsScrolledDown < 0: 386 raise ValueError, 'Scroll offset must be at least 0' 387 if self._scrollOffsetY != pixelsScrolledDown: 388 self._scrollOffsetY = pixelsScrolledDown 389 self.requestRepaint()
390 391
392 - def setScrollOffsetY(self, pixels):
393 """@deprecated: use setScrollTop() method instead""" 394 warn('use setScrollTop() method instead', DeprecationWarning) 395 self.setScrollTop(pixels)
396 397
398 - def replaceComponent(self, oldComponent, newComponent):
399 self._content.replaceComponent(oldComponent, newComponent)
400 401
402 - def componentAttachedToContainer(self, event):
403 """A new component is attached to container. 404 405 @see: L{IComponentAttachListener.componentAttachedToContainer} 406 """ 407 if event.getContainer() == self._content: 408 self.fireComponentAttachEvent(event.getAttachedComponent())
409 410
411 - def componentDetachedFromContainer(self, event):
412 """A component has been detached from container. 413 414 @see: L{IComponentDetachListener.componentDetachedFromContainer} 415 """ 416 if event.getContainer() == self._content: 417 self.fireComponentDetachEvent(event.getDetachedComponent())
418 419
420 - def attach(self):
421 """Notifies the component that it is connected to an application. 422 423 @see: L{IComponent.attach} 424 """ 425 # can't call parent here as this is Panels hierarchy is a hack 426 self.requestRepaint() 427 if self._content is not None: 428 self._content.attach()
429 430
431 - def detach(self):
432 """Notifies the component that it is detached from the application. 433 434 @see: L{IComponent.detach} 435 """ 436 # can't call parent here as this is Panels hierarchy is a hack 437 if self._content is not None: 438 self._content.detach()
439 440
441 - def removeAllComponents(self):
442 """Removes all components from this container. 443 444 @see: L{IComponentContainer.removeAllComponents} 445 """ 446 self._content.removeAllComponents()
447 448
449 - def getActionManager(self):
450 if self.actionManager is None: 451 self.actionManager = ActionManager(self) 452 return self.actionManager
453 454
455 - def addAction(self, action):
457 458
459 - def removeAction(self, action):
460 if self.actionManager is not None: 461 self.actionManager.removeAction(action)
462 463
464 - def addActionHandler(self, actionHandler):
465 self.getActionManager().addActionHandler(actionHandler)
466 467
468 - def removeActionHandler(self, actionHandler):
469 if self.actionManager is not None: 470 self.actionManager.removeActionHandler(actionHandler)
471 472
473 - def removeAllActionHandlers(self):
474 """Removes all action handlers""" 475 if self.actionManager is not None: 476 self.actionManager.removeAllActionHandlers()
477 478
479 - def addListener(self, listener, iface=None):
480 """Add a click listener to the Panel. The listener is called whenever 481 the user clicks inside the Panel. Also when the click targets a 482 component inside the Panel, provided the targeted component does not 483 prevent the click event from propagating. 484 485 Use L{removeListener} to remove the listener. 486 487 @param listener: 488 The listener to add 489 """ 490 if (isinstance(listener, IClickListener) and 491 (iface is None or issubclass(iface, IClickListener))): 492 self.registerListener(self._CLICK_EVENT, ClickEvent, 493 listener, IClickListener.clickMethod) 494 495 super(Panel, self).addListener(listener, iface)
496 497
498 - def addCallback(self, callback, eventType=None, *args):
499 if eventType is None: 500 eventType = callback._eventType 501 502 if issubclass(eventType, ClickEvent): 503 self.registerCallback(ClickEvent, callback, 504 self._CLICK_EVENT, *args) 505 else: 506 super(Panel, self).addCallback(callback, eventType, *args)
507 508
509 - def removeListener(self, listener, iface=None):
510 """Remove a click listener from the Panel. The listener should earlier 511 have been added using L{addListener}. 512 513 @param listener: 514 The listener to remove 515 """ 516 if (isinstance(listener, IClickListener) and 517 (iface is None or issubclass(iface, IClickListener))): 518 self.withdrawListener(self._CLICK_EVENT, ClickEvent, listener) 519 520 super(Panel, self).removeListener(listener, iface)
521 522
523 - def removeCallback(self, callback, eventType=None):
524 if eventType is None: 525 eventType = callback._eventType 526 527 if issubclass(eventType, ClickEvent): 528 self.withdrawCallback(ClickEvent, callback, self._CLICK_EVENT) 529 else: 530 super(Panel, self).removeCallback(callback, eventType)
531 532
533 - def fireClick(self, parameters):
534 """Fire a click event to all click listeners. 535 536 @param parameters: 537 The raw "value" of the variable change from the client side 538 """ 539 params = parameters.get('mouseDetails') 540 mouseDetails = MouseEventDetails.deSerialize(params) 541 self.fireEvent( ClickEvent(self, mouseDetails) )
542 543
544 - def getTabIndex(self):
545 return self._tabIndex
546 547
548 - def setTabIndex(self, tabIndex):
549 self._tabIndex = tabIndex 550 self.requestRepaint()
551 552
553 - def focus(self):
554 """Moves keyboard focus to the component. 555 556 @see: L{IFocusable.focus} 557 """ 558 super(Panel, self).focus()
559