guiSplitContainer.cpp
Engine/source/gui/containers/guiSplitContainer.cpp
Public Functions
ConsoleDocClass(GuiSplitContainer , "@brief A container that splits its area between two child <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">controls.\n\n</a>" "A <a href="/coding/class/classguisplitcontainer/">GuiSplitContainer</a> can be used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dynamically subdivide an area between two child controls. " "A splitter bar is placed between the two controls and allows <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dynamically adjust the sizing " "ratio between the two sides. Splitting can be either horizontal (subdividing top and bottom) " "or vertical (subdividing left and right) depending on #<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">orientation.\n\n</a>" "By using # fixedPanel)
DefineEngineMethod(GuiSplitContainer , setSplitPoint , void , (Point2I splitPoint) , "Set the position of the split handle." )
ImplementEnumType(GuiSplitFixedPanel , "Which side of the splitter <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> keep at <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> fixed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> (<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> any).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup GuiContainers" )
ImplementEnumType(GuiSplitOrientation , "Axis along which <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> divide the container's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">space.\n\n</a>" "@ingroup GuiContainers" )
Detailed Description
Public Functions
ConsoleDocClass(GuiSplitContainer , "@brief A container that splits its area between two child <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">controls.\n\n</a>" "A <a href="/coding/class/classguisplitcontainer/">GuiSplitContainer</a> can be used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dynamically subdivide an area between two child controls. " "A splitter bar is placed between the two controls and allows <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dynamically adjust the sizing " "ratio between the two sides. Splitting can be either horizontal (subdividing top and bottom) " "or vertical (subdividing left and right) depending on #<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">orientation.\n\n</a>" "By using # fixedPanel)
DefineEngineMethod(GuiSplitContainer , setSplitPoint , void , (Point2I splitPoint) , "Set the position of the split handle." )
IMPLEMENT_CONOBJECT(GuiSplitContainer )
ImplementEnumType(GuiSplitFixedPanel , "Which side of the splitter <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> keep at <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> fixed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> (<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> any).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup GuiContainers" )
ImplementEnumType(GuiSplitOrientation , "Axis along which <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> divide the container's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">space.\n\n</a>" "@ingroup GuiContainers" )
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "gui/containers/guiSplitContainer.h" 26 27#include "gui/core/guiCanvas.h" 28#include "console/consoleTypes.h" 29#include "gfx/gfxDrawUtil.h" 30 31 32IMPLEMENT_CONOBJECT( GuiSplitContainer ); 33 34ConsoleDocClass( GuiSplitContainer, 35 "@brief A container that splits its area between two child controls.\n\n" 36 37 "A GuiSplitContainer can be used to dynamically subdivide an area between two child controls. " 38 "A splitter bar is placed between the two controls and allows to dynamically adjust the sizing " 39 "ratio between the two sides. Splitting can be either horizontal (subdividing top and bottom) " 40 "or vertical (subdividing left and right) depending on #orientation.\n\n" 41 42 "By using #fixedPanel, one of the panels can be chosen to remain at a fixed size (#fixedSize)." 43 44 "@tsexample\n" 45 "// Create a vertical splitter with a fixed-size left panel.\n" 46 "%splitter = new GuiSplitContainer()\n" 47 "{\n" 48 " orientation = \"Vertical\";\n" 49 " fixedPanel = \"FirstPanel\";\n" 50 " fixedSize = 100;\n" 51 "\n" 52 " new GuiScrollCtrl()\n" 53 " {\n" 54 " new GuiMLTextCtrl()\n" 55 " {\n" 56 " text = %longText;\n" 57 " };\n" 58 " };\n" 59 "\n" 60 " new GuiScrollCtrl()\n" 61 " {\n" 62 " new GuiMLTextCtrl()\n" 63 " {\n" 64 " text = %moreLongText;\n" 65 " };\n" 66 " };\n" 67 "};\n" 68 "@endtsexample\n\n" 69 70 "@note The children placed inside GuiSplitContainers must be GuiContainers.\n\n" 71 72 "@ingroup GuiContainers" 73); 74 75 76ImplementEnumType( GuiSplitOrientation, 77 "Axis along which to divide the container's space.\n\n" 78 "@ingroup GuiContainers" ) 79 { GuiSplitContainer::Vertical, "Vertical", "Divide vertically placing one child left and one child right." }, 80 { GuiSplitContainer::Horizontal, "Horizontal", "Divide horizontally placing one child on top and one child below." } 81EndImplementEnumType; 82 83ImplementEnumType( GuiSplitFixedPanel, 84 "Which side of the splitter to keep at a fixed size (if any).\n\n" 85 "@ingroup GuiContainers" ) 86 { GuiSplitContainer::None, "None", "Allow both childs to resize (default)." }, 87 { GuiSplitContainer::FirstPanel, "FirstPanel", "Keep " }, 88 { GuiSplitContainer::SecondPanel, "SecondPanel" } 89EndImplementEnumType; 90 91 92//----------------------------------------------------------------------------- 93 94GuiSplitContainer::GuiSplitContainer() 95 : mFixedPanel( None ), 96 mFixedPanelSize( 100 ), 97 mOrientation( Vertical ), 98 mSplitterSize( 2 ), 99 mSplitPoint( 0, 0 ), 100 mSplitRect( 0, 0, mSplitterSize, mSplitterSize ), 101 mDragging( false ) 102{ 103 setMinExtent( Point2I(64,64) ); 104 setExtent(200,200); 105 setDocking( Docking::dockNone ); 106 107 mSplitPoint.set( 300, 100 ); 108 109 // We only support client docked items in a split container 110 mValidDockingMask = Docking::dockClient; 111} 112 113//----------------------------------------------------------------------------- 114 115void GuiSplitContainer::initPersistFields() 116{ 117 addGroup( "Splitter", "Options to configure split panels contained by this control" ); 118 119 addField( "orientation", TYPEID< Orientation >(), Offset( mOrientation, GuiSplitContainer), 120 "Whether to split between top and bottom (horizontal) or between left and right (vertical)." ); 121 addField( "splitterSize", TypeS32, Offset( mSplitterSize, GuiSplitContainer), 122 "Width of the splitter bar between the two sides. Default is 2." ); 123 addField( "splitPoint", TypePoint2I, Offset( mSplitPoint, GuiSplitContainer), 124 "Point on control through which the splitter goes.\n\n" 125 "Changed relatively if size of control changes." ); 126 addField( "fixedPanel", TYPEID< FixedPanel >(), Offset( mFixedPanel, GuiSplitContainer), 127 "Which (if any) side of the splitter to keep at a fixed size." ); 128 addField( "fixedSize", TypeS32, Offset( mFixedPanelSize, GuiSplitContainer), 129 "Width of the fixed panel specified by #fixedPanel (if any)." ); 130 131 endGroup( "Splitter" ); 132 133 Parent::initPersistFields(); 134} 135 136//----------------------------------------------------------------------------- 137 138bool GuiSplitContainer::onAdd() 139{ 140 if ( !Parent::onAdd() ) 141 return false; 142 143 return true; 144} 145 146//----------------------------------------------------------------------------- 147 148bool GuiSplitContainer::onWake() 149{ 150 if ( !Parent::onWake() ) 151 return false; 152 153 // Create Panel 1 154 if ( empty() ) 155 { 156 GuiPanel *newPanel = new GuiPanel(); 157 AssertFatal( newPanel, "GuiSplitContainer::onAdd - Cannot create subordinate panel #1!" ); 158 newPanel->registerObject(); 159 newPanel->setInternalName( "Panel1" ); 160 newPanel->setDocking( Docking::dockClient ); 161 addObject( (SimObject*)newPanel ); 162 } 163 else 164 { 165 GuiContainer *containerCtrl = dynamic_cast<GuiContainer*>( at(0) ); 166 if ( containerCtrl ) 167 { 168 containerCtrl->setInternalName( "Panel1" ); 169 containerCtrl->setDocking( Docking::dockClient ); 170 } 171 } 172 173 if ( size() == 1 ) 174 { 175 // Create Panel 2 176 GuiPanel *newPanel = new GuiPanel(); 177 AssertFatal( newPanel, "GuiSplitContainer::onAdd - Cannot create subordinate panel #2!" ); 178 newPanel->registerObject(); 179 newPanel->setInternalName( "Panel2" ); 180 newPanel->setDocking( Docking::dockClient ); 181 addObject( (SimObject*)newPanel ); 182 } 183 else 184 { 185 GuiContainer *containerCtrl = dynamic_cast<GuiContainer*>( at(1) ); 186 if ( containerCtrl ) 187 { 188 containerCtrl->setInternalName( "Panel2" ); 189 containerCtrl->setDocking( Docking::dockClient ); 190 } 191 } 192 193 // Has FixedWidth been specified? 194 if ( mFixedPanelSize == 0 ) 195 { 196 // Nope, so try to guess as best we can 197 GuiContainer *firstPanel = dynamic_cast<GuiContainer*>( at(0) ); 198 GuiContainer *secondPanel = dynamic_cast<GuiContainer*>( at(1) ); 199 if ( mFixedPanel == FirstPanel ) 200 { 201 if ( mOrientation == Horizontal ) 202 mFixedPanelSize = firstPanel->getExtent().y; 203 else 204 mFixedPanelSize = firstPanel->getExtent().x; 205 206 mSplitPoint = Point2I( mFixedPanelSize, mFixedPanelSize ); 207 } 208 else if ( mFixedPanel == SecondPanel ) 209 { 210 if ( mOrientation == Horizontal ) 211 mFixedPanelSize = getExtent().y - secondPanel->getExtent().y; 212 else 213 mFixedPanelSize = getExtent().x - secondPanel->getExtent().x; 214 215 mSplitPoint = getExtent() - Point2I( mFixedPanelSize, mFixedPanelSize ); 216 } 217 218 } 219 220 setUpdateLayout(); 221 222 return true; 223} 224 225//----------------------------------------------------------------------------- 226 227void GuiSplitContainer::onRender( Point2I offset, const RectI &updateRect ) 228{ 229 Parent::onRender( offset, updateRect ); 230 231 // Only render if we're dragging the splitter 232 if ( mDragging && mSplitRect.isValidRect() ) 233 { 234 // Splitter Rectangle (will adjust positioning only) 235 RectI splitterRect = mSplitRect; 236 237 // Currently being dragged to Rect 238 Point2I splitterPoint = localToGlobalCoord( mSplitRect.point ); 239 splitterRect.point = localToGlobalCoord( mSplitPoint ); 240 241 RectI clientRect = getClientRect(); 242 clientRect.point = localToGlobalCoord( clientRect.point ); 243 244 if ( mOrientation == Horizontal ) 245 { 246 splitterRect.point.y -= mSplitterSize; 247 splitterRect.point.x = splitterPoint.x; 248 } 249 else 250 { 251 splitterRect.point.x -= mSplitterSize; 252 splitterRect.point.y = splitterPoint.y; 253 } 254 255 RectI oldClip = GFX->getClipRect(); 256 GFX->setClipRect( clientRect ); 257 GFX->getDrawUtil()->drawRectFill( splitterRect, mProfile->mFillColorHL ); 258 GFX->setClipRect( oldClip ); 259 260 } 261 else 262 { 263 RectI splitterRect = mSplitRect; 264 splitterRect.point += offset; 265 GFX->getDrawUtil()->drawRectFill( splitterRect, mProfile->mFillColor ); 266 } 267} 268 269//----------------------------------------------------------------------------- 270 271Point2I GuiSplitContainer::getMinExtent() const 272{ 273 GuiContainer *panelOne = dynamic_cast<GuiContainer*>( at(0) ); 274 GuiContainer *panelTwo = dynamic_cast<GuiContainer*>( at(1) ); 275 276 if ( !panelOne || !panelTwo ) 277 return Parent::getMinExtent(); 278 279 Point2I minExtent = Point2I(0,0); 280 Point2I panelOneMinExtent = panelOne->getMinExtent(); 281 Point2I panelTwoMinExtent = panelTwo->getMinExtent(); 282 283 if ( mOrientation == Horizontal ) 284 { 285 minExtent.y = 2 * mSplitterSize + panelOneMinExtent.y + panelTwoMinExtent.y; 286 minExtent.x = getMax( panelOneMinExtent.x, panelTwoMinExtent.x ); 287 } 288 else 289 { 290 minExtent.x = 2 * mSplitterSize + panelOneMinExtent.x + panelTwoMinExtent.x; 291 minExtent.y = getMax( panelOneMinExtent.y, panelTwoMinExtent.y ); 292 } 293 294 return minExtent; 295} 296 297//----------------------------------------------------------------------------- 298 299void GuiSplitContainer::parentResized( const RectI &oldParentRect, const RectI &newParentRect ) 300{ 301 Parent::parentResized( oldParentRect, newParentRect ); 302 return; 303 304 // TODO: Is this right James? This isn't needed anymore? 305 /* 306 307 // GuiSplitContainer overrides parentResized to make sure that the proper fixed frame's width/height 308 // is not compromised in the call 309 310 if ( size() < 2 ) 311 return; 312 313 GuiContainer *panelOne = dynamic_cast<GuiContainer*>( at(0) ); 314 GuiContainer *panelTwo = dynamic_cast<GuiContainer*>( at(1) ); 315 316 AssertFatal( panelOne && panelTwo, "GuiSplitContainer::parentResized - Missing/Invalid Subordinate Controls! Split contained controls must derive from GuiContainer!" ); 317 318 Point2I newDragPos; 319 if ( mFixedPanel == FirstPanel ) 320 { 321 newDragPos = panelOne->getExtent(); 322 newDragPos += Point2I( mSplitterSize, mSplitterSize ); 323 } 324 else if ( mFixedPanel == SecondPanel ) 325 { 326 newDragPos = getExtent() - panelTwo->getExtent(); 327 newDragPos -= Point2I( mSplitterSize, mSplitterSize ); 328 } 329 else // None 330 newDragPos.set( 1, 1); 331 332 RectI clientRect = getClientRect(); 333 solvePanelConstraints( newDragPos, panelOne, panelTwo, clientRect ); 334 335 setUpdateLayout(); 336 */ 337} 338 339//----------------------------------------------------------------------------- 340 341bool GuiSplitContainer::resize( const Point2I &newPosition, const Point2I &newExtent ) 342{ 343 // Save previous extent. 344 Point2I oldExtent = getExtent(); 345 346 // Resize ourselves. 347 if ( !Parent::resize( newPosition, newExtent ) || size() < 2 ) 348 return false; 349 350 GuiContainer *panelOne = dynamic_cast<GuiContainer*>( at(0) ); 351 GuiContainer *panelTwo = dynamic_cast<GuiContainer*>( at(1) ); 352 353 // 354 AssertFatal( panelOne && panelTwo, "GuiSplitContainer::resize - Missing/Invalid Subordinate Controls! Split contained controls must derive from GuiContainer!" ); 355 356 // We only need to update the split point if our second panel is fixed. 357 // If the first is fixed, then we can leave the split point alone because 358 // the remainder of the size will be added to or taken from the second panel 359 Point2I newDragPos; 360 if ( mFixedPanel == SecondPanel ) 361 { 362 S32 deltaX = newExtent.x - oldExtent.x; 363 S32 deltaY = newExtent.y - oldExtent.y; 364 365 if( mOrientation == Horizontal ) 366 mSplitPoint.y += deltaY; 367 else 368 mSplitPoint.x += deltaX; 369 } 370 371 // If we got here, parent returned true 372 return true; 373} 374 375//----------------------------------------------------------------------------- 376 377bool GuiSplitContainer::layoutControls( RectI &clientRect ) 378{ 379 if ( size() < 2 ) 380 return false; 381 382 GuiContainer *panelOne = dynamic_cast<GuiContainer*>( at(0) ); 383 GuiContainer *panelTwo = dynamic_cast<GuiContainer*>( at(1) ); 384 385 // 386 AssertFatal( panelOne && panelTwo, "GuiSplitContainer::layoutControl - Missing/Invalid Subordinate Controls! Split contained controls must derive from GuiContainer!" ); 387 388 RectI panelOneRect = RectI( clientRect.point, Point2I( 0, 0 ) ); 389 RectI panelTwoRect; 390 RectI splitRect; 391 392 solvePanelConstraints( getSplitPoint(), panelOne, panelTwo, clientRect ); 393 394 switch( mOrientation ) 395 { 396 case Horizontal: 397 panelOneRect.extent = Point2I( clientRect.extent.x, getSplitPoint().y ); 398 panelTwoRect = panelOneRect; 399 panelTwoRect.intersect( clientRect ); 400 panelTwoRect.point.y = panelOneRect.extent.y; 401 panelTwoRect.extent.y = clientRect.extent.y - panelOneRect.extent.y; 402 403 // Generate new Splitter Rectangle 404 splitRect = panelTwoRect; 405 splitRect.extent.y = 0; 406 splitRect.inset( 0, -mSplitterSize ); 407 408 panelOneRect.extent.y -= mSplitterSize; 409 panelTwoRect.point.y += mSplitterSize; 410 panelTwoRect.extent.y -= mSplitterSize; 411 412 break; 413 414 case Vertical: 415 panelOneRect.extent = Point2I( getSplitPoint().x, clientRect.extent.y ); 416 panelTwoRect = panelOneRect; 417 panelTwoRect.intersect( clientRect ); 418 panelTwoRect.point.x = panelOneRect.extent.x; 419 panelTwoRect.extent.x = clientRect.extent.x - panelOneRect.extent.x; 420 421 // Generate new Splitter Rectangle 422 splitRect = panelTwoRect; 423 splitRect.extent.x = 0; 424 splitRect.inset( -mSplitterSize, 0 ); 425 426 panelOneRect.extent.x -= mSplitterSize; 427 panelTwoRect.point.x += mSplitterSize; 428 panelTwoRect.extent.x -= mSplitterSize; 429 430 break; 431 } 432 433 // Update Split Rect 434 mSplitRect = splitRect; 435 436 // Dock Appropriately 437 if( !( mFixedPanel == FirstPanel && !panelOne->isVisible() ) ) 438 dockControl( panelOne, panelOne->getDocking(), panelOneRect ); 439 if( !( mFixedPanel == FirstPanel && !panelTwo->isVisible() ) ) 440 dockControl( panelTwo, panelOne->getDocking(), panelTwoRect ); 441 442 // 443 return false; 444} 445 446//----------------------------------------------------------------------------- 447 448void GuiSplitContainer::solvePanelConstraints(Point2I newDragPos, GuiContainer * firstPanel, GuiContainer * secondPanel, const RectI& clientRect) 449{ 450 if( !firstPanel || !secondPanel ) 451 return; 452 453 if ( mOrientation == Horizontal ) 454 { 455 // Constrain based on Y axis (Horizontal Splitter) 456 457 // This accounts for the splitter width 458 S32 splitterSize = (S32)(mSplitRect.extent.y * 0.5); 459 460 // Collapsed fixed panel 461 if ( mFixedPanel == SecondPanel && !secondPanel->isVisible() ) 462 { 463 newDragPos = Point2I(mSplitPoint.x, getExtent().y - splitterSize ); 464 } 465 else if( mFixedPanel == SecondPanel && !firstPanel->isVisible() ) 466 { 467 newDragPos = Point2I(mSplitPoint.x, splitterSize ); 468 } 469 else // Normal constraints 470 { 471 //newDragPos.y -= splitterSize; 472 S32 newPosition = mClamp( newDragPos.y, 473 firstPanel->getMinExtent().y + splitterSize, 474 getExtent().y - secondPanel->getMinExtent().y - splitterSize ); 475 newDragPos = Point2I( mSplitPoint.x, newPosition ); 476 } 477 } 478 else 479 { 480 // Constrain based on X axis (Vertical Splitter) 481 482 // This accounts for the splitter width 483 S32 splitterSize = (S32)(mSplitRect.extent.x * 0.5); 484 485 // Collapsed fixed panel 486 if ( mFixedPanel == SecondPanel && !secondPanel->isVisible() ) 487 { 488 newDragPos = Point2I(getExtent().x - splitterSize, mSplitPoint.y ); 489 } 490 else if ( mFixedPanel == FirstPanel && !firstPanel->isVisible() ) 491 { 492 newDragPos = Point2I( splitterSize, mSplitPoint.x ); 493 } 494 else // Normal constraints 495 { 496 S32 newPosition = mClamp( newDragPos.x, firstPanel->getMinExtent().x + splitterSize, 497 getExtent().x - secondPanel->getMinExtent().x - splitterSize ); 498 newDragPos = Point2I( newPosition, mSplitPoint.y ); 499 } 500 } 501 502 // Just in case, clamp to bounds of controls 503 newDragPos.x = mClamp( newDragPos.x, clientRect.point.x, clientRect.point.x + clientRect.extent.x ); 504 newDragPos.y = mClamp( newDragPos.y, clientRect.point.y, clientRect.point.y + clientRect.extent.y ); 505 506 mSplitPoint = newDragPos; 507} 508 509//----------------------------------------------------------------------------- 510 511void GuiSplitContainer::getCursor( GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent ) 512{ 513 GuiCanvas *rootCtrl = getRoot(); 514 if ( !rootCtrl ) 515 return; 516 517 S32 desiredCursor = 0; 518 RectI splitRect = getSplitRect(); 519 520 // Figure out which cursor we want if we need one 521 if ( mOrientation == Horizontal ) 522 desiredCursor = PlatformCursorController::curResizeHorz; 523 else if ( mOrientation == Vertical ) 524 desiredCursor = PlatformCursorController::curResizeVert; 525 526 PlatformWindow *platformWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow(); 527 AssertFatal( platformWindow != NULL,"GuiControl without owning platform window! This should not be possible." ); 528 529 PlatformCursorController *cusrorController = platformWindow->getCursorController(); 530 AssertFatal( cusrorController != NULL,"PlatformWindow without an owned CursorController!" ); 531 532 // Check to see if we need one or just the default... 533 534 Point2I localPoint = Point2I( globalToLocalCoord( lastGuiEvent.mousePoint ) ); 535 if ( splitRect.pointInRect( localPoint ) || mDragging ) 536 { 537 // Do we need to change it or is it already set? 538 if ( rootCtrl->mCursorChanged != desiredCursor ) 539 { 540 // We've already changed the cursor, so set it back 541 if ( rootCtrl->mCursorChanged != -1 ) 542 cusrorController->popCursor(); 543 544 // Now change the cursor shape 545 cusrorController->pushCursor( desiredCursor ); 546 rootCtrl->mCursorChanged = desiredCursor; 547 } 548 } 549 else if ( rootCtrl->mCursorChanged != -1 ) 550 { 551 // Just the default 552 cusrorController->popCursor(); 553 rootCtrl->mCursorChanged = -1; 554 } 555} 556 557//----------------------------------------------------------------------------- 558 559void GuiSplitContainer::onMouseDown( const GuiEvent &event ) 560{ 561 GuiContainer *firstPanel = dynamic_cast<GuiContainer*>(at(0)); 562 GuiContainer *secondPanel = dynamic_cast<GuiContainer*>(at(1)); 563 564 // This function will constrain the panels to their minExtents and update the mSplitPoint 565 if ( firstPanel && secondPanel ) 566 { 567 mouseLock(); 568 mDragging = true; 569 570 RectI clientRect = getClientRect(); 571 Point2I newDragPos = globalToLocalCoord( event.mousePoint ); 572 573 solvePanelConstraints(newDragPos, firstPanel, secondPanel, clientRect); 574 } 575} 576 577//----------------------------------------------------------------------------- 578 579void GuiSplitContainer::onMouseUp( const GuiEvent &event ) 580{ 581 // If we've been dragging, we need to update the fixed panel extent. 582 // NOTE : This should ONLY ever happen in this function. the Fixed panel 583 // is to REMAIN FIXED unless the user changes it. 584 if ( mDragging ) 585 { 586 Point2I newSplitPoint = getSplitPoint(); 587 588 // Update Fixed Panel Extent 589 if ( mFixedPanel == FirstPanel ) 590 mFixedPanelSize = ( mOrientation == Horizontal ) ? newSplitPoint.y : newSplitPoint.x; 591 else 592 mFixedPanelSize = ( mOrientation == Horizontal ) ? getExtent().y - newSplitPoint.y : getExtent().x - newSplitPoint.x; 593 594 setUpdateLayout(); 595 } 596 597 mDragging = false; 598 mouseUnlock(); 599} 600 601//----------------------------------------------------------------------------- 602 603void GuiSplitContainer::onMouseDragged( const GuiEvent &event ) 604{ 605 GuiContainer *firstPanel = dynamic_cast<GuiContainer*>(at(0)); 606 GuiContainer *secondPanel = dynamic_cast<GuiContainer*>(at(1)); 607 608 // This function will constrain the panels to their minExtents and update the mSplitPoint 609 if ( mDragging && firstPanel && secondPanel ) 610 { 611 RectI clientRect = getClientRect(); 612 Point2I newDragPos = globalToLocalCoord( event.mousePoint ); 613 614 solvePanelConstraints(newDragPos, firstPanel, secondPanel, clientRect); 615 } 616} 617 618void GuiSplitContainer::setSplitPoint(Point2I splitPoint) 619{ 620 GuiContainer *firstPanel = dynamic_cast<GuiContainer*>(at(0)); 621 GuiContainer *secondPanel = dynamic_cast<GuiContainer*>(at(1)); 622 623 // This function will constrain the panels to their minExtents and update the mSplitPoint 624 if (firstPanel && secondPanel) 625 { 626 RectI clientRect = getClientRect(); 627 628 solvePanelConstraints(splitPoint, firstPanel, secondPanel, clientRect); 629 630 layoutControls(clientRect); 631 } 632} 633 634DefineEngineMethod(GuiSplitContainer, setSplitPoint, void, (Point2I splitPoint), , 635 "Set the position of the split handle.") 636{ 637 object->setSplitPoint(splitPoint); 638} 639