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

Source Code for Module pyjamas.ui.vertsplitpanel

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