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

Source Code for Module library.pyjamas.ui.SplitPanel

  1  """ 
  2  - /* 
  3  - * Copyright 2008 Google Inc. 
  4  - * Copyright (C) 2009 Luke Kenneth Casson Leighton <lkcl@lkcl.net> 
  5  - * Copyright (C) 2010 Rich Newpol <rich.newpol@gmail.com> 
  6  - * 
  7  - * Licensed under the Apache License, Version 2.0 (the "License") you may not 
  8  - * use this file except in compliance with the License. You may obtain a copy 
  9  - * of the License at 
 10  - * 
 11  - * http:#www.apache.org/licenses/LICENSE-2.0 
 12  - * 
 13  - * Unless required by applicable law or agreed to in writing, software 
 14  - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 15  - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
 16  - * License for the specific language governing permissions and limitations 
 17  - * under the License. 
 18  - */ 
 19  """ 
 20   
 21  from __pyjamas__ import console 
 22  from pyjamas import DOM 
 23  from pyjamas import Window 
 24  from pyjamas import DeferredCommand 
 25  from pyjamas.EventController import EventGenerator 
 26  from pyjamas.ui import GlassWidget 
 27  from pyjamas.ui.SimplePanel import SimplePanel 
 28  from pyjamas.ui.AbsolutePanel import AbsolutePanel 
 29  from pyjamas.ui.ScrollPanel import ScrollPanel 
 30  from pyjamas.ui.MouseListener import MouseHandler, fireMouseEvent 
 31  from pyjamas.ui import Event 
 32   
 33   
34 -class SplitPanelSplitter(SimplePanel, MouseHandler):
35 """ a splitter is just a SimplePanel which can receive mouse events """ 36
37 - def __init__(self, splitPanel, **kwargs):
38 # keep a ref to our parent panel for event callback 39 self._splitpanel = splitPanel 40 SimplePanel.__init__(self, **kwargs) 41 MouseHandler.__init__(self) 42 self.addMouseListener(self) 43 # set some constant styles 44 elem = self.getElement() 45 # the following allows splitter to be small enough in IE 46 DOM.setStyleAttribute(elem, "overflow", "hidden")
47
48 - def onMouseDown(self, sender, x, y):
49 """ catch a mouse down for parent """ 50 51 ev = DOM.eventGetCurrentEvent() 52 # ignore right-button downs 53 if DOM.eventGetButton(ev) != Event.BUTTON_LEFT: 54 return 55 DOM.eventPreventDefault(DOM.eventGetCurrentEvent()) 56 # parent will capture the mouse and handle the dragging from here 57 self._splitpanel.startSplitterDrag(x, y)
58 59
60 -class SplitPanel(AbsolutePanel, MouseHandler, EventGenerator):
61 """ Provides the SplitPanel baseclass functionality 62 A SplitPanel is an AbsolutePanel containing an HTMLTable 63 with three cells. The first cell holds the first ScrollPanel, 64 while the center cell holds a Splitter, and the last cell 65 holds the other ScrollPanel. 66 """ 67
68 - def __init__(self, vertical=False, **kwargs):
69 # set defaults 70 if not 'StyleName' in kwargs: 71 if vertical: # vertical split panel 72 kwargs['StyleName'] = "gwt-VerticalSplitPanel" 73 else: 74 kwargs['StyleName'] = "gwt-HorizontalSplitPanel" 75 # splitter drag state vars 76 self._drag_start = None 77 self._pos = "50%" 78 # orientation 79 self._vertical = vertical 80 # now init the bases 81 AbsolutePanel.__init__(self, **kwargs) 82 MouseHandler.__init__(self) 83 # add our event support? 84 self.addListenedEvent("Resize") 85 # create the top/left widget container 86 self._container1 = ScrollPanel() 87 # create the bottom/right widget container 88 self._container2 = ScrollPanel() 89 # create the splitter 90 self._splitter = SplitPanelSplitter(self) 91 # add splitter handling 92 self._splitter.addMouseListener(self) 93 # add mouse event handling 94 self.addMouseListener(self) 95 # add the parts 96 AbsolutePanel.add(self, self._container1, 0, 0) 97 AbsolutePanel.add(self, self._splitter, 0, 0) 98 AbsolutePanel.add(self, self._container2, 0, 0) 99 100 # set the layout 101 if vertical: # vertical split panel 102 self._splitter.setStyleName("vsplitter") 103 self._splitter.setWidth("100%") 104 self._container1.setWidth("100%") 105 self._container2.setWidth("100%") 106 # set drag cursor 107 DOM.setStyleAttribute(self._splitter.getElement(), 108 "cursor", "n-resize") 109 else: # horizontal split panel 110 self._splitter.setStyleName("hsplitter") 111 self._splitter.setHeight("100%") 112 self._container1.setHeight("100%") 113 self._container2.setHeight("100%") 114 # set drag cursor 115 DOM.setStyleAttribute(self._splitter.getElement(), 116 "cursor", "e-resize")
117
118 - def onAttach(self):
121 122 # fixup the container 2 size and position
123 - def _finalizePositions(self, pos=None):
124 finalized = False 125 if self._vertical: 126 if pos is None: 127 pos = self._container1.getOffsetHeight() 128 space = self.getOffsetHeight() 129 sz = self._splitter.getOffsetHeight() 130 if space > 0 and sz > 0 and pos > 0: 131 # limit pos 132 if pos > space - sz: 133 pos = space - sz 134 self._container1.setHeight(pos) 135 self.setWidgetPosition(self._splitter, 0, pos) 136 self.setWidgetPosition(self._container2, 0, pos + sz) 137 self._container2.setHeight(space - (pos + sz)) 138 finalized = True 139 else: 140 if pos is None: 141 pos = self._container1.getOffsetWidth() 142 space = self.getOffsetWidth() 143 sz = self._splitter.getOffsetWidth() 144 if space > 0 and sz > 0 and pos > 0: 145 # limit pos 146 if pos > space - sz: 147 pos = space - sz 148 self._container1.setWidth(pos) 149 self.setWidgetPosition(self._splitter, pos, 0) 150 self.setWidgetPosition(self._container2, pos + sz, 0) 151 self._container2.setWidth(space - (pos + sz)) 152 finalized = True 153 if finalized: 154 self.dispatchResizeEvent(self, pos) 155 return finalized
156 157 # end a drag operation
158 - def _stopDragging(self):
159 if self._drag_start is not None: 160 # we are no longer dragging 161 self._drag_start = None 162 # deactivate the transparent overlay 163 GlassWidget.hide() 164 # don't let a mouse-up become a click event 165 DOM.eventCancelBubble(DOM.eventGetCurrentEvent(), True)
166
167 - def _isDragging(self):
168 return self._drag_start is not None
169 170 # start a drag operation (called by splitter)
171 - def startSplitterDrag(self, x, y):
172 if self._drag_start is None: 173 # remember where on the slider we are dragging 174 if self._vertical: 175 self._drag_start = y 176 else: 177 self._drag_start = x 178 # activate the transparent overlay to keep mouse events flowing to 179 # the splitter (and to us) even if the mouse leaves the splitter 180 GlassWidget.show(self)
181 182 # add handlers for mouse events to support dragging the slider 183 # NOTE: the x,y positioni s relative to the splitter
184 - def onMouseMove(self, sender, x, y):
185 # if dragging, then use current mouse position 186 # to reset splitter position 187 if not self._isDragging(): 188 return 189 # remove the offset into the splitter 190 # where we started dragging 191 if self._vertical: 192 self._pos = y - self._drag_start 193 else: 194 self._pos = x - self._drag_start 195 # apply limit 196 if self._pos < 1: 197 self._pos = 1 198 # apply new position 199 self.setSplitPosition()
200
201 - def onMouseUp(self, sender, x, y):
202 ev = DOM.eventGetCurrentEvent() 203 # ignore right-button ups 204 if DOM.eventGetButton(ev) != Event.BUTTON_LEFT: 205 return 206 DOM.eventPreventDefault(ev) 207 # if we are dragging 208 if self._isDragging(): 209 # stop dragging on mouse up 210 self._stopDragging()
211 212 # called when we start dragging
213 - def onMouseGlassEnter(self, sender):
214 pass
215 216 # called when we drag out of the window 217 # (NOT called when we just stop dragging)
218 - def onMouseGlassLeave(self, sender):
219 # we left the window, so stop dragging 220 self._stopDragging()
221 222 # 223 # Start the inherited 'public' API 224 # 225 226 # specify splitter position in pix OR percentage 227 # if pixels (number) specified, we can make change now 228 # otherwise, we have to set the offset as specified, then 229 # 'fixup' the remaining space after rendering
230 - def setSplitPosition(self, pos=None):
231 if pos is not None: 232 # remember last pos set 233 self._pos = pos 234 else: 235 pos = self._pos 236 if pos < 1: 237 pos = 1 238 self._pos = pos 239 # change adjustable dimension 240 if self._vertical: 241 self._container1.setHeight(pos) 242 else: 243 self._container1.setWidth(pos) 244 # if pix are given, we can try to finalize the positions 245 finalized = False 246 if isinstance(pos, int): 247 finalized = self._finalizePositions(pos) 248 # if needed, queue callback to finalize 249 if not finalized: 250 DeferredCommand.add(self._finalizePositions)
251
252 - def getWidget(self, index):
253 if index == 0: 254 return self._container1.getWidget() 255 return self._container2.getWidget()
256
257 - def setWidget(self, index, widget):
258 if index == 0: 259 return self._container1.setWidget(widget) 260 return self._container2.setWidget(widget)
261 262 # Adds a widget to a pane
263 - def add(self, widget):
264 if self.getWidget(0) == None: 265 self.setWidget(0, widget) 266 elif self.getWidget(1) == None: 267 self.setWidget(1, widget) 268 else: 269 console.error("SimplePanel can only contain one child widget")
270 271 # Removes a child widget.
272 - def remove(self, widget):
273 if self.getWidget(0) == widget: 274 self._container1.remove(widget) 275 elif self.getWidget(1) == widget: 276 self._container2.remove(widget) 277 else: 278 AbsolutePanel.remove(self, widget)
279 280 # Gets the content element for the given index.
281 - def getElement(self, index=None):
282 if index is None: 283 return AbsolutePanel.getElement(self) 284 return self.getWidget(index).getElement()
285 286 # Gets the widget in the pane at end of the line direction for the layout
287 - def getEndOfLineWidget(self):
288 return self.getWidget(1)
289 290 # Gets the element that is acting as the splitter.
291 - def getSplitElement(self):
292 return self._splitter.getElement()
293 294 # Gets the widget in the pane at the start of line direction for the layout
295 - def getStartOfLineWidget(self):
296 return self.getWidget(0)
297 298 # Indicates whether the split panel is being resized.
299 - def isResizing(self):
300 return False
301 302 # Sets the widget in the pane at the end of line direction for the layout
303 - def setEndOfLineWidget(self, widget):
304 self.setWidget(1, widget)
305
306 - def setStartOfLineWidget(self, widget):
307 self.setWidget(0, widget)
308