guiWindowCtrl.cpp
Engine/source/gui/containers/guiWindowCtrl.cpp
Public Functions
ConsoleDocClass(GuiWindowCtrl , "@brief A window with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> title bar and an optional set of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">buttons.\n\n</a>" "The <a href="/coding/class/classguiwindowctrl/">GuiWindowCtrl</a> class implements windows that can be freely placed within the <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> window. Additionally, " "the windows can be resized and maximized/<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">minimized.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classguiwindowctrl/">GuiWindowCtrl</a>(MyWindow)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " text=\"My Window\"; // The text that is displayed on the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" " resizeWidth = true; // Allow horizontal resizing by user via <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mouse.\n</a>" " resizeHeight = true; // Allow vertical resizing by user via <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mouse.\n</a>" " canClose = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> close button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" " canMinimize = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> minimize button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" " canMaximize = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> maximize button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@ingroup GuiContainers" )
DefineEngineMethod(GuiWindowCtrl , attachTo , void , (GuiWindowCtrl *window) , "" )
DefineEngineMethod(GuiWindowCtrl , selectWindow , void , () , "Bring the window <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the front." )
DefineEngineMethod(GuiWindowCtrl , setCollapseGroup , void , (bool state) , "Set the window's collapsing state." )
DefineEngineMethod(GuiWindowCtrl , toggleCollapseGroup , void , () , "Toggle the window collapsing." )
DefineEngineStaticMethod(GuiWindowCtrl , attach , void , (GuiWindowCtrl *bottomWindow, GuiWindowCtrl *topWindow) , "Attach @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> bottomWindow <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> @topWindow so that @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> bottomWindow moves along with @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> topWindow when it is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dragged.\n\n</a>" "@param bottomWindow \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param topWindow " )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onClose , void , () , () , "Called when the close button has been pressed." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onCollapse , void , () , () , "Called when the window is collapsed by clicking its title bar." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onMaximize , void , () , () , "Called when the window has been maximized." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onMinimize , void , () , () , "Called when the window has been minimized." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onRestore , void , () , () , "Called when the window is restored from minimized, maximized , or collapsed state." )
Detailed Description
Public Functions
ConsoleDocClass(GuiWindowCtrl , "@brief A window with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> title bar and an optional set of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">buttons.\n\n</a>" "The <a href="/coding/class/classguiwindowctrl/">GuiWindowCtrl</a> class implements windows that can be freely placed within the <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> window. Additionally, " "the windows can be resized and maximized/<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">minimized.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classguiwindowctrl/">GuiWindowCtrl</a>(MyWindow)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " text=\"My Window\"; // The text that is displayed on the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" " resizeWidth = true; // Allow horizontal resizing by user via <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mouse.\n</a>" " resizeHeight = true; // Allow vertical resizing by user via <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mouse.\n</a>" " canClose = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> close button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" " canMinimize = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> minimize button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" " canMaximize = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> maximize button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@ingroup GuiContainers" )
DefineEngineMethod(GuiWindowCtrl , attachTo , void , (GuiWindowCtrl *window) , "" )
DefineEngineMethod(GuiWindowCtrl , selectWindow , void , () , "Bring the window <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the front." )
DefineEngineMethod(GuiWindowCtrl , setCollapseGroup , void , (bool state) , "Set the window's collapsing state." )
DefineEngineMethod(GuiWindowCtrl , toggleCollapseGroup , void , () , "Toggle the window collapsing." )
DefineEngineStaticMethod(GuiWindowCtrl , attach , void , (GuiWindowCtrl *bottomWindow, GuiWindowCtrl *topWindow) , "Attach @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> bottomWindow <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> @topWindow so that @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> bottomWindow moves along with @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> topWindow when it is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dragged.\n\n</a>" "@param bottomWindow \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param topWindow " )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onClose , void , () , () , "Called when the close button has been pressed." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onCollapse , void , () , () , "Called when the window is collapsed by clicking its title bar." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onMaximize , void , () , () , "Called when the window has been maximized." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onMinimize , void , () , () , "Called when the window has been minimized." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onRestore , void , () , () , "Called when the window is restored from minimized, maximized , or collapsed state." )
IMPLEMENT_CONOBJECT(GuiWindowCtrl )
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/guiWindowCtrl.h" 26 27#include "console/consoleTypes.h" 28#include "console/console.h" 29#include "console/engineAPI.h" 30#include "gui/core/guiCanvas.h" 31#include "gui/core/guiDefaultControlRender.h" 32#include "gfx/gfxDevice.h" 33#include "gfx/gfxDrawUtil.h" 34#include "gui/containers/guiRolloutCtrl.h" 35#include "gui/editor/guiMenuBar.h" 36 37IMPLEMENT_CONOBJECT( GuiWindowCtrl ); 38 39ConsoleDocClass( GuiWindowCtrl, 40 "@brief A window with a title bar and an optional set of buttons.\n\n" 41 42 "The GuiWindowCtrl class implements windows that can be freely placed within the render window. Additionally, " 43 "the windows can be resized and maximized/minimized.\n\n" 44 45 "@tsexample\n" 46 "new GuiWindowCtrl( MyWindow )\n" 47 "{\n" 48 " text = \"My Window\"; // The text that is displayed on the title bar.\n" 49 " resizeWidth = true; // Allow horizontal resizing by user via mouse.\n" 50 " resizeHeight = true; // Allow vertical resizing by user via mouse.\n" 51 " canClose = true; // Display a close button in the title bar.\n" 52 " canMinimize = true; // Display a minimize button in the title bar.\n" 53 " canMaximize = true; // Display a maximize button in the title bar.\n" 54 "};\n" 55 "@endtsexample\n\n" 56 57 "@ingroup GuiContainers" 58); 59 60IMPLEMENT_CALLBACK( GuiWindowCtrl, onClose, void, (), (), 61 "Called when the close button has been pressed." ); 62IMPLEMENT_CALLBACK( GuiWindowCtrl, onMinimize, void, (), (), 63 "Called when the window has been minimized." ); 64IMPLEMENT_CALLBACK( GuiWindowCtrl, onMaximize, void, (), (), 65 "Called when the window has been maximized." ); 66IMPLEMENT_CALLBACK( GuiWindowCtrl, onCollapse, void, (), (), 67 "Called when the window is collapsed by clicking its title bar." ); 68IMPLEMENT_CALLBACK( GuiWindowCtrl, onRestore, void, (), (), 69 "Called when the window is restored from minimized, maximized, or collapsed state." ); 70 71//----------------------------------------------------------------------------- 72 73GuiWindowCtrl::GuiWindowCtrl() 74 : mResizeWidth(true), 75 mResizeEdge(edgeNone), 76 mResizeHeight(true), 77 mCanMove(true), 78 mResizeMargin(2.f), 79 mCanClose(true), 80 mCanMinimize(true), 81 mCanMaximize(true), 82 mCanCollapse(false), 83 mCanDock(false), 84 mEdgeSnap(true), 85 mCollapseGroup(-1), 86 mCollapseGroupNum(-1), 87 mIsCollapsed(false), 88 mIsMouseResizing(false) 89{ 90 // mTitleHeight will change in instanciation most likely... 91 mTitleHeight = 24; 92 93 mIsContainer = true; 94 95 mCloseCommand = StringTable->EmptyString(); 96 97 mMinimized = false; 98 mMaximized = false; 99 mMouseMovingWin = false; 100 mMouseResizeWidth = false; 101 mMouseResizeHeight = false; 102 mMinimizeIndex = -1; 103 mTabIndex = -1; 104 mBitmapBounds = NULL; 105 setExtent(100, 200); 106 107 RectI closeRect(80, 2, 16, 16); 108 mCloseButton = closeRect; 109 closeRect.point.x -= 18; 110 mMaximizeButton = closeRect; 111 closeRect.point.x -= 18; 112 mMinimizeButton = closeRect; 113 114 // Other defaults 115 mActive = true; 116 mCloseButtonPressed = false; 117 mMaximizeButtonPressed = false; 118 mMinimizeButtonPressed = false; 119 120 mRepositionWindow = false; 121 mResizeWindow = false; 122 mSnapSignal = false; 123 mPreCollapsedYExtent = 200; 124 mPreCollapsedYMinExtent = 176; 125 mText = "New Window"; 126} 127 128//----------------------------------------------------------------------------- 129 130void GuiWindowCtrl::initPersistFields() 131{ 132 addGroup( "Window" ); 133 134 addField( "text", TypeRealString, Offset( mText, GuiWindowCtrl ), 135 "Text label to display in titlebar." ); 136 addField( "resizeWidth", TypeBool, Offset( mResizeWidth, GuiWindowCtrl ), 137 "Whether the window can be resized horizontally." ); 138 addField( "resizeHeight", TypeBool, Offset( mResizeHeight, GuiWindowCtrl ), 139 "Whether the window can be resized vertically." ); 140 addField( "canMove", TypeBool, Offset( mCanMove, GuiWindowCtrl ), 141 "Whether the window can be moved by dragging its titlebar." ); 142 addField( "canClose", TypeBool, Offset( mCanClose, GuiWindowCtrl ), 143 "Whether the window has a close button." ); 144 addField( "canMinimize", TypeBool, Offset( mCanMinimize, GuiWindowCtrl ), 145 "Whether the window has a minimize button." ); 146 addField( "canMaximize", TypeBool, Offset( mCanMaximize, GuiWindowCtrl ), 147 "Whether the window has a maximize button." ); 148 addField( "canCollapse", TypeBool, Offset( mCanCollapse, GuiWindowCtrl ), 149 "Whether the window can be collapsed by clicking its title bar." ); 150 addField( "closeCommand", TypeString, Offset( mCloseCommand, GuiWindowCtrl ), 151 "Script code to execute when the window is closed." ); 152 addField( "edgeSnap", TypeBool, Offset( mEdgeSnap,GuiWindowCtrl ), 153 "If true, the window will snap to the edges of other windows when moved close to them." ); 154 155 endGroup( "Window" ); 156 157 Parent::initPersistFields(); 158} 159 160//============================================================================= 161// Collapsing. 162//============================================================================= 163// MARK: ---- Collapsing ---- 164 165//----------------------------------------------------------------------------- 166 167void GuiWindowCtrl::moveFromCollapseGroup() 168{ 169 GuiControl *parent = getParent(); 170 if( !parent ) 171 return; 172 173 S32 groupVec = mCollapseGroup; 174 S32 vecPos = mCollapseGroupNum; 175 S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1; 176 177 CollapseGroupNumVec collapseGroupNumVec; 178 179 if( groupVecCount > vecPos ) 180 { 181 if (vecPos == 1) 182 { 183 CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin(); 184 for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ ) 185 { 186 if((*iter)->mCollapseGroupNum >= vecPos) 187 collapseGroupNumVec.push_back((*iter)); 188 } 189 190 parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1; 191 parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1; 192 parent->mCollapseGroupVec[groupVec].erase(U32(0)); 193 parent->mCollapseGroupVec[groupVec].setSize(groupVecCount - 1); 194 parent->mCollapseGroupVec.erase(groupVec); 195 if(groupVec > 0) 196 parent->mCollapseGroupVec.setSize(groupVec); 197 198 parent->mCollapseGroupVec.push_back( collapseGroupNumVec ); 199 } 200 else 201 { 202 // Iterate through the group i was once in, gather myself and the controls below me and store them in an array 203 CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin(); 204 for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ ) 205 { 206 if((*iter)->mCollapseGroupNum >= vecPos) 207 collapseGroupNumVec.push_back((*iter)); 208 } 209 210 // Iterate through the newly created array; delete my references in the old group, create a new group and organize me accord. 211 S32 assignWindowNumber = 0; 212 CollapseGroupNumVec::iterator iter2 = collapseGroupNumVec.begin(); 213 for(; iter2 != collapseGroupNumVec.end(); iter2++ ) 214 { 215 parent->mCollapseGroupVec[groupVec].pop_back(); 216 parent->mCollapseGroupVec[groupVec].setSize(groupVecCount); 217 (*iter2)->mCollapseGroup = (parent->mCollapseGroupVec.size()); 218 (*iter2)->mCollapseGroupNum = assignWindowNumber; 219 220 assignWindowNumber++; 221 groupVecCount--; 222 } 223 224 parent->mCollapseGroupVec.push_back( collapseGroupNumVec ); 225 } 226 } 227 else 228 { 229 parent->mCollapseGroupVec[groupVec].erase(mCollapseGroupNum); 230 parent->mCollapseGroupVec[groupVec].setSize(groupVecCount); 231 mCollapseGroup = -1; 232 mCollapseGroupNum = -1; 233 234 if(groupVecCount <= 1) 235 { 236 parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1; 237 parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1; 238 parent->mCollapseGroupVec[groupVec].erase(U32(0)); 239 parent->mCollapseGroupVec[groupVec].setSize(groupVecCount - 1); 240 parent->mCollapseGroupVec.erase(groupVec); 241 } 242 } 243 244 // Re id collapse groups 245 refreshCollapseGroups(); 246} 247 248//----------------------------------------------------------------------------- 249 250void GuiWindowCtrl::moveToCollapseGroup(GuiWindowCtrl* hitWindow, bool orientation ) 251{ 252 // Orientation 0 - window in question is being connected to top of another window 253 // Orientation 1 - window in question is being connected to bottom of another window 254 255 GuiControl *parent = getParent(); 256 if( !parent ) 257 return; 258 259 S32 groupVec = mCollapseGroup; 260 S32 attatchedGroupVec = hitWindow->mCollapseGroup; 261 S32 vecPos = mCollapseGroupNum; 262 263 if(mCollapseGroup == attatchedGroupVec && vecPos != -1) 264 return; 265 266 CollapseGroupNumVec collapseGroupNumVec; 267 268 // Window colliding with is not in a collapse group 269 if(hitWindow->mCollapseGroup < 0) 270 { 271 // We(the collider) are in a group of windows 272 if(mCollapseGroup >= 0) 273 { 274 S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1; 275 276 // Copy pointer window data in my array, and store in a temp array 277 CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin(); 278 for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ ) 279 { 280 if((*iter)->mCollapseGroupNum >= vecPos) 281 { 282 collapseGroupNumVec.push_back((*iter)); 283 groupVecCount--; 284 } 285 } 286 287 // Kill my old array group and erase its footprints 288 if( vecPos <= 1 || groupVecCount == 0 ) 289 { 290 // Isn't covered in the renumbering of groups, so we it here 291 parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1; 292 parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1; 293 294 parent->mCollapseGroupVec[groupVec].clear(); 295 parent->mCollapseGroupVec[groupVec].setSize(U32(0)); 296 parent->mCollapseGroupVec.erase(groupVec); 297 298 if(groupVec > 0) 299 parent->mCollapseGroupVec.setSize(groupVec); 300 } 301 302 // Push the collided window 303 if(orientation == 0) 304 collapseGroupNumVec.push_back(hitWindow); 305 else 306 collapseGroupNumVec.push_front(hitWindow); 307 308 // Push the temp array in the guiControl array holder 309 parent->mCollapseGroupVec.push_back( collapseGroupNumVec ); 310 } 311 else 312 { 313 if(orientation == 0) 314 { 315 collapseGroupNumVec.push_front(hitWindow); 316 collapseGroupNumVec.push_front(this); 317 } 318 else 319 { 320 collapseGroupNumVec.push_front(this); 321 collapseGroupNumVec.push_front(hitWindow); 322 } 323 324 parent->mCollapseGroupVec.push_back( collapseGroupNumVec ); 325 } 326 } 327 else // Window colliding with *IS* in a collapse group 328 { 329 330 if(mCollapseGroup >= 0) 331 { 332 S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1; 333 334 CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin(); 335 for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ ) 336 { 337 if((*iter)->mCollapseGroupNum >= vecPos) 338 { 339 // Push back the pointer window controls to the collided array 340 parent->mCollapseGroupVec[attatchedGroupVec].push_back((*iter)); 341 groupVecCount--; 342 } 343 } 344 345 if( vecPos <= 1 || groupVecCount == 0 ) 346 { 347 // Isn't covered in the renumbering of groups, so we it here 348 parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1; 349 parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1; 350 351 parent->mCollapseGroupVec[groupVec].clear(); 352 parent->mCollapseGroupVec[groupVec].setSize(U32(0)); 353 parent->mCollapseGroupVec.erase(groupVec); 354 355 if(groupVec > 0) 356 parent->mCollapseGroupVec.setSize(groupVec); 357 } 358 359 // Since we killed my old array group, run in case the guiControl array moved me down a notch 360 if(attatchedGroupVec > groupVec ) 361 attatchedGroupVec--; 362 363 } 364 else 365 { 366 groupVec = hitWindow->mCollapseGroup; 367 368 if(orientation == 0) 369 parent->mCollapseGroupVec[groupVec].push_front(this); 370 else 371 parent->mCollapseGroupVec[groupVec].push_back(this); 372 } 373 } 374 375 // Re id collapse groups 376 refreshCollapseGroups(); 377 378 // Select the current window and its collapse group 379 selectWindow(); 380} 381 382//----------------------------------------------------------------------------- 383 384void GuiWindowCtrl::refreshCollapseGroups() 385{ 386 GuiControl *parent = getParent(); 387 if( !parent ) 388 return; 389 390 CollapseGroupNumVec collapseGroupNumVec; 391 392 // iterate through the collided array, renumbering the windows pointers 393 S32 assignGroupNum = 0; 394 CollapseGroupVec::iterator iter = parent->mCollapseGroupVec.begin(); 395 for(; iter != parent->mCollapseGroupVec.end(); iter++ ) 396 { 397 S32 assignWindowNumber = 0; 398 CollapseGroupNumVec::iterator iter2 = parent->mCollapseGroupVec[assignGroupNum].begin(); 399 for(; iter2 != parent->mCollapseGroupVec[assignGroupNum].end(); iter2++ ) 400 { 401 (*iter2)->mCollapseGroup = assignGroupNum; 402 (*iter2)->mCollapseGroupNum = assignWindowNumber; 403 assignWindowNumber++; 404 } 405 406 assignGroupNum++; 407 } 408} 409 410//----------------------------------------------------------------------------- 411 412void GuiWindowCtrl::moveWithCollapseGroup(Point2I windowPosition) 413{ 414 GuiControl *parent = getParent(); 415 if( !parent ) 416 return; 417 418 Point2I newChildPosition(0, 0); 419 S32 addedPosition = getExtent().y; 420 421 // Iterate through windows below this. Move them according to my position. 422 CollapseGroupNumVec collapseGroupNumVec; 423 CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin(); 424 for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ ) 425 { 426 if((*iter)->mCollapseGroupNum > mCollapseGroupNum) 427 { 428 newChildPosition.x = windowPosition.x; 429 newChildPosition.y = windowPosition.y + addedPosition; 430 431 (*iter)->resize(newChildPosition, (*iter)->getExtent()); 432 addedPosition += (*iter)->getExtent().y; 433 } 434 } 435} 436 437//----------------------------------------------------------------------------- 438 439void GuiWindowCtrl::setCollapseGroup(bool state) 440{ 441 GuiControl *parent = getParent(); 442 if( !parent ) 443 return; 444 445 if( mIsCollapsed != state ) 446 { 447 mIsCollapsed = state; 448 handleCollapseGroup(); 449 } 450} 451 452//----------------------------------------------------------------------------- 453 454void GuiWindowCtrl::toggleCollapseGroup() 455{ 456 GuiControl *parent = getParent(); 457 if( !parent ) 458 return; 459 460 mIsCollapsed = !mIsCollapsed; 461 handleCollapseGroup(); 462} 463 464//----------------------------------------------------------------------------- 465 466void GuiWindowCtrl::handleCollapseGroup() 467{ 468 GuiControl *parent = getParent(); 469 if( !parent ) 470 return; 471 472 CollapseGroupNumVec collapseGroupNumVec; 473 474 if( mIsCollapsed ) // minimize window up to its header bar 475 { 476 //save settings 477 mPreCollapsedYExtent = getExtent().y; 478 mPreCollapsedYMinExtent = getMinExtent().y; 479 480 //create settings for collapsed window to abide by 481 mResizeHeight = false; 482 setMinExtent( Point2I( getMinExtent().x, 24 ) ); 483 484 iterator i; 485 for(i = begin(); i != end(); i++) 486 { 487 GuiControl *ctrl = static_cast<GuiControl *>(*i); 488 ctrl->setVisible(false); 489 ctrl->mCanResize = false; 490 } 491 492 resize( getPosition(), Point2I( getExtent().x, 24 ) ); 493 494 if(mCollapseGroup >= 0) 495 { 496 S32 moveChildYBy = (mPreCollapsedYExtent - 24); 497 498 CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin(); 499 for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ ) 500 { 501 if((*iter)->mCollapseGroupNum > mCollapseGroupNum) 502 { 503 Point2I newChildPosition = (*iter)->getPosition(); 504 newChildPosition.y -= moveChildYBy; 505 (*iter)->resize(newChildPosition, (*iter)->getExtent()); 506 } 507 } 508 } 509 510 onCollapse_callback(); 511 } 512 else // maximize the window to its previous position 513 { 514 //create and load settings 515 mResizeHeight = true; 516 setMinExtent( Point2I( getMinExtent().x, mPreCollapsedYMinExtent ) ); 517 518 resize( getPosition(), Point2I( getExtent().x, mPreCollapsedYExtent ) ); 519 520 iterator i; 521 for(i = begin(); i != end(); i++) 522 { 523 GuiControl *ctrl = static_cast<GuiControl *>(*i); 524 ctrl->setVisible(true); 525 ctrl->mCanResize = true; 526 } 527 528 if(mCollapseGroup >= 0) 529 { 530 S32 moveChildYBy = (mPreCollapsedYExtent - 24); 531 532 CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin(); 533 for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ ) 534 { 535 if((*iter)->mCollapseGroupNum > mCollapseGroupNum) 536 { 537 Point2I newChildPosition = (*iter)->getPosition(); 538 newChildPosition.y += moveChildYBy; 539 (*iter)->resize(newChildPosition, (*iter)->getExtent()); 540 } 541 } 542 } 543 544 onRestore_callback(); 545 } 546} 547 548//----------------------------------------------------------------------------- 549 550bool GuiWindowCtrl::resizeCollapseGroup(bool resizeX, bool resizeY, Point2I resizePos, Point2I resizeExtent) 551{ 552 GuiControl *parent = getParent(); 553 if( !parent ) 554 return false; 555 556 CollapseGroupNumVec collapseGroupNumVec; 557 558 bool canResize = true; 559 CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin(); 560 for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ ) 561 { 562 if((*iter) == this) 563 continue; 564 565 Point2I newChildPosition = (*iter)->getPosition(); 566 Point2I newChildExtent = (*iter)->getExtent(); 567 568 if( resizeX == true ) 569 { 570 newChildPosition.x -= resizePos.x; 571 newChildExtent.x -= resizeExtent.x; 572 573 } 574 if( resizeY == true ) 575 { 576 if((*iter)->mCollapseGroupNum > mCollapseGroupNum) 577 { 578 newChildPosition.y -= resizeExtent.y; 579 newChildPosition.y -= resizePos.y; 580 } 581 else if((*iter)->mCollapseGroupNum == mCollapseGroupNum - 1) 582 newChildExtent.y -= resizePos.y; 583 } 584 585 // check is done for normal extent of windows. if false, check again in case its just giving false 586 // due to being a collapsed window. if your truly being forced passed your extent, return false 587 if( !(*iter)->mIsCollapsed && newChildExtent.y >= (*iter)->getMinExtent().y ) 588 (*iter)->resize( newChildPosition, newChildExtent); 589 else 590 { 591 if( (*iter)->mIsCollapsed ) 592 (*iter)->resize( newChildPosition, newChildExtent); 593 else 594 canResize = false; 595 } 596 } 597 return canResize; 598} 599 600//----------------------------------------------------------------------------- 601// Mouse Methods 602//----------------------------------------------------------------------------- 603S32 GuiWindowCtrl::findHitEdges( const Point2I &globalPoint ) 604{ 605 // No Edges 606 S32 edgeMask = edgeNone; 607 608 GuiControl *parent = getParent(); 609 if( !parent ) 610 return edgeMask; 611 612 RectI bounds( getGlobalBounds() ); 613 614 // Create an EdgeRectI structure that has four edges 615 // Left/Right/Top/Bottom 616 // Each Edge structure has a hit operation that will take 617 // another edge and test for a hit on the edge with a margin 618 // specified by the .margin scalar 619 EdgeRectI edges = EdgeRectI(bounds, mResizeMargin); 620 621 // Get Cursor Edges 622 Edge cursorVertEdge = Edge( globalPoint, Point2F( 1.f, 0.f ) ); 623 Edge cursorHorzEdge = Edge( globalPoint, Point2F( 0.f, 1.f ) ); 624 625 if( edges.left.hit( cursorVertEdge ) ) 626 edgeMask |= edgeLeft; 627 else if( edges.right.hit( cursorVertEdge ) ) 628 edgeMask |= edgeRight; 629 630 if( edges.bottom.hit( cursorHorzEdge ) ) 631 edgeMask |= edgeBottom; 632 else if( edges.top.hit( cursorHorzEdge ) ) 633 { 634 // Only the top window in a collapse group can be extended from the top 635 if( mCanCollapse && mCollapseGroup >= 0 ) 636 { 637 if( parent->mCollapseGroupVec[mCollapseGroup].first() != this ) 638 return edgeMask; 639 } 640 641 edgeMask |= edgeTop; 642 } 643 644 // Return the resulting mask 645 return edgeMask; 646} 647 648void GuiWindowCtrl::getSnappableWindows( Vector<GuiWindowCtrl*> &windowOutVector, bool canCollapse ) 649{ 650 GuiControl *parent = getParent(); 651 if( !parent ) 652 return; 653 654 S32 parentSize = parent->size(); 655 for( S32 i = 0; i < parentSize; i++ ) 656 { 657 GuiWindowCtrl *childWindow = dynamic_cast<GuiWindowCtrl*>(parent->at(i)); 658 if( !childWindow || !childWindow->isVisible() || childWindow == this || childWindow->mEdgeSnap == false) 659 continue; 660 661 if( canCollapse && !childWindow->mCanCollapse ) 662 continue; 663 664 windowOutVector.push_back(childWindow); 665 } 666 667} 668 669//============================================================================= 670// Events. 671//============================================================================= 672// MARK: ---- Events ---- 673 674//----------------------------------------------------------------------------- 675 676bool GuiWindowCtrl::onWake() 677{ 678 if (! Parent::onWake()) 679 return false; 680 681 //get the texture for the close, minimize, and maximize buttons 682 mTextureObject = mProfile->mTextureObject; 683 bool result = mProfile->constructBitmapArray() >= NumBitmaps; 684 if( !result ) 685 { 686 Con::errorf( "GuiWindowCtrl::onWake - failed to create bitmap array from profile bitmap." ); 687 return false; 688 } 689 690 mBitmapBounds = mProfile->mBitmapArrayRects.address(); 691 S32 buttonHeight = mBitmapBounds[BmpStates * BmpClose].extent.y; 692 693 mTitleHeight = buttonHeight + 4; 694 695 //set the button coords 696 positionButtons(); 697 698 //set the tab index 699 mTabIndex = -1; 700 GuiControl *parent = getParent(); 701 if (parent && mFirstResponder) 702 { 703 mTabIndex = 0; 704 705 //count the number of windows preceeding this one 706 iterator i; 707 for (i = parent->begin(); i != parent->end(); i++) 708 { 709 GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i); 710 if (ctrl) 711 { 712 if (ctrl == this) break; 713 else if (ctrl->mFirstResponder) mTabIndex++; 714 } 715 } 716 } 717 718 return true; 719} 720 721//----------------------------------------------------------------------------- 722 723void GuiWindowCtrl::onSleep() 724{ 725 mTextureObject = NULL; 726 mMousePosition = Point2I(0,0); 727 Parent::onSleep(); 728} 729 730//----------------------------------------------------------------------------- 731 732void GuiWindowCtrl::onMouseDown(const GuiEvent &event) 733{ 734 setUpdate(); 735 736 mOrigBounds = getBounds(); 737 738 mMouseDownPosition = event.mousePoint; 739 Point2I localPoint = globalToLocalCoord(event.mousePoint); 740 741 // Select this window - move it to the front, and set the first responder 742 selectWindow(); 743 744 mMouseMovingWin = false; 745 746 S32 hitEdges = findHitEdges( event.mousePoint ); 747 748 mResizeEdge = edgeNone; 749 750 // Set flag by default so we only clear it 751 // if we don't match either edge 752 mMouseResizeHeight = true; 753 754 // Check Bottom/Top edges (Mutually Exclusive) 755 if( mResizeHeight && hitEdges & edgeBottom ) 756 mResizeEdge |= edgeBottom; 757 else if( mResizeHeight && hitEdges & edgeTop ) 758 mResizeEdge |= edgeTop; 759 else 760 mMouseResizeHeight = false; 761 762 // Set flag by default so we only clear it 763 // if we don't match either edge 764 mMouseResizeWidth = true; 765 766 // Check Left/Right edges (Mutually Exclusive) 767 if( mResizeWidth && hitEdges & edgeLeft ) 768 mResizeEdge |= edgeLeft; 769 else if( mResizeWidth && hitEdges & edgeRight ) 770 mResizeEdge |= edgeRight; 771 else 772 mMouseResizeWidth = false; 773 774 775 // If we clicked within the title bar 776 if ( !(mResizeEdge & ( edgeTop | edgeLeft | edgeRight ) ) && localPoint.y < mTitleHeight) 777 { 778 if (mCanClose && mCloseButton.pointInRect(localPoint)) 779 { 780 mCloseButtonPressed = mCanClose; 781 } 782 else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint)) 783 { 784 mMaximizeButtonPressed = mCanMaximize; 785 } 786 else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint)) 787 { 788 mMinimizeButtonPressed = mCanMinimize; 789 } 790 else // We clicked anywhere else within the title 791 { 792 S32 docking = getDocking(); 793 if( docking == Docking::dockInvalid || docking == Docking::dockNone ) 794 mMouseMovingWin = mCanMove; 795 796 mMouseResizeWidth = false; 797 mMouseResizeHeight = false; 798 } 799 } 800 801 802 if (mMouseMovingWin || mResizeEdge != edgeNone || 803 mCloseButtonPressed || mMaximizeButtonPressed || mMinimizeButtonPressed) 804 { 805 mouseLock(); 806 } 807 else 808 { 809 810 GuiControl *ctrl = findHitControl(localPoint); 811 if (ctrl && ctrl != this) 812 ctrl->onMouseDown(event); 813 } 814} 815 816//----------------------------------------------------------------------------- 817 818void GuiWindowCtrl::onMouseDragged(const GuiEvent &event) 819{ 820 GuiControl *parent = getParent(); 821 GuiCanvas *root = getRoot(); 822 if ( !root ) 823 return; 824 825 mMousePosition = globalToLocalCoord(event.mousePoint); 826 Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition; 827 828 Point2I newPosition = getPosition(); 829 Point2I newExtent = getExtent(); 830 bool resizeX = false; 831 bool resizeY = false; 832 mResizeWindow = false; 833 mRepositionWindow = false; 834 835 if (mMouseMovingWin && parent) 836 { 837 if( parent != root ) 838 { 839 newPosition.x = mOrigBounds.point.x + deltaMousePosition.x; 840 newPosition.y = getMax(0, mOrigBounds.point.y + deltaMousePosition.y ); 841 mRepositionWindow = true; 842 } 843 else 844 { 845 newPosition.x = getMax(0, getMin(parent->getWidth() - getWidth(), mOrigBounds.point.x + deltaMousePosition.x)); 846 newPosition.y = getMax(0, getMin(parent->getHeight() - getHeight(), mOrigBounds.point.y + deltaMousePosition.y)); 847 } 848 849 // Check snapping to other windows 850 if( mEdgeSnap ) 851 { 852 RectI bounds = getGlobalBounds(); 853 bounds.point = mOrigBounds.point + deltaMousePosition; 854 EdgeRectI edges = EdgeRectI( bounds, mResizeMargin ); 855 856 // Create a global-space rectangle that covers the snapping 857 // zone of this window. Double the space in which snapping occurs 858 // for top and bottom. 859 RectI snapZone = bounds; 860 snapZone.point.x -= SnapDistance; 861 snapZone.point.y -= SnapDistance; 862 snapZone.extent.x += SnapDistance + SnapDistance; 863 snapZone.extent.y += SnapDistance + SnapDistance; 864 865 //check if we need to offset because of the menubar 866 U32 menuBarHeight = 0; 867 GuiCanvas* guiCanvas = getRoot(); 868 if (guiCanvas) 869 { 870 GuiMenuBar* menuBar = dynamic_cast<GuiMenuBar*>(guiCanvas->getMenuBar()); 871 if (menuBar) 872 { 873 menuBarHeight = menuBar->getHeight(); 874 } 875 } 876 877 // Build valid snap and window vectors to compare against 878 Vector< GuiWindowCtrl*> windowList; 879 getSnappableWindows( windowList ); 880 881 for( S32 i =0; i < windowList.size(); i++ ) 882 { 883 // Make sure the window is both horizontally and vertically 884 // within the snap zone for this window. 885 RectI windowBounds = windowList[i]->getGlobalBounds(); 886 //offset position by menubar height 887 windowBounds.point.y -= menuBarHeight; 888 889 if( !snapZone.overlaps(windowBounds) ) 890 continue; 891 892 // Build edges for snap detection 893 EdgeRectI snapRect(windowBounds, mResizeMargin ); 894 895 if( snapRect.right.position.x <= edges.left.position.x + SnapDistance && 896 snapRect.right.position.x >= edges.left.position.x - SnapDistance ) 897 { 898 newPosition.x = snapRect.right.position.x; 899 } 900 else if( snapRect.left.position.x <= edges.right.position.x + SnapDistance && 901 snapRect.left.position.x >= edges.right.position.x - SnapDistance ) 902 { 903 newPosition.x = snapRect.left.position.x - bounds.extent.x; 904 } 905 else if( snapRect.top.position.y <= edges.bottom.position.y + SnapDistance + SnapDistance && 906 snapRect.top.position.y >= edges.bottom.position.y - SnapDistance - SnapDistance ) 907 { 908 // Ensure that we're not snapping to the middle of collapse groups 909 if( (windowList[i]->mCanCollapse && windowList[i]->mCollapseGroup >= 0) || 910 (mCanCollapse && mCollapseGroup >= 0) ) 911 continue; 912 newPosition.x = snapRect.left.position.x; 913 newPosition.y = snapRect.top.position.y - bounds.extent.y; 914 } 915 else if( snapRect.bottom.position.y <= edges.top.position.y + SnapDistance + SnapDistance && 916 snapRect.bottom.position.y >= edges.top.position.y - SnapDistance - SnapDistance) 917 { 918 // Ensure that we're not snapping to the middle of collapse groups 919 // We are not in a group, or we are not in the same group 920 if( mCollapseGroup == -1 || ( mCollapseGroup >= 0 && mCollapseGroup != windowList[i]->mCollapseGroup ) ) 921 { 922 // If the window checked is in a group, if its anything but the last, its n/a 923 if( windowList[i]->mCollapseGroup >= 0 && 924 windowList[i] != parent->mCollapseGroupVec[windowList[i]->mCollapseGroup].last() ) 925 continue; 926 } 927 else // We are in the same group, we can't obviously be [0] 928 { 929 // If we are [-1/0] we have a serious problem. Also, we only allow connection to the window directly above us 930 if( mCollapseGroupNum <= 0 || 931 windowList[i] != parent->mCollapseGroupVec[mCollapseGroup][mCollapseGroupNum-1] ) 932 continue; 933 } 934 935 newPosition.x = snapRect.left.position.x; 936 newPosition.y = snapRect.bottom.position.y; 937 } 938 } 939 } 940 } 941 else if(mCloseButtonPressed || mMaximizeButtonPressed || mMinimizeButtonPressed) 942 { 943 setUpdate(); 944 return; 945 } 946 else 947 { 948 if( ( !mMouseResizeHeight && !mMouseResizeWidth ) || !parent ) 949 return; 950 951 mResizeWindow = true; 952 if( mResizeEdge & edgeBottom ) 953 { 954 newExtent.y = getMin(parent->getHeight(), mOrigBounds.extent.y + deltaMousePosition.y); 955 resizeY = true; 956 } 957 else if ( mResizeEdge & edgeTop ) 958 { 959 newExtent.y = getMin(parent->getHeight(), mOrigBounds.extent.y - deltaMousePosition.y); 960 if ( newExtent.y >= mMinExtent.y ) 961 { 962 // Standard reposition as we're not travelling into the min extent range 963 newPosition.y = mOrigBounds.point.y + deltaMousePosition.y; 964 } 965 else 966 { 967 // We're into the min extent, so adjust the position up to the min extent 968 // so the window doesn't appear to jump 969 newPosition.y = mOrigBounds.point.y + (mOrigBounds.extent.y - mMinExtent.y); 970 } 971 resizeY = true; 972 } 973 974 if( mResizeEdge & edgeRight ) 975 { 976 newExtent.x = getMin(parent->getWidth(), mOrigBounds.extent.x + deltaMousePosition.x); 977 resizeX = true; 978 } 979 else if( mResizeEdge & edgeLeft ) 980 { 981 newExtent.x = getMin(parent->getWidth(), mOrigBounds.extent.x - deltaMousePosition.x); 982 if ( newExtent.x >= mMinExtent.x ) 983 { 984 // Standard reposition as we're not travelling into the min extent range 985 newPosition.x = mOrigBounds.point.x + deltaMousePosition.x; 986 } 987 else 988 { 989 // We're into the min extent, so adjust the position up to the min extent 990 // so the window doesn't appear to jump 991 newPosition.x = mOrigBounds.point.x + (mOrigBounds.extent.x - mMinExtent.x); 992 } 993 resizeX = true; 994 } 995 } 996 997 // Resize this 998 Point2I pos = parent->localToGlobalCoord(getPosition()); 999 root->addUpdateRegion(pos, getExtent()); 1000 1001 if(mCanCollapse && mCollapseGroup >= 0 && mRepositionWindow == true) 1002 moveWithCollapseGroup(newPosition); 1003 1004 if(mCanCollapse && mCollapseGroup >= 0 && mResizeWindow == true ) 1005 { 1006 // Resize the window if allowed 1007 if( newExtent.y >= getMinExtent().y && newExtent.x >= getMinExtent().x) 1008 { 1009 mIsMouseResizing = true; 1010 if( resizeCollapseGroup( resizeX, resizeY, (getPosition() - newPosition), (getExtent() - newExtent) ) ) 1011 resize(newPosition, newExtent); 1012 } 1013 } 1014 else // Normal window sizing functionality 1015 resize(newPosition, newExtent); 1016} 1017 1018//----------------------------------------------------------------------------- 1019 1020void GuiWindowCtrl::onMouseUp(const GuiEvent &event) 1021{ 1022 bool closing = mCloseButtonPressed; 1023 bool maximizing = mMaximizeButtonPressed; 1024 bool minimizing = mMinimizeButtonPressed; 1025 1026 mCloseButtonPressed = false; 1027 mMaximizeButtonPressed = false; 1028 mMinimizeButtonPressed = false; 1029 1030 TORQUE_UNUSED(event); 1031 mouseUnlock(); 1032 1033 mMouseMovingWin = false; 1034 mMouseResizeWidth = false; 1035 mMouseResizeHeight = false; 1036 1037 GuiControl *parent = getParent(); 1038 if (! parent) 1039 return; 1040 1041 if( mIsMouseResizing ) 1042 { 1043 mIsMouseResizing = false; 1044 return; 1045 } 1046 1047 Point2I localPoint = globalToLocalCoord(event.mousePoint); 1048 if (closing && mCloseButton.pointInRect(localPoint)) 1049 { 1050 // Here is where were going to put our other if statement 1051 // if the window closes, and there were only 2 windows in the array, then just delete the array and default there params 1052 // if not, delete the window from the array, default its params, and renumber the windows 1053 1054 if( engineAPI::gUseConsoleInterop ) 1055 evaluate( mCloseCommand ); 1056 onClose_callback(); 1057 } 1058 else if (maximizing && mMaximizeButton.pointInRect(localPoint)) 1059 { 1060 if (mMaximized) 1061 { 1062 // Resize to the previous position and extent, bounded by the parent 1063 resize(Point2I(getMax(0, getMin(parent->getWidth() - mStandardBounds.extent.x, mStandardBounds.point.x)), 1064 getMax(0, getMin(parent->getHeight() - mStandardBounds.extent.y, mStandardBounds.point.y))), 1065 mStandardBounds.extent); 1066 // Set the flag 1067 mMaximized = false; 1068 1069 onRestore_callback(); 1070 } 1071 else 1072 { 1073 // Only save the position if we're not minimized 1074 if (! mMinimized) 1075 mStandardBounds = getBounds(); 1076 else 1077 mMinimized = false; 1078 1079 // Resize to fit the parent 1080 resize(Point2I(0, 0), parent->getExtent()); 1081 1082 // Set the flag 1083 mMaximized = true; 1084 1085 onMaximize_callback(); 1086 } 1087 } 1088 else if (minimizing && mMinimizeButton.pointInRect(localPoint)) 1089 { 1090 if (mMinimized) 1091 { 1092 // Resize to the previous position and extent, bounded by the parent 1093 resize(Point2I(getMax(0, getMin(parent->getWidth() - mStandardBounds.extent.x, mStandardBounds.point.x)), 1094 getMax(0, getMin(parent->getHeight() - mStandardBounds.extent.y, mStandardBounds.point.y))), 1095 mStandardBounds.extent); 1096 // Set the flag 1097 mMinimized = false; 1098 1099 onRestore_callback(); 1100 } 1101 else 1102 { 1103 if (parent->getWidth() < 100 || parent->getHeight() < mTitleHeight + 3) 1104 return; 1105 1106 // Only save the position if we're not maximized 1107 if (! mMaximized) 1108 { 1109 mStandardBounds = getBounds(); 1110 } 1111 else 1112 { 1113 mMaximized = false; 1114 } 1115 1116 // First find the lowest unused minimized index up to 32 minimized windows 1117 U32 indexMask = 0; 1118 iterator i; 1119 S32 count = 0; 1120 for (i = parent->begin(); i != parent->end() && count < 32; i++) 1121 { 1122 count++; 1123 S32 index; 1124 GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i); 1125 if (ctrl && ctrl->isMinimized(index)) 1126 { 1127 indexMask |= (1 << index); 1128 } 1129 } 1130 1131 // Now find the first unused bit 1132 for (count = 0; count < 32; count++) 1133 { 1134 if (! (indexMask & (1 << count))) break; 1135 } 1136 1137 // If we have more than 32 minimized windows, use the first position 1138 count = getMax(0, count); 1139 1140 // This algorithm assumes all window have the same title height, and will minimize to 98 pix 1141 Point2I newExtent(98, mTitleHeight); 1142 1143 // First, how many can fit across 1144 S32 numAcross = getMax(1, (parent->getWidth() / newExtent.x + 2)); 1145 1146 // Find the new "mini position" 1147 Point2I newPosition; 1148 newPosition.x = (count % numAcross) * (newExtent.x + 2) + 2; 1149 newPosition.y = parent->getHeight() - (((count / numAcross) + 1) * (newExtent.y + 2)) - 2; 1150 1151 // Find the minimized position and extent 1152 resize(newPosition, newExtent); 1153 1154 // Set the index so other windows will not try to minimize to the same location 1155 mMinimizeIndex = count; 1156 1157 // Set the flag 1158 mMinimized = true; 1159 1160 onMinimize_callback(); 1161 } 1162 } 1163 else if ( !(mResizeEdge & edgeTop) && localPoint.y < mTitleHeight && event.mousePoint == mMouseDownPosition) 1164 { 1165 if (mCanClose && mCloseButton.pointInRect(localPoint)) 1166 return; 1167 else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint)) 1168 return; 1169 else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint)) 1170 return; 1171 else if( mCanCollapse ) // If we clicked anywhere else on the title bar 1172 toggleCollapseGroup(); 1173 } 1174 else if( mEdgeSnap && mCanCollapse ) 1175 { 1176 // Create storage pointer 1177 GuiWindowCtrl* hitWindow = NULL; 1178 S32 snapType = -1; 1179 1180 Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition; 1181 Point2I newExtent = getExtent(); 1182 1183 RectI bounds = getGlobalBounds(); 1184 bounds.point = mOrigBounds.point + deltaMousePosition; 1185 EdgeRectI edges = EdgeRectI( bounds, mResizeMargin ); 1186 1187 RectI snapZone = bounds; 1188 snapZone.point.x -= SnapDistance; 1189 snapZone.point.y -= SnapDistance; 1190 snapZone.extent.x += SnapDistance + SnapDistance; 1191 snapZone.extent.y += SnapDistance + SnapDistance; 1192 1193 Vector<EdgeRectI> snapList; 1194 Vector<GuiWindowCtrl*> windowList; 1195 1196 getSnappableWindows( windowList, true ); 1197 1198 for( S32 i =0; i < windowList.size(); i++ ) 1199 { 1200 if( !snapZone.overlaps( windowList[i]->getGlobalBounds() ) ) 1201 continue; 1202 1203 // Build edges for snap detection 1204 EdgeRectI snapRect( windowList[i]->getGlobalBounds(), mResizeMargin ); 1205 1206 if( windowList[i]->mCollapseGroupNum == -1 ) 1207 { 1208 // BECOMES "PARENT" 1209 if( snapRect.top.position.y <= edges.bottom.position.y + SnapDistance + SnapDistance && 1210 snapRect.top.position.y >= edges.bottom.position.y - SnapDistance - SnapDistance ) 1211 { 1212 hitWindow = windowList[i]; 1213 snapType = 0; 1214 newExtent.x = snapRect.right.position.x - snapRect.left.position.x; 1215 break; 1216 } 1217 } 1218 1219 if( (windowList[i]->mCollapseGroupNum == -1) || (windowList[i]->mCollapseGroupNum == mCollapseGroupNum - 1) || 1220 (!parent->mCollapseGroupVec.empty() && parent->mCollapseGroupVec[windowList[i]->mCollapseGroup].last() == windowList[i]) ) 1221 { 1222 // BECOMES "CHILD" 1223 if( snapRect.bottom.position.y <= edges.top.position.y + SnapDistance + SnapDistance && 1224 snapRect.bottom.position.y >= edges.top.position.y - SnapDistance - SnapDistance) 1225 { 1226 hitWindow = windowList[i]; 1227 snapType = 1; 1228 newExtent.x = snapRect.right.position.x - snapRect.left.position.x; 1229 break; 1230 } 1231 } 1232 } 1233 1234 // We're either moving out of a collapse group or moving to another one 1235 // Not valid for windows not previously in a group 1236 if( mCollapseGroup >= 0 && 1237 (snapType == -1 || (hitWindow && snapType >= 0 && mCollapseGroup != hitWindow->mCollapseGroup))) 1238 moveFromCollapseGroup(); 1239 1240 // No window to connect to 1241 if( !hitWindow ) 1242 return; 1243 1244 moveToCollapseGroup( hitWindow, snapType ); 1245 resize( getPosition(), newExtent ); 1246 } 1247} 1248 1249//----------------------------------------------------------------------------- 1250 1251void GuiWindowCtrl::onMouseMove(const GuiEvent &event) 1252{ 1253 mMousePosition = globalToLocalCoord(event.mousePoint); 1254} 1255 1256bool GuiWindowCtrl::onKeyDown(const GuiEvent &event) 1257{ 1258 // If this control is a dead end, kill the event 1259 if ((! mVisible) || (! mActive) || (! mAwake)) return true; 1260 1261 if ((event.keyCode == KEY_TAB) && (event.modifier & SI_PRIMARY_CTRL)) 1262 { 1263 // Find the next sibling window, and select it 1264 GuiControl *parent = getParent(); 1265 if (parent) 1266 { 1267 GuiWindowCtrl *firstWindow = NULL; 1268 iterator i; 1269 for (i = parent->begin(); i != parent->end(); i++) 1270 { 1271 GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i); 1272 if (ctrl && ctrl->getTabIndex() == mTabIndex + 1) 1273 { 1274 ctrl->selectWindow(); 1275 return true; 1276 } 1277 else if (ctrl && ctrl->getTabIndex() == 0) 1278 { 1279 firstWindow = ctrl; 1280 } 1281 } 1282 // Recycle from the beginning 1283 if (firstWindow != this) 1284 { 1285 firstWindow->selectWindow(); 1286 return true; 1287 } 1288 } 1289 } 1290 1291 return Parent::onKeyDown(event); 1292} 1293 1294//----------------------------------------------------------------------------- 1295 1296void GuiWindowCtrl::onRender(Point2I offset, const RectI &updateRect) 1297{ 1298 if( !mProfile || mProfile->mFont == NULL || mProfile->mBitmapArrayRects.size() < NumBitmaps ) 1299 return Parent::onRender( offset, updateRect ); 1300 1301 // Draw the outline 1302 RectI winRect; 1303 winRect.point = offset; 1304 winRect.extent = getExtent(); 1305 GuiCanvas *root = getRoot(); 1306 GuiControl *firstResponder = root ? root->getFirstResponder() : NULL; 1307 1308 bool isKey = (!firstResponder || controlIsChild(firstResponder)); 1309 1310 U32 topBase = isKey ? BorderTopLeftKey : BorderTopLeftNoKey; 1311 winRect.point.x += mBitmapBounds[BorderLeft].extent.x; 1312 winRect.point.y += mBitmapBounds[topBase + 2].extent.y; 1313 1314 winRect.extent.x -= mBitmapBounds[BorderLeft].extent.x + mBitmapBounds[BorderRight].extent.x; 1315 winRect.extent.y -= mBitmapBounds[topBase + 2].extent.y + mBitmapBounds[BorderBottom].extent.y; 1316 1317 winRect.extent.x += 1; 1318 1319 GFXDrawUtil* drawUtil = GFX->getDrawUtil(); 1320 1321 drawUtil->drawRectFill(winRect, mProfile->mFillColor); 1322 1323 drawUtil->clearBitmapModulation(); 1324 drawUtil->drawBitmapSR(mTextureObject, offset, mBitmapBounds[topBase]); 1325 drawUtil->drawBitmapSR(mTextureObject, Point2I(offset.x + getWidth() - mBitmapBounds[topBase+1].extent.x, offset.y), 1326 mBitmapBounds[topBase + 1]); 1327 1328 RectI destRect; 1329 destRect.point.x = offset.x + mBitmapBounds[topBase].extent.x; 1330 destRect.point.y = offset.y; 1331 destRect.extent.x = getWidth() - mBitmapBounds[topBase].extent.x - mBitmapBounds[topBase + 1].extent.x; 1332 destRect.extent.y = mBitmapBounds[topBase + 2].extent.y; 1333 RectI stretchRect = mBitmapBounds[topBase + 2]; 1334 stretchRect.inset(1,0); 1335 drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); 1336 1337 destRect.point.x = offset.x; 1338 destRect.point.y = offset.y + mBitmapBounds[topBase].extent.y; 1339 destRect.extent.x = mBitmapBounds[BorderLeft].extent.x; 1340 destRect.extent.y = getHeight() - mBitmapBounds[topBase].extent.y - mBitmapBounds[BorderBottomLeft].extent.y; 1341 stretchRect = mBitmapBounds[BorderLeft]; 1342 stretchRect.inset(0,1); 1343 drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); 1344 1345 destRect.point.x = offset.x + getWidth() - mBitmapBounds[BorderRight].extent.x; 1346 destRect.extent.x = mBitmapBounds[BorderRight].extent.x; 1347 destRect.point.y = offset.y + mBitmapBounds[topBase + 1].extent.y; 1348 destRect.extent.y = getHeight() - mBitmapBounds[topBase + 1].extent.y - mBitmapBounds[BorderBottomRight].extent.y; 1349 1350 stretchRect = mBitmapBounds[BorderRight]; 1351 stretchRect.inset(0,1); 1352 drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); 1353 1354 drawUtil->drawBitmapSR(mTextureObject, offset + Point2I(0, getHeight() - mBitmapBounds[BorderBottomLeft].extent.y), mBitmapBounds[BorderBottomLeft]); 1355 drawUtil->drawBitmapSR(mTextureObject, offset + getExtent() - mBitmapBounds[BorderBottomRight].extent, mBitmapBounds[BorderBottomRight]); 1356 1357 destRect.point.x = offset.x + mBitmapBounds[BorderBottomLeft].extent.x; 1358 destRect.extent.x = getWidth() - mBitmapBounds[BorderBottomLeft].extent.x - mBitmapBounds[BorderBottomRight].extent.x; 1359 1360 destRect.point.y = offset.y + getHeight() - mBitmapBounds[BorderBottom].extent.y; 1361 destRect.extent.y = mBitmapBounds[BorderBottom].extent.y; 1362 stretchRect = mBitmapBounds[BorderBottom]; 1363 stretchRect.inset(1,0); 1364 1365 drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); 1366 1367 // Draw the title 1368 // dhc addition: copied/modded from renderJustifiedText, since we enforce a 1369 // different color usage here. NOTE: it currently CAN overdraw the controls 1370 // if mis-positioned or 'scrunched' in a small width. 1371 drawUtil->setBitmapModulation(mProfile->mFontColor); 1372 S32 textWidth = mProfile->mFont->getStrWidth((const UTF8 *)mText); 1373 Point2I start(0,0); 1374 1375 // Align the horizontal 1376 if ( mProfile->mAlignment == GuiControlProfile::RightJustify ) 1377 start.set( winRect.extent.x - textWidth, 0 ); 1378 else if ( mProfile->mAlignment == GuiControlProfile::CenterJustify ) 1379 start.set( ( winRect.extent.x - textWidth) / 2, 0 ); 1380 else // GuiControlProfile::LeftJustify or garbage... ;) 1381 start.set( 0, 0 ); 1382 // If the text is longer then the box size, (it'll get clipped) so force Left Justify 1383 if( textWidth > winRect.extent.x ) start.set( 0, 0 ); 1384 // center the vertical 1385// start.y = ( winRect.extent.y - ( font->getHeight() - 2 ) ) / 2; 1386 drawUtil->drawText( mProfile->mFont, start + offset + mProfile->mTextOffset, mText ); 1387 1388 // Deal with rendering the titlebar controls 1389 AssertFatal(root, "Unable to get the root GuiCanvas."); 1390 1391 // Draw the close button 1392 Point2I tempUL; 1393 Point2I tempLR; 1394 S32 bmp = BmpStates * BmpClose; 1395 1396 if( mCanClose ) { 1397 if( mCloseButton.pointInRect( mMousePosition ) ) 1398 { 1399 if( mCloseButtonPressed ) 1400 bmp += BmpDown; 1401 else 1402 bmp += BmpHilite; 1403 } 1404 1405 drawUtil->clearBitmapModulation(); 1406 drawUtil->drawBitmapSR(mTextureObject, offset + mCloseButton.point, mBitmapBounds[bmp]); 1407 } 1408 1409 // Draw the maximize button 1410 if( mMaximized ) 1411 bmp = BmpStates * BmpNormal; 1412 else 1413 bmp = BmpStates * BmpMaximize; 1414 1415 if( mCanMaximize ) { 1416 if( mMaximizeButton.pointInRect( mMousePosition ) ) 1417 { 1418 if( mMaximizeButtonPressed ) 1419 bmp += BmpDown; 1420 else 1421 bmp += BmpHilite; 1422 } 1423 1424 drawUtil->clearBitmapModulation(); 1425 drawUtil->drawBitmapSR( mTextureObject, offset + mMaximizeButton.point, mBitmapBounds[bmp] ); 1426 } 1427 1428 // Draw the minimize button 1429 if( mMinimized ) 1430 bmp = BmpStates * BmpNormal; 1431 else 1432 bmp = BmpStates * BmpMinimize; 1433 1434 if( mCanMinimize ) { 1435 if( mMinimizeButton.pointInRect( mMousePosition ) ) 1436 { 1437 if( mMinimizeButtonPressed ) 1438 bmp += BmpDown; 1439 else 1440 bmp += BmpHilite; 1441 } 1442 1443 drawUtil->clearBitmapModulation(); 1444 drawUtil->drawBitmapSR( mTextureObject, offset + mMinimizeButton.point, mBitmapBounds[bmp] ); 1445 } 1446 1447 if( !mMinimized ) 1448 { 1449 // Render the children 1450 renderChildControls( offset, updateRect ); 1451 } 1452} 1453 1454//============================================================================= 1455// Misc. 1456//============================================================================= 1457// MARK: ---- Misc ---- 1458 1459//----------------------------------------------------------------------------- 1460 1461const RectI GuiWindowCtrl::getClientRect() 1462{ 1463 if( !mProfile || mProfile->mBitmapArrayRects.size() < NumBitmaps ) 1464 return Parent::getClientRect(); 1465 1466 if( !mBitmapBounds ) 1467 mBitmapBounds = mProfile->mBitmapArrayRects.address(); 1468 1469 RectI winRect; 1470 winRect.point.x = mBitmapBounds[BorderLeft].extent.x; 1471 winRect.point.y = mBitmapBounds[BorderTopKey].extent.y; 1472 1473 winRect.extent.y = getHeight() - ( winRect.point.y + mBitmapBounds[BorderBottom].extent.y ); 1474 winRect.extent.x = getWidth() - ( winRect.point.x + mBitmapBounds[BorderRight].extent.x ); 1475 1476 // Finally, inset it by padding 1477 // Inset by padding. margin is specified for all t/b/l/r but 1478 // uses only pointx pointy uniformly on both ends. This should be fixed. - JDD 1479 // winRect.inset( mSizingOptions.mPadding.point.x, mSizingOptions.mPadding.point.y ); 1480 1481 return winRect; 1482} 1483 1484//----------------------------------------------------------------------------- 1485 1486bool GuiWindowCtrl::isMinimized(S32 &index) 1487{ 1488 index = mMinimizeIndex; 1489 return mMinimized && mVisible; 1490} 1491 1492//----------------------------------------------------------------------------- 1493 1494void GuiWindowCtrl::positionButtons(void) 1495{ 1496 if( !mBitmapBounds || !mAwake ) 1497 return; 1498 1499 S32 buttonWidth = mBitmapBounds[BmpStates * BmpClose].extent.x; 1500 S32 buttonHeight = mBitmapBounds[BmpStates * BmpClose].extent.y; 1501 Point2I mainOff = mProfile->mTextOffset; 1502 1503 // Until a pref, if alignment is LEFT, put buttons RIGHT justified. 1504 // ELSE, put buttons LEFT justified. 1505 S32 closeLeft = mainOff.x, closeTop = mainOff.y, closeOff = buttonWidth + 2; 1506 if ( mProfile->mAlignment == GuiControlProfile::LeftJustify ) 1507 { 1508 closeOff = -closeOff; 1509 closeLeft = getWidth() - buttonWidth - mainOff.x; 1510 } 1511 RectI closeRect(closeLeft, closeTop, buttonHeight, buttonWidth); 1512 mCloseButton = closeRect; 1513 1514 // Always put Minimize on left side of Maximize. 1515 closeRect.point.x += closeOff; 1516 if (closeOff>0) 1517 { 1518 mMinimizeButton = closeRect; 1519 closeRect.point.x += closeOff; 1520 mMaximizeButton = closeRect; 1521 } 1522 else 1523 { 1524 mMaximizeButton = closeRect; 1525 closeRect.point.x += closeOff; 1526 mMinimizeButton = closeRect; 1527 } 1528} 1529 1530//----------------------------------------------------------------------------- 1531 1532void GuiWindowCtrl::setCloseCommand(const char *newCmd) 1533{ 1534 if (newCmd) 1535 mCloseCommand = StringTable->insert(newCmd); 1536 else 1537 mCloseCommand = StringTable->EmptyString(); 1538} 1539 1540//----------------------------------------------------------------------------- 1541 1542GuiControl* GuiWindowCtrl::findHitControl(const Point2I &pt, S32 initialLayer) 1543{ 1544 if (! mMinimized) 1545 return Parent::findHitControl(pt, initialLayer); 1546 else 1547 return this; 1548} 1549 1550//----------------------------------------------------------------------------- 1551 1552bool GuiWindowCtrl::resize(const Point2I &newPosition, const Point2I &newExtent) 1553{ 1554 if( !Parent::resize(newPosition, newExtent) ) 1555 return false; 1556 1557 // Set the button coords 1558 positionButtons(); 1559 1560 return true; 1561} 1562 1563//----------------------------------------------------------------------------- 1564 1565GuiControl *GuiWindowCtrl::findNextTabable(GuiControl *curResponder, bool firstCall) 1566{ 1567 // Set the global if this is the first call (directly from the canvas) 1568 if (firstCall) 1569 { 1570 GuiControl::smCurResponder = NULL; 1571 } 1572 1573 // If the window does not already contain the first responder, return false 1574 // ie. Can't tab into or out of a window 1575 if (! controlIsChild(curResponder)) 1576 { 1577 return NULL; 1578 } 1579 1580 // Loop through, checking each child to see if it is the one that follows the firstResponder 1581 GuiControl *tabCtrl = NULL; 1582 iterator i; 1583 for (i = begin(); i != end(); i++) 1584 { 1585 GuiControl *ctrl = static_cast<GuiControl *>(*i); 1586 tabCtrl = ctrl->findNextTabable(curResponder, false); 1587 if (tabCtrl) break; 1588 } 1589 1590 // To ensure the tab cycles within the current window... 1591 if (! tabCtrl) 1592 { 1593 tabCtrl = findFirstTabable(); 1594 } 1595 1596 mFirstResponder = tabCtrl; 1597 return tabCtrl; 1598} 1599 1600//----------------------------------------------------------------------------- 1601 1602GuiControl *GuiWindowCtrl::findPrevTabable(GuiControl *curResponder, bool firstCall) 1603{ 1604 if (firstCall) 1605 { 1606 GuiControl::smPrevResponder = NULL; 1607 } 1608 1609 // If the window does not already contain the first responder, return false 1610 // ie. Can't tab into or out of a window 1611 if (! controlIsChild(curResponder)) 1612 { 1613 return NULL; 1614 } 1615 1616 // Loop through, checking each child to see if it is the one that follows the firstResponder 1617 GuiControl *tabCtrl = NULL; 1618 iterator i; 1619 for (i = begin(); i != end(); i++) 1620 { 1621 GuiControl *ctrl = static_cast<GuiControl *>(*i); 1622 tabCtrl = ctrl->findPrevTabable(curResponder, false); 1623 if (tabCtrl) break; 1624 } 1625 1626 // To ensure the tab cycles within the current window... 1627 if (! tabCtrl) 1628 { 1629 tabCtrl = findLastTabable(); 1630 } 1631 1632 mFirstResponder = tabCtrl; 1633 return tabCtrl; 1634} 1635 1636//----------------------------------------------------------------------------- 1637 1638void GuiWindowCtrl::selectWindow(void) 1639{ 1640 // First make sure this window is the front most of its siblings 1641 GuiControl *parent = getParent(); 1642 if (parent && *parent->end() != this ) 1643 { 1644 // Valid collapse groups have to be selected together 1645 if( mCanCollapse && mCollapseGroup >= 0 ) 1646 { 1647 CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin(); 1648 for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ ) 1649 { 1650 parent->pushObjectToBack( (*iter) ); 1651 } 1652 } 1653 else 1654 { 1655 parent->pushObjectToBack( this ); 1656 } 1657 } 1658 1659 // Also set the first responder to be the one within this window 1660 setFirstResponder(mFirstResponder); 1661} 1662 1663//----------------------------------------------------------------------------- 1664 1665void GuiWindowCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent) 1666{ 1667 GuiCanvas *pRoot = getRoot(); 1668 if( !pRoot ) 1669 return; 1670 PlatformWindow *pWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow(); 1671 AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible."); 1672 PlatformCursorController *pController = pWindow->getCursorController(); 1673 AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!"); 1674 1675 S32 desiredCursor = PlatformCursorController::curArrow; 1676 S32 hitEdges = findHitEdges( lastGuiEvent.mousePoint ); 1677 1678 if( hitEdges & edgeBottom && hitEdges & edgeLeft && mResizeHeight ) 1679 desiredCursor = PlatformCursorController::curResizeNESW; 1680 else if( hitEdges & edgeBottom && hitEdges & edgeRight && mResizeHeight ) 1681 desiredCursor = PlatformCursorController::curResizeNWSE; 1682 else if( hitEdges & edgeBottom && mResizeHeight ) 1683 desiredCursor = PlatformCursorController::curResizeHorz; 1684 else if( hitEdges & edgeTop && hitEdges & edgeLeft && mResizeHeight ) 1685 desiredCursor = PlatformCursorController::curResizeNWSE; 1686 else if( hitEdges & edgeTop && hitEdges & edgeRight && mResizeHeight ) 1687 desiredCursor = PlatformCursorController::curResizeNESW; 1688 else if( hitEdges & edgeTop && mResizeHeight ) 1689 desiredCursor = PlatformCursorController::curResizeHorz; 1690 else if ( hitEdges & edgeLeft && mResizeWidth ) 1691 desiredCursor = PlatformCursorController::curResizeVert; 1692 else if( hitEdges & edgeRight && mResizeWidth ) 1693 desiredCursor = PlatformCursorController::curResizeVert; 1694 else 1695 desiredCursor = PlatformCursorController::curArrow; 1696 1697 // Bail if we're already at the desired cursor 1698 if(pRoot->mCursorChanged == desiredCursor ) 1699 return; 1700 1701 // Now change the cursor shape 1702 pController->popCursor(); 1703 pController->pushCursor(desiredCursor); 1704 pRoot->mCursorChanged = desiredCursor; 1705} 1706 1707//----------------------------------------------------------------------------- 1708 1709void GuiWindowCtrl::parentResized(const RectI &oldParentRect, const RectI &newParentRect) 1710{ 1711 if(!mCanResize) 1712 return; 1713 1714 GuiControl *parent = getParent(); 1715 if( !parent ) 1716 return; 1717 1718 // Bail if were not sized both by windowrelative 1719 if( mHorizSizing != horizResizeWindowRelative || mHorizSizing != vertResizeWindowRelative ) 1720 return Parent::parentResized( oldParentRect, newParentRect ); 1721 1722 Point2I newPosition = getPosition(); 1723 Point2I newExtent = getExtent(); 1724 1725 bool doCollapse = false; 1726 1727 S32 deltaX = newParentRect.extent.x - oldParentRect.extent.x; 1728 S32 deltaY = newParentRect.extent.y - oldParentRect.extent.y;// + mProfile->mYPositionOffset; 1729 1730 if( newPosition.x > ( oldParentRect.extent.x / 2 ) ) 1731 newPosition.x = newPosition.x + deltaX; 1732 1733 if (oldParentRect.extent.y != 0) 1734 { 1735 // Only if were apart of a group 1736 if ( mCanCollapse && mCollapseGroup >= 0 ) 1737 { 1738 // Setup parsing mechanisms 1739 CollapseGroupNumVec collapseGroupNumVec; 1740 1741 // Lets grab the information we need (this should probably be already stored on each individual window object) 1742 S32 groupNum = mCollapseGroup; 1743 S32 groupMax = parent->mCollapseGroupVec[ groupNum ].size() - 1; 1744 1745 // Set up vars that we're going to be using 1746 S32 groupPos = 0; 1747 S32 groupExtent = 0; 1748 S32 tempGroupExtent = 0; 1749 1750 // Set up the vector/iterator combo we need 1751 collapseGroupNumVec = parent->mCollapseGroupVec[ groupNum ]; 1752 CollapseGroupNumVec::iterator iter = collapseGroupNumVec.begin(); 1753 1754 // Grab some more information we need later ( this info should also be located on each ind. window object ) 1755 for( ; iter != collapseGroupNumVec.end(); iter++ ) 1756 { 1757 if((*iter)->getCollapseGroupNum() == 0) 1758 { 1759 groupPos = (*iter)->getPosition().y; 1760 } 1761 1762 groupExtent += (*iter)->getExtent().y; 1763 } 1764 1765 // Use the information we just gatherered; only enter this block if we need to 1766 tempGroupExtent = groupPos + groupExtent; 1767 if( tempGroupExtent > ( newParentRect.extent.y / 2 ) ) 1768 { 1769 // Lets size the collapse group 1770 S32 windowParser = groupMax; 1771 bool secondLoop = false; 1772 while( tempGroupExtent >= newParentRect.extent.y ) 1773 { 1774 1775 if( windowParser == -1) 1776 { 1777 if( !secondLoop ) 1778 { 1779 secondLoop = true; 1780 windowParser = groupMax; 1781 } 1782 else 1783 break; 1784 } 1785 1786 GuiWindowCtrl *tempWindow = collapseGroupNumVec[windowParser]; 1787 if(tempWindow->mIsCollapsed) 1788 { 1789 windowParser--; 1790 continue; 1791 } 1792 1793 // Resizing block for the loop... if we can get away with just resizing the bottom window do it before 1794 // resizing the whole block. else, go through the group and start setting min extents. if that doesnt work 1795 // on the second go around, start collpsing the windows. 1796 if( tempGroupExtent - ( tempWindow->getExtent().y - tempWindow->getMinExtent().y ) <= newParentRect.extent.y ) 1797 { 1798 if( this == tempWindow ) 1799 newExtent.y = newExtent.y - ( tempGroupExtent - newParentRect.extent.y ); 1800 1801 tempGroupExtent = tempGroupExtent - newParentRect.extent.y; 1802 } 1803 else 1804 { 1805 if( secondLoop ) 1806 { 1807 tempGroupExtent = tempGroupExtent - (tempWindow->getExtent().y + 32); 1808 1809 if( this == tempWindow ) 1810 doCollapse = true; 1811 } 1812 else 1813 { 1814 tempGroupExtent = tempGroupExtent - (tempWindow->getExtent().y - tempWindow->getMinExtent().y); 1815 if( this == tempWindow ) 1816 newExtent.y = tempWindow->getMinExtent().y; 1817 } 1818 } 1819 windowParser--; 1820 } 1821 } 1822 } 1823 else if( newPosition.y > ( oldParentRect.extent.y / 2 ) ) 1824 { 1825 newPosition.y = newPosition.y + deltaY - 1; 1826 } 1827 } 1828 1829 if( newExtent.x >= getMinExtent().x && newExtent.y >= getMinExtent().y ) 1830 { 1831 // If we are already outside the reach of the main window, lets not place ourselves 1832 // further out; but if were trying to improve visibility, go for it 1833 // note: small tolerance (4) added to keep guis that are very slightly outside 1834 // the main window (like all of the editor windows!) from appearing to 'undock' 1835 // when the resolution is changed. 1836 if( (newPosition.x + newExtent.x) > newParentRect.extent.x + 4 ) 1837 { 1838 if( (newPosition.x + newExtent.x) > (getPosition().x + getExtent().x) ) 1839 { 1840 newPosition.x = getPosition().x; 1841 newExtent.x = getExtent().x; 1842 } 1843 } 1844 if( (newPosition.y + newExtent.y) > newParentRect.extent.y + 4) 1845 { 1846 if( (newPosition.y + newExtent.y) > (getPosition().y + getExtent().y) ) 1847 { 1848 newPosition.y = getPosition().y; 1849 newExtent.y = getExtent().y; 1850 } 1851 } 1852 1853 // Only for collpasing groups, if were not, then do it like normal windows 1854 if( mCanCollapse && mCollapseGroup >= 0 ) 1855 { 1856 bool resizeMe = false; 1857 1858 // Only the group window should control positioning 1859 if( mCollapseGroupNum == 0 ) 1860 { 1861 resizeMe = resizeCollapseGroup( true, true, (getPosition() - newPosition), (getExtent() - newExtent) ); 1862 if(resizeMe == true) 1863 resize(newPosition, newExtent); 1864 } 1865 else if( getExtent() != newExtent) 1866 { 1867 resizeMe = resizeCollapseGroup( false, true, (getPosition() - newPosition), (getExtent() - newExtent) ); 1868 if(resizeMe == true) 1869 resize(getPosition(), newExtent); 1870 } 1871 } 1872 else 1873 { 1874 resize(newPosition, newExtent); 1875 } 1876 } 1877 1878 if( mCanCollapse && !mIsCollapsed && doCollapse ) 1879 toggleCollapseGroup(); 1880 1881 // If docking is invalid on this control, then bail out here 1882 if( getDocking() & Docking::dockInvalid || getDocking() & Docking::dockNone) 1883 return; 1884 1885 // Update Self 1886 RectI oldThisRect = getBounds(); 1887 anchorControl( this, Point2I( deltaX, deltaY ) ); 1888 RectI newThisRect = getBounds(); 1889 1890 // Update Deltas to pass on to children 1891 deltaX = newThisRect.extent.x - oldThisRect.extent.x; 1892 deltaY = newThisRect.extent.y - oldThisRect.extent.y; 1893 1894 // Iterate over all children and update their anchors 1895 iterator nI = begin(); 1896 for( ; nI != end(); nI++ ) 1897 { 1898 // Sanity 1899 GuiControl *control = dynamic_cast<GuiControl*>( (*nI) ); 1900 if( control ) 1901 control->parentResized( oldThisRect, newThisRect ); 1902 } 1903} 1904 1905//============================================================================= 1906// Console Methods. 1907//============================================================================= 1908// MARK: ---- Console Methods ---- 1909 1910//----------------------------------------------------------------------------- 1911 1912DefineEngineMethod( GuiWindowCtrl, selectWindow, void, (),, 1913 "Bring the window to the front." ) 1914{ 1915 object->selectWindow(); 1916} 1917 1918//----------------------------------------------------------------------------- 1919 1920DefineEngineMethod( GuiWindowCtrl, setCollapseGroup, void, ( bool state ),, 1921 "Set the window's collapsing state." ) 1922{ 1923 object->setCollapseGroup(state); 1924} 1925 1926//----------------------------------------------------------------------------- 1927 1928DefineEngineMethod( GuiWindowCtrl, toggleCollapseGroup, void, (),, 1929 "Toggle the window collapsing." ) 1930{ 1931 object->toggleCollapseGroup(); 1932} 1933 1934//----------------------------------------------------------------------------- 1935 1936DefineEngineMethod( GuiWindowCtrl, attachTo, void, ( GuiWindowCtrl* window ),, 1937 "" ) 1938{ 1939 object->moveToCollapseGroup( window, 1 ); 1940} 1941 1942//----------------------------------------------------------------------------- 1943 1944DefineEngineStaticMethod( GuiWindowCtrl, attach, void, ( GuiWindowCtrl* bottomWindow, GuiWindowCtrl* topWindow ),, 1945 "Attach @a bottomWindow to @topWindow so that @a bottomWindow moves along with @a topWindow when it is dragged.\n\n" 1946 "@param bottomWindow \n" 1947 "@param topWindow " ) 1948{ 1949 if(bottomWindow == NULL || topWindow == NULL) 1950 { 1951 Con::warnf("Warning: AttachWindows - could not find windows"); 1952 return; 1953 } 1954 1955 bottomWindow->moveToCollapseGroup( topWindow, 1 ); 1956} 1957