Package pyjamas :: Package ui :: Module horizsplitpanel
[hide private]
[frames] | no frames]

Source Code for Module pyjamas.ui.horizsplitpanel

  1  """ 
  2      Horizontal Split Panel: Left and Right layouts with a movable splitter. 
  3   
  4  /* 
  5   * Copyright 2008 Google Inc. 
  6   * Copyright 2009 Luke Kenneth Casson Leighton <lkcl@lkcl.net> 
  7   *  
  8   * Licensed under the Apache License, Version 2.0 (the "License") you may not 
  9   * use this file except in compliance with the License. You may obtain a copy of 
 10   * the License at 
 11   *  
 12   * http:#www.apache.org/licenses/LICENSE-2.0 
 13   *  
 14   * Unless required by applicable law or agreed to in writing, software 
 15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
 17   * License for the specific language governing permissions and limitations under 
 18   * the License. 
 19   */ 
 20  """ 
 21   
 22  from __pyjamas__ import JS 
 23  from pyjamas.ui.splitpanel import SplitPanel 
 24  from pyjamas import DOM 
 25  from pyjamas import DeferredCommand 
 26   
27 -class ImplHorizontalSplitPanel:
28 """ The standard implementation for horizontal split panels. 29 """
30 - def __init__(self, panel):
31 self.panel = panel 32 33 DOM.setStyleAttribute(panel.getElement(), "position", "relative") 34 35 self.expandToFitParentHorizontally(panel.getWidgetElement(0)) 36 self.expandToFitParentHorizontally(panel.getWidgetElement(1)) 37 self.expandToFitParentHorizontally(panel.getSplitElement()) 38 39 self.panel.expandToFitParentUsingCssOffsets(panel.container) 40 41 # Right now, both panes are stacked on top of each other 42 # on either the left side or the right side of the containing 43 # panel. This happens because both panes have position:absolute 44 # and no left/top values. The panes will be on the left side 45 # if the directionality is LTR, and on the right side if the 46 # directionality is RTL. In the LTR case, we need to snap the 47 # right pane to the right of the container, and in the RTL case, 48 # we need to snap the left pane to the left of the container. 49 50 if True: # TODO: (LocaleInfo.getCurrentLocale().isRTL()): 51 self.panel.setLeft(self.panel.getWidgetElement(0), "0px") 52 else: 53 self.panel.setRight(self.panel.getWidgetElement(1), "0px")
54
55 - def expandToFitParentHorizontally(self, elem):
56 self.panel.addAbsolutePositoning(elem) 57 zeroSize = "0px" 58 self.panel.setTop(elem, zeroSize) 59 self.panel.setBottom(elem, zeroSize)
60 61
62 - def onAttach(self):
63 pass
64 - def onDetach(self):
65 pass
66
67 - def onTimer(self, sender):
68 pass
69 - def execute(self):
70 pass
71 - def addResizeListener(self, container):
72 pass
73 - def onResize(self):
74 pass
75
76 - def onSplitterResize(self, px):
78
79 - def setSplitPosition(self, pos):
80 leftElem = self.panel.getWidgetElement(0) 81 self.panel.setElemWidth(leftElem, pos) 82 self.setSplitPositionUsingPixels(self.panel.getOffsetWidth(leftElem))
83
84 - def setSplitPositionUsingPixels(self, px):
86
87 - def _setSplitPositionUsingPixels(self, px):
88 """ Set the splitter's position in units of pixels. 89 90 px represents the splitter's position as a distance 91 of px pixels from the left edge of the container. This is 92 true even in a bidi environment. Callers of this method 93 must be aware of this constraint. 94 """ 95 splitElem = self.panel.getSplitElement() 96 97 rootElemWidth = self.panel.getOffsetWidth(self.panel.container) 98 splitElemWidth = self.panel.getOffsetWidth(splitElem) 99 100 # This represents an invalid state where layout is incomplete. This 101 # typically happens before DOM attachment, but I leave it here as a 102 # precaution because negative width/height style attributes produce 103 # errors on IE. 104 if (rootElemWidth < splitElemWidth): 105 return 106 107 # Compute the new right side width. 108 newRightWidth = rootElemWidth - px - splitElemWidth 109 110 # Constrain the dragging to the physical size of the panel. 111 if (px < 0): 112 px = 0 113 newRightWidth = rootElemWidth - splitElemWidth 114 elif (newRightWidth < 0): 115 px = rootElemWidth - splitElemWidth 116 newRightWidth = 0 117 118 rightElem = self.panel.getWidgetElement(1) 119 120 # Set the width of the left side. 121 self.panel.setElemWidth(self.panel.getWidgetElement(0), "%dpx" % px) 122 123 # Move the splitter to the right edge of the left element. 124 self.panel.setLeft(splitElem, "%dpx" % px) 125 126 # Move the right element to the right of the splitter. 127 self.panel.setLeft(rightElem, "%dpx" % (px + splitElemWidth)) 128 129 self.updateRightWidth(rightElem, newRightWidth)
130 131
132 - def updateRightWidth(self, rightElem, newRightWidth):
133 # No need to update the width of the right side this will be 134 # recomputed automatically by CSS. This is helpful, as we do not 135 # have to worry about watching for resize events and adjusting the 136 # right-side width. 137 pass
138
139 -class HorizontalSplitPanel(SplitPanel):
140 """ A panel that arranges two widgets in a single horizontal row 141 and allows the user to interactively change the proportion 142 of the width dedicated to each of the two widgets. Widgets 143 contained within a <code>HorizontalSplitPanel</code> will 144 be automatically decorated with scrollbars when necessary. 145 146 Default layout behaviour of HorizontalSplitPanels is to 100% fill 147 its parent vertically and horizontally [this is NOT normal!] 148 """ 149
150 - def __init__(self, **kwargs):
151 """ Creates an empty horizontal split panel. 152 """ 153 154 if not kwargs.has_key('StyleName'): kwargs['StyleName']="gwt-HorizontalSplitPanel" 155 SplitPanel.__init__(self, DOM.createDiv(), 156 DOM.createDiv(), 157 self.preventBoxStyles(DOM.createDiv()), 158 self.preventBoxStyles(DOM.createDiv()), 159 **kwargs) 160 161 self.container = self.preventBoxStyles(DOM.createDiv()) 162 163 self.buildDOM() 164 165 self.impl = ImplHorizontalSplitPanel(self) 166 167 # By default the panel will fill its parent vertically and horizontally. 168 # The horizontal case is covered by the fact that the top level div is 169 # block display. 170 self.setHeight("100%") 171 172 self.lastSplitPosition = "50%" 173 self.initialLeftWidth = 0 174 self.initialThumbPos = 0
175
176 - def add(self, w):
177 """ 178 * Adds a widget to a pane in the HorizontalSplitPanel. The method 179 * will first attempt to add the widget to the left pane. If a 180 * widget is already in that position, it will attempt to add the 181 * widget to the right pane. If a widget is already in that position, 182 * an exception will be thrown, as a HorizontalSplitPanel can 183 * contain at most two widgets. 184 * 185 * Note that this method is bidi-sensitive. In an RTL environment, 186 * this method will first attempt to add the widget to the right pane, 187 * and if a widget is already in that position, it will attempt to add 188 * the widget to the left pane. 189 * 190 * @param w the widget to be added 191 * @throws IllegalStateException 192 """ 193 if self.getStartOfLineWidget() is None: 194 self.setStartOfLineWidget(w) 195 elif self.getEndOfLineWidget() is None: 196 self.setEndOfLineWidget(w) 197 else: 198 return
199 # TODO throw new IllegalStateException( 200 # "A Splitter can only contain two Widgets.") 201
202 - def getEndOfLineWidget(self):
203 """ 204 * Gets the widget in the pane that is at the end of the line 205 * direction for the layout. That is, in an RTL layout, gets 206 * the widget in the left pane, and in an LTR layout, gets 207 * the widget in the right pane. 208 * 209 * @return the widget, <code>null</code> if there is not one. 210 """ 211 return self.getWidget(self.getEndOfLinePos())
212
213 - def getLeftWidget(self):
214 """ 215 * Gets the widget in the left side of the panel. 216 * 217 * @return the widget, <code>null</code> if there is not one. 218 """ 219 return self.getWidget(0)
220
221 - def getRightWidget(self):
222 """ 223 * Gets the widget in the right side of the panel. 224 * 225 * @return the widget, <code>null</code> if there is not one. 226 """ 227 return self.getWidget(1)
228
229 - def getStartOfLineWidget(self):
230 """ 231 * Gets the widget in the pane that is at the start of the line 232 * direction for the layout. That is, in an RTL environment, gets 233 * the widget in the right pane, and in an LTR environment, gets 234 * the widget in the left pane. 235 * 236 * @return the widget, <code>null</code> if there is not one. 237 """ 238 return self.getWidget(self.getStartOfLinePos())
239
240 - def setEndOfLineWidget(self, w):
241 """ 242 * Sets the widget in the pane that is at the end of the line direction 243 * for the layout. That is, in an RTL layout, sets the widget in 244 * the left pane, and in and RTL layout, sets the widget in the 245 * right pane. 246 * 247 * @param w the widget 248 """ 249 self.setWidget(self.getEndOfLinePos(), w)
250
251 - def setLeftWidget(self, w):
252 """ 253 * Sets the widget in the left side of the panel. 254 * 255 * @param w the widget 256 """ 257 self.setWidget(0, w)
258
259 - def setRightWidget(self, w):
260 """ 261 * Sets the widget in the right side of the panel. 262 * 263 * @param w the widget 264 """ 265 self.setWidget(1, w)
266
267 - def setSplitPosition(self, pos):
268 """ 269 * Moves the position of the splitter. 270 * 271 * This method is not bidi-sensitive. The size specified is always 272 * the size of the left region, regardless of directionality. 273 * 274 * @param pos the new size of the left region in CSS units (e.g. "10px", 275 * "1em") 276 """ 277 self.lastSplitPosition = pos 278 self.impl.setSplitPosition(pos)
279
280 - def setStartOfLineWidget(self, w):
281 """ 282 * Sets the widget in the pane that is at the start of the line direction 283 * for the layout. That is, in an RTL layout, sets the widget in 284 * the right pane, and in and RTL layout, sets the widget in the 285 * left pane. 286 * 287 * @param w the widget 288 """ 289 self.setWidget(self.getStartOfLinePos(), w)
290
291 - def onLoad(self):
292 self.impl.onAttach() 293 # Set the position realizing it might not work until 294 # after layout runs. This first call is simply to try 295 # to avoid a jitter effect if possible. 296 self.setSplitPosition(self.lastSplitPosition) 297 DeferredCommand.add(self)
298
299 - def execute(self):
300 self.setSplitPosition(self.lastSplitPosition)
301
302 - def onUnload(self):
303 self.impl.onDetach()
304
305 - def onSplitterResize(self, x, y):
306 self.impl.onSplitterResize(self.initialLeftWidth + x - 307 self.initialThumbPos)
308
309 - def onSplitterResizeStarted(self, x, y):
310 self.initialThumbPos = x 311 self.initialLeftWidth = self.getOffsetWidth(self.getWidgetElement(0))
312 313
314 - def buildDOM(self):
315 316 leftDiv = self.getWidgetElement(0) 317 rightDiv = self.getWidgetElement(1) 318 splitDiv = self.getSplitElement() 319 320 DOM.appendChild(self.getElement(), self.container) 321 322 DOM.appendChild(self.container, leftDiv) 323 DOM.appendChild(self.container, splitDiv) 324 DOM.appendChild(self.container, rightDiv) 325 326 # Sadly, this is the only way I've found to get vertical 327 # centering in this case. The usually CSS hacks (display: 328 # table-cell, vertical-align: middle) don't work in an 329 # absolute positioned DIV. 330 thumb_html = '<img src="splitPanelThumb.png" />' 331 DOM.setInnerHTML(splitDiv, 332 "<table class='hsplitter' height='100%' cellpadding='0' " + 333 "cellspacing='0'><tr><td align='center' valign='middle'>" + 334 thumb_html + 335 "</td></tr></table>") 336 337 self.addScrolling(leftDiv) 338 self.addScrolling(rightDiv)
339
340 - def getEndOfLinePos(self):
341 return 0
342 # TODO: return (LocaleInfo.getCurrentLocale().isRTL() ? 0 : 1) 343
344 - def getStartOfLinePos(self):
345 return 1
346 # TODO: return (LocaleInfo.getCurrentLocale().isRTL() ? 1 : 0) 347