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

Source Code for Module pyjamas.ui.VerticalSplitPanel

  1  """ 
  2      Vertical Split Panel: Top and Bottom layouts with a movable splitter. 
  3   
  4  /* 
  5   * Copyright 2008 Google Inc. 
  6   * Copyright (C) 2008, 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 self 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 SplitPanel import SplitPanel 
 23  from pyjamas import Factory 
 24  from pyjamas import DOM 
 25  from pyjamas import DeferredCommand 
 26  from pyjamas.Timer import Timer 
 27  from __pyjamas__ import JS 
 28   
29 -class ImplVerticalSplitPanel:
30 """ Provides a base implementation for splitter layout that relies on CSS 31 positioned layout. 32 """
33 - def __init__(self, panel):
34 self.panel = panel 35 36 DOM.setStyleAttribute(panel.getElement(), "position", "relative") 37 38 topElem = panel.getWidgetElement(0) 39 bottomElem = panel.getWidgetElement(1) 40 41 self.expandToFitParentHorizontally(topElem) 42 self.expandToFitParentHorizontally(bottomElem) 43 self.expandToFitParentHorizontally(panel.getSplitElement()) 44 45 self.panel.expandToFitParentUsingCssOffsets(panel.container) 46 47 # Snap the bottom wrapper to the bottom side. 48 DOM.setStyleAttribute(bottomElem, "bottom", "0")
49
50 - def expandToFitParentHorizontally(self, elem):
51 self.panel.addAbsolutePositoning(elem) 52 DOM.setStyleAttribute(elem, "left", "0") 53 DOM.setStyleAttribute(elem, "right", "0")
54
55 - def onAttach(self):
56 pass
57
58 - def onDetach(self):
59 pass
60
61 - def onSplitterResize(self, px):
62 self.setSplitPosition(px)
63
64 - def setSplitPosition(self, px):
65 splitElem = self.panel.getSplitElement() 66 67 rootElemHeight = DOM.getOffsetHeight(self.panel.container) 68 splitElemHeight = DOM.getOffsetHeight(splitElem) 69 70 # layout not settled, set height to what it _should_ be... yuk. 71 if splitElemHeight == 0: 72 splitElemHeight = 7 73 74 if rootElemHeight < splitElemHeight: 75 return 76 77 newBottomHeight = rootElemHeight - px - splitElemHeight 78 if px < 0: 79 px = 0 80 newBottomHeight = rootElemHeight - splitElemHeight 81 elif newBottomHeight < 0: 82 px = rootElemHeight - splitElemHeight 83 newBottomHeight = 0 84 85 self.updateElements(self.panel.getWidgetElement(0), 86 splitElem, 87 self.panel.getWidgetElement(1), 88 px, px + splitElemHeight, newBottomHeight)
89
90 - def updateElements(self, topElem, splitElem, 91 bottomElem, topHeight, bottomTop, bottomHeight):
92 self.panel.setElemHeight(topElem, "%dpx" % topHeight) 93 self.panel.setTop(splitElem, "%dpx" % topHeight) 94 self.panel.setTop(bottomElem, "%dpx" % bottomTop)
95 # bottom's height is handled by CSS. 96
97 -class ImplIE6VerticalSplitPanel:
98 """ Provides an implementation for IE6/7 that relies on 100% length in CSS. 99 """ 100
101 - def __init__(self, panel):
102 self.panel = panel 103 self.isResizeInProgress = False 104 self.isTopHidden = False 105 self.isBottomHidden = False 106 107 elem = panel.getElement() 108 109 # Prevents inherited text-align settings from interfering with the 110 # panel's layout. 111 DOM.setStyleAttribute(elem, "textAlign", "left") 112 DOM.setStyleAttribute(elem, "position", "relative") 113 114 topElem = panel.getWidgetElement(0) 115 bottomElem = panel.getWidgetElement(1) 116 117 self.expandToFitParentHorizontally(topElem) 118 self.expandToFitParentHorizontally(bottomElem) 119 self.expandToFitParentHorizontally(panel.getSplitElement()) 120 121 self.expandToFitParentUsingPercentages(panel.container)
122
123 - def expandToFitParentHorizontally(self, elem):
124 self.addAbsolutePositoning(elem) 125 self.setLeft(elem, "0") 126 self.setElemWidth(elem, "100%")
127
128 - def onAttach(self):
129 self.addResizeListener(self.panel.container) 130 self.onResize()
131
132 - def onDetach(self):
133 DOM.setElementProperty(self.panel.container, "onresize", None)
134
135 - def onSplitterResize(self, px):
136 """ IE6/7 has event priority issues that will prevent 137 the repaints from happening quickly enough causing the 138 interaction to seem unresponsive. The following is simply 139 a poor man's mouse event coalescing. 140 """ 141 resizeUpdatePeriod = 20 # ms 142 if not self.isResizeInProgress: 143 self.isResizeInProgress = True 144 Timer(resizeUpdatePeriod, self) 145 self.splitPosition = px
146
147 - def onTimer(self, t):
148 self.setSplitPosition(splitPosition) 149 self.isResizeInProgress = False
150
151 - def updateElements(topElem, splitElem, bottomElem, 152 topHeight, bottomTop, bottomHeight):
153 """ IE6/7 has a quirk where a zero height element with 154 non-zero height children will expand larger than 100%. To 155 prevent self, the width is explicitly set to zero when 156 height is zero. 157 """ 158 if topHeight == 0: 159 self.setWidth(topElem, "0px") 160 self.isTopHidden = True 161 elif self.isTopHidden: 162 self.setWidth(topElem, "100%") 163 self.isTopHidden = False 164 165 if bottomHeight == 0: 166 self.setElemWidth(bottomElem, "0px") 167 self.isBottomHidden = True 168 elif self.isBottomHidden: 169 self.setElemWidth(bottomElem, "100%") 170 self.isBottomHidden = False 171 172 self.panel.setElemHeight(topElem, "%dpx" % topHeight) 173 self.panel.setTop(splitElem, "%dpx" % topHeight) 174 self.panel.setTop(bottomElem, "%dpx" % bottomTop) 175 # IE6/7 cannot update properly with CSS alone. 176 self.panel.setElemHeight(bottomElem, bottomHeight + "px")
177
178 - def addResizeListener(self, container):
179 JS(""" 180 this.container.onresize = function() { 181 __ImplIE6VerticalSplitPanel_onResize(); 182 } 183 """)
184
185 - def onResize(self):
187
188 -class VerticalSplitPanel(SplitPanel):
189 """ A panel that arranges two widgets in a single vertical 190 column and allows the user to interactively 191 change the proportion of the height dedicated to 192 each of the two widgets. Widgets contained within a 193 <code>VerticalSplitterPanel</code> will be automatically 194 decorated with scrollbars when necessary. 195 """ 196
197 - def __init__(self, **kwargs):
198 """ Creates an empty vertical split panel. 199 """ 200 if not kwargs.has_key('StyleName'): kwargs['StyleName']="gwt-VerticalSplitPanel" 201 if kwargs.has_key('Element'): 202 element = kwargs.pop('Element') 203 else: 204 element = DOM.createDiv() 205 SplitPanel.__init__(self, element, 206 DOM.createDiv(), 207 self.preventBoxStyles(DOM.createDiv()), 208 self.preventBoxStyles(DOM.createDiv()), 209 **kwargs) 210 211 self.container = self.preventBoxStyles(DOM.createDiv()) 212 self.buildDOM() 213 214 self.impl = ImplVerticalSplitPanel(self) 215 216 self.setSplitPosition("50%") 217 218 # Captures the height of the top container when drag resizing starts. 219 self.initialTopHeight = 0 220 221 # Captures the offset of a user's mouse pointer during drag resizing. 222 self.initialThumbPos = 0 223 224 self.lastSplitPosition = ""
225
226 - def getBottomWidget(self):
227 """ Gets the widget in the bottom of the panel. 228 @return the widget, <code>None</code> if there is not one 229 """ 230 return self.getWidget(1)
231
232 - def getTopWidget(self):
233 """ Gets the widget in the top of the panel. 234 @return the widget, <code>None</code> if there is not one 235 """ 236 return self.getWidget(0)
237
238 - def setBottomWidget(self, w):
239 """ Sets the widget in the bottom of the panel. 240 @param w the widget 241 """ 242 self.setWidget(1, w)
243
244 - def setSplitPosition(self, pos):
245 self.lastSplitPosition = pos 246 topElem = self.getWidgetElement(0) 247 self.setElemHeight(topElem, pos) 248 self.impl.setSplitPosition(DOM.getOffsetHeight(topElem))
249
250 - def setTopWidget(self, w):
251 """ Sets the widget in the top of the panel. 252 @param w the widget 253 """ 254 self.setWidget(0, w)
255
256 - def onLoad(self):
257 self.impl.onAttach() 258 # Set the position realizing it might not work until 259 # after layout runs. This first call is simply to try 260 # to avoid a jitter effect if possible. 261 self.setSplitPosition(self.lastSplitPosition) 262 DeferredCommand.add(self)
263
264 - def execute(self):
265 self.setSplitPosition(self.lastSplitPosition)
266
267 - def onUnload(self):
268 self.impl.onDetach()
269
270 - def onSplitterResize(self, x, y):
271 self.impl.onSplitterResize(self.initialTopHeight + y - 272 self.initialThumbPos)
273
274 - def onSplitterResizeStarted(self, x, y):
275 self.initialThumbPos = y 276 self.initialTopHeight = DOM.getOffsetHeight(self.getWidgetElement(0))
277
278 - def buildDOM(self):
279 topDiv = self.getWidgetElement(0) 280 bottomDiv = self.getWidgetElement(1) 281 splitDiv = self.getSplitElement() 282 283 DOM.appendChild(self.getElement(), self.container) 284 285 DOM.appendChild(self.container, topDiv) 286 DOM.appendChild(self.container, splitDiv) 287 DOM.appendChild(self.container, bottomDiv) 288 289 # The style name is placed on the table rather than splitElem 290 # to allow the splitter to be styled without interfering 291 # with layout. 292 293 DOM.setInnerHTML(splitDiv, "<div class='vsplitter' " + 294 "style='text-align:center'>" + 295 self.getThumbImageHTML() + "</div>") 296 297 self.addScrolling(topDiv) 298 self.addScrolling(bottomDiv)
299 300 Factory.registerClass('pyjamas.ui.VerticalSplitPanel', VerticalSplitPanel) 301