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

Source Code for Module muntjac.ui.absolute_layout

  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 layout implementation that mimics html absolute positioning.""" 
 17   
 18  import re 
 19   
 20  from muntjac.ui.abstract_layout import AbstractLayout 
 21  from muntjac.terminal.gwt.client.event_id import EventId 
 22  from muntjac.terminal.sizeable import ISizeable 
 23   
 24  from muntjac.event.layout_events import \ 
 25      LayoutClickEvent, ILayoutClickListener, ILayoutClickNotifier 
 26   
 27  from muntjac.util import OrderedSet 
 28   
 29   
30 -class AbsoluteLayout(AbstractLayout, ILayoutClickNotifier):
31 """AbsoluteLayout is a layout implementation that mimics html 32 absolute positioning. 33 """ 34 35 CLIENT_WIDGET = None #ClientWidget(VAbsoluteLayout) 36 37 _CLICK_EVENT = EventId.LAYOUT_CLICK 38
39 - def __init__(self):
40 """Creates an AbsoluteLayout with full size.""" 41 super(AbsoluteLayout, self).__init__() 42 43 #: The components in the layout 44 self._components = OrderedSet() 45 46 #: Maps each component to a position 47 self._componentToCoordinates = dict() 48 49 self.setSizeFull()
50 51
52 - def getComponentIterator(self):
53 """Gets an iterator for going through all components enclosed 54 in the absolute layout. 55 """ 56 return iter(self._components)
57 58
59 - def getComponentCount(self):
60 """Gets the number of contained components. Consistent with 61 the iterator returned by L{getComponentIterator}. 62 63 @return: the number of contained components 64 """ 65 return len(self._components)
66 67
68 - def replaceComponent(self, oldComponent, newComponent):
69 """Replaces one component with another one. The new component 70 inherits the old components position. 71 """ 72 position = self.getPosition(oldComponent) 73 self.removeComponent(oldComponent) 74 self.addComponent(newComponent) 75 self._componentToCoordinates[newComponent] = position
76 77
78 - def addComponent(self, c, cssPosition=None):
79 """Adds a component to the layout. The component can be positioned 80 by providing a string formatted in CSS-format. 81 82 For example the string "top:10px;left:10px" will position the 83 component 10 pixels from the left and 10 pixels from the top. The 84 identifiers: "top","left","right" and "bottom" can be used to 85 specify the position. 86 87 @param c: 88 The component to add to the layout 89 @param cssPosition: 90 The css position string 91 """ 92 # Create position instance and add it to componentToCoordinates 93 # map. We need to do this before we call addComponent so the 94 # attachListeners can access this position. #6368 95 if cssPosition is not None: 96 position = ComponentPosition(self) 97 position.setCSSString(cssPosition) 98 self._componentToCoordinates[c] = position 99 100 self._components.add(c) 101 try: 102 super(AbsoluteLayout, self).addComponent(c) 103 self.requestRepaint() 104 except ValueError, e: 105 self._components.remove(c) 106 if cssPosition is not None: 107 # Remove component coordinates if adding fails 108 del self._componentToCoordinates[c] 109 raise e
110 111
112 - def removeComponent(self, c):
113 if c in self._components: 114 self._components.remove(c) 115 if c in self._componentToCoordinates: 116 del self._componentToCoordinates[c] 117 super(AbsoluteLayout, self).removeComponent(c) 118 self.requestRepaint()
119 120
121 - def getPosition(self, component):
122 """Gets the position of a component in the layout. Returns C{None} 123 if component is not attached to the layout. 124 125 @param component: 126 The component which position is needed 127 @return: An instance of ComponentPosition containing the position 128 of the component, or null if the component is not enclosed 129 in the layout. 130 """ 131 if component.getParent() != self: 132 return None 133 elif component in self._componentToCoordinates: 134 return self._componentToCoordinates.get(component) 135 else: 136 coords = ComponentPosition(self) 137 self._componentToCoordinates[component] = coords 138 return coords
139 140
141 - def paintContent(self, target):
142 super(AbsoluteLayout, self).paintContent(target) 143 for component in self._components: 144 target.startTag('cc') 145 css = self.getPosition(component).getCSSString() 146 target.addAttribute('css', css) 147 component.paint(target) 148 target.endTag('cc')
149 150
151 - def addListener(self, listener, iface=None):
152 if (isinstance(listener, ILayoutClickListener) and 153 (iface is None or issubclass(iface, ILayoutClickListener))): 154 self.registerListener(self._CLICK_EVENT, LayoutClickEvent, 155 listener, ILayoutClickListener.clickMethod) 156 157 super(AbsoluteLayout, self).addListener(listener, iface)
158 159
160 - def addCallback(self, callback, eventType=None, *args):
161 if eventType is None: 162 eventType = callback._eventType 163 164 if issubclass(eventType, LayoutClickEvent): 165 self.registerCallback(LayoutClickEvent, callback, 166 self._CLICK_EVENT, *args) 167 else: 168 super(AbsoluteLayout, self).addCallback(callback, eventType, *args)
169 170
171 - def removeListener(self, listener, iface=None):
172 if (isinstance(listener, ILayoutClickListener) and 173 (iface is None or issubclass(iface, ILayoutClickListener))): 174 self.withdrawListener(self._CLICK_EVENT, LayoutClickEvent, 175 listener) 176 177 super(AbsoluteLayout, self).removeListener(listener, iface)
178 179
180 - def removeCallback(self, callback, eventType=None):
181 if eventType is None: 182 eventType = callback._eventType 183 184 if issubclass(eventType, LayoutClickEvent): 185 self.withdrawCallback(LayoutClickEvent, callback, 186 self._CLICK_EVENT) 187 else: 188 super(AbsoluteLayout, self).removeCallback(callback, eventType)
189 190
191 -class ComponentPosition(object):
192 """The CompontPosition class represents a components position within 193 the absolute layout. It contains the attributes for left, right, top 194 and bottom and the units used to specify them. 195 """ 196
197 - def __init__(self, layout):
198 self._zIndex = -1 199 self._topValue = None 200 self._rightValue = None 201 self._bottomValue = None 202 self._leftValue = None 203 204 self._topUnits = 0 205 self._rightUnits = 0 206 self._bottomUnits = 0 207 self._leftUnits = 0 208 209 self._layout = layout
210 211
212 - def setCSSString(self, css):
213 """Sets the position attributes using CSS syntax. Attributes not 214 included in the string are reset to their unset states. 215 216 C{setCSSString("top:10px;left:20%;z-index:16;")} 217 """ 218 self._topValue = self._bottomValue = None 219 self._rightValue = self._leftValue = None 220 self._topUnits = self._bottomUnits = 0 221 self._rightUnits = self._leftUnits = 0 222 self._zIndex = -1 223 224 if css is None: 225 return 226 227 cssProperties = css.split(';') 228 for i in range(len(cssProperties)): 229 keyValuePair = cssProperties[i].split(':') 230 key = keyValuePair[0].strip() 231 if key == '': 232 continue 233 234 if key == 'z-index': 235 self._zIndex = int( keyValuePair[1].strip() ) 236 else: 237 if len(keyValuePair) > 1: 238 value = keyValuePair[1].strip() 239 else: 240 value = '' 241 242 unit = re.sub('[0-9\\.\\-]+', '', value) 243 if not (unit == ''): 244 value = value[:value.find(unit)].strip() 245 246 v = float(value) 247 unitInt = self.parseCssUnit(unit) 248 249 if key == 'top': 250 self._topValue = v 251 self._topUnits = unitInt 252 253 elif key == 'right': 254 self._rightValue = v 255 self._rightUnits = unitInt 256 257 elif key == 'bottom': 258 self._bottomValue = v 259 self._bottomUnits = unitInt 260 261 elif key == 'left': 262 self._leftValue = v 263 self._leftUnits = unitInt 264 265 self._layout.requestRepaint()
266 267
268 - def parseCssUnit(self, string):
269 """Parses a string and checks if a unit is found. If a unit 270 is not found from the string the unit pixels is used. 271 272 @param string: 273 The string to parse the unit from 274 @return: The found unit 275 """ 276 for i in range(len(ISizeable.UNIT_SYMBOLS)): 277 if ISizeable.UNIT_SYMBOLS[i] == string: 278 return i 279 return 0 # defaults to px (eg. top:0;)
280 281
282 - def getCSSString(self):
283 """Converts the internal values into a valid CSS string. 284 285 @return: A valid CSS string 286 """ 287 s = '' 288 if self._topValue is not None: 289 symbol = ISizeable.UNIT_SYMBOLS[self._topUnits] 290 s += 'top:' + str(self._topValue) + symbol + ';' 291 292 if self._rightValue is not None: 293 symbol = ISizeable.UNIT_SYMBOLS[self._rightUnits] 294 s += 'right:' + str(self._rightValue) + symbol + ';' 295 296 if self._bottomValue is not None: 297 symbol = ISizeable.UNIT_SYMBOLS[self._bottomUnits] 298 s += 'bottom:' + str(self._bottomValue) + symbol + ';' 299 300 if self._leftValue is not None: 301 symbol = ISizeable.UNIT_SYMBOLS[self._leftUnits] 302 s += 'left:' + str(self._leftValue) + symbol + ';' 303 304 if self._zIndex >= 0: 305 s += 'z-index:' + str(self._zIndex) + ';' 306 307 return s
308 309
310 - def setTop(self, topValue, topUnits):
311 """Sets the 'top' attribute; distance from the top of the 312 component to the top edge of the layout. 313 314 @param topValue: 315 The value of the 'top' attribute 316 @param topUnits: 317 The unit of the 'top' attribute. See UNIT_SYMBOLS 318 for a description of the available units. 319 """ 320 self._topValue = topValue 321 self._topUnits = topUnits 322 self._layout.requestRepaint()
323 324
325 - def setRight(self, rightValue, rightUnits):
326 """Sets the 'right' attribute; distance from the right of the 327 component to the right edge of the layout. 328 329 @param rightValue: 330 The value of the 'right' attribute 331 @param rightUnits: 332 The unit of the 'right' attribute. See UNIT_SYMBOLS 333 for a description of the available units. 334 """ 335 self._rightValue = rightValue 336 self._rightUnits = rightUnits 337 self._layout.requestRepaint()
338 339
340 - def setBottom(self, bottomValue, bottomUnits):
341 """Sets the 'bottom' attribute; distance from the bottom of the 342 component to the bottom edge of the layout. 343 344 @param bottomValue: 345 The value of the 'bottom' attribute 346 @param bottomUnits: 347 The unit of the 'bottom' attribute. See UNIT_SYMBOLS 348 for a description of the available units. 349 """ 350 self._bottomValue = bottomValue 351 self._bottomUnits = bottomUnits 352 self._layout.requestRepaint()
353 354
355 - def setLeft(self, leftValue, leftUnits):
356 """Sets the 'left' attribute; distance from the left of the 357 component to the left edge of the layout. 358 359 @param leftValue: 360 The value of the 'left' attribute 361 @param leftUnits: 362 The unit of the 'left' attribute. See UNIT_SYMBOLS 363 for a description of the available units. 364 """ 365 self._leftValue = leftValue 366 self._leftUnits = leftUnits 367 self._layout.requestRepaint()
368 369
370 - def setZIndex(self, zIndex):
371 """Sets the 'z-index' attribute; the visual stacking order 372 373 @param zIndex: 374 The z-index for the component. 375 """ 376 self._zIndex = zIndex 377 self._layout.requestRepaint()
378 379
380 - def setTopValue(self, topValue):
381 """Sets the value of the 'top' attribute; distance from the top 382 of the component to the top edge of the layout. 383 384 @param topValue: 385 The value of the 'left' attribute 386 """ 387 self._topValue = topValue 388 self._layout.requestRepaint()
389 390
391 - def getTopValue(self):
392 """Gets the 'top' attributes value in current units. 393 394 @see: L{getTopUnits} 395 @return: The value of the 'top' attribute, null if not set 396 """ 397 return self._topValue
398 399
400 - def getRightValue(self):
401 """Gets the 'right' attributes value in current units. 402 403 @return: The value of the 'right' attribute, null if not set 404 @see: L{getRightUnits} 405 """ 406 return self._rightValue
407 408
409 - def setRightValue(self, rightValue):
410 """Sets the 'right' attribute value (distance from the right 411 of the component to the right edge of the layout). Currently 412 active units are maintained. 413 414 @param rightValue: 415 The value of the 'right' attribute 416 @see: L{setRightUnits} 417 """ 418 self._rightValue = rightValue 419 self._layout.requestRepaint()
420 421
422 - def getBottomValue(self):
423 """Gets the 'bottom' attributes value using current units. 424 425 @return: The value of the 'bottom' attribute, null if not set 426 @see: L{getBottomUnits} 427 """ 428 return self._bottomValue
429 430
431 - def setBottomValue(self, bottomValue):
432 """Sets the 'bottom' attribute value (distance from the bottom 433 of the component to the bottom edge of the layout). Currently 434 active units are maintained. 435 436 @param bottomValue: 437 The value of the 'bottom' attribute 438 @see: L{setBottomUnits} 439 """ 440 self._bottomValue = bottomValue 441 self._layout.requestRepaint()
442 443
444 - def getLeftValue(self):
445 """Gets the 'left' attributes value using current units. 446 447 @return: The value of the 'left' attribute, null if not set 448 @see: L{getLeftUnits} 449 """ 450 return self._leftValue
451 452
453 - def setLeftValue(self, leftValue):
454 """Sets the 'left' attribute value (distance from the left of 455 the component to the left edge of the layout). Currently active 456 units are maintained. 457 458 @param leftValue: 459 The value of the 'left' CSS-attribute 460 @see: L{setLeftUnits} 461 """ 462 self._leftValue = leftValue 463 self._layout.requestRepaint()
464 465
466 - def getTopUnits(self):
467 """Gets the unit for the 'top' attribute 468 469 @return: See L{ISizeable} UNIT_SYMBOLS for a description of 470 the available units. 471 """ 472 return self._topUnits
473 474
475 - def setTopUnits(self, topUnits):
476 """Sets the unit for the 'top' attribute 477 478 @param topUnits: 479 See L{ISizeable} UNIT_SYMBOLS for a description 480 of the available units. 481 """ 482 self._topUnits = topUnits 483 self._layout.requestRepaint()
484 485
486 - def getRightUnits(self):
487 """Gets the unit for the 'right' attribute 488 489 @return: See L{ISizeable} UNIT_SYMBOLS for a description of 490 the available units. 491 """ 492 return self._rightUnits
493 494
495 - def setRightUnits(self, rightUnits):
496 """Sets the unit for the 'right' attribute 497 498 @param rightUnits: 499 See L{ISizeable} UNIT_SYMBOLS for a description 500 of the available units. 501 """ 502 self._rightUnits = rightUnits 503 self._layout.requestRepaint()
504 505
506 - def getBottomUnits(self):
507 """Gets the unit for the 'bottom' attribute 508 509 @return: See L{ISizeable} UNIT_SYMBOLS for a description of 510 the available units. 511 """ 512 return self._bottomUnits
513 514
515 - def setBottomUnits(self, bottomUnits):
516 """Sets the unit for the 'bottom' attribute 517 518 @param bottomUnits: 519 See L{ISizeable} UNIT_SYMBOLS for a description 520 of the available units. 521 """ 522 self._bottomUnits = bottomUnits 523 self._layout.requestRepaint()
524 525
526 - def getLeftUnits(self):
527 """Gets the unit for the 'left' attribute 528 529 @return: See L{ISizeable} UNIT_SYMBOLS for a description 530 of the available units. 531 """ 532 return self._leftUnits
533 534
535 - def setLeftUnits(self, leftUnits):
536 """Sets the unit for the 'left' attribute 537 538 @param leftUnits: 539 See L{ISizeable} UNIT_SYMBOLS for a description 540 of the available units. 541 """ 542 self._leftUnits = leftUnits 543 self._layout.requestRepaint()
544 545
546 - def getZIndex(self):
547 """Gets the 'z-index' attribute. 548 549 @return: the zIndex The z-index attribute 550 """ 551 return self._zIndex
552 553
554 - def toString(self):
555 return self.getCSSString()
556