2012-09-19 15:15:01 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Copyright (c) 2012 GarageGames, LLC
|
|
|
|
|
//
|
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
// of this software and associated documentation files (the "Software"), to
|
|
|
|
|
// deal in the Software without restriction, including without limitation the
|
|
|
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
|
//
|
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
|
//
|
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
|
// IN THE SOFTWARE.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
#include "platform/platform.h"
|
|
|
|
|
#include "gui/containers/guiWindowCtrl.h"
|
|
|
|
|
|
|
|
|
|
#include "console/consoleTypes.h"
|
|
|
|
|
#include "console/console.h"
|
|
|
|
|
#include "console/engineAPI.h"
|
|
|
|
|
#include "gui/core/guiCanvas.h"
|
|
|
|
|
#include "gui/core/guiDefaultControlRender.h"
|
|
|
|
|
#include "gfx/gfxDevice.h"
|
|
|
|
|
#include "gfx/gfxDrawUtil.h"
|
|
|
|
|
#include "gui/containers/guiRolloutCtrl.h"
|
2019-11-18 09:30:04 +00:00
|
|
|
#include "gui/editor/guiMenuBar.h"
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
IMPLEMENT_CONOBJECT( GuiWindowCtrl );
|
|
|
|
|
|
|
|
|
|
ConsoleDocClass( GuiWindowCtrl,
|
|
|
|
|
"@brief A window with a title bar and an optional set of buttons.\n\n"
|
|
|
|
|
|
|
|
|
|
"The GuiWindowCtrl class implements windows that can be freely placed within the render window. Additionally, "
|
|
|
|
|
"the windows can be resized and maximized/minimized.\n\n"
|
|
|
|
|
|
|
|
|
|
"@tsexample\n"
|
|
|
|
|
"new GuiWindowCtrl( MyWindow )\n"
|
|
|
|
|
"{\n"
|
|
|
|
|
" text = \"My Window\"; // The text that is displayed on the title bar.\n"
|
|
|
|
|
" resizeWidth = true; // Allow horizontal resizing by user via mouse.\n"
|
|
|
|
|
" resizeHeight = true; // Allow vertical resizing by user via mouse.\n"
|
|
|
|
|
" canClose = true; // Display a close button in the title bar.\n"
|
|
|
|
|
" canMinimize = true; // Display a minimize button in the title bar.\n"
|
|
|
|
|
" canMaximize = true; // Display a maximize button in the title bar.\n"
|
|
|
|
|
"};\n"
|
|
|
|
|
"@endtsexample\n\n"
|
|
|
|
|
|
|
|
|
|
"@ingroup GuiContainers"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
IMPLEMENT_CALLBACK( GuiWindowCtrl, onClose, void, (), (),
|
|
|
|
|
"Called when the close button has been pressed." );
|
|
|
|
|
IMPLEMENT_CALLBACK( GuiWindowCtrl, onMinimize, void, (), (),
|
|
|
|
|
"Called when the window has been minimized." );
|
|
|
|
|
IMPLEMENT_CALLBACK( GuiWindowCtrl, onMaximize, void, (), (),
|
|
|
|
|
"Called when the window has been maximized." );
|
|
|
|
|
IMPLEMENT_CALLBACK( GuiWindowCtrl, onCollapse, void, (), (),
|
|
|
|
|
"Called when the window is collapsed by clicking its title bar." );
|
|
|
|
|
IMPLEMENT_CALLBACK( GuiWindowCtrl, onRestore, void, (), (),
|
|
|
|
|
"Called when the window is restored from minimized, maximized, or collapsed state." );
|
Adjusted handling for the bitmap and bitmapAsset fields for guiBitmapButtonCtrl to forcefully update the button states when changed, ensuring that the bitmaps refresh when changed via the field
Added callback for onResize to guiWindowCtrl so controls - such as the EditorTree - can be properly resized in certain circumstances when the window is changed
Added getIncrement() and getRange() to GuiGameSettingsCtrl to better facilitate options manipulation on the script side
Corrected some of the console method documentation strings in GuiGameSettingsCtrl
Removed some unneeded, extraneous files and their asset definitions that came from odd import conversions. Where applicable, created cleaned up versions to make naming conventions and references stable
Fixed canvas mode update text typo: FSAA -> FXAA
Added logic to DOF, Light Rays, SSAO and Vignette postFX's to check both the preset setting AND the user preference before enabling.
Shifted initialization order so PostFX's are loaded before we configure the canvas, to ensure stuff like the FXAAPostFX exists and can be toggled on on load
Fixed multiple issues with options menu:
When using gamepad, unable to navigate from categories to options. Fixed so can now traverse as normal
Input limitations on gamepad necessitated changing of how setting applying happens, is now done as a 'apply or discard' prompt when leaving the options menu
Added proper handling for adjusting settings with gamepad with left/right inputs
Fixed issue where the unapplied change for an option was sometimes being processed as an object name rather than an implicit string. Now made to be explicit strings to avoid issue.
Made the menu button input for "Select" to go from categories to options gamepad only, and hidden when in the options list
Fixed issue where changing window mode didn't correctly affect resolution option. Now set up so changing this field correctly refreshes the resolution option. Specifically, when on borderless, the resolution field does not show, preventing confusion as it is always full resolution
Generally have the options list refresh when changes happen to allow any and all fields to be able to dynamically respond to other options having changed improving flexibility.
Cleaned up old, unused, commented out functions
Added ability on OKCancel message boxes to override the button text if needed
Fixed issue with AssetBrowser where the shrink/grow icons next to the preview size slider were not anchored correctly.
Adjusted callback logic so if preview slider is clicked on, rather than dragged, it will correctly update the zoom values
Added sorting to Modules List dropdown for the AssetBrowser
Improved standardization of double-clicking in AssetBrowser. Now defaults to editing action if regularly browsing and selecting if in select mode. Still allows regular per-type overrides as normal
Moved definition of GuiDisabledTextEditProfile to gui profiles.ed.tscript file, removed duplicates to stop error spam
Adjusted default settings value for double-click action in AB to be edit to prevent unstable behavior
Removed old file refs from Load Recent list in the default settings
2022-03-27 03:36:37 +00:00
|
|
|
IMPLEMENT_CALLBACK(GuiWindowCtrl, onResize, void, (S32 posX, S32 posY, S32 width, S32 height), (0, 0, 0, 0),
|
|
|
|
|
"Called when the window is resized in a regular manner by mouse manipulation.");
|
2023-09-05 03:50:45 +00:00
|
|
|
IMPLEMENT_CALLBACK(GuiWindowCtrl, onMouseDragged, void, (), (),
|
|
|
|
|
"Called when the height has changed.");
|
Adjusted handling for the bitmap and bitmapAsset fields for guiBitmapButtonCtrl to forcefully update the button states when changed, ensuring that the bitmaps refresh when changed via the field
Added callback for onResize to guiWindowCtrl so controls - such as the EditorTree - can be properly resized in certain circumstances when the window is changed
Added getIncrement() and getRange() to GuiGameSettingsCtrl to better facilitate options manipulation on the script side
Corrected some of the console method documentation strings in GuiGameSettingsCtrl
Removed some unneeded, extraneous files and their asset definitions that came from odd import conversions. Where applicable, created cleaned up versions to make naming conventions and references stable
Fixed canvas mode update text typo: FSAA -> FXAA
Added logic to DOF, Light Rays, SSAO and Vignette postFX's to check both the preset setting AND the user preference before enabling.
Shifted initialization order so PostFX's are loaded before we configure the canvas, to ensure stuff like the FXAAPostFX exists and can be toggled on on load
Fixed multiple issues with options menu:
When using gamepad, unable to navigate from categories to options. Fixed so can now traverse as normal
Input limitations on gamepad necessitated changing of how setting applying happens, is now done as a 'apply or discard' prompt when leaving the options menu
Added proper handling for adjusting settings with gamepad with left/right inputs
Fixed issue where the unapplied change for an option was sometimes being processed as an object name rather than an implicit string. Now made to be explicit strings to avoid issue.
Made the menu button input for "Select" to go from categories to options gamepad only, and hidden when in the options list
Fixed issue where changing window mode didn't correctly affect resolution option. Now set up so changing this field correctly refreshes the resolution option. Specifically, when on borderless, the resolution field does not show, preventing confusion as it is always full resolution
Generally have the options list refresh when changes happen to allow any and all fields to be able to dynamically respond to other options having changed improving flexibility.
Cleaned up old, unused, commented out functions
Added ability on OKCancel message boxes to override the button text if needed
Fixed issue with AssetBrowser where the shrink/grow icons next to the preview size slider were not anchored correctly.
Adjusted callback logic so if preview slider is clicked on, rather than dragged, it will correctly update the zoom values
Added sorting to Modules List dropdown for the AssetBrowser
Improved standardization of double-clicking in AssetBrowser. Now defaults to editing action if regularly browsing and selecting if in select mode. Still allows regular per-type overrides as normal
Moved definition of GuiDisabledTextEditProfile to gui profiles.ed.tscript file, removed duplicates to stop error spam
Adjusted default settings value for double-click action in AB to be edit to prevent unstable behavior
Removed old file refs from Load Recent list in the default settings
2022-03-27 03:36:37 +00:00
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
GuiWindowCtrl::GuiWindowCtrl()
|
2017-04-11 04:45:02 +00:00
|
|
|
: mResizeWidth(true),
|
|
|
|
|
mResizeEdge(edgeNone),
|
2012-09-19 15:15:01 +00:00
|
|
|
mResizeHeight(true),
|
|
|
|
|
mCanMove(true),
|
2023-12-29 18:15:30 +00:00
|
|
|
mResizeMargin(5.f),
|
2012-09-19 15:15:01 +00:00
|
|
|
mCanClose(true),
|
|
|
|
|
mCanMinimize(true),
|
|
|
|
|
mCanMaximize(true),
|
|
|
|
|
mCanCollapse(false),
|
2017-04-11 04:45:02 +00:00
|
|
|
mCanDock(false),
|
2012-09-19 15:15:01 +00:00
|
|
|
mEdgeSnap(true),
|
|
|
|
|
mCollapseGroup(-1),
|
|
|
|
|
mCollapseGroupNum(-1),
|
|
|
|
|
mIsCollapsed(false),
|
2023-09-05 03:50:45 +00:00
|
|
|
mIsMouseResizing(false),
|
|
|
|
|
mButtonOffset(0, 3)
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
|
|
|
|
// mTitleHeight will change in instanciation most likely...
|
|
|
|
|
mTitleHeight = 24;
|
|
|
|
|
|
|
|
|
|
mIsContainer = true;
|
|
|
|
|
|
|
|
|
|
mCloseCommand = StringTable->EmptyString();
|
|
|
|
|
|
|
|
|
|
mMinimized = false;
|
|
|
|
|
mMaximized = false;
|
|
|
|
|
mMouseMovingWin = false;
|
|
|
|
|
mMouseResizeWidth = false;
|
|
|
|
|
mMouseResizeHeight = false;
|
|
|
|
|
mMinimizeIndex = -1;
|
|
|
|
|
mTabIndex = -1;
|
|
|
|
|
mBitmapBounds = NULL;
|
2014-11-07 15:25:10 +00:00
|
|
|
setExtent(100, 200);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
RectI closeRect(80, 2, 16, 16);
|
|
|
|
|
mCloseButton = closeRect;
|
|
|
|
|
closeRect.point.x -= 18;
|
|
|
|
|
mMaximizeButton = closeRect;
|
|
|
|
|
closeRect.point.x -= 18;
|
|
|
|
|
mMinimizeButton = closeRect;
|
|
|
|
|
|
|
|
|
|
// Other defaults
|
|
|
|
|
mActive = true;
|
|
|
|
|
mCloseButtonPressed = false;
|
|
|
|
|
mMaximizeButtonPressed = false;
|
|
|
|
|
mMinimizeButtonPressed = false;
|
|
|
|
|
|
2020-05-11 20:03:27 +00:00
|
|
|
mRepositionWindow = false;
|
|
|
|
|
mResizeWindow = false;
|
|
|
|
|
mSnapSignal = false;
|
|
|
|
|
mPreCollapsedYExtent = 200;
|
|
|
|
|
mPreCollapsedYMinExtent = 176;
|
2012-09-19 15:15:01 +00:00
|
|
|
mText = "New Window";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::initPersistFields()
|
|
|
|
|
{
|
2023-01-27 07:13:15 +00:00
|
|
|
docsURL;
|
2012-09-19 15:15:01 +00:00
|
|
|
addGroup( "Window" );
|
|
|
|
|
|
|
|
|
|
addField( "text", TypeRealString, Offset( mText, GuiWindowCtrl ),
|
|
|
|
|
"Text label to display in titlebar." );
|
|
|
|
|
addField( "resizeWidth", TypeBool, Offset( mResizeWidth, GuiWindowCtrl ),
|
|
|
|
|
"Whether the window can be resized horizontally." );
|
|
|
|
|
addField( "resizeHeight", TypeBool, Offset( mResizeHeight, GuiWindowCtrl ),
|
|
|
|
|
"Whether the window can be resized vertically." );
|
2023-12-29 18:15:30 +00:00
|
|
|
addField("resizeMargin", TypeF32, Offset(mResizeMargin, GuiWindowCtrl),
|
|
|
|
|
"Margin along the window edge to allow grabbing.");
|
2012-09-19 15:15:01 +00:00
|
|
|
addField( "canMove", TypeBool, Offset( mCanMove, GuiWindowCtrl ),
|
|
|
|
|
"Whether the window can be moved by dragging its titlebar." );
|
|
|
|
|
addField( "canClose", TypeBool, Offset( mCanClose, GuiWindowCtrl ),
|
|
|
|
|
"Whether the window has a close button." );
|
|
|
|
|
addField( "canMinimize", TypeBool, Offset( mCanMinimize, GuiWindowCtrl ),
|
|
|
|
|
"Whether the window has a minimize button." );
|
|
|
|
|
addField( "canMaximize", TypeBool, Offset( mCanMaximize, GuiWindowCtrl ),
|
|
|
|
|
"Whether the window has a maximize button." );
|
|
|
|
|
addField( "canCollapse", TypeBool, Offset( mCanCollapse, GuiWindowCtrl ),
|
|
|
|
|
"Whether the window can be collapsed by clicking its title bar." );
|
|
|
|
|
addField( "closeCommand", TypeString, Offset( mCloseCommand, GuiWindowCtrl ),
|
|
|
|
|
"Script code to execute when the window is closed." );
|
|
|
|
|
addField( "edgeSnap", TypeBool, Offset( mEdgeSnap,GuiWindowCtrl ),
|
|
|
|
|
"If true, the window will snap to the edges of other windows when moved close to them." );
|
2023-09-05 03:50:45 +00:00
|
|
|
addField( "buttonOffset", TypePoint2I, Offset (mButtonOffset, GuiWindowCtrl),
|
|
|
|
|
"Margin between window edge and the button(s).");
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
endGroup( "Window" );
|
|
|
|
|
|
|
|
|
|
Parent::initPersistFields();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
// Collapsing.
|
|
|
|
|
//=============================================================================
|
|
|
|
|
// MARK: ---- Collapsing ----
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::moveFromCollapseGroup()
|
|
|
|
|
{
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
S32 groupVec = mCollapseGroup;
|
|
|
|
|
S32 vecPos = mCollapseGroupNum;
|
|
|
|
|
S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1;
|
|
|
|
|
|
|
|
|
|
CollapseGroupNumVec collapseGroupNumVec;
|
|
|
|
|
|
|
|
|
|
if( groupVecCount > vecPos )
|
|
|
|
|
{
|
|
|
|
|
if (vecPos == 1)
|
|
|
|
|
{
|
|
|
|
|
CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin();
|
|
|
|
|
for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
if((*iter)->mCollapseGroupNum >= vecPos)
|
|
|
|
|
collapseGroupNumVec.push_back((*iter));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1;
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1;
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].erase(U32(0));
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].setSize(groupVecCount - 1);
|
|
|
|
|
parent->mCollapseGroupVec.erase(groupVec);
|
|
|
|
|
if(groupVec > 0)
|
|
|
|
|
parent->mCollapseGroupVec.setSize(groupVec);
|
|
|
|
|
|
|
|
|
|
parent->mCollapseGroupVec.push_back( collapseGroupNumVec );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Iterate through the group i was once in, gather myself and the controls below me and store them in an array
|
|
|
|
|
CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin();
|
|
|
|
|
for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
if((*iter)->mCollapseGroupNum >= vecPos)
|
|
|
|
|
collapseGroupNumVec.push_back((*iter));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Iterate through the newly created array; delete my references in the old group, create a new group and organize me accord.
|
|
|
|
|
S32 assignWindowNumber = 0;
|
|
|
|
|
CollapseGroupNumVec::iterator iter2 = collapseGroupNumVec.begin();
|
|
|
|
|
for(; iter2 != collapseGroupNumVec.end(); iter2++ )
|
|
|
|
|
{
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].pop_back();
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].setSize(groupVecCount);
|
|
|
|
|
(*iter2)->mCollapseGroup = (parent->mCollapseGroupVec.size());
|
|
|
|
|
(*iter2)->mCollapseGroupNum = assignWindowNumber;
|
|
|
|
|
|
|
|
|
|
assignWindowNumber++;
|
|
|
|
|
groupVecCount--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent->mCollapseGroupVec.push_back( collapseGroupNumVec );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].erase(mCollapseGroupNum);
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].setSize(groupVecCount);
|
|
|
|
|
mCollapseGroup = -1;
|
|
|
|
|
mCollapseGroupNum = -1;
|
|
|
|
|
|
|
|
|
|
if(groupVecCount <= 1)
|
|
|
|
|
{
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1;
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1;
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].erase(U32(0));
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].setSize(groupVecCount - 1);
|
2017-01-12 04:34:46 +00:00
|
|
|
parent->mCollapseGroupVec.erase(groupVec);
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Re id collapse groups
|
|
|
|
|
refreshCollapseGroups();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::moveToCollapseGroup(GuiWindowCtrl* hitWindow, bool orientation )
|
|
|
|
|
{
|
|
|
|
|
// Orientation 0 - window in question is being connected to top of another window
|
|
|
|
|
// Orientation 1 - window in question is being connected to bottom of another window
|
|
|
|
|
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
S32 groupVec = mCollapseGroup;
|
|
|
|
|
S32 attatchedGroupVec = hitWindow->mCollapseGroup;
|
|
|
|
|
S32 vecPos = mCollapseGroupNum;
|
|
|
|
|
|
|
|
|
|
if(mCollapseGroup == attatchedGroupVec && vecPos != -1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
CollapseGroupNumVec collapseGroupNumVec;
|
|
|
|
|
|
|
|
|
|
// Window colliding with is not in a collapse group
|
|
|
|
|
if(hitWindow->mCollapseGroup < 0)
|
|
|
|
|
{
|
|
|
|
|
// We(the collider) are in a group of windows
|
|
|
|
|
if(mCollapseGroup >= 0)
|
|
|
|
|
{
|
|
|
|
|
S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1;
|
|
|
|
|
|
|
|
|
|
// Copy pointer window data in my array, and store in a temp array
|
|
|
|
|
CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin();
|
|
|
|
|
for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
if((*iter)->mCollapseGroupNum >= vecPos)
|
|
|
|
|
{
|
|
|
|
|
collapseGroupNumVec.push_back((*iter));
|
|
|
|
|
groupVecCount--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Kill my old array group and erase its footprints
|
|
|
|
|
if( vecPos <= 1 || groupVecCount == 0 )
|
|
|
|
|
{
|
|
|
|
|
// Isn't covered in the renumbering of groups, so we it here
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1;
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1;
|
|
|
|
|
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].clear();
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].setSize(U32(0));
|
|
|
|
|
parent->mCollapseGroupVec.erase(groupVec);
|
|
|
|
|
|
|
|
|
|
if(groupVec > 0)
|
|
|
|
|
parent->mCollapseGroupVec.setSize(groupVec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Push the collided window
|
|
|
|
|
if(orientation == 0)
|
|
|
|
|
collapseGroupNumVec.push_back(hitWindow);
|
|
|
|
|
else
|
|
|
|
|
collapseGroupNumVec.push_front(hitWindow);
|
|
|
|
|
|
|
|
|
|
// Push the temp array in the guiControl array holder
|
|
|
|
|
parent->mCollapseGroupVec.push_back( collapseGroupNumVec );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(orientation == 0)
|
|
|
|
|
{
|
|
|
|
|
collapseGroupNumVec.push_front(hitWindow);
|
|
|
|
|
collapseGroupNumVec.push_front(this);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
collapseGroupNumVec.push_front(this);
|
|
|
|
|
collapseGroupNumVec.push_front(hitWindow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent->mCollapseGroupVec.push_back( collapseGroupNumVec );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // Window colliding with *IS* in a collapse group
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if(mCollapseGroup >= 0)
|
|
|
|
|
{
|
|
|
|
|
S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1;
|
|
|
|
|
|
|
|
|
|
CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin();
|
|
|
|
|
for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
if((*iter)->mCollapseGroupNum >= vecPos)
|
|
|
|
|
{
|
|
|
|
|
// Push back the pointer window controls to the collided array
|
|
|
|
|
parent->mCollapseGroupVec[attatchedGroupVec].push_back((*iter));
|
|
|
|
|
groupVecCount--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( vecPos <= 1 || groupVecCount == 0 )
|
|
|
|
|
{
|
|
|
|
|
// Isn't covered in the renumbering of groups, so we it here
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1;
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1;
|
|
|
|
|
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].clear();
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].setSize(U32(0));
|
|
|
|
|
parent->mCollapseGroupVec.erase(groupVec);
|
|
|
|
|
|
|
|
|
|
if(groupVec > 0)
|
|
|
|
|
parent->mCollapseGroupVec.setSize(groupVec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Since we killed my old array group, run in case the guiControl array moved me down a notch
|
|
|
|
|
if(attatchedGroupVec > groupVec )
|
|
|
|
|
attatchedGroupVec--;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-03-15 00:07:03 +00:00
|
|
|
groupVec = hitWindow->mCollapseGroup;
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
if(orientation == 0)
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].push_front(this);
|
|
|
|
|
else
|
|
|
|
|
parent->mCollapseGroupVec[groupVec].push_back(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Re id collapse groups
|
|
|
|
|
refreshCollapseGroups();
|
|
|
|
|
|
|
|
|
|
// Select the current window and its collapse group
|
|
|
|
|
selectWindow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::refreshCollapseGroups()
|
|
|
|
|
{
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return;
|
|
|
|
|
|
2017-01-12 04:34:46 +00:00
|
|
|
CollapseGroupNumVec collapseGroupNumVec;
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
// iterate through the collided array, renumbering the windows pointers
|
|
|
|
|
S32 assignGroupNum = 0;
|
|
|
|
|
CollapseGroupVec::iterator iter = parent->mCollapseGroupVec.begin();
|
|
|
|
|
for(; iter != parent->mCollapseGroupVec.end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
S32 assignWindowNumber = 0;
|
|
|
|
|
CollapseGroupNumVec::iterator iter2 = parent->mCollapseGroupVec[assignGroupNum].begin();
|
|
|
|
|
for(; iter2 != parent->mCollapseGroupVec[assignGroupNum].end(); iter2++ )
|
|
|
|
|
{
|
|
|
|
|
(*iter2)->mCollapseGroup = assignGroupNum;
|
|
|
|
|
(*iter2)->mCollapseGroupNum = assignWindowNumber;
|
|
|
|
|
assignWindowNumber++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assignGroupNum++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::moveWithCollapseGroup(Point2I windowPosition)
|
|
|
|
|
{
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Point2I newChildPosition(0, 0);
|
|
|
|
|
S32 addedPosition = getExtent().y;
|
|
|
|
|
|
|
|
|
|
// Iterate through windows below this. Move them according to my position.
|
|
|
|
|
CollapseGroupNumVec collapseGroupNumVec;
|
|
|
|
|
CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin();
|
|
|
|
|
for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
if((*iter)->mCollapseGroupNum > mCollapseGroupNum)
|
|
|
|
|
{
|
|
|
|
|
newChildPosition.x = windowPosition.x;
|
|
|
|
|
newChildPosition.y = windowPosition.y + addedPosition;
|
|
|
|
|
|
|
|
|
|
(*iter)->resize(newChildPosition, (*iter)->getExtent());
|
|
|
|
|
addedPosition += (*iter)->getExtent().y;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::setCollapseGroup(bool state)
|
|
|
|
|
{
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if( mIsCollapsed != state )
|
|
|
|
|
{
|
|
|
|
|
mIsCollapsed = state;
|
|
|
|
|
handleCollapseGroup();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::toggleCollapseGroup()
|
|
|
|
|
{
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
mIsCollapsed = !mIsCollapsed;
|
|
|
|
|
handleCollapseGroup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::handleCollapseGroup()
|
|
|
|
|
{
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return;
|
|
|
|
|
|
2017-01-12 04:34:46 +00:00
|
|
|
CollapseGroupNumVec collapseGroupNumVec;
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
if( mIsCollapsed ) // minimize window up to its header bar
|
|
|
|
|
{
|
|
|
|
|
//save settings
|
|
|
|
|
mPreCollapsedYExtent = getExtent().y;
|
|
|
|
|
mPreCollapsedYMinExtent = getMinExtent().y;
|
|
|
|
|
|
|
|
|
|
//create settings for collapsed window to abide by
|
|
|
|
|
mResizeHeight = false;
|
|
|
|
|
setMinExtent( Point2I( getMinExtent().x, 24 ) );
|
|
|
|
|
|
|
|
|
|
iterator i;
|
|
|
|
|
for(i = begin(); i != end(); i++)
|
|
|
|
|
{
|
|
|
|
|
GuiControl *ctrl = static_cast<GuiControl *>(*i);
|
|
|
|
|
ctrl->setVisible(false);
|
|
|
|
|
ctrl->mCanResize = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resize( getPosition(), Point2I( getExtent().x, 24 ) );
|
|
|
|
|
|
|
|
|
|
if(mCollapseGroup >= 0)
|
|
|
|
|
{
|
|
|
|
|
S32 moveChildYBy = (mPreCollapsedYExtent - 24);
|
|
|
|
|
|
|
|
|
|
CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin();
|
|
|
|
|
for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
if((*iter)->mCollapseGroupNum > mCollapseGroupNum)
|
|
|
|
|
{
|
|
|
|
|
Point2I newChildPosition = (*iter)->getPosition();
|
|
|
|
|
newChildPosition.y -= moveChildYBy;
|
|
|
|
|
(*iter)->resize(newChildPosition, (*iter)->getExtent());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onCollapse_callback();
|
|
|
|
|
}
|
|
|
|
|
else // maximize the window to its previous position
|
|
|
|
|
{
|
|
|
|
|
//create and load settings
|
|
|
|
|
mResizeHeight = true;
|
|
|
|
|
setMinExtent( Point2I( getMinExtent().x, mPreCollapsedYMinExtent ) );
|
|
|
|
|
|
|
|
|
|
resize( getPosition(), Point2I( getExtent().x, mPreCollapsedYExtent ) );
|
|
|
|
|
|
|
|
|
|
iterator i;
|
|
|
|
|
for(i = begin(); i != end(); i++)
|
|
|
|
|
{
|
|
|
|
|
GuiControl *ctrl = static_cast<GuiControl *>(*i);
|
|
|
|
|
ctrl->setVisible(true);
|
|
|
|
|
ctrl->mCanResize = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(mCollapseGroup >= 0)
|
|
|
|
|
{
|
|
|
|
|
S32 moveChildYBy = (mPreCollapsedYExtent - 24);
|
|
|
|
|
|
|
|
|
|
CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin();
|
|
|
|
|
for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
if((*iter)->mCollapseGroupNum > mCollapseGroupNum)
|
|
|
|
|
{
|
|
|
|
|
Point2I newChildPosition = (*iter)->getPosition();
|
2017-01-12 04:34:46 +00:00
|
|
|
newChildPosition.y += moveChildYBy;
|
2012-09-19 15:15:01 +00:00
|
|
|
(*iter)->resize(newChildPosition, (*iter)->getExtent());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onRestore_callback();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bool GuiWindowCtrl::resizeCollapseGroup(bool resizeX, bool resizeY, Point2I resizePos, Point2I resizeExtent)
|
|
|
|
|
{
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return false;
|
|
|
|
|
|
2017-01-12 04:34:46 +00:00
|
|
|
CollapseGroupNumVec collapseGroupNumVec;
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
bool canResize = true;
|
|
|
|
|
CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin();
|
|
|
|
|
for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
if((*iter) == this)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Point2I newChildPosition = (*iter)->getPosition();
|
|
|
|
|
Point2I newChildExtent = (*iter)->getExtent();
|
|
|
|
|
|
|
|
|
|
if( resizeX == true )
|
|
|
|
|
{
|
|
|
|
|
newChildPosition.x -= resizePos.x;
|
|
|
|
|
newChildExtent.x -= resizeExtent.x;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if( resizeY == true )
|
|
|
|
|
{
|
|
|
|
|
if((*iter)->mCollapseGroupNum > mCollapseGroupNum)
|
|
|
|
|
{
|
|
|
|
|
newChildPosition.y -= resizeExtent.y;
|
|
|
|
|
newChildPosition.y -= resizePos.y;
|
|
|
|
|
}
|
|
|
|
|
else if((*iter)->mCollapseGroupNum == mCollapseGroupNum - 1)
|
|
|
|
|
newChildExtent.y -= resizePos.y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check is done for normal extent of windows. if false, check again in case its just giving false
|
|
|
|
|
// due to being a collapsed window. if your truly being forced passed your extent, return false
|
|
|
|
|
if( !(*iter)->mIsCollapsed && newChildExtent.y >= (*iter)->getMinExtent().y )
|
|
|
|
|
(*iter)->resize( newChildPosition, newChildExtent);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if( (*iter)->mIsCollapsed )
|
|
|
|
|
(*iter)->resize( newChildPosition, newChildExtent);
|
|
|
|
|
else
|
|
|
|
|
canResize = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return canResize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Mouse Methods
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
S32 GuiWindowCtrl::findHitEdges( const Point2I &globalPoint )
|
|
|
|
|
{
|
|
|
|
|
// No Edges
|
|
|
|
|
S32 edgeMask = edgeNone;
|
|
|
|
|
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return edgeMask;
|
|
|
|
|
|
|
|
|
|
RectI bounds( getGlobalBounds() );
|
|
|
|
|
|
|
|
|
|
// Create an EdgeRectI structure that has four edges
|
|
|
|
|
// Left/Right/Top/Bottom
|
|
|
|
|
// Each Edge structure has a hit operation that will take
|
|
|
|
|
// another edge and test for a hit on the edge with a margin
|
|
|
|
|
// specified by the .margin scalar
|
|
|
|
|
EdgeRectI edges = EdgeRectI(bounds, mResizeMargin);
|
|
|
|
|
|
|
|
|
|
// Get Cursor Edges
|
|
|
|
|
Edge cursorVertEdge = Edge( globalPoint, Point2F( 1.f, 0.f ) );
|
|
|
|
|
Edge cursorHorzEdge = Edge( globalPoint, Point2F( 0.f, 1.f ) );
|
|
|
|
|
|
|
|
|
|
if( edges.left.hit( cursorVertEdge ) )
|
|
|
|
|
edgeMask |= edgeLeft;
|
|
|
|
|
else if( edges.right.hit( cursorVertEdge ) )
|
|
|
|
|
edgeMask |= edgeRight;
|
|
|
|
|
|
|
|
|
|
if( edges.bottom.hit( cursorHorzEdge ) )
|
|
|
|
|
edgeMask |= edgeBottom;
|
|
|
|
|
else if( edges.top.hit( cursorHorzEdge ) )
|
|
|
|
|
{
|
|
|
|
|
// Only the top window in a collapse group can be extended from the top
|
|
|
|
|
if( mCanCollapse && mCollapseGroup >= 0 )
|
|
|
|
|
{
|
|
|
|
|
if( parent->mCollapseGroupVec[mCollapseGroup].first() != this )
|
|
|
|
|
return edgeMask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
edgeMask |= edgeTop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the resulting mask
|
|
|
|
|
return edgeMask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::getSnappableWindows( Vector<GuiWindowCtrl*> &windowOutVector, bool canCollapse )
|
|
|
|
|
{
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
S32 parentSize = parent->size();
|
|
|
|
|
for( S32 i = 0; i < parentSize; i++ )
|
|
|
|
|
{
|
|
|
|
|
GuiWindowCtrl *childWindow = dynamic_cast<GuiWindowCtrl*>(parent->at(i));
|
|
|
|
|
if( !childWindow || !childWindow->isVisible() || childWindow == this || childWindow->mEdgeSnap == false)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if( canCollapse && !childWindow->mCanCollapse )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
windowOutVector.push_back(childWindow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
// Events.
|
|
|
|
|
//=============================================================================
|
|
|
|
|
// MARK: ---- Events ----
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bool GuiWindowCtrl::onWake()
|
|
|
|
|
{
|
|
|
|
|
if (! Parent::onWake())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//get the texture for the close, minimize, and maximize buttons
|
|
|
|
|
bool result = mProfile->constructBitmapArray() >= NumBitmaps;
|
|
|
|
|
if( !result )
|
|
|
|
|
{
|
|
|
|
|
Con::errorf( "GuiWindowCtrl::onWake - failed to create bitmap array from profile bitmap." );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 06:07:08 +00:00
|
|
|
mTextureObject = mProfile->getBitmapResource();
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
mBitmapBounds = mProfile->mBitmapArrayRects.address();
|
2023-04-27 03:27:35 +00:00
|
|
|
S32 buttonHeight = mBitmapBounds[(U32)BmpStates * (U32)BmpClose].extent.y;
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
mTitleHeight = buttonHeight + 4;
|
|
|
|
|
|
|
|
|
|
//set the button coords
|
|
|
|
|
positionButtons();
|
|
|
|
|
|
|
|
|
|
//set the tab index
|
|
|
|
|
mTabIndex = -1;
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if (parent && mFirstResponder)
|
|
|
|
|
{
|
|
|
|
|
mTabIndex = 0;
|
|
|
|
|
|
|
|
|
|
//count the number of windows preceeding this one
|
|
|
|
|
iterator i;
|
|
|
|
|
for (i = parent->begin(); i != parent->end(); i++)
|
|
|
|
|
{
|
|
|
|
|
GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
|
|
|
|
|
if (ctrl)
|
|
|
|
|
{
|
|
|
|
|
if (ctrl == this) break;
|
|
|
|
|
else if (ctrl->mFirstResponder) mTabIndex++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::onSleep()
|
|
|
|
|
{
|
|
|
|
|
mTextureObject = NULL;
|
|
|
|
|
mMousePosition = Point2I(0,0);
|
|
|
|
|
Parent::onSleep();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::onMouseDown(const GuiEvent &event)
|
|
|
|
|
{
|
|
|
|
|
setUpdate();
|
|
|
|
|
|
|
|
|
|
mOrigBounds = getBounds();
|
|
|
|
|
|
|
|
|
|
mMouseDownPosition = event.mousePoint;
|
|
|
|
|
Point2I localPoint = globalToLocalCoord(event.mousePoint);
|
|
|
|
|
|
|
|
|
|
// Select this window - move it to the front, and set the first responder
|
|
|
|
|
selectWindow();
|
|
|
|
|
|
|
|
|
|
mMouseMovingWin = false;
|
|
|
|
|
|
|
|
|
|
S32 hitEdges = findHitEdges( event.mousePoint );
|
|
|
|
|
|
|
|
|
|
mResizeEdge = edgeNone;
|
|
|
|
|
|
|
|
|
|
// Set flag by default so we only clear it
|
|
|
|
|
// if we don't match either edge
|
|
|
|
|
mMouseResizeHeight = true;
|
|
|
|
|
|
|
|
|
|
// Check Bottom/Top edges (Mutually Exclusive)
|
|
|
|
|
if( mResizeHeight && hitEdges & edgeBottom )
|
|
|
|
|
mResizeEdge |= edgeBottom;
|
|
|
|
|
else if( mResizeHeight && hitEdges & edgeTop )
|
|
|
|
|
mResizeEdge |= edgeTop;
|
|
|
|
|
else
|
|
|
|
|
mMouseResizeHeight = false;
|
|
|
|
|
|
|
|
|
|
// Set flag by default so we only clear it
|
|
|
|
|
// if we don't match either edge
|
|
|
|
|
mMouseResizeWidth = true;
|
|
|
|
|
|
|
|
|
|
// Check Left/Right edges (Mutually Exclusive)
|
|
|
|
|
if( mResizeWidth && hitEdges & edgeLeft )
|
|
|
|
|
mResizeEdge |= edgeLeft;
|
|
|
|
|
else if( mResizeWidth && hitEdges & edgeRight )
|
|
|
|
|
mResizeEdge |= edgeRight;
|
|
|
|
|
else
|
|
|
|
|
mMouseResizeWidth = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If we clicked within the title bar
|
|
|
|
|
if ( !(mResizeEdge & ( edgeTop | edgeLeft | edgeRight ) ) && localPoint.y < mTitleHeight)
|
|
|
|
|
{
|
|
|
|
|
if (mCanClose && mCloseButton.pointInRect(localPoint))
|
|
|
|
|
{
|
|
|
|
|
mCloseButtonPressed = mCanClose;
|
|
|
|
|
}
|
|
|
|
|
else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint))
|
|
|
|
|
{
|
|
|
|
|
mMaximizeButtonPressed = mCanMaximize;
|
|
|
|
|
}
|
|
|
|
|
else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint))
|
|
|
|
|
{
|
|
|
|
|
mMinimizeButtonPressed = mCanMinimize;
|
|
|
|
|
}
|
|
|
|
|
else // We clicked anywhere else within the title
|
|
|
|
|
{
|
|
|
|
|
S32 docking = getDocking();
|
|
|
|
|
if( docking == Docking::dockInvalid || docking == Docking::dockNone )
|
|
|
|
|
mMouseMovingWin = mCanMove;
|
|
|
|
|
|
|
|
|
|
mMouseResizeWidth = false;
|
|
|
|
|
mMouseResizeHeight = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (mMouseMovingWin || mResizeEdge != edgeNone ||
|
|
|
|
|
mCloseButtonPressed || mMaximizeButtonPressed || mMinimizeButtonPressed)
|
|
|
|
|
{
|
|
|
|
|
mouseLock();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
GuiControl *ctrl = findHitControl(localPoint);
|
|
|
|
|
if (ctrl && ctrl != this)
|
|
|
|
|
ctrl->onMouseDown(event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::onMouseDragged(const GuiEvent &event)
|
|
|
|
|
{
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
GuiCanvas *root = getRoot();
|
|
|
|
|
if ( !root )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
mMousePosition = globalToLocalCoord(event.mousePoint);
|
|
|
|
|
Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition;
|
|
|
|
|
|
|
|
|
|
Point2I newPosition = getPosition();
|
|
|
|
|
Point2I newExtent = getExtent();
|
|
|
|
|
bool resizeX = false;
|
|
|
|
|
bool resizeY = false;
|
|
|
|
|
mResizeWindow = false;
|
|
|
|
|
mRepositionWindow = false;
|
|
|
|
|
|
|
|
|
|
if (mMouseMovingWin && parent)
|
|
|
|
|
{
|
|
|
|
|
if( parent != root )
|
|
|
|
|
{
|
|
|
|
|
newPosition.x = mOrigBounds.point.x + deltaMousePosition.x;
|
|
|
|
|
newPosition.y = getMax(0, mOrigBounds.point.y + deltaMousePosition.y );
|
|
|
|
|
mRepositionWindow = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
newPosition.x = getMax(0, getMin(parent->getWidth() - getWidth(), mOrigBounds.point.x + deltaMousePosition.x));
|
|
|
|
|
newPosition.y = getMax(0, getMin(parent->getHeight() - getHeight(), mOrigBounds.point.y + deltaMousePosition.y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check snapping to other windows
|
|
|
|
|
if( mEdgeSnap )
|
|
|
|
|
{
|
|
|
|
|
RectI bounds = getGlobalBounds();
|
|
|
|
|
bounds.point = mOrigBounds.point + deltaMousePosition;
|
|
|
|
|
EdgeRectI edges = EdgeRectI( bounds, mResizeMargin );
|
|
|
|
|
|
|
|
|
|
// Create a global-space rectangle that covers the snapping
|
|
|
|
|
// zone of this window. Double the space in which snapping occurs
|
|
|
|
|
// for top and bottom.
|
|
|
|
|
RectI snapZone = bounds;
|
|
|
|
|
snapZone.point.x -= SnapDistance;
|
|
|
|
|
snapZone.point.y -= SnapDistance;
|
|
|
|
|
snapZone.extent.x += SnapDistance + SnapDistance;
|
|
|
|
|
snapZone.extent.y += SnapDistance + SnapDistance;
|
2019-11-18 09:30:04 +00:00
|
|
|
|
|
|
|
|
//check if we need to offset because of the menubar
|
|
|
|
|
U32 menuBarHeight = 0;
|
|
|
|
|
GuiCanvas* guiCanvas = getRoot();
|
|
|
|
|
if (guiCanvas)
|
|
|
|
|
{
|
2022-06-13 17:38:08 +00:00
|
|
|
#ifdef TORQUE_TOOLS
|
2019-11-18 09:30:04 +00:00
|
|
|
GuiMenuBar* menuBar = dynamic_cast<GuiMenuBar*>(guiCanvas->getMenuBar());
|
|
|
|
|
if (menuBar)
|
|
|
|
|
{
|
|
|
|
|
menuBarHeight = menuBar->getHeight();
|
|
|
|
|
}
|
2022-06-13 17:38:08 +00:00
|
|
|
#endif
|
2019-11-18 09:30:04 +00:00
|
|
|
}
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
// Build valid snap and window vectors to compare against
|
|
|
|
|
Vector< GuiWindowCtrl* > windowList;
|
|
|
|
|
getSnappableWindows( windowList );
|
|
|
|
|
|
|
|
|
|
for( S32 i =0; i < windowList.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
// Make sure the window is both horizontally and vertically
|
|
|
|
|
// within the snap zone for this window.
|
2019-11-18 09:30:04 +00:00
|
|
|
RectI windowBounds = windowList[i]->getGlobalBounds();
|
|
|
|
|
//offset position by menubar height
|
|
|
|
|
windowBounds.point.y -= menuBarHeight;
|
|
|
|
|
|
|
|
|
|
if( !snapZone.overlaps(windowBounds) )
|
2012-09-19 15:15:01 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Build edges for snap detection
|
2019-11-18 09:30:04 +00:00
|
|
|
EdgeRectI snapRect(windowBounds, mResizeMargin );
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
if( snapRect.right.position.x <= edges.left.position.x + SnapDistance &&
|
|
|
|
|
snapRect.right.position.x >= edges.left.position.x - SnapDistance )
|
|
|
|
|
{
|
|
|
|
|
newPosition.x = snapRect.right.position.x;
|
|
|
|
|
}
|
|
|
|
|
else if( snapRect.left.position.x <= edges.right.position.x + SnapDistance &&
|
|
|
|
|
snapRect.left.position.x >= edges.right.position.x - SnapDistance )
|
|
|
|
|
{
|
|
|
|
|
newPosition.x = snapRect.left.position.x - bounds.extent.x;
|
|
|
|
|
}
|
|
|
|
|
else if( snapRect.top.position.y <= edges.bottom.position.y + SnapDistance + SnapDistance &&
|
|
|
|
|
snapRect.top.position.y >= edges.bottom.position.y - SnapDistance - SnapDistance )
|
|
|
|
|
{
|
|
|
|
|
// Ensure that we're not snapping to the middle of collapse groups
|
|
|
|
|
if( (windowList[i]->mCanCollapse && windowList[i]->mCollapseGroup >= 0) ||
|
|
|
|
|
(mCanCollapse && mCollapseGroup >= 0) )
|
|
|
|
|
continue;
|
|
|
|
|
newPosition.x = snapRect.left.position.x;
|
|
|
|
|
newPosition.y = snapRect.top.position.y - bounds.extent.y;
|
|
|
|
|
}
|
|
|
|
|
else if( snapRect.bottom.position.y <= edges.top.position.y + SnapDistance + SnapDistance &&
|
|
|
|
|
snapRect.bottom.position.y >= edges.top.position.y - SnapDistance - SnapDistance)
|
|
|
|
|
{
|
|
|
|
|
// Ensure that we're not snapping to the middle of collapse groups
|
|
|
|
|
// We are not in a group, or we are not in the same group
|
|
|
|
|
if( mCollapseGroup == -1 || ( mCollapseGroup >= 0 && mCollapseGroup != windowList[i]->mCollapseGroup ) )
|
|
|
|
|
{
|
|
|
|
|
// If the window checked is in a group, if its anything but the last, its n/a
|
|
|
|
|
if( windowList[i]->mCollapseGroup >= 0 &&
|
|
|
|
|
windowList[i] != parent->mCollapseGroupVec[windowList[i]->mCollapseGroup].last() )
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else // We are in the same group, we can't obviously be [0]
|
|
|
|
|
{
|
|
|
|
|
// If we are [-1/0] we have a serious problem. Also, we only allow connection to the window directly above us
|
|
|
|
|
if( mCollapseGroupNum <= 0 ||
|
|
|
|
|
windowList[i] != parent->mCollapseGroupVec[mCollapseGroup][mCollapseGroupNum-1] )
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newPosition.x = snapRect.left.position.x;
|
|
|
|
|
newPosition.y = snapRect.bottom.position.y;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(mCloseButtonPressed || mMaximizeButtonPressed || mMinimizeButtonPressed)
|
|
|
|
|
{
|
|
|
|
|
setUpdate();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if( ( !mMouseResizeHeight && !mMouseResizeWidth ) || !parent )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
mResizeWindow = true;
|
|
|
|
|
if( mResizeEdge & edgeBottom )
|
|
|
|
|
{
|
|
|
|
|
newExtent.y = getMin(parent->getHeight(), mOrigBounds.extent.y + deltaMousePosition.y);
|
|
|
|
|
resizeY = true;
|
|
|
|
|
}
|
|
|
|
|
else if ( mResizeEdge & edgeTop )
|
|
|
|
|
{
|
|
|
|
|
newExtent.y = getMin(parent->getHeight(), mOrigBounds.extent.y - deltaMousePosition.y);
|
|
|
|
|
if ( newExtent.y >= mMinExtent.y )
|
|
|
|
|
{
|
|
|
|
|
// Standard reposition as we're not travelling into the min extent range
|
|
|
|
|
newPosition.y = mOrigBounds.point.y + deltaMousePosition.y;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// We're into the min extent, so adjust the position up to the min extent
|
|
|
|
|
// so the window doesn't appear to jump
|
|
|
|
|
newPosition.y = mOrigBounds.point.y + (mOrigBounds.extent.y - mMinExtent.y);
|
|
|
|
|
}
|
|
|
|
|
resizeY = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( mResizeEdge & edgeRight )
|
|
|
|
|
{
|
|
|
|
|
newExtent.x = getMin(parent->getWidth(), mOrigBounds.extent.x + deltaMousePosition.x);
|
|
|
|
|
resizeX = true;
|
|
|
|
|
}
|
|
|
|
|
else if( mResizeEdge & edgeLeft )
|
|
|
|
|
{
|
|
|
|
|
newExtent.x = getMin(parent->getWidth(), mOrigBounds.extent.x - deltaMousePosition.x);
|
|
|
|
|
if ( newExtent.x >= mMinExtent.x )
|
|
|
|
|
{
|
|
|
|
|
// Standard reposition as we're not travelling into the min extent range
|
|
|
|
|
newPosition.x = mOrigBounds.point.x + deltaMousePosition.x;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// We're into the min extent, so adjust the position up to the min extent
|
|
|
|
|
// so the window doesn't appear to jump
|
|
|
|
|
newPosition.x = mOrigBounds.point.x + (mOrigBounds.extent.x - mMinExtent.x);
|
|
|
|
|
}
|
|
|
|
|
resizeX = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resize this
|
|
|
|
|
Point2I pos = parent->localToGlobalCoord(getPosition());
|
|
|
|
|
root->addUpdateRegion(pos, getExtent());
|
|
|
|
|
|
|
|
|
|
if(mCanCollapse && mCollapseGroup >= 0 && mRepositionWindow == true)
|
|
|
|
|
moveWithCollapseGroup(newPosition);
|
|
|
|
|
|
|
|
|
|
if(mCanCollapse && mCollapseGroup >= 0 && mResizeWindow == true )
|
2017-01-12 04:34:46 +00:00
|
|
|
{
|
2012-09-19 15:15:01 +00:00
|
|
|
// Resize the window if allowed
|
|
|
|
|
if( newExtent.y >= getMinExtent().y && newExtent.x >= getMinExtent().x)
|
|
|
|
|
{
|
|
|
|
|
mIsMouseResizing = true;
|
|
|
|
|
if( resizeCollapseGroup( resizeX, resizeY, (getPosition() - newPosition), (getExtent() - newExtent) ) )
|
|
|
|
|
resize(newPosition, newExtent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // Normal window sizing functionality
|
|
|
|
|
resize(newPosition, newExtent);
|
2023-09-05 03:50:45 +00:00
|
|
|
|
|
|
|
|
// Add a callback for the GUI scripts
|
|
|
|
|
onMouseDragged_callback();
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::onMouseUp(const GuiEvent &event)
|
|
|
|
|
{
|
|
|
|
|
bool closing = mCloseButtonPressed;
|
|
|
|
|
bool maximizing = mMaximizeButtonPressed;
|
|
|
|
|
bool minimizing = mMinimizeButtonPressed;
|
|
|
|
|
|
|
|
|
|
mCloseButtonPressed = false;
|
|
|
|
|
mMaximizeButtonPressed = false;
|
|
|
|
|
mMinimizeButtonPressed = false;
|
|
|
|
|
|
|
|
|
|
TORQUE_UNUSED(event);
|
|
|
|
|
mouseUnlock();
|
|
|
|
|
|
|
|
|
|
mMouseMovingWin = false;
|
|
|
|
|
mMouseResizeWidth = false;
|
|
|
|
|
mMouseResizeHeight = false;
|
|
|
|
|
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if (! parent)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if( mIsMouseResizing )
|
|
|
|
|
{
|
|
|
|
|
mIsMouseResizing = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Point2I localPoint = globalToLocalCoord(event.mousePoint);
|
|
|
|
|
if (closing && mCloseButton.pointInRect(localPoint))
|
|
|
|
|
{
|
|
|
|
|
// Here is where were going to put our other if statement
|
|
|
|
|
// if the window closes, and there were only 2 windows in the array, then just delete the array and default there params
|
|
|
|
|
// if not, delete the window from the array, default its params, and renumber the windows
|
|
|
|
|
|
|
|
|
|
if( engineAPI::gUseConsoleInterop )
|
|
|
|
|
evaluate( mCloseCommand );
|
|
|
|
|
onClose_callback();
|
|
|
|
|
}
|
|
|
|
|
else if (maximizing && mMaximizeButton.pointInRect(localPoint))
|
|
|
|
|
{
|
|
|
|
|
if (mMaximized)
|
|
|
|
|
{
|
|
|
|
|
// Resize to the previous position and extent, bounded by the parent
|
|
|
|
|
resize(Point2I(getMax(0, getMin(parent->getWidth() - mStandardBounds.extent.x, mStandardBounds.point.x)),
|
|
|
|
|
getMax(0, getMin(parent->getHeight() - mStandardBounds.extent.y, mStandardBounds.point.y))),
|
|
|
|
|
mStandardBounds.extent);
|
|
|
|
|
// Set the flag
|
|
|
|
|
mMaximized = false;
|
|
|
|
|
|
|
|
|
|
onRestore_callback();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Only save the position if we're not minimized
|
|
|
|
|
if (! mMinimized)
|
|
|
|
|
mStandardBounds = getBounds();
|
|
|
|
|
else
|
|
|
|
|
mMinimized = false;
|
|
|
|
|
|
|
|
|
|
// Resize to fit the parent
|
|
|
|
|
resize(Point2I(0, 0), parent->getExtent());
|
|
|
|
|
|
|
|
|
|
// Set the flag
|
|
|
|
|
mMaximized = true;
|
|
|
|
|
|
|
|
|
|
onMaximize_callback();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (minimizing && mMinimizeButton.pointInRect(localPoint))
|
|
|
|
|
{
|
|
|
|
|
if (mMinimized)
|
|
|
|
|
{
|
|
|
|
|
// Resize to the previous position and extent, bounded by the parent
|
|
|
|
|
resize(Point2I(getMax(0, getMin(parent->getWidth() - mStandardBounds.extent.x, mStandardBounds.point.x)),
|
|
|
|
|
getMax(0, getMin(parent->getHeight() - mStandardBounds.extent.y, mStandardBounds.point.y))),
|
|
|
|
|
mStandardBounds.extent);
|
|
|
|
|
// Set the flag
|
|
|
|
|
mMinimized = false;
|
|
|
|
|
|
|
|
|
|
onRestore_callback();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (parent->getWidth() < 100 || parent->getHeight() < mTitleHeight + 3)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Only save the position if we're not maximized
|
|
|
|
|
if (! mMaximized)
|
|
|
|
|
{
|
|
|
|
|
mStandardBounds = getBounds();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mMaximized = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// First find the lowest unused minimized index up to 32 minimized windows
|
|
|
|
|
U32 indexMask = 0;
|
|
|
|
|
iterator i;
|
|
|
|
|
S32 count = 0;
|
|
|
|
|
for (i = parent->begin(); i != parent->end() && count < 32; i++)
|
|
|
|
|
{
|
|
|
|
|
count++;
|
|
|
|
|
S32 index;
|
|
|
|
|
GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
|
|
|
|
|
if (ctrl && ctrl->isMinimized(index))
|
|
|
|
|
{
|
|
|
|
|
indexMask |= (1 << index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now find the first unused bit
|
|
|
|
|
for (count = 0; count < 32; count++)
|
|
|
|
|
{
|
|
|
|
|
if (! (indexMask & (1 << count))) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have more than 32 minimized windows, use the first position
|
|
|
|
|
count = getMax(0, count);
|
|
|
|
|
|
|
|
|
|
// This algorithm assumes all window have the same title height, and will minimize to 98 pix
|
|
|
|
|
Point2I newExtent(98, mTitleHeight);
|
|
|
|
|
|
|
|
|
|
// First, how many can fit across
|
|
|
|
|
S32 numAcross = getMax(1, (parent->getWidth() / newExtent.x + 2));
|
|
|
|
|
|
|
|
|
|
// Find the new "mini position"
|
|
|
|
|
Point2I newPosition;
|
|
|
|
|
newPosition.x = (count % numAcross) * (newExtent.x + 2) + 2;
|
|
|
|
|
newPosition.y = parent->getHeight() - (((count / numAcross) + 1) * (newExtent.y + 2)) - 2;
|
|
|
|
|
|
|
|
|
|
// Find the minimized position and extent
|
|
|
|
|
resize(newPosition, newExtent);
|
|
|
|
|
|
|
|
|
|
// Set the index so other windows will not try to minimize to the same location
|
|
|
|
|
mMinimizeIndex = count;
|
|
|
|
|
|
|
|
|
|
// Set the flag
|
|
|
|
|
mMinimized = true;
|
|
|
|
|
|
|
|
|
|
onMinimize_callback();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ( !(mResizeEdge & edgeTop) && localPoint.y < mTitleHeight && event.mousePoint == mMouseDownPosition)
|
|
|
|
|
{
|
|
|
|
|
if (mCanClose && mCloseButton.pointInRect(localPoint))
|
|
|
|
|
return;
|
|
|
|
|
else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint))
|
|
|
|
|
return;
|
|
|
|
|
else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint))
|
|
|
|
|
return;
|
|
|
|
|
else if( mCanCollapse ) // If we clicked anywhere else on the title bar
|
|
|
|
|
toggleCollapseGroup();
|
|
|
|
|
}
|
|
|
|
|
else if( mEdgeSnap && mCanCollapse )
|
|
|
|
|
{
|
|
|
|
|
// Create storage pointer
|
|
|
|
|
GuiWindowCtrl* hitWindow = NULL;
|
|
|
|
|
S32 snapType = -1;
|
|
|
|
|
|
|
|
|
|
Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition;
|
|
|
|
|
Point2I newExtent = getExtent();
|
|
|
|
|
|
|
|
|
|
RectI bounds = getGlobalBounds();
|
|
|
|
|
bounds.point = mOrigBounds.point + deltaMousePosition;
|
|
|
|
|
EdgeRectI edges = EdgeRectI( bounds, mResizeMargin );
|
|
|
|
|
|
|
|
|
|
RectI snapZone = bounds;
|
|
|
|
|
snapZone.point.x -= SnapDistance;
|
|
|
|
|
snapZone.point.y -= SnapDistance;
|
|
|
|
|
snapZone.extent.x += SnapDistance + SnapDistance;
|
|
|
|
|
snapZone.extent.y += SnapDistance + SnapDistance;
|
|
|
|
|
|
|
|
|
|
Vector<EdgeRectI> snapList;
|
|
|
|
|
Vector<GuiWindowCtrl*> windowList;
|
|
|
|
|
|
|
|
|
|
getSnappableWindows( windowList, true );
|
|
|
|
|
|
|
|
|
|
for( S32 i =0; i < windowList.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
if( !snapZone.overlaps( windowList[i]->getGlobalBounds() ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Build edges for snap detection
|
|
|
|
|
EdgeRectI snapRect( windowList[i]->getGlobalBounds(), mResizeMargin );
|
|
|
|
|
|
|
|
|
|
if( windowList[i]->mCollapseGroupNum == -1 )
|
|
|
|
|
{
|
|
|
|
|
// BECOMES "PARENT"
|
|
|
|
|
if( snapRect.top.position.y <= edges.bottom.position.y + SnapDistance + SnapDistance &&
|
|
|
|
|
snapRect.top.position.y >= edges.bottom.position.y - SnapDistance - SnapDistance )
|
|
|
|
|
{
|
|
|
|
|
hitWindow = windowList[i];
|
|
|
|
|
snapType = 0;
|
|
|
|
|
newExtent.x = snapRect.right.position.x - snapRect.left.position.x;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( (windowList[i]->mCollapseGroupNum == -1) || (windowList[i]->mCollapseGroupNum == mCollapseGroupNum - 1) ||
|
|
|
|
|
(!parent->mCollapseGroupVec.empty() && parent->mCollapseGroupVec[windowList[i]->mCollapseGroup].last() == windowList[i]) )
|
|
|
|
|
{
|
|
|
|
|
// BECOMES "CHILD"
|
|
|
|
|
if( snapRect.bottom.position.y <= edges.top.position.y + SnapDistance + SnapDistance &&
|
|
|
|
|
snapRect.bottom.position.y >= edges.top.position.y - SnapDistance - SnapDistance)
|
|
|
|
|
{
|
|
|
|
|
hitWindow = windowList[i];
|
|
|
|
|
snapType = 1;
|
|
|
|
|
newExtent.x = snapRect.right.position.x - snapRect.left.position.x;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We're either moving out of a collapse group or moving to another one
|
|
|
|
|
// Not valid for windows not previously in a group
|
|
|
|
|
if( mCollapseGroup >= 0 &&
|
2017-01-12 04:34:46 +00:00
|
|
|
(snapType == -1 || (hitWindow && snapType >= 0 && mCollapseGroup != hitWindow->mCollapseGroup)))
|
2012-09-19 15:15:01 +00:00
|
|
|
moveFromCollapseGroup();
|
|
|
|
|
|
|
|
|
|
// No window to connect to
|
|
|
|
|
if( !hitWindow )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
moveToCollapseGroup( hitWindow, snapType );
|
|
|
|
|
resize( getPosition(), newExtent );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::onMouseMove(const GuiEvent &event)
|
|
|
|
|
{
|
|
|
|
|
mMousePosition = globalToLocalCoord(event.mousePoint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GuiWindowCtrl::onKeyDown(const GuiEvent &event)
|
|
|
|
|
{
|
|
|
|
|
// If this control is a dead end, kill the event
|
|
|
|
|
if ((! mVisible) || (! mActive) || (! mAwake)) return true;
|
|
|
|
|
|
|
|
|
|
if ((event.keyCode == KEY_TAB) && (event.modifier & SI_PRIMARY_CTRL))
|
|
|
|
|
{
|
|
|
|
|
// Find the next sibling window, and select it
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if (parent)
|
|
|
|
|
{
|
|
|
|
|
GuiWindowCtrl *firstWindow = NULL;
|
|
|
|
|
iterator i;
|
|
|
|
|
for (i = parent->begin(); i != parent->end(); i++)
|
|
|
|
|
{
|
|
|
|
|
GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
|
|
|
|
|
if (ctrl && ctrl->getTabIndex() == mTabIndex + 1)
|
|
|
|
|
{
|
|
|
|
|
ctrl->selectWindow();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if (ctrl && ctrl->getTabIndex() == 0)
|
|
|
|
|
{
|
|
|
|
|
firstWindow = ctrl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Recycle from the beginning
|
|
|
|
|
if (firstWindow != this)
|
|
|
|
|
{
|
|
|
|
|
firstWindow->selectWindow();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Parent::onKeyDown(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::onRender(Point2I offset, const RectI &updateRect)
|
|
|
|
|
{
|
|
|
|
|
if( !mProfile || mProfile->mFont == NULL || mProfile->mBitmapArrayRects.size() < NumBitmaps )
|
|
|
|
|
return Parent::onRender( offset, updateRect );
|
|
|
|
|
|
|
|
|
|
// Draw the outline
|
|
|
|
|
RectI winRect;
|
|
|
|
|
winRect.point = offset;
|
|
|
|
|
winRect.extent = getExtent();
|
|
|
|
|
GuiCanvas *root = getRoot();
|
|
|
|
|
GuiControl *firstResponder = root ? root->getFirstResponder() : NULL;
|
|
|
|
|
|
|
|
|
|
bool isKey = (!firstResponder || controlIsChild(firstResponder));
|
|
|
|
|
|
|
|
|
|
U32 topBase = isKey ? BorderTopLeftKey : BorderTopLeftNoKey;
|
|
|
|
|
winRect.point.x += mBitmapBounds[BorderLeft].extent.x;
|
|
|
|
|
winRect.point.y += mBitmapBounds[topBase + 2].extent.y;
|
|
|
|
|
|
|
|
|
|
winRect.extent.x -= mBitmapBounds[BorderLeft].extent.x + mBitmapBounds[BorderRight].extent.x;
|
|
|
|
|
winRect.extent.y -= mBitmapBounds[topBase + 2].extent.y + mBitmapBounds[BorderBottom].extent.y;
|
|
|
|
|
|
|
|
|
|
winRect.extent.x += 1;
|
|
|
|
|
|
2015-07-14 03:51:17 +00:00
|
|
|
GFXDrawUtil* drawUtil = GFX->getDrawUtil();
|
2012-09-19 15:15:01 +00:00
|
|
|
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->drawRectFill(winRect, mProfile->mFillColor);
|
|
|
|
|
|
|
|
|
|
drawUtil->clearBitmapModulation();
|
|
|
|
|
drawUtil->drawBitmapSR(mTextureObject, offset, mBitmapBounds[topBase]);
|
|
|
|
|
drawUtil->drawBitmapSR(mTextureObject, Point2I(offset.x + getWidth() - mBitmapBounds[topBase+1].extent.x, offset.y),
|
2012-09-19 15:15:01 +00:00
|
|
|
mBitmapBounds[topBase + 1]);
|
|
|
|
|
|
|
|
|
|
RectI destRect;
|
|
|
|
|
destRect.point.x = offset.x + mBitmapBounds[topBase].extent.x;
|
|
|
|
|
destRect.point.y = offset.y;
|
|
|
|
|
destRect.extent.x = getWidth() - mBitmapBounds[topBase].extent.x - mBitmapBounds[topBase + 1].extent.x;
|
|
|
|
|
destRect.extent.y = mBitmapBounds[topBase + 2].extent.y;
|
|
|
|
|
RectI stretchRect = mBitmapBounds[topBase + 2];
|
|
|
|
|
stretchRect.inset(1,0);
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
destRect.point.x = offset.x;
|
|
|
|
|
destRect.point.y = offset.y + mBitmapBounds[topBase].extent.y;
|
|
|
|
|
destRect.extent.x = mBitmapBounds[BorderLeft].extent.x;
|
|
|
|
|
destRect.extent.y = getHeight() - mBitmapBounds[topBase].extent.y - mBitmapBounds[BorderBottomLeft].extent.y;
|
|
|
|
|
stretchRect = mBitmapBounds[BorderLeft];
|
|
|
|
|
stretchRect.inset(0,1);
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
destRect.point.x = offset.x + getWidth() - mBitmapBounds[BorderRight].extent.x;
|
|
|
|
|
destRect.extent.x = mBitmapBounds[BorderRight].extent.x;
|
|
|
|
|
destRect.point.y = offset.y + mBitmapBounds[topBase + 1].extent.y;
|
|
|
|
|
destRect.extent.y = getHeight() - mBitmapBounds[topBase + 1].extent.y - mBitmapBounds[BorderBottomRight].extent.y;
|
|
|
|
|
|
|
|
|
|
stretchRect = mBitmapBounds[BorderRight];
|
|
|
|
|
stretchRect.inset(0,1);
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->drawBitmapSR(mTextureObject, offset + Point2I(0, getHeight() - mBitmapBounds[BorderBottomLeft].extent.y), mBitmapBounds[BorderBottomLeft]);
|
|
|
|
|
drawUtil->drawBitmapSR(mTextureObject, offset + getExtent() - mBitmapBounds[BorderBottomRight].extent, mBitmapBounds[BorderBottomRight]);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
destRect.point.x = offset.x + mBitmapBounds[BorderBottomLeft].extent.x;
|
|
|
|
|
destRect.extent.x = getWidth() - mBitmapBounds[BorderBottomLeft].extent.x - mBitmapBounds[BorderBottomRight].extent.x;
|
|
|
|
|
|
|
|
|
|
destRect.point.y = offset.y + getHeight() - mBitmapBounds[BorderBottom].extent.y;
|
|
|
|
|
destRect.extent.y = mBitmapBounds[BorderBottom].extent.y;
|
|
|
|
|
stretchRect = mBitmapBounds[BorderBottom];
|
|
|
|
|
stretchRect.inset(1,0);
|
|
|
|
|
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
// Draw the title
|
|
|
|
|
// dhc addition: copied/modded from renderJustifiedText, since we enforce a
|
|
|
|
|
// different color usage here. NOTE: it currently CAN overdraw the controls
|
|
|
|
|
// if mis-positioned or 'scrunched' in a small width.
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->setBitmapModulation(mProfile->mFontColor);
|
2012-09-19 15:15:01 +00:00
|
|
|
S32 textWidth = mProfile->mFont->getStrWidth((const UTF8 *)mText);
|
|
|
|
|
Point2I start(0,0);
|
|
|
|
|
|
|
|
|
|
// Align the horizontal
|
|
|
|
|
if ( mProfile->mAlignment == GuiControlProfile::RightJustify )
|
|
|
|
|
start.set( winRect.extent.x - textWidth, 0 );
|
|
|
|
|
else if ( mProfile->mAlignment == GuiControlProfile::CenterJustify )
|
|
|
|
|
start.set( ( winRect.extent.x - textWidth) / 2, 0 );
|
|
|
|
|
else // GuiControlProfile::LeftJustify or garbage... ;)
|
|
|
|
|
start.set( 0, 0 );
|
|
|
|
|
// If the text is longer then the box size, (it'll get clipped) so force Left Justify
|
|
|
|
|
if( textWidth > winRect.extent.x ) start.set( 0, 0 );
|
|
|
|
|
// center the vertical
|
|
|
|
|
// start.y = ( winRect.extent.y - ( font->getHeight() - 2 ) ) / 2;
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->drawText( mProfile->mFont, start + offset + mProfile->mTextOffset, mText );
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
// Deal with rendering the titlebar controls
|
|
|
|
|
AssertFatal(root, "Unable to get the root GuiCanvas.");
|
|
|
|
|
|
|
|
|
|
// Draw the close button
|
|
|
|
|
Point2I tempUL;
|
|
|
|
|
Point2I tempLR;
|
2023-04-27 03:27:35 +00:00
|
|
|
S32 bmp = (U32)BmpStates * (U32)BmpClose;
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
if( mCanClose ) {
|
|
|
|
|
if( mCloseButton.pointInRect( mMousePosition ) )
|
|
|
|
|
{
|
|
|
|
|
if( mCloseButtonPressed )
|
|
|
|
|
bmp += BmpDown;
|
|
|
|
|
else
|
|
|
|
|
bmp += BmpHilite;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->clearBitmapModulation();
|
2023-09-05 03:50:45 +00:00
|
|
|
drawUtil->drawBitmapSR(mTextureObject, mButtonOffset + offset + mCloseButton.point, mBitmapBounds[bmp]);
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw the maximize button
|
|
|
|
|
if( mMaximized )
|
2023-04-27 03:27:35 +00:00
|
|
|
bmp = (U32)BmpStates * (U32)BmpNormal;
|
2012-09-19 15:15:01 +00:00
|
|
|
else
|
2023-04-27 03:27:35 +00:00
|
|
|
bmp = (U32)BmpStates * (U32)BmpMaximize;
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
if( mCanMaximize ) {
|
|
|
|
|
if( mMaximizeButton.pointInRect( mMousePosition ) )
|
|
|
|
|
{
|
|
|
|
|
if( mMaximizeButtonPressed )
|
|
|
|
|
bmp += BmpDown;
|
|
|
|
|
else
|
|
|
|
|
bmp += BmpHilite;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->clearBitmapModulation();
|
2023-09-05 03:50:45 +00:00
|
|
|
drawUtil->drawBitmapSR( mTextureObject, mButtonOffset + offset + mMaximizeButton.point, mBitmapBounds[bmp] );
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw the minimize button
|
|
|
|
|
if( mMinimized )
|
2023-04-27 03:27:35 +00:00
|
|
|
bmp = (U32)BmpStates * (U32)BmpNormal;
|
2012-09-19 15:15:01 +00:00
|
|
|
else
|
2023-04-27 03:27:35 +00:00
|
|
|
bmp = (U32)BmpStates * (U32)BmpMinimize;
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
if( mCanMinimize ) {
|
|
|
|
|
if( mMinimizeButton.pointInRect( mMousePosition ) )
|
|
|
|
|
{
|
|
|
|
|
if( mMinimizeButtonPressed )
|
|
|
|
|
bmp += BmpDown;
|
|
|
|
|
else
|
|
|
|
|
bmp += BmpHilite;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-14 03:51:17 +00:00
|
|
|
drawUtil->clearBitmapModulation();
|
2023-09-05 03:50:45 +00:00
|
|
|
drawUtil->drawBitmapSR( mTextureObject, mButtonOffset + offset + mMinimizeButton.point, mBitmapBounds[bmp] );
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( !mMinimized )
|
|
|
|
|
{
|
|
|
|
|
// Render the children
|
|
|
|
|
renderChildControls( offset, updateRect );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
// Misc.
|
|
|
|
|
//=============================================================================
|
|
|
|
|
// MARK: ---- Misc ----
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
const RectI GuiWindowCtrl::getClientRect()
|
|
|
|
|
{
|
|
|
|
|
if( !mProfile || mProfile->mBitmapArrayRects.size() < NumBitmaps )
|
|
|
|
|
return Parent::getClientRect();
|
|
|
|
|
|
|
|
|
|
if( !mBitmapBounds )
|
|
|
|
|
mBitmapBounds = mProfile->mBitmapArrayRects.address();
|
|
|
|
|
|
|
|
|
|
RectI winRect;
|
|
|
|
|
winRect.point.x = mBitmapBounds[BorderLeft].extent.x;
|
|
|
|
|
winRect.point.y = mBitmapBounds[BorderTopKey].extent.y;
|
|
|
|
|
|
|
|
|
|
winRect.extent.y = getHeight() - ( winRect.point.y + mBitmapBounds[BorderBottom].extent.y );
|
|
|
|
|
winRect.extent.x = getWidth() - ( winRect.point.x + mBitmapBounds[BorderRight].extent.x );
|
|
|
|
|
|
|
|
|
|
// Finally, inset it by padding
|
|
|
|
|
// Inset by padding. margin is specified for all t/b/l/r but
|
|
|
|
|
// uses only pointx pointy uniformly on both ends. This should be fixed. - JDD
|
2023-12-29 18:55:28 +00:00
|
|
|
winRect.inset(mResizeMargin, mResizeMargin);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
return winRect;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bool GuiWindowCtrl::isMinimized(S32 &index)
|
|
|
|
|
{
|
|
|
|
|
index = mMinimizeIndex;
|
|
|
|
|
return mMinimized && mVisible;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::positionButtons(void)
|
|
|
|
|
{
|
|
|
|
|
if( !mBitmapBounds || !mAwake )
|
|
|
|
|
return;
|
|
|
|
|
|
2023-04-27 03:27:35 +00:00
|
|
|
S32 buttonWidth = mBitmapBounds[(U32)BmpStates * (U32)BmpClose].extent.x;
|
|
|
|
|
S32 buttonHeight = mBitmapBounds[(U32)BmpStates * (U32)BmpClose].extent.y;
|
2012-09-19 15:15:01 +00:00
|
|
|
Point2I mainOff = mProfile->mTextOffset;
|
|
|
|
|
|
|
|
|
|
// Until a pref, if alignment is LEFT, put buttons RIGHT justified.
|
|
|
|
|
// ELSE, put buttons LEFT justified.
|
2013-08-04 21:26:01 +00:00
|
|
|
S32 closeLeft = mainOff.x, closeTop = mainOff.y, closeOff = buttonWidth + 2;
|
2012-09-19 15:15:01 +00:00
|
|
|
if ( mProfile->mAlignment == GuiControlProfile::LeftJustify )
|
|
|
|
|
{
|
|
|
|
|
closeOff = -closeOff;
|
|
|
|
|
closeLeft = getWidth() - buttonWidth - mainOff.x;
|
|
|
|
|
}
|
|
|
|
|
RectI closeRect(closeLeft, closeTop, buttonHeight, buttonWidth);
|
|
|
|
|
mCloseButton = closeRect;
|
|
|
|
|
|
|
|
|
|
// Always put Minimize on left side of Maximize.
|
|
|
|
|
closeRect.point.x += closeOff;
|
|
|
|
|
if (closeOff>0)
|
|
|
|
|
{
|
|
|
|
|
mMinimizeButton = closeRect;
|
|
|
|
|
closeRect.point.x += closeOff;
|
|
|
|
|
mMaximizeButton = closeRect;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mMaximizeButton = closeRect;
|
|
|
|
|
closeRect.point.x += closeOff;
|
|
|
|
|
mMinimizeButton = closeRect;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::setCloseCommand(const char *newCmd)
|
|
|
|
|
{
|
|
|
|
|
if (newCmd)
|
|
|
|
|
mCloseCommand = StringTable->insert(newCmd);
|
|
|
|
|
else
|
2017-01-12 04:21:29 +00:00
|
|
|
mCloseCommand = StringTable->EmptyString();
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
GuiControl* GuiWindowCtrl::findHitControl(const Point2I &pt, S32 initialLayer)
|
|
|
|
|
{
|
|
|
|
|
if (! mMinimized)
|
|
|
|
|
return Parent::findHitControl(pt, initialLayer);
|
|
|
|
|
else
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bool GuiWindowCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
|
|
|
|
|
{
|
|
|
|
|
if( !Parent::resize(newPosition, newExtent) )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Set the button coords
|
|
|
|
|
positionButtons();
|
|
|
|
|
|
Adjusted handling for the bitmap and bitmapAsset fields for guiBitmapButtonCtrl to forcefully update the button states when changed, ensuring that the bitmaps refresh when changed via the field
Added callback for onResize to guiWindowCtrl so controls - such as the EditorTree - can be properly resized in certain circumstances when the window is changed
Added getIncrement() and getRange() to GuiGameSettingsCtrl to better facilitate options manipulation on the script side
Corrected some of the console method documentation strings in GuiGameSettingsCtrl
Removed some unneeded, extraneous files and their asset definitions that came from odd import conversions. Where applicable, created cleaned up versions to make naming conventions and references stable
Fixed canvas mode update text typo: FSAA -> FXAA
Added logic to DOF, Light Rays, SSAO and Vignette postFX's to check both the preset setting AND the user preference before enabling.
Shifted initialization order so PostFX's are loaded before we configure the canvas, to ensure stuff like the FXAAPostFX exists and can be toggled on on load
Fixed multiple issues with options menu:
When using gamepad, unable to navigate from categories to options. Fixed so can now traverse as normal
Input limitations on gamepad necessitated changing of how setting applying happens, is now done as a 'apply or discard' prompt when leaving the options menu
Added proper handling for adjusting settings with gamepad with left/right inputs
Fixed issue where the unapplied change for an option was sometimes being processed as an object name rather than an implicit string. Now made to be explicit strings to avoid issue.
Made the menu button input for "Select" to go from categories to options gamepad only, and hidden when in the options list
Fixed issue where changing window mode didn't correctly affect resolution option. Now set up so changing this field correctly refreshes the resolution option. Specifically, when on borderless, the resolution field does not show, preventing confusion as it is always full resolution
Generally have the options list refresh when changes happen to allow any and all fields to be able to dynamically respond to other options having changed improving flexibility.
Cleaned up old, unused, commented out functions
Added ability on OKCancel message boxes to override the button text if needed
Fixed issue with AssetBrowser where the shrink/grow icons next to the preview size slider were not anchored correctly.
Adjusted callback logic so if preview slider is clicked on, rather than dragged, it will correctly update the zoom values
Added sorting to Modules List dropdown for the AssetBrowser
Improved standardization of double-clicking in AssetBrowser. Now defaults to editing action if regularly browsing and selecting if in select mode. Still allows regular per-type overrides as normal
Moved definition of GuiDisabledTextEditProfile to gui profiles.ed.tscript file, removed duplicates to stop error spam
Adjusted default settings value for double-click action in AB to be edit to prevent unstable behavior
Removed old file refs from Load Recent list in the default settings
2022-03-27 03:36:37 +00:00
|
|
|
onResize_callback(newPosition.x, newPosition.y, newExtent.x, newExtent.y);
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
GuiControl *GuiWindowCtrl::findNextTabable(GuiControl *curResponder, bool firstCall)
|
|
|
|
|
{
|
|
|
|
|
// Set the global if this is the first call (directly from the canvas)
|
|
|
|
|
if (firstCall)
|
|
|
|
|
{
|
|
|
|
|
GuiControl::smCurResponder = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the window does not already contain the first responder, return false
|
|
|
|
|
// ie. Can't tab into or out of a window
|
|
|
|
|
if (! controlIsChild(curResponder))
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Loop through, checking each child to see if it is the one that follows the firstResponder
|
|
|
|
|
GuiControl *tabCtrl = NULL;
|
|
|
|
|
iterator i;
|
|
|
|
|
for (i = begin(); i != end(); i++)
|
|
|
|
|
{
|
|
|
|
|
GuiControl *ctrl = static_cast<GuiControl *>(*i);
|
|
|
|
|
tabCtrl = ctrl->findNextTabable(curResponder, false);
|
|
|
|
|
if (tabCtrl) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// To ensure the tab cycles within the current window...
|
|
|
|
|
if (! tabCtrl)
|
|
|
|
|
{
|
|
|
|
|
tabCtrl = findFirstTabable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mFirstResponder = tabCtrl;
|
|
|
|
|
return tabCtrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
GuiControl *GuiWindowCtrl::findPrevTabable(GuiControl *curResponder, bool firstCall)
|
|
|
|
|
{
|
|
|
|
|
if (firstCall)
|
|
|
|
|
{
|
|
|
|
|
GuiControl::smPrevResponder = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the window does not already contain the first responder, return false
|
|
|
|
|
// ie. Can't tab into or out of a window
|
|
|
|
|
if (! controlIsChild(curResponder))
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Loop through, checking each child to see if it is the one that follows the firstResponder
|
|
|
|
|
GuiControl *tabCtrl = NULL;
|
|
|
|
|
iterator i;
|
|
|
|
|
for (i = begin(); i != end(); i++)
|
|
|
|
|
{
|
|
|
|
|
GuiControl *ctrl = static_cast<GuiControl *>(*i);
|
|
|
|
|
tabCtrl = ctrl->findPrevTabable(curResponder, false);
|
|
|
|
|
if (tabCtrl) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// To ensure the tab cycles within the current window...
|
|
|
|
|
if (! tabCtrl)
|
|
|
|
|
{
|
|
|
|
|
tabCtrl = findLastTabable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mFirstResponder = tabCtrl;
|
|
|
|
|
return tabCtrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::selectWindow(void)
|
|
|
|
|
{
|
|
|
|
|
// First make sure this window is the front most of its siblings
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if (parent && *parent->end() != this )
|
|
|
|
|
{
|
|
|
|
|
// Valid collapse groups have to be selected together
|
|
|
|
|
if( mCanCollapse && mCollapseGroup >= 0 )
|
|
|
|
|
{
|
|
|
|
|
CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin();
|
|
|
|
|
for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
parent->pushObjectToBack( (*iter) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
parent->pushObjectToBack( this );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Also set the first responder to be the one within this window
|
|
|
|
|
setFirstResponder(mFirstResponder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent)
|
|
|
|
|
{
|
|
|
|
|
GuiCanvas *pRoot = getRoot();
|
|
|
|
|
if( !pRoot )
|
|
|
|
|
return;
|
|
|
|
|
PlatformWindow *pWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow();
|
|
|
|
|
AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible.");
|
|
|
|
|
PlatformCursorController *pController = pWindow->getCursorController();
|
|
|
|
|
AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!");
|
|
|
|
|
|
|
|
|
|
S32 desiredCursor = PlatformCursorController::curArrow;
|
|
|
|
|
S32 hitEdges = findHitEdges( lastGuiEvent.mousePoint );
|
|
|
|
|
|
|
|
|
|
if( hitEdges & edgeBottom && hitEdges & edgeLeft && mResizeHeight )
|
|
|
|
|
desiredCursor = PlatformCursorController::curResizeNESW;
|
|
|
|
|
else if( hitEdges & edgeBottom && hitEdges & edgeRight && mResizeHeight )
|
|
|
|
|
desiredCursor = PlatformCursorController::curResizeNWSE;
|
|
|
|
|
else if( hitEdges & edgeBottom && mResizeHeight )
|
|
|
|
|
desiredCursor = PlatformCursorController::curResizeHorz;
|
|
|
|
|
else if( hitEdges & edgeTop && hitEdges & edgeLeft && mResizeHeight )
|
|
|
|
|
desiredCursor = PlatformCursorController::curResizeNWSE;
|
|
|
|
|
else if( hitEdges & edgeTop && hitEdges & edgeRight && mResizeHeight )
|
|
|
|
|
desiredCursor = PlatformCursorController::curResizeNESW;
|
|
|
|
|
else if( hitEdges & edgeTop && mResizeHeight )
|
|
|
|
|
desiredCursor = PlatformCursorController::curResizeHorz;
|
|
|
|
|
else if ( hitEdges & edgeLeft && mResizeWidth )
|
|
|
|
|
desiredCursor = PlatformCursorController::curResizeVert;
|
|
|
|
|
else if( hitEdges & edgeRight && mResizeWidth )
|
|
|
|
|
desiredCursor = PlatformCursorController::curResizeVert;
|
|
|
|
|
else
|
|
|
|
|
desiredCursor = PlatformCursorController::curArrow;
|
|
|
|
|
|
|
|
|
|
// Bail if we're already at the desired cursor
|
|
|
|
|
if(pRoot->mCursorChanged == desiredCursor )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Now change the cursor shape
|
|
|
|
|
pController->popCursor();
|
|
|
|
|
pController->pushCursor(desiredCursor);
|
|
|
|
|
pRoot->mCursorChanged = desiredCursor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void GuiWindowCtrl::parentResized(const RectI &oldParentRect, const RectI &newParentRect)
|
|
|
|
|
{
|
|
|
|
|
if(!mCanResize)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
GuiControl *parent = getParent();
|
|
|
|
|
if( !parent )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Bail if were not sized both by windowrelative
|
|
|
|
|
if( mHorizSizing != horizResizeWindowRelative || mHorizSizing != vertResizeWindowRelative )
|
|
|
|
|
return Parent::parentResized( oldParentRect, newParentRect );
|
|
|
|
|
|
|
|
|
|
Point2I newPosition = getPosition();
|
|
|
|
|
Point2I newExtent = getExtent();
|
|
|
|
|
|
|
|
|
|
bool doCollapse = false;
|
|
|
|
|
|
|
|
|
|
S32 deltaX = newParentRect.extent.x - oldParentRect.extent.x;
|
|
|
|
|
S32 deltaY = newParentRect.extent.y - oldParentRect.extent.y;// + mProfile->mYPositionOffset;
|
|
|
|
|
|
|
|
|
|
if( newPosition.x > ( oldParentRect.extent.x / 2 ) )
|
|
|
|
|
newPosition.x = newPosition.x + deltaX;
|
|
|
|
|
|
|
|
|
|
if (oldParentRect.extent.y != 0)
|
|
|
|
|
{
|
|
|
|
|
// Only if were apart of a group
|
|
|
|
|
if ( mCanCollapse && mCollapseGroup >= 0 )
|
|
|
|
|
{
|
|
|
|
|
// Setup parsing mechanisms
|
|
|
|
|
CollapseGroupNumVec collapseGroupNumVec;
|
|
|
|
|
|
|
|
|
|
// Lets grab the information we need (this should probably be already stored on each individual window object)
|
|
|
|
|
S32 groupNum = mCollapseGroup;
|
|
|
|
|
S32 groupMax = parent->mCollapseGroupVec[ groupNum ].size() - 1;
|
|
|
|
|
|
|
|
|
|
// Set up vars that we're going to be using
|
|
|
|
|
S32 groupPos = 0;
|
|
|
|
|
S32 groupExtent = 0;
|
|
|
|
|
S32 tempGroupExtent = 0;
|
|
|
|
|
|
|
|
|
|
// Set up the vector/iterator combo we need
|
|
|
|
|
collapseGroupNumVec = parent->mCollapseGroupVec[ groupNum ];
|
|
|
|
|
CollapseGroupNumVec::iterator iter = collapseGroupNumVec.begin();
|
|
|
|
|
|
|
|
|
|
// Grab some more information we need later ( this info should also be located on each ind. window object )
|
|
|
|
|
for( ; iter != collapseGroupNumVec.end(); iter++ )
|
|
|
|
|
{
|
|
|
|
|
if((*iter)->getCollapseGroupNum() == 0)
|
|
|
|
|
{
|
|
|
|
|
groupPos = (*iter)->getPosition().y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
groupExtent += (*iter)->getExtent().y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use the information we just gatherered; only enter this block if we need to
|
|
|
|
|
tempGroupExtent = groupPos + groupExtent;
|
|
|
|
|
if( tempGroupExtent > ( newParentRect.extent.y / 2 ) )
|
|
|
|
|
{
|
|
|
|
|
// Lets size the collapse group
|
|
|
|
|
S32 windowParser = groupMax;
|
|
|
|
|
bool secondLoop = false;
|
|
|
|
|
while( tempGroupExtent >= newParentRect.extent.y )
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if( windowParser == -1)
|
|
|
|
|
{
|
|
|
|
|
if( !secondLoop )
|
|
|
|
|
{
|
|
|
|
|
secondLoop = true;
|
|
|
|
|
windowParser = groupMax;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GuiWindowCtrl *tempWindow = collapseGroupNumVec[windowParser];
|
|
|
|
|
if(tempWindow->mIsCollapsed)
|
|
|
|
|
{
|
|
|
|
|
windowParser--;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resizing block for the loop... if we can get away with just resizing the bottom window do it before
|
|
|
|
|
// resizing the whole block. else, go through the group and start setting min extents. if that doesnt work
|
|
|
|
|
// on the second go around, start collpsing the windows.
|
|
|
|
|
if( tempGroupExtent - ( tempWindow->getExtent().y - tempWindow->getMinExtent().y ) <= newParentRect.extent.y )
|
|
|
|
|
{
|
|
|
|
|
if( this == tempWindow )
|
|
|
|
|
newExtent.y = newExtent.y - ( tempGroupExtent - newParentRect.extent.y );
|
|
|
|
|
|
|
|
|
|
tempGroupExtent = tempGroupExtent - newParentRect.extent.y;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if( secondLoop )
|
|
|
|
|
{
|
|
|
|
|
tempGroupExtent = tempGroupExtent - (tempWindow->getExtent().y + 32);
|
|
|
|
|
|
|
|
|
|
if( this == tempWindow )
|
|
|
|
|
doCollapse = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tempGroupExtent = tempGroupExtent - (tempWindow->getExtent().y - tempWindow->getMinExtent().y);
|
|
|
|
|
if( this == tempWindow )
|
|
|
|
|
newExtent.y = tempWindow->getMinExtent().y;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
windowParser--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if( newPosition.y > ( oldParentRect.extent.y / 2 ) )
|
|
|
|
|
{
|
|
|
|
|
newPosition.y = newPosition.y + deltaY - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( newExtent.x >= getMinExtent().x && newExtent.y >= getMinExtent().y )
|
|
|
|
|
{
|
|
|
|
|
// If we are already outside the reach of the main window, lets not place ourselves
|
|
|
|
|
// further out; but if were trying to improve visibility, go for it
|
|
|
|
|
// note: small tolerance (4) added to keep guis that are very slightly outside
|
|
|
|
|
// the main window (like all of the editor windows!) from appearing to 'undock'
|
|
|
|
|
// when the resolution is changed.
|
|
|
|
|
if( (newPosition.x + newExtent.x) > newParentRect.extent.x + 4 )
|
|
|
|
|
{
|
|
|
|
|
if( (newPosition.x + newExtent.x) > (getPosition().x + getExtent().x) )
|
|
|
|
|
{
|
|
|
|
|
newPosition.x = getPosition().x;
|
|
|
|
|
newExtent.x = getExtent().x;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if( (newPosition.y + newExtent.y) > newParentRect.extent.y + 4)
|
|
|
|
|
{
|
|
|
|
|
if( (newPosition.y + newExtent.y) > (getPosition().y + getExtent().y) )
|
|
|
|
|
{
|
|
|
|
|
newPosition.y = getPosition().y;
|
|
|
|
|
newExtent.y = getExtent().y;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only for collpasing groups, if were not, then do it like normal windows
|
|
|
|
|
if( mCanCollapse && mCollapseGroup >= 0 )
|
2017-01-12 04:34:46 +00:00
|
|
|
{
|
2012-09-19 15:15:01 +00:00
|
|
|
bool resizeMe = false;
|
|
|
|
|
|
|
|
|
|
// Only the group window should control positioning
|
|
|
|
|
if( mCollapseGroupNum == 0 )
|
|
|
|
|
{
|
|
|
|
|
resizeMe = resizeCollapseGroup( true, true, (getPosition() - newPosition), (getExtent() - newExtent) );
|
|
|
|
|
if(resizeMe == true)
|
|
|
|
|
resize(newPosition, newExtent);
|
|
|
|
|
}
|
|
|
|
|
else if( getExtent() != newExtent)
|
|
|
|
|
{
|
|
|
|
|
resizeMe = resizeCollapseGroup( false, true, (getPosition() - newPosition), (getExtent() - newExtent) );
|
|
|
|
|
if(resizeMe == true)
|
|
|
|
|
resize(getPosition(), newExtent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
resize(newPosition, newExtent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( mCanCollapse && !mIsCollapsed && doCollapse )
|
|
|
|
|
toggleCollapseGroup();
|
|
|
|
|
|
|
|
|
|
// If docking is invalid on this control, then bail out here
|
|
|
|
|
if( getDocking() & Docking::dockInvalid || getDocking() & Docking::dockNone)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Update Self
|
|
|
|
|
RectI oldThisRect = getBounds();
|
|
|
|
|
anchorControl( this, Point2I( deltaX, deltaY ) );
|
|
|
|
|
RectI newThisRect = getBounds();
|
|
|
|
|
|
|
|
|
|
// Update Deltas to pass on to children
|
|
|
|
|
deltaX = newThisRect.extent.x - oldThisRect.extent.x;
|
|
|
|
|
deltaY = newThisRect.extent.y - oldThisRect.extent.y;
|
|
|
|
|
|
|
|
|
|
// Iterate over all children and update their anchors
|
|
|
|
|
iterator nI = begin();
|
|
|
|
|
for( ; nI != end(); nI++ )
|
|
|
|
|
{
|
|
|
|
|
// Sanity
|
|
|
|
|
GuiControl *control = dynamic_cast<GuiControl*>( (*nI) );
|
|
|
|
|
if( control )
|
|
|
|
|
control->parentResized( oldThisRect, newThisRect );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
// Console Methods.
|
|
|
|
|
//=============================================================================
|
|
|
|
|
// MARK: ---- Console Methods ----
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
DefineEngineMethod( GuiWindowCtrl, selectWindow, void, (),,
|
|
|
|
|
"Bring the window to the front." )
|
|
|
|
|
{
|
|
|
|
|
object->selectWindow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
DefineEngineMethod( GuiWindowCtrl, setCollapseGroup, void, ( bool state ),,
|
|
|
|
|
"Set the window's collapsing state." )
|
|
|
|
|
{
|
|
|
|
|
object->setCollapseGroup(state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
DefineEngineMethod( GuiWindowCtrl, toggleCollapseGroup, void, (),,
|
|
|
|
|
"Toggle the window collapsing." )
|
|
|
|
|
{
|
|
|
|
|
object->toggleCollapseGroup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
DefineEngineMethod( GuiWindowCtrl, attachTo, void, ( GuiWindowCtrl* window ),,
|
|
|
|
|
"" )
|
|
|
|
|
{
|
|
|
|
|
object->moveToCollapseGroup( window, 1 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
DefineEngineStaticMethod( GuiWindowCtrl, attach, void, ( GuiWindowCtrl* bottomWindow, GuiWindowCtrl* topWindow ),,
|
|
|
|
|
"Attach @a bottomWindow to @topWindow so that @a bottomWindow moves along with @a topWindow when it is dragged.\n\n"
|
|
|
|
|
"@param bottomWindow \n"
|
|
|
|
|
"@param topWindow " )
|
|
|
|
|
{
|
|
|
|
|
if(bottomWindow == NULL || topWindow == NULL)
|
|
|
|
|
{
|
|
|
|
|
Con::warnf("Warning: AttachWindows - could not find windows");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bottomWindow->moveToCollapseGroup( topWindow, 1 );
|
|
|
|
|
}
|