guiEditCtrl.cpp
Engine/source/gui/editor/guiEditCtrl.cpp
Classes:
class
Public Variables
Public Functions
ConsoleDocClass(GuiEditCtrl , "@brief Native side of the GUI <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">editor.\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
ConsoleDocClass(GuiEditorRuler , "@brief Visual representation of markers on top and left sides of GUI <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(GuiEditCtrl , addNewCtrl , void , (GuiControl *ctrl) , "(GuiControl ctrl)" )
DefineEngineMethod(GuiEditCtrl , addSelection , void , (S32 id) , "selects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> control." )
DefineEngineMethod(GuiEditCtrl , bringToFront , void , () , "" )
DefineEngineMethod(GuiEditCtrl , clearGuides , void , (S32 axis) , (-1) , "( [ int axis ] ) - Clear all currently set guide lines." )
DefineEngineMethod(GuiEditCtrl , clearSelection , void , () , "Clear selected controls list." )
DefineEngineMethod(GuiEditCtrl , deleteSelection , void , () , "() - Delete the selected controls." )
DefineEngineMethod(GuiEditCtrl , fitIntoParents , void , (bool width, bool height) , (true, true) , "( bool width=true, bool height=true ) - Fit selected controls into their parents." )
DefineEngineMethod(GuiEditCtrl , getContentControl , S32 , () , "() - Return the toplevel <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> edited inside the GUI editor." )
DefineEngineMethod(GuiEditCtrl , getCurrentAddSet , S32 , () , "Returns the set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> which <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> controls will be added" )
DefineEngineMethod(GuiEditCtrl , getMouseMode , const char * , () , "() - Return the current mouse mode." )
DefineEngineMethod(GuiEditCtrl , getNumSelected , S32 , () , "() - Return the number of controls currently selected." )
DefineEngineMethod(GuiEditCtrl , getSelection , SimSet * , () , "Gets the set of GUI controls currently selected in the editor." )
DefineEngineMethod(GuiEditCtrl , getSelectionGlobalBounds , const char * , () , "() - Returns global bounds of current selection as vector 'x y width height'." )
DefineEngineMethod(GuiEditCtrl , getTrash , SimGroup * , () , "Gets the GUI controls(s) that are currently in the trash." )
DefineEngineMethod(GuiEditCtrl , justify , void , (U32 mode) , "(int <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a>)" )
DefineEngineMethod(GuiEditCtrl , loadSelection , void , (const char *filename) , (nullAsType< const char * >()) , "( string fileName=null ) - Load selection from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> or clipboard." )
DefineEngineMethod(GuiEditCtrl , moveSelection , void , (S32 dx, S32 dy) , "Move all controls in the selection by (dx,dy) pixels." )
DefineEngineMethod(GuiEditCtrl , pushToBack , void , () , "" )
DefineEngineMethod(GuiEditCtrl , readGuides , void , (GuiControl *ctrl, S32 axis) , (-1) , "( GuiControl ctrl [, int axis ] ) - Read the guides from the given control." )
DefineEngineMethod(GuiEditCtrl , removeSelection , void , (S32 id) , "deselects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> control." )
DefineEngineMethod(GuiEditCtrl , saveSelection , void , (const char *filename) , (nullAsType< const char * >()) , "( string fileName=null ) - Save selection <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> or clipboard." )
DefineEngineMethod(GuiEditCtrl , select , void , (GuiControl *ctrl) , "(GuiControl ctrl)" )
DefineEngineMethod(GuiEditCtrl , selectAll , void , () , "()" )
DefineEngineMethod(GuiEditCtrl , selectChildren , void , (bool addToSelection) , (false) , "( bool addToSelection=false ) - Select children of currently selected controls." )
DefineEngineMethod(GuiEditCtrl , selectParents , void , (bool addToSelection) , (false) , "( bool addToSelection=false ) - Select parents of currently selected controls." )
DefineEngineMethod(GuiEditCtrl , setContentControl , void , (GuiControl *ctrl) , "( GuiControl ctrl ) - Set the toplevel <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> edit in the GUI editor." )
DefineEngineMethod(GuiEditCtrl , setCurrentAddSet , void , (GuiControl *addSet) , "(GuiControl ctrl)" )
DefineEngineMethod(GuiEditCtrl , setSnapToGrid , void , (U32 gridsize) , "GuiEditCtrl.setSnapToGrid(gridsize)" )
DefineEngineMethod(GuiEditCtrl , toggle , void , () , "Toggle activation." )
DefineEngineMethod(GuiEditCtrl , writeGuides , void , (GuiControl *ctrl, S32 axis) , (-1) , "( GuiControl ctrl [, int axis ] ) - Write the guides <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the given control." )
IMPLEMENT_CALLBACK(GuiEditCtrl , onAddNewCtrl , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onAddNewCtrlSet , void , (SimSet *set) , (set) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onAddSelected , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onClearSelected , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onControlInspectPostApply , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onControlInspectPreApply , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onDelete , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onFitIntoParent , void , (bool width, bool height) , (width, height) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onHierarchyChanged , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onMouseModeChange , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPostEdit , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPostSelectionNudged , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPreEdit , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPreSelectionNudged , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onRemoveSelected , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionCloned , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionMoved , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionResized , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onTrashSelection , void , (SimSet *selection) , (selection) , "" )
Detailed Description
Public Variables
GuiControl * control
onSelect
void
Public Functions
ConsoleDocClass(GuiEditCtrl , "@brief Native side of the GUI <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">editor.\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
ConsoleDocClass(GuiEditorRuler , "@brief Visual representation of markers on top and left sides of GUI <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(GuiEditCtrl , addNewCtrl , void , (GuiControl *ctrl) , "(GuiControl ctrl)" )
DefineEngineMethod(GuiEditCtrl , addSelection , void , (S32 id) , "selects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> control." )
DefineEngineMethod(GuiEditCtrl , bringToFront , void , () , "" )
DefineEngineMethod(GuiEditCtrl , clearGuides , void , (S32 axis) , (-1) , "( [ int axis ] ) - Clear all currently set guide lines." )
DefineEngineMethod(GuiEditCtrl , clearSelection , void , () , "Clear selected controls list." )
DefineEngineMethod(GuiEditCtrl , deleteSelection , void , () , "() - Delete the selected controls." )
DefineEngineMethod(GuiEditCtrl , fitIntoParents , void , (bool width, bool height) , (true, true) , "( bool width=true, bool height=true ) - Fit selected controls into their parents." )
DefineEngineMethod(GuiEditCtrl , getContentControl , S32 , () , "() - Return the toplevel <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> edited inside the GUI editor." )
DefineEngineMethod(GuiEditCtrl , getCurrentAddSet , S32 , () , "Returns the set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> which <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> controls will be added" )
DefineEngineMethod(GuiEditCtrl , getMouseMode , const char * , () , "() - Return the current mouse mode." )
DefineEngineMethod(GuiEditCtrl , getNumSelected , S32 , () , "() - Return the number of controls currently selected." )
DefineEngineMethod(GuiEditCtrl , getSelection , SimSet * , () , "Gets the set of GUI controls currently selected in the editor." )
DefineEngineMethod(GuiEditCtrl , getSelectionGlobalBounds , const char * , () , "() - Returns global bounds of current selection as vector 'x y width height'." )
DefineEngineMethod(GuiEditCtrl , getTrash , SimGroup * , () , "Gets the GUI controls(s) that are currently in the trash." )
DefineEngineMethod(GuiEditCtrl , justify , void , (U32 mode) , "(int <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a>)" )
DefineEngineMethod(GuiEditCtrl , loadSelection , void , (const char *filename) , (nullAsType< const char * >()) , "( string fileName=null ) - Load selection from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> or clipboard." )
DefineEngineMethod(GuiEditCtrl , moveSelection , void , (S32 dx, S32 dy) , "Move all controls in the selection by (dx,dy) pixels." )
DefineEngineMethod(GuiEditCtrl , pushToBack , void , () , "" )
DefineEngineMethod(GuiEditCtrl , readGuides , void , (GuiControl *ctrl, S32 axis) , (-1) , "( GuiControl ctrl [, int axis ] ) - Read the guides from the given control." )
DefineEngineMethod(GuiEditCtrl , removeSelection , void , (S32 id) , "deselects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> control." )
DefineEngineMethod(GuiEditCtrl , saveSelection , void , (const char *filename) , (nullAsType< const char * >()) , "( string fileName=null ) - Save selection <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> or clipboard." )
DefineEngineMethod(GuiEditCtrl , select , void , (GuiControl *ctrl) , "(GuiControl ctrl)" )
DefineEngineMethod(GuiEditCtrl , selectAll , void , () , "()" )
DefineEngineMethod(GuiEditCtrl , selectChildren , void , (bool addToSelection) , (false) , "( bool addToSelection=false ) - Select children of currently selected controls." )
DefineEngineMethod(GuiEditCtrl , selectParents , void , (bool addToSelection) , (false) , "( bool addToSelection=false ) - Select parents of currently selected controls." )
DefineEngineMethod(GuiEditCtrl , setContentControl , void , (GuiControl *ctrl) , "( GuiControl ctrl ) - Set the toplevel <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> edit in the GUI editor." )
DefineEngineMethod(GuiEditCtrl , setCurrentAddSet , void , (GuiControl *addSet) , "(GuiControl ctrl)" )
DefineEngineMethod(GuiEditCtrl , setSnapToGrid , void , (U32 gridsize) , "GuiEditCtrl.setSnapToGrid(gridsize)" )
DefineEngineMethod(GuiEditCtrl , toggle , void , () , "Toggle activation." )
DefineEngineMethod(GuiEditCtrl , writeGuides , void , (GuiControl *ctrl, S32 axis) , (-1) , "( GuiControl ctrl [, int axis ] ) - Write the guides <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the given control." )
IMPLEMENT_CALLBACK(GuiEditCtrl , onAddNewCtrl , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onAddNewCtrlSet , void , (SimSet *set) , (set) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onAddSelected , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onClearSelected , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onControlInspectPostApply , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onControlInspectPreApply , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onDelete , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onFitIntoParent , void , (bool width, bool height) , (width, height) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onHierarchyChanged , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onMouseModeChange , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPostEdit , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPostSelectionNudged , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPreEdit , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPreSelectionNudged , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onRemoveSelected , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionCloned , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionMoved , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionResized , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onTrashSelection , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CONOBJECT(GuiEditCtrl )
IMPLEMENT_CONOBJECT(GuiEditorRuler )
snapPoint(Point2I point, Point2I delta, Point2I gridSnap)
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/editor/guiEditCtrl.h" 26 27#include "core/frameAllocator.h" 28#include "core/stream/fileStream.h" 29#include "core/stream/memStream.h" 30#include "console/consoleTypes.h" 31#include "gui/core/guiCanvas.h" 32#include "gui/containers/guiScrollCtrl.h" 33#include "core/strings/stringUnit.h" 34#include "console/engineAPI.h" 35 36 37IMPLEMENT_CONOBJECT( GuiEditCtrl ); 38 39ConsoleDocClass( GuiEditCtrl, 40 "@brief Native side of the GUI editor.\n\n" 41 "Editor use only.\n\n" 42 "@internal" 43); 44 45IMPLEMENT_CALLBACK( GuiEditCtrl, onHierarchyChanged, void, (), (), 46 "" ); 47IMPLEMENT_CALLBACK( GuiEditCtrl, onDelete, void, (), (), 48 "" ); 49IMPLEMENT_CALLBACK( GuiEditCtrl, onPreEdit, void, ( SimSet* selection ), ( selection ), 50 "" ); 51IMPLEMENT_CALLBACK( GuiEditCtrl, onPostEdit, void, ( SimSet* selection ), ( selection ), 52 "" ); 53IMPLEMENT_CALLBACK( GuiEditCtrl, onClearSelected, void, (), (), 54 "" ) 55IMPLEMENT_CALLBACK( GuiEditCtrl, onSelect, void, ( GuiControl* control ), ( control ), 56 "" ); 57IMPLEMENT_CALLBACK( GuiEditCtrl, onAddSelected, void, ( GuiControl* control ), ( control ), 58 "" ); 59IMPLEMENT_CALLBACK( GuiEditCtrl, onRemoveSelected, void, ( GuiControl* control ), ( control ), 60 "" ); 61IMPLEMENT_CALLBACK( GuiEditCtrl, onPreSelectionNudged, void, ( SimSet* selection ), ( selection ), 62 "" ); 63IMPLEMENT_CALLBACK( GuiEditCtrl, onPostSelectionNudged, void, ( SimSet* selection ), ( selection ), 64 "" ); 65IMPLEMENT_CALLBACK( GuiEditCtrl, onSelectionMoved, void, ( GuiControl* control ), ( control ), 66 "" ); 67IMPLEMENT_CALLBACK( GuiEditCtrl, onSelectionCloned, void, ( SimSet* selection ), ( selection ), 68 "" ); 69IMPLEMENT_CALLBACK( GuiEditCtrl, onTrashSelection, void, ( SimSet* selection ), ( selection ), 70 "" ); 71IMPLEMENT_CALLBACK( GuiEditCtrl, onAddNewCtrl, void, ( GuiControl* control ), ( control ), 72 "" ); 73IMPLEMENT_CALLBACK( GuiEditCtrl, onAddNewCtrlSet, void, ( SimSet* set ), ( set ), 74 "" ); 75IMPLEMENT_CALLBACK( GuiEditCtrl, onSelectionResized, void, ( GuiControl* control ), ( control ), 76 "" ); 77IMPLEMENT_CALLBACK( GuiEditCtrl, onFitIntoParent, void, ( bool width, bool height ), ( width, height ), 78 "" ); 79IMPLEMENT_CALLBACK( GuiEditCtrl, onMouseModeChange, void, (), (), 80 "" ); 81IMPLEMENT_CALLBACK( GuiEditCtrl, onControlInspectPreApply, void, ( GuiControl* control ), ( control ), 82 "" ); 83IMPLEMENT_CALLBACK( GuiEditCtrl, onControlInspectPostApply, void, ( GuiControl* control ), ( control ), 84 "" ); 85 86 87StringTableEntry GuiEditCtrl::smGuidesPropertyName[ 2 ]; 88 89 90//----------------------------------------------------------------------------- 91 92GuiEditCtrl::GuiEditCtrl() 93 : mCurrentAddSet( NULL ), 94 mContentControl( NULL ), 95 mGridSnap( 0, 0 ), 96 mDragBeginPoint( -1, -1 ), 97 mSnapToControls( true ), 98 mSnapToEdges( true ), 99 mSnapToGuides( true ), 100 mSnapToCenters( true ), 101 mSnapToCanvas( true ), 102 mDrawBorderLines( true ), 103 mFullBoxSelection( false ), 104 mSnapSensitivity( 2 ), 105 mDrawGuides( true ), 106 mDragAddSelection(false), 107 mDragMoveUndo(false) 108{ 109 VECTOR_SET_ASSOCIATION( mSelectedControls ); 110 VECTOR_SET_ASSOCIATION( mDragBeginPoints ); 111 VECTOR_SET_ASSOCIATION( mSnapHits[ 0 ] ); 112 VECTOR_SET_ASSOCIATION( mSnapHits[ 1 ] ); 113 114 mActive = true; 115 mDotSB = NULL; 116 117 mSnapped[ SnapVertical ] = false; 118 mSnapped[ SnapHorizontal ] = false; 119 120 mDragGuide[ GuideVertical ] = false; 121 mDragGuide[ GuideHorizontal ] = false; 122 mDragGuideIndex[0] = 0; 123 mDragGuideIndex[1] = 1; 124 125 std::fill_n(mSnapOffset, 2, 0); 126 std::fill_n(mSnapEdge, 2, SnapEdgeMin); 127 128 if( !smGuidesPropertyName[ GuideVertical ] ) 129 smGuidesPropertyName[ GuideVertical ] = StringTable->insert( "guidesVertical" ); 130 if( !smGuidesPropertyName[ GuideHorizontal ] ) 131 smGuidesPropertyName[ GuideHorizontal ] = StringTable->insert( "guidesHorizontal" ); 132 133 mTrash = NULL; 134 mSelectedSet = NULL; 135 mMouseDownMode = GuiEditCtrl::Selecting; 136 mSizingMode = GuiEditCtrl::sizingNone; 137} 138 139//----------------------------------------------------------------------------- 140 141void GuiEditCtrl::initPersistFields() 142{ 143 addGroup( "Snapping" ); 144 addField( "snapToControls", TypeBool, Offset( mSnapToControls, GuiEditCtrl ), 145 "If true, edge and center snapping will work against controls." ); 146 addField( "snapToGuides", TypeBool, Offset( mSnapToGuides, GuiEditCtrl ), 147 "If true, edge and center snapping will work against guides." ); 148 addField( "snapToCanvas", TypeBool, Offset( mSnapToCanvas, GuiEditCtrl ), 149 "If true, edge and center snapping will work against canvas (toplevel control)." ); 150 addField( "snapToEdges", TypeBool, Offset( mSnapToEdges, GuiEditCtrl ), 151 "If true, selection edges will snap into alignment when moved or resized." ); 152 addField( "snapToCenters", TypeBool, Offset( mSnapToCenters, GuiEditCtrl ), 153 "If true, selection centers will snap into alignment when moved or resized." ); 154 addField( "snapSensitivity", TypeS32, Offset( mSnapSensitivity, GuiEditCtrl ), 155 "Distance in pixels that edge and center snapping will work across." ); 156 endGroup( "Snapping" ); 157 158 addGroup( "Selection" ); 159 addField( "fullBoxSelection", TypeBool, Offset( mFullBoxSelection, GuiEditCtrl ), 160 "If true, rectangle selection will only select controls fully inside the drag rectangle." ); 161 endGroup( "Selection" ); 162 163 addGroup( "Rendering" ); 164 addField( "drawBorderLines", TypeBool, Offset( mDrawBorderLines, GuiEditCtrl ), 165 "If true, lines will be drawn extending along the edges of selected objects." ); 166 addField( "drawGuides", TypeBool, Offset( mDrawGuides, GuiEditCtrl ), 167 "If true, guides will be included in rendering." ); 168 endGroup( "Rendering" ); 169 170 Parent::initPersistFields(); 171} 172 173//============================================================================= 174// Events. 175//============================================================================= 176// MARK: ---- Events ---- 177 178//----------------------------------------------------------------------------- 179 180bool GuiEditCtrl::onAdd() 181{ 182 if( !Parent::onAdd() ) 183 return false; 184 185 mTrash = new SimGroup(); 186 mSelectedSet = new SimSet(); 187 188 if( !mTrash->registerObject() ) 189 return false; 190 if( !mSelectedSet->registerObject() ) 191 return false; 192 193 return true; 194} 195 196//----------------------------------------------------------------------------- 197 198void GuiEditCtrl::onRemove() 199{ 200 Parent::onRemove(); 201 202 mDotSB = NULL; 203 204 mTrash->deleteObject(); 205 mSelectedSet->deleteObject(); 206 207 mTrash = NULL; 208 mSelectedSet = NULL; 209} 210 211//----------------------------------------------------------------------------- 212 213bool GuiEditCtrl::onWake() 214{ 215 if (! Parent::onWake()) 216 return false; 217 218 // Set GUI Controls to DesignTime mode 219 GuiControl::smDesignTime = true; 220 GuiControl::smEditorHandle = this; 221 222 setEditMode(true); 223 224 return true; 225} 226 227//----------------------------------------------------------------------------- 228 229void GuiEditCtrl::onSleep() 230{ 231 // Set GUI Controls to run time mode 232 GuiControl::smDesignTime = false; 233 GuiControl::smEditorHandle = NULL; 234 235 Parent::onSleep(); 236} 237 238//----------------------------------------------------------------------------- 239 240bool GuiEditCtrl::onKeyDown(const GuiEvent &event) 241{ 242 if (! mActive) 243 return Parent::onKeyDown(event); 244 245 if (!(event.modifier & SI_PRIMARY_CTRL)) 246 { 247 switch(event.keyCode) 248 { 249 case KEY_BACKSPACE: 250 case KEY_DELETE: 251 deleteSelection(); 252 onDelete_callback(); 253 return true; 254 default: 255 break; 256 } 257 } 258 return false; 259} 260 261//----------------------------------------------------------------------------- 262 263void GuiEditCtrl::onMouseDown(const GuiEvent &event) 264{ 265 if (! mActive) 266 { 267 Parent::onMouseDown(event); 268 return; 269 } 270 if(!mContentControl) 271 return; 272 273 setFirstResponder(); 274 mouseLock(); 275 276 mLastMousePos = globalToLocalCoord( event.mousePoint ); 277 278 // Check whether we've hit a guide. If so, start a guide drag. 279 // Don't do this if SHIFT is down. 280 281 if( !( event.modifier & SI_SHIFT ) ) 282 { 283 for( U32 axis = 0; axis < 2; ++ axis ) 284 { 285 const S32 guide = findGuide( ( guideAxis ) axis, event.mousePoint, 1 ); 286 if( guide != -1 ) 287 { 288 setMouseMode( DragGuide ); 289 290 mDragGuide[ axis ] = true; 291 mDragGuideIndex[ axis ] = guide; 292 } 293 } 294 295 if( mMouseDownMode == DragGuide ) 296 return; 297 } 298 299 // Check whether we have hit a sizing knob on any of the currently selected 300 // controls. 301 302 for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i ) 303 { 304 GuiControl* ctrl = mSelectedControls[ i ]; 305 306 Point2I cext = ctrl->getExtent(); 307 Point2I ctOffset = globalToLocalCoord( ctrl->localToGlobalCoord( Point2I( 0, 0 ) ) ); 308 309 RectI box( ctOffset.x, ctOffset.y, cext.x, cext.y ); 310 311 if( ( mSizingMode = ( GuiEditCtrl::sizingModes ) getSizingHitKnobs( mLastMousePos, box ) ) != 0 ) 312 { 313 setMouseMode( SizingSelection ); 314 mLastDragPos = event.mousePoint; 315 316 // undo 317 onPreEdit_callback( getSelectedSet() ); 318 return; 319 } 320 } 321 322 // Find the control we have hit. 323 324 GuiControl* ctrl = mContentControl->findHitControl( mLastMousePos, getCurrentAddSet()->mLayer ); 325 326 // Give the control itself the opportunity to handle the event 327 // to implement custom editing logic. 328 329 bool handledEvent = ctrl->onMouseDownEditor( event, localToGlobalCoord( Point2I(0,0) ) ); 330 if( handledEvent == true ) 331 { 332 // The Control handled the event and requested the edit ctrl 333 // *NOT* act on it. 334 return; 335 } 336 else if( event.modifier & SI_SHIFT ) 337 { 338 // Shift is down. Start rectangle selection in add mode 339 // no matter what we have hit. 340 341 startDragRectangle( event.mousePoint ); 342 mDragAddSelection = true; 343 } 344 else if( selectionContains( ctrl ) ) 345 { 346 // We hit a selected control. If the multiselect key is pressed, 347 // deselect the control. Otherwise start a drag move. 348 349 if( event.modifier & SI_MULTISELECT ) 350 { 351 removeSelection( ctrl ); 352 353 //set the mode 354 setMouseMode( Selecting ); 355 } 356 else if( event.modifier & SI_PRIMARY_ALT ) 357 { 358 // Alt is down. Start a drag clone. 359 360 startDragClone( event.mousePoint ); 361 } 362 else 363 { 364 startDragMove( event.mousePoint ); 365 } 366 } 367 else 368 { 369 // We clicked an unselected control. 370 371 if( ctrl == getContentControl() ) 372 { 373 // Clicked in toplevel control. Start a rectangle selection. 374 375 startDragRectangle( event.mousePoint ); 376 mDragAddSelection = false; 377 } 378 else if( event.modifier & SI_PRIMARY_ALT && ctrl != getContentControl() ) 379 { 380 // Alt is down. Start a drag clone. 381 382 clearSelection(); 383 addSelection( ctrl ); 384 385 startDragClone( event.mousePoint ); 386 } 387 else if( event.modifier & SI_MULTISELECT ) 388 addSelection( ctrl ); 389 else 390 { 391 // Clicked on child control. Start move. 392 393 clearSelection(); 394 addSelection( ctrl ); 395 396 startDragMove( event.mousePoint ); 397 } 398 } 399} 400 401//----------------------------------------------------------------------------- 402 403void GuiEditCtrl::onMouseUp(const GuiEvent &event) 404{ 405 if (! mActive || !mContentControl || !getCurrentAddSet() ) 406 { 407 Parent::onMouseUp(event); 408 return; 409 } 410 411 //find the control we clicked 412 GuiControl *ctrl = mContentControl->findHitControl(mLastMousePos, getCurrentAddSet()->mLayer); 413 414 bool handledEvent = ctrl->onMouseUpEditor( event, localToGlobalCoord( Point2I(0,0) ) ); 415 if( handledEvent == true ) 416 { 417 // The Control handled the event and requested the edit ctrl 418 // *NOT* act on it. The dude abides. 419 return; 420 } 421 422 //unlock the mouse 423 mouseUnlock(); 424 425 // Reset Drag Axis Alignment Information 426 mDragBeginPoint.set(-1,-1); 427 mDragBeginPoints.clear(); 428 429 mLastMousePos = globalToLocalCoord(event.mousePoint); 430 if( mMouseDownMode == DragGuide ) 431 { 432 // Check to see if the mouse has moved off the canvas. If so, 433 // remove the guides being dragged. 434 435 for( U32 axis = 0; axis < 2; ++ axis ) 436 if( mDragGuide[ axis ] && !getContentControl()->getGlobalBounds().pointInRect( event.mousePoint ) ) 437 mGuides[ axis ].erase( mDragGuideIndex[ axis ] ); 438 } 439 else if( mMouseDownMode == DragSelecting ) 440 { 441 // If not multiselecting, clear the current selection. 442 443 if( !( event.modifier & SI_MULTISELECT ) && !mDragAddSelection ) 444 clearSelection(); 445 446 RectI rect; 447 getDragRect( rect ); 448 449 // If the region is somewhere less than at least 2x2, count this as a 450 // normal, non-rectangular selection. 451 452 if( rect.extent.x <= 2 && rect.extent.y <= 2 ) 453 addSelectControlAt( rect.point ); 454 else 455 { 456 // Use HIT_AddParentHits by default except if ALT is pressed. 457 // Use HIT_ParentPreventsChildHit if ALT+CTRL is pressed. 458 459 U32 hitFlags = 0; 460 if( !( event.modifier & SI_PRIMARY_ALT ) ) 461 hitFlags |= HIT_AddParentHits; 462 if( event.modifier & SI_PRIMARY_ALT && event.modifier & SI_CTRL ) 463 hitFlags |= HIT_ParentPreventsChildHit; 464 465 addSelectControlsInRegion( rect, hitFlags ); 466 } 467 } 468 else if( ctrl == getContentControl() && mMouseDownMode == Selecting ) 469 setCurrentAddSet( NULL, true ); 470 471 // deliver post edit event if we've been editing 472 // note: paxorr: this may need to be moved earlier, if the selection has changed. 473 // undo 474 if( mMouseDownMode == SizingSelection || ( mMouseDownMode == MovingSelection && mDragMoveUndo ) ) 475 onPostEdit_callback( getSelectedSet() ); 476 477 //reset the mouse mode 478 setFirstResponder(); 479 setMouseMode( Selecting ); 480 mSizingMode = sizingNone; 481 482 // Clear snapping state. 483 484 mSnapped[ SnapVertical ] = false; 485 mSnapped[ SnapHorizontal ] = false; 486 487 mSnapTargets[ SnapVertical ] = NULL; 488 mSnapTargets[ SnapHorizontal ] = NULL; 489 490 // Clear guide drag state. 491 492 mDragGuide[ GuideVertical ] = false; 493 mDragGuide[ GuideHorizontal ] = false; 494} 495 496//----------------------------------------------------------------------------- 497 498void GuiEditCtrl::onMouseDragged( const GuiEvent &event ) 499{ 500 if( !mActive || !mContentControl || !getCurrentAddSet() ) 501 { 502 Parent::onMouseDragged(event); 503 return; 504 } 505 506 Point2I mousePoint = globalToLocalCoord( event.mousePoint ); 507 508 //find the control we clicked 509 GuiControl *ctrl = mContentControl->findHitControl( mousePoint, getCurrentAddSet()->mLayer ); 510 511 bool handledEvent = ctrl->onMouseDraggedEditor( event, localToGlobalCoord( Point2I(0,0) ) ); 512 if( handledEvent == true ) 513 { 514 // The Control handled the event and requested the edit ctrl 515 // *NOT* act on it. The dude abides. 516 return; 517 } 518 519 // If we're doing a drag clone, see if we have crossed the move threshold. If so, 520 // clone the selection and switch to move mode. 521 522 if( mMouseDownMode == DragClone ) 523 { 524 // If we haven't yet crossed the mouse delta to actually start the 525 // clone, check if we have now. 526 527 S32 delta = mAbs( ( mousePoint - mDragBeginPoint ).len() ); 528 if( delta >= 4 ) 529 { 530 cloneSelection(); 531 mLastMousePos = mDragBeginPoint; 532 mDragMoveUndo = false; 533 534 setMouseMode( MovingSelection ); 535 } 536 } 537 538 if( mMouseDownMode == DragGuide ) 539 { 540 for( U32 axis = 0; axis < 2; ++ axis ) 541 if( mDragGuide[ axis ] ) 542 { 543 // Set the guide to the coordinate of the mouse cursor 544 // on the guide's axis. 545 546 Point2I point = event.mousePoint; 547 point -= localToGlobalCoord( Point2I( 0, 0 ) ); 548 point[ axis ] = mClamp( point[ axis ], 0, getExtent()[ axis ] - 1 ); 549 550 mGuides[ axis ][ mDragGuideIndex[ axis ] ] = point[ axis ]; 551 } 552 } 553 else if( mMouseDownMode == SizingSelection ) 554 { 555 // Snap the mouse cursor to grid if active. Do this on the mouse cursor so that we handle 556 // incremental drags correctly. 557 558 Point2I dragPoint = event.mousePoint; 559 snapToGrid(dragPoint); 560 561 Point2I delta = dragPoint - mLastDragPos; 562 563 // If CTRL is down, apply smart snapping. 564 565 if( event.modifier & SI_CTRL ) 566 { 567 RectI selectionBounds = getSelectionBounds(); 568 569 doSnapping( event, selectionBounds, delta ); 570 } 571 else 572 { 573 mSnapped[ SnapVertical ] = false; 574 mSnapped[ SnapHorizontal ] = false; 575 } 576 577 // If ALT is down, do a move instead of a resize on the control 578 // knob's axis. Otherwise resize. 579 580 if( event.modifier & SI_PRIMARY_ALT ) 581 { 582 if( !( mSizingMode & sizingLeft ) && !( mSizingMode & sizingRight ) ) 583 { 584 mSnapped[ SnapVertical ] = false; 585 delta.x = 0; 586 } 587 if( !( mSizingMode & sizingTop ) && !( mSizingMode & sizingBottom ) ) 588 { 589 mSnapped[ SnapHorizontal ] = false; 590 delta.y = 0; 591 } 592 593 moveSelection( delta ); 594 } 595 else 596 resizeControlsInSelectionBy( delta, mSizingMode ); 597 598 // Remember drag point. 599 600 mLastDragPos = dragPoint; 601 } 602 else if (mMouseDownMode == MovingSelection && mSelectedControls.size()) 603 { 604 Point2I delta = mousePoint - mLastMousePos; 605 RectI selectionBounds = getSelectionBounds(); 606 607 // Apply snaps. 608 609 doSnapping( event, selectionBounds, delta ); 610 611 //RDTODO: to me seems to be in need of revision 612 // Do we want to align this drag to the X and Y axes within a certain threshold? 613 if( event.modifier & SI_SHIFT && !( event.modifier & SI_PRIMARY_ALT ) ) 614 { 615 Point2I dragTotalDelta = event.mousePoint - localToGlobalCoord( mDragBeginPoint ); 616 if( dragTotalDelta.y < 10 && dragTotalDelta.y > -10 ) 617 { 618 for(S32 i = 0; i < mSelectedControls.size(); i++) 619 { 620 Point2I selCtrlPos = mSelectedControls[i]->getPosition(); 621 Point2I snapBackPoint( selCtrlPos.x, mDragBeginPoints[i].y); 622 // This is kind of nasty but we need to snap back if we're not at origin point with selection - JDD 623 if( selCtrlPos.y != mDragBeginPoints[i].y ) 624 mSelectedControls[i]->setPosition( snapBackPoint ); 625 } 626 delta.y = 0; 627 } 628 if( dragTotalDelta.x < 10 && dragTotalDelta.x > -10 ) 629 { 630 for(S32 i = 0; i < mSelectedControls.size(); i++) 631 { 632 Point2I selCtrlPos = mSelectedControls[i]->getPosition(); 633 Point2I snapBackPoint( mDragBeginPoints[i].x, selCtrlPos.y); 634 // This is kind of nasty but we need to snap back if we're not at origin point with selection - JDD 635 if( selCtrlPos.x != mDragBeginPoints[i].x ) 636 mSelectedControls[i]->setPosition( snapBackPoint ); 637 } 638 delta.x = 0; 639 } 640 } 641 642 if( delta.x || delta.y ) 643 moveSelection( delta, mDragMoveUndo ); 644 645 // find the current control under the mouse 646 647 canHitSelectedControls( false ); 648 GuiControl *inCtrl = mContentControl->findHitControl(mousePoint, getCurrentAddSet()->mLayer); 649 canHitSelectedControls( true ); 650 651 // find the nearest control up the heirarchy from the control the mouse is in 652 // that is flagged as a container. 653 while( !inCtrl->mIsContainer ) 654 inCtrl = inCtrl->getParent(); 655 656 // if the control under the mouse is not our parent, move the selected controls 657 // into the new parent. 658 if(mSelectedControls[0]->getParent() != inCtrl && inCtrl->mIsContainer) 659 { 660 moveSelectionToCtrl( inCtrl, mDragMoveUndo ); 661 setCurrentAddSet( inCtrl, false ); 662 } 663 664 mLastMousePos += delta; 665 } 666 else 667 mLastMousePos = mousePoint; 668} 669 670//----------------------------------------------------------------------------- 671 672void GuiEditCtrl::onRightMouseDown(const GuiEvent &event) 673{ 674 if (! mActive || !mContentControl) 675 { 676 Parent::onRightMouseDown(event); 677 return; 678 } 679 setFirstResponder(); 680 681 //search for the control hit in any layer below the edit layer 682 GuiControl *hitCtrl = mContentControl->findHitControl(globalToLocalCoord(event.mousePoint), mLayer - 1); 683 if (hitCtrl != getCurrentAddSet()) 684 { 685 setCurrentAddSet( hitCtrl ); 686 } 687 // select the parent if we right-click on the current add set 688 else if( getCurrentAddSet() != mContentControl) 689 { 690 setCurrentAddSet( hitCtrl->getParent() ); 691 select(hitCtrl); 692 } 693 694 //Design time mouse events 695 GuiEvent designEvent = event; 696 designEvent.mousePoint = mLastMousePos; 697 hitCtrl->onRightMouseDownEditor( designEvent, localToGlobalCoord( Point2I(0,0) ) ); 698 699} 700 701//============================================================================= 702// Rendering. 703//============================================================================= 704// MARK: ---- Rendering ---- 705 706//----------------------------------------------------------------------------- 707 708void GuiEditCtrl::onPreRender() 709{ 710 setUpdate(); 711} 712 713//----------------------------------------------------------------------------- 714 715void GuiEditCtrl::onRender(Point2I offset, const RectI &updateRect) 716{ 717 Point2I ctOffset; 718 Point2I cext; 719 bool keyFocused = isFirstResponder(); 720 721 GFXDrawUtil *drawer = GFX->getDrawUtil(); 722 723 if (mActive) 724 { 725 if( getCurrentAddSet() != getContentControl() ) 726 { 727 // draw a white frame inset around the current add set. 728 cext = getCurrentAddSet()->getExtent(); 729 ctOffset = getCurrentAddSet()->localToGlobalCoord(Point2I(0,0)); 730 RectI box(ctOffset.x, ctOffset.y, cext.x, cext.y); 731 732 box.inset( -5, -5 ); 733 drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) ); 734 box.inset( 1, 1 ); 735 drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) ); 736 box.inset( 1, 1 ); 737 drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) ); 738 box.inset( 1, 1 ); 739 drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) ); 740 box.inset( 1, 1 ); 741 drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) ); 742 } 743 Vector<GuiControl *>::iterator i; 744 bool multisel = mSelectedControls.size() > 1; 745 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 746 { 747 GuiControl *ctrl = (*i); 748 cext = ctrl->getExtent(); 749 ctOffset = ctrl->localToGlobalCoord(Point2I(0,0)); 750 RectI box(ctOffset.x,ctOffset.y, cext.x, cext.y); 751 ColorI nutColor = multisel ? ColorI( 255, 255, 255, 100 ) : ColorI( 0, 0, 0, 100 ); 752 ColorI outlineColor = multisel ? ColorI( 0, 0, 0, 100 ) : ColorI( 255, 255, 255, 100 ); 753 if(!keyFocused) 754 nutColor.set( 128, 128, 128, 100 ); 755 756 drawNuts(box, outlineColor, nutColor); 757 } 758 } 759 760 renderChildControls(offset, updateRect); 761 762 // Draw selection rectangle. 763 764 if( mActive && mMouseDownMode == DragSelecting ) 765 { 766 RectI b; 767 getDragRect(b); 768 b.point += offset; 769 770 // Draw outline. 771 772 drawer->drawRect( b, ColorI( 100, 100, 100, 128 ) ); 773 774 // Draw fill. 775 776 b.inset( 1, 1 ); 777 drawer->drawRectFill( b, ColorI( 150, 150, 150, 128 ) ); 778 } 779 780 // Draw grid. 781 782 if( mActive && 783 ( mMouseDownMode == MovingSelection || mMouseDownMode == SizingSelection ) && 784 ( mGridSnap.x || mGridSnap.y ) ) 785 { 786 cext = getContentControl()->getExtent(); 787 Point2I coff = getContentControl()->localToGlobalCoord(Point2I(0,0)); 788 789 // create point-dots 790 const Point2I& snap = mGridSnap; 791 U32 maxdot = (U32)(mCeil(cext.x / (F32)snap.x) - 1) * (U32)(mCeil(cext.y / (F32)snap.y) - 1); 792 793 if( mDots.isNull() || maxdot != mDots->mNumVerts) 794 { 795 mDots.set(GFX, maxdot, GFXBufferTypeStatic); 796 797 U32 ndot = 0; 798 mDots.lock(); 799 for(U32 ix = snap.x; ix < cext.x; ix += snap.x) 800 { 801 for(U32 iy = snap.y; ndot < maxdot && iy < cext.y; iy += snap.y) 802 { 803 mDots[ndot].color.set( 50, 50, 254, 100 ); 804 mDots[ndot].point.x = F32(ix + coff.x); 805 mDots[ndot].point.y = F32(iy + coff.y); 806 mDots[ndot].point.z = 0.0f; 807 ndot++; 808 } 809 } 810 mDots.unlock(); 811 AssertFatal(ndot <= maxdot, "dot overflow"); 812 AssertFatal(ndot == maxdot, "dot underflow"); 813 } 814 815 if (!mDotSB) 816 { 817 GFXStateBlockDesc dotdesc; 818 dotdesc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); 819 dotdesc.setCullMode( GFXCullNone ); 820 mDotSB = GFX->createStateBlock( dotdesc ); 821 } 822 823 GFX->setStateBlock(mDotSB); 824 825 // draw the points. 826 GFX->setVertexBuffer( mDots ); 827 GFX->drawPrimitive( GFXPointList, 0, mDots->mNumVerts ); 828 } 829 830 // Draw snapping lines. 831 832 if( mActive && getContentControl() ) 833 { 834 RectI bounds = getContentControl()->getGlobalBounds(); 835 836 // Draw guide lines. 837 838 if( mDrawGuides ) 839 { 840 for( U32 axis = 0; axis < 2; ++ axis ) 841 { 842 for( U32 i = 0, num = mGuides[ axis ].size(); i < num; ++ i ) 843 drawCrossSection( axis, mGuides[ axis ][ i ] + bounds.point[ axis ], 844 bounds, ColorI( 0, 255, 0, 100 ), drawer ); 845 } 846 } 847 848 // Draw smart snap lines. 849 850 for( U32 axis = 0; axis < 2; ++ axis ) 851 { 852 if( mSnapped[ axis ] ) 853 { 854 // Draw the snap line. 855 856 drawCrossSection( axis, mSnapOffset[ axis ], 857 bounds, ColorI( 0, 0, 255, 100 ), drawer ); 858 859 // Draw a border around the snap target control. 860 861 if( mSnapTargets[ axis ] ) 862 { 863 RectI snapBounds = mSnapTargets[ axis ]->getGlobalBounds(); 864 drawer->drawRect(snapBounds, ColorI( 128, 128, 128, 128 ) ); 865 } 866 } 867 } 868 } 869} 870 871//----------------------------------------------------------------------------- 872 873void GuiEditCtrl::drawNuts(RectI &box, ColorI &outlineColor, ColorI &nutColor) 874{ 875 GFXDrawUtil *drawer = GFX->getDrawUtil(); 876 877 S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1; 878 S32 cx = (lx + rx) >> 1; 879 S32 ty = box.point.y, by = box.point.y + box.extent.y - 1; 880 S32 cy = (ty + by) >> 1; 881 882 if( mDrawBorderLines ) 883 { 884 ColorI lineColor( 179, 179, 179, 64 ); 885 ColorI lightLineColor( 128, 128, 128, 26); 886 887 if(lx > 0 && ty > 0) 888 { 889 drawer->drawLine(0, ty, lx, ty, lineColor); // Left edge to top-left corner. 890 drawer->drawLine(lx, 0, lx, ty, lineColor); // Top edge to top-left corner. 891 } 892 893 if(lx > 0 && by > 0) 894 drawer->drawLine(0, by, lx, by, lineColor); // Left edge to bottom-left corner. 895 896 if(rx > 0 && ty > 0) 897 drawer->drawLine(rx, 0, rx, ty, lineColor); // Top edge to top-right corner. 898 899 Point2I extent = localToGlobalCoord(getExtent()); 900 901 if(lx < extent.x && by < extent.y) 902 drawer->drawLine(lx, by, lx, extent.y, lightLineColor); // Bottom-left corner to bottom edge. 903 if(rx < extent.x && by < extent.y) 904 { 905 drawer->drawLine(rx, by, rx, extent.y, lightLineColor); // Bottom-right corner to bottom edge. 906 drawer->drawLine(rx, by, extent.x, by, lightLineColor); // Bottom-right corner to right edge. 907 } 908 if(rx < extent.x && ty < extent.y) 909 drawer->drawLine(rx, ty, extent.x, ty, lightLineColor); // Top-right corner to right edge. 910 } 911 912 // Adjust nuts, so they dont straddle the controls. 913 914 lx -= NUT_SIZE + 1; 915 ty -= NUT_SIZE + 1; 916 rx += 1; 917 by += 1; 918 919 // Draw nuts. 920 921 drawNut( Point2I( lx - NUT_SIZE, ty - NUT_SIZE ), outlineColor, nutColor ); // Top left 922 drawNut( Point2I( lx - NUT_SIZE, cy - NUT_SIZE / 2 ), outlineColor, nutColor ); // Mid left 923 drawNut( Point2I( lx - NUT_SIZE, by ), outlineColor, nutColor ); // Bottom left 924 drawNut( Point2I( rx, ty - NUT_SIZE ), outlineColor, nutColor ); // Top right 925 drawNut( Point2I( rx, cy - NUT_SIZE / 2 ), outlineColor, nutColor ); // Mid right 926 drawNut( Point2I( rx, by ), outlineColor, nutColor ); // Bottom right 927 drawNut( Point2I( cx - NUT_SIZE / 2, ty - NUT_SIZE ), outlineColor, nutColor ); // Mid top 928 drawNut( Point2I( cx - NUT_SIZE / 2, by ), outlineColor, nutColor ); // Mid bottom 929} 930 931//----------------------------------------------------------------------------- 932 933void GuiEditCtrl::drawNut(const Point2I &nut, ColorI &outlineColor, ColorI &nutColor) 934{ 935 RectI r( nut.x, nut.y, NUT_SIZE * 2, NUT_SIZE * 2 ); 936 GFX->getDrawUtil()->drawRect( r, outlineColor ); 937 r.inset( 1, 1 ); 938 GFX->getDrawUtil()->drawRectFill( r, nutColor ); 939} 940 941//============================================================================= 942// Selections. 943//============================================================================= 944// MARK: ---- Selections ---- 945 946//----------------------------------------------------------------------------- 947 948void GuiEditCtrl::clearSelection(void) 949{ 950 mSelectedControls.clear(); 951 onClearSelected_callback(); 952} 953 954//----------------------------------------------------------------------------- 955 956void GuiEditCtrl::setSelection(GuiControl *ctrl, bool inclusive) 957{ 958 //sanity check 959 if( !ctrl ) 960 return; 961 962 if( mSelectedControls.size() == 1 && mSelectedControls[ 0 ] == ctrl ) 963 return; 964 965 if( !inclusive ) 966 clearSelection(); 967 968 if( mContentControl == ctrl ) 969 setCurrentAddSet( ctrl, false ); 970 else 971 addSelection( ctrl ); 972} 973 974//----------------------------------------------------------------------------- 975 976void GuiEditCtrl::addSelection(S32 id) 977{ 978 GuiControl * ctrl; 979 if( Sim::findObject( id, ctrl ) ) 980 addSelection( ctrl ); 981} 982 983//----------------------------------------------------------------------------- 984 985void GuiEditCtrl::addSelection( GuiControl* ctrl ) 986{ 987 // Only add if this isn't the content control and the 988 // control isn't yet in the selection. 989 990 if( ctrl != getContentControl() && !selectionContains( ctrl ) ) 991 { 992 mSelectedControls.push_back( ctrl ); 993 994 if( mSelectedControls.size() == 1 ) 995 { 996 // Update the add set. 997 998 if( ctrl->mIsContainer ) 999 setCurrentAddSet( ctrl, false ); 1000 else 1001 setCurrentAddSet( ctrl->getParent(), false ); 1002 1003 // Notify script. 1004 1005 onSelect_callback( ctrl ); 1006 } 1007 else 1008 { 1009 // Notify script. 1010 1011 onAddSelected_callback( ctrl ); 1012 } 1013 } 1014} 1015 1016//----------------------------------------------------------------------------- 1017 1018void GuiEditCtrl::removeSelection( S32 id ) 1019{ 1020 GuiControl * ctrl; 1021 if ( Sim::findObject( id, ctrl ) ) 1022 removeSelection( ctrl ); 1023} 1024 1025//----------------------------------------------------------------------------- 1026 1027void GuiEditCtrl::removeSelection( GuiControl* ctrl ) 1028{ 1029 if( selectionContains( ctrl ) ) 1030 { 1031 Vector< GuiControl* >::iterator i = T3D::find( mSelectedControls.begin(), mSelectedControls.end(), ctrl ); 1032 if ( i != mSelectedControls.end() ) 1033 mSelectedControls.erase( i ); 1034 1035 onRemoveSelected_callback( ctrl ); 1036 } 1037} 1038 1039//----------------------------------------------------------------------------- 1040 1041void GuiEditCtrl::canHitSelectedControls( bool state ) 1042{ 1043 for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i ) 1044 mSelectedControls[ i ]->setCanHit( state ); 1045} 1046 1047//----------------------------------------------------------------------------- 1048 1049void GuiEditCtrl::moveSelectionToCtrl( GuiControl *newParent, bool callback ) 1050{ 1051 for( U32 i = 0; i < mSelectedControls.size(); ++ i ) 1052 { 1053 GuiControl* ctrl = mSelectedControls[i]; 1054 if( ctrl->getParent() == newParent 1055 || ctrl->isLocked() 1056 || selectionContainsParentOf( ctrl ) ) 1057 continue; 1058 1059 Point2I globalpos = ctrl->localToGlobalCoord(Point2I(0,0)); 1060 newParent->addObject(ctrl); 1061 Point2I newpos = ctrl->globalToLocalCoord(globalpos) + ctrl->getPosition(); 1062 ctrl->setPosition(newpos); 1063 } 1064 1065 onHierarchyChanged_callback(); 1066 1067 //TODO: undo 1068} 1069 1070//----------------------------------------------------------------------------- 1071 1072static Point2I snapPoint(Point2I point, Point2I delta, Point2I gridSnap) 1073{ 1074 S32 snap; 1075 if(gridSnap.x && delta.x) 1076 { 1077 snap = point.x % gridSnap.x; 1078 point.x -= snap; 1079 if(delta.x > 0 && snap != 0) 1080 point.x += gridSnap.x; 1081 } 1082 if(gridSnap.y && delta.y) 1083 { 1084 snap = point.y % gridSnap.y; 1085 point.y -= snap; 1086 if(delta.y > 0 && snap != 0) 1087 point.y += gridSnap.y; 1088 } 1089 return point; 1090} 1091 1092void GuiEditCtrl::moveAndSnapSelection( const Point2I &delta, bool callback ) 1093{ 1094 // move / nudge gets a special callback so that multiple small moves can be 1095 // coalesced into one large undo action. 1096 // undo 1097 1098 if( callback ) 1099 onPreSelectionNudged_callback( getSelectedSet() ); 1100 1101 Vector<GuiControl *>::iterator i; 1102 Point2I newPos; 1103 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1104 { 1105 GuiControl* ctrl = *i; 1106 if( !ctrl->isLocked() && !selectionContainsParentOf( ctrl ) ) 1107 { 1108 newPos = ctrl->getPosition() + delta; 1109 newPos = snapPoint( newPos, delta, mGridSnap ); 1110 ctrl->setPosition( newPos ); 1111 } 1112 } 1113 1114 // undo 1115 if( callback ) 1116 onPostSelectionNudged_callback( getSelectedSet() ); 1117 1118 // allow script to update the inspector 1119 if( callback && mSelectedControls.size() > 0 ) 1120 onSelectionMoved_callback( mSelectedControls[ 0 ] ); 1121} 1122 1123//----------------------------------------------------------------------------- 1124 1125void GuiEditCtrl::moveSelection( const Point2I &delta, bool callback ) 1126{ 1127 Vector<GuiControl *>::iterator i; 1128 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1129 { 1130 GuiControl* ctrl = *i; 1131 if( !ctrl->isLocked() && !selectionContainsParentOf( ctrl ) ) 1132 ctrl->setPosition( ctrl->getPosition() + delta ); 1133 } 1134 1135 // allow script to update the inspector 1136 if( callback ) 1137 onSelectionMoved_callback( mSelectedControls[ 0 ] ); 1138} 1139 1140//----------------------------------------------------------------------------- 1141 1142void GuiEditCtrl::justifySelection( Justification j ) 1143{ 1144 S32 minX, maxX; 1145 S32 minY, maxY; 1146 S32 extentX, extentY; 1147 1148 if (mSelectedControls.size() < 2) 1149 return; 1150 1151 Vector<GuiControl *>::iterator i = mSelectedControls.begin(); 1152 minX = (*i)->getLeft(); 1153 maxX = minX + (*i)->getWidth(); 1154 minY = (*i)->getTop(); 1155 maxY = minY + (*i)->getHeight(); 1156 extentX = (*i)->getWidth(); 1157 extentY = (*i)->getHeight(); 1158 i++; 1159 for(;i != mSelectedControls.end(); i++) 1160 { 1161 minX = getMin(minX, (*i)->getLeft()); 1162 maxX = getMax(maxX, (*i)->getLeft() + (*i)->getWidth()); 1163 minY = getMin(minY, (*i)->getTop()); 1164 maxY = getMax(maxY, (*i)->getTop() + (*i)->getHeight()); 1165 extentX += (*i)->getWidth(); 1166 extentY += (*i)->getHeight(); 1167 } 1168 S32 deltaX = maxX - minX; 1169 S32 deltaY = maxY - minY; 1170 switch(j) 1171 { 1172 case JUSTIFY_LEFT: 1173 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1174 if( !( *i )->isLocked() ) 1175 (*i)->setLeft( minX ); 1176 break; 1177 case JUSTIFY_TOP: 1178 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1179 if( !( *i )->isLocked() ) 1180 (*i)->setTop( minY ); 1181 break; 1182 case JUSTIFY_RIGHT: 1183 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1184 if( !( *i )->isLocked() ) 1185 (*i)->setLeft( maxX - (*i)->getWidth() + 1 ); 1186 break; 1187 case JUSTIFY_BOTTOM: 1188 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1189 if( !( *i )->isLocked() ) 1190 (*i)->setTop( maxY - (*i)->getHeight() + 1 ); 1191 break; 1192 case JUSTIFY_CENTER_VERTICAL: 1193 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1194 if( !( *i )->isLocked() ) 1195 (*i)->setLeft( minX + ((deltaX - (*i)->getWidth()) >> 1 )); 1196 break; 1197 case JUSTIFY_CENTER_HORIZONTAL: 1198 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1199 if( !( *i )->isLocked() ) 1200 (*i)->setTop( minY + ((deltaY - (*i)->getHeight()) >> 1 )); 1201 break; 1202 case SPACING_VERTICAL: 1203 { 1204 Vector<GuiControl*> sortedList; 1205 Vector<GuiControl *>::iterator k; 1206 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1207 { 1208 for(k = sortedList.begin(); k != sortedList.end(); k++) 1209 { 1210 if ((*i)->getTop() < (*k)->getTop()) 1211 break; 1212 } 1213 sortedList.insert(k, *i); 1214 } 1215 S32 space = (deltaY - extentY) / (mSelectedControls.size() - 1); 1216 S32 curY = minY; 1217 for(k = sortedList.begin(); k != sortedList.end(); k++) 1218 { 1219 if( !( *k )->isLocked() ) 1220 (*k)->setTop( curY ); 1221 curY += (*k)->getHeight() + space; 1222 } 1223 } 1224 break; 1225 case SPACING_HORIZONTAL: 1226 { 1227 Vector<GuiControl*> sortedList; 1228 Vector<GuiControl *>::iterator k; 1229 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1230 { 1231 for(k = sortedList.begin(); k != sortedList.end(); k++) 1232 { 1233 if ((*i)->getLeft() < (*k)->getLeft()) 1234 break; 1235 } 1236 sortedList.insert(k, *i); 1237 } 1238 S32 space = (deltaX - extentX) / (mSelectedControls.size() - 1); 1239 S32 curX = minX; 1240 for(k = sortedList.begin(); k != sortedList.end(); k++) 1241 { 1242 if( !( *k )->isLocked() ) 1243 (*k)->setLeft( curX ); 1244 curX += (*k)->getWidth() + space; 1245 } 1246 } 1247 break; 1248 } 1249} 1250 1251//----------------------------------------------------------------------------- 1252 1253void GuiEditCtrl::cloneSelection() 1254{ 1255 Vector< GuiControl*> newSelection; 1256 1257 // Clone the controls in the current selection. 1258 1259 const U32 numOldControls = mSelectedControls.size(); 1260 for( U32 i = 0; i < numOldControls; ++ i ) 1261 { 1262 GuiControl* ctrl = mSelectedControls[ i ]; 1263 1264 // If parent is in selection, too, skip to prevent multiple clones. 1265 1266 if( ctrl->getParent() && selectionContains( ctrl->getParent() ) ) 1267 continue; 1268 1269 // Clone and add to set. 1270 1271 GuiControl* clone = dynamic_cast< GuiControl* >( ctrl->deepClone() ); 1272 if( clone ) 1273 newSelection.push_back( clone ); 1274 } 1275 1276 // Exchange the selection set. 1277 1278 clearSelection(); 1279 const U32 numNewControls = newSelection.size(); 1280 for( U32 i = 0; i < numNewControls; ++ i ) 1281 addSelection( newSelection[ i ] ); 1282 1283 // Callback for undo. 1284 1285 onSelectionCloned_callback( getSelectedSet() ); 1286} 1287 1288//----------------------------------------------------------------------------- 1289 1290void GuiEditCtrl::deleteSelection() 1291{ 1292 // Notify script for undo. 1293 1294 onTrashSelection_callback( getSelectedSet() ); 1295 1296 // Move all objects in selection to trash. 1297 1298 Vector< GuiControl* >::iterator i; 1299 for( i = mSelectedControls.begin(); i != mSelectedControls.end(); i ++ ) 1300 { 1301 if( ( *i ) == getCurrentAddSet() ) 1302 setCurrentAddSet( getContentControl(), false ); 1303 1304 mTrash->addObject( *i ); 1305 } 1306 1307 clearSelection(); 1308 1309 // Notify script it needs to update its views. 1310 1311 onHierarchyChanged_callback(); 1312} 1313 1314//----------------------------------------------------------------------------- 1315 1316void GuiEditCtrl::loadSelection( const char* filename ) 1317{ 1318 // Set redefine behavior to rename. 1319 1320 const char* oldRedefineBehavior = Con::getVariable( "$Con::redefineBehavior" ); 1321 Con::setVariable( "$Con::redefineBehavior", "renameNew" ); 1322 1323 // Exec the file or clipboard contents with the saved selection set. 1324 1325 if( filename ) 1326 Con::executef( "exec", filename ); 1327 else 1328 Con::evaluate( Platform::getClipboard() ); 1329 1330 SimSet* set; 1331 if( !Sim::findObject( "guiClipboard", set ) ) 1332 { 1333 if( filename ) 1334 Con::errorf( "GuiEditCtrl::loadSelection() - could not find 'guiClipboard' in '%s'", filename ); 1335 else 1336 Con::errorf( "GuiEditCtrl::loadSelection() - could not find 'guiClipboard'" ); 1337 return; 1338 } 1339 1340 // Restore redefine behavior. 1341 1342 Con::setVariable( "$Con::redefineBehavior", oldRedefineBehavior ); 1343 1344 // Add the objects in the set. 1345 1346 if( set->size() ) 1347 { 1348 clearSelection(); 1349 1350 GuiControlVector ctrls; 1351 for( U32 i = 0, num = set->size(); i < num; ++ i ) 1352 { 1353 GuiControl *ctrl = dynamic_cast< GuiControl* >( ( *set )[ i ] ); 1354 if( ctrl ) 1355 { 1356 getCurrentAddSet()->addObject( ctrl ); 1357 ctrls.push_back( ctrl ); 1358 } 1359 } 1360 1361 // Select all controls. We need to perform this here rather than in the 1362 // loop above as addSelection() will modify the current add set. 1363 for( U32 i = 0; i < ctrls.size(); ++ i ) 1364 { 1365 addSelection( ctrls[i] ); 1366 } 1367 1368 // Undo 1369 onAddNewCtrlSet_callback( getSelectedSet() ); 1370 1371 // Notify the script it needs to update its treeview. 1372 1373 onHierarchyChanged_callback(); 1374 } 1375 set->deleteObject(); 1376} 1377 1378//----------------------------------------------------------------------------- 1379 1380void GuiEditCtrl::saveSelection( const char* filename ) 1381{ 1382 // If there are no selected objects, then don't save. 1383 1384 if( mSelectedControls.size() == 0 ) 1385 return; 1386 1387 // Open the stream. 1388 1389 Stream* stream; 1390 if( filename ) 1391 { 1392 stream = FileStream::createAndOpen( filename, Torque::FS::File::Write ); 1393 if( !stream ) 1394 { 1395 Con::errorf( "GuiEditCtrl::saveSelection - could not open '%s' for writing", filename ); 1396 return; 1397 } 1398 } 1399 else 1400 stream = new MemStream( 4096 ); 1401 1402 // Create a temporary SimSet. 1403 1404 SimSet* clipboardSet = new SimSet; 1405 clipboardSet->registerObject(); 1406 Sim::getRootGroup()->addObject( clipboardSet, "guiClipboard" ); 1407 1408 // Add the selected controls to the set. 1409 1410 for( Vector< GuiControl* >::iterator i = mSelectedControls.begin(); 1411 i != mSelectedControls.end(); ++ i ) 1412 { 1413 GuiControl* ctrl = *i; 1414 if( !selectionContainsParentOf( ctrl ) ) 1415 clipboardSet->addObject( ctrl ); 1416 } 1417 1418 // Write the SimSet. Use the IgnoreCanSave to ensure the controls 1419 // get actually written out (also disables the default parent inheritance 1420 // behavior for the flag). 1421 1422 clipboardSet->write( *stream, 0, IgnoreCanSave ); 1423 clipboardSet->deleteObject(); 1424 1425 // If we were writing to a memory stream, copy to clipboard 1426 // now. 1427 1428 if( !filename ) 1429 { 1430 MemStream* memStream = static_cast< MemStream* >( stream ); 1431 memStream->write( U8( 0 ) ); 1432 Platform::setClipboard( ( const char* ) memStream->getBuffer() ); 1433 } 1434 1435 delete stream; 1436} 1437 1438//----------------------------------------------------------------------------- 1439 1440void GuiEditCtrl::selectAll() 1441{ 1442 GuiControl::iterator i; 1443 1444 clearSelection(); 1445 for(i = getCurrentAddSet()->begin(); i != getCurrentAddSet()->end(); i++) 1446 { 1447 GuiControl *ctrl = dynamic_cast<GuiControl *>(*i); 1448 addSelection( ctrl ); 1449 } 1450} 1451 1452//----------------------------------------------------------------------------- 1453 1454void GuiEditCtrl::bringToFront() 1455{ 1456 if( getNumSelected() != 1 ) 1457 return; 1458 1459 GuiControl* ctrl = mSelectedControls.first(); 1460 ctrl->getParent()->pushObjectToBack( ctrl ); 1461} 1462 1463//----------------------------------------------------------------------------- 1464 1465void GuiEditCtrl::pushToBack() 1466{ 1467 if( getNumSelected() != 1 ) 1468 return; 1469 1470 GuiControl* ctrl = mSelectedControls.first(); 1471 ctrl->getParent()->bringObjectToFront( ctrl ); 1472} 1473 1474//----------------------------------------------------------------------------- 1475 1476RectI GuiEditCtrl::getSelectionBounds() const 1477{ 1478 Vector<GuiControl *>::const_iterator i = mSelectedControls.begin(); 1479 1480 Point2I minPos = (*i)->localToGlobalCoord( Point2I( 0, 0 ) ); 1481 Point2I maxPos = minPos; 1482 1483 for(; i != mSelectedControls.end(); i++) 1484 { 1485 Point2I iPos = (**i).localToGlobalCoord( Point2I( 0 , 0 ) ); 1486 1487 minPos.x = getMin( iPos.x, minPos.x ); 1488 minPos.y = getMin( iPos.y, minPos.y ); 1489 1490 Point2I iExt = ( **i ).getExtent(); 1491 1492 iPos.x += iExt.x; 1493 iPos.y += iExt.y; 1494 1495 maxPos.x = getMax( iPos.x, maxPos.x ); 1496 maxPos.y = getMax( iPos.y, maxPos.y ); 1497 } 1498 1499 minPos = getContentControl()->globalToLocalCoord( minPos ); 1500 maxPos = getContentControl()->globalToLocalCoord( maxPos ); 1501 1502 return RectI( minPos.x, minPos.y, ( maxPos.x - minPos.x ), ( maxPos.y - minPos.y ) ); 1503} 1504 1505//----------------------------------------------------------------------------- 1506 1507RectI GuiEditCtrl::getSelectionGlobalBounds() const 1508{ 1509 Point2I minb( S32_MAX, S32_MAX ); 1510 Point2I maxb( S32_MIN, S32_MIN ); 1511 1512 for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i ) 1513 { 1514 // Min. 1515 1516 Point2I pos = mSelectedControls[ i ]->localToGlobalCoord( Point2I( 0, 0 ) ); 1517 1518 minb.x = getMin( minb.x, pos.x ); 1519 minb.y = getMin( minb.y, pos.y ); 1520 1521 // Max. 1522 1523 const Point2I extent = mSelectedControls[ i ]->getExtent(); 1524 1525 maxb.x = getMax( maxb.x, pos.x + extent.x ); 1526 maxb.y = getMax( maxb.y, pos.y + extent.y ); 1527 } 1528 1529 RectI bounds( minb.x, minb.y, maxb.x - minb.x, maxb.y - minb.y ); 1530 return bounds; 1531} 1532 1533//----------------------------------------------------------------------------- 1534 1535bool GuiEditCtrl::selectionContains( GuiControl *ctrl ) 1536{ 1537 Vector<GuiControl *>::iterator i; 1538 for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1539 if (ctrl == *i) return true; 1540 return false; 1541} 1542 1543//----------------------------------------------------------------------------- 1544 1545bool GuiEditCtrl::selectionContainsParentOf( GuiControl* ctrl ) 1546{ 1547 GuiControl* parent = ctrl->getParent(); 1548 while( parent && parent != getContentControl() ) 1549 { 1550 if( selectionContains( parent ) ) 1551 return true; 1552 1553 parent = parent->getParent(); 1554 } 1555 1556 return false; 1557} 1558 1559//----------------------------------------------------------------------------- 1560 1561void GuiEditCtrl::select( GuiControl* ctrl ) 1562{ 1563 clearSelection(); 1564 addSelection( ctrl ); 1565} 1566 1567//----------------------------------------------------------------------------- 1568 1569void GuiEditCtrl::updateSelectedSet() 1570{ 1571 mSelectedSet->clear(); 1572 Vector<GuiControl*>::iterator i; 1573 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 1574 { 1575 mSelectedSet->addObject(*i); 1576 } 1577} 1578 1579//----------------------------------------------------------------------------- 1580 1581void GuiEditCtrl::addSelectControlsInRegion( const RectI& rect, U32 flags ) 1582{ 1583 // Do a hit test on the content control. 1584 1585 canHitSelectedControls( false ); 1586 Vector< GuiControl*> hits; 1587 1588 if( mFullBoxSelection ) 1589 flags |= GuiControl::HIT_FullBoxOnly; 1590 1591 getContentControl()->findHitControls( rect, hits, flags ); 1592 canHitSelectedControls( true ); 1593 1594 // Add all controls that got hit. 1595 1596 for( U32 i = 0, num = hits.size(); i < num; ++ i ) 1597 addSelection( hits[ i ] ); 1598} 1599 1600//----------------------------------------------------------------------------- 1601 1602void GuiEditCtrl::addSelectControlAt( const Point2I& pos ) 1603{ 1604 // Run a hit test. 1605 1606 canHitSelectedControls( false ); 1607 GuiControl* hit = getContentControl()->findHitControl( pos ); 1608 canHitSelectedControls( true ); 1609 1610 // Add to selection. 1611 1612 if( hit ) 1613 addSelection( hit ); 1614} 1615 1616//----------------------------------------------------------------------------- 1617 1618void GuiEditCtrl::resizeControlsInSelectionBy( const Point2I& delta, U32 mode ) 1619{ 1620 for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i ) 1621 { 1622 GuiControl *ctrl = mSelectedControls[ i ]; 1623 if( ctrl->isLocked() ) 1624 continue; 1625 1626 Point2I minExtent = ctrl->getMinExtent(); 1627 Point2I newPosition = ctrl->getPosition(); 1628 Point2I newExtent = ctrl->getExtent(); 1629 1630 if( mSizingMode & sizingLeft ) 1631 { 1632 newPosition.x += delta.x; 1633 newExtent.x -= delta.x; 1634 1635 if( newExtent.x < minExtent.x ) 1636 { 1637 newPosition.x -= minExtent.x - newExtent.x; 1638 newExtent.x = minExtent.x; 1639 } 1640 } 1641 else if( mSizingMode & sizingRight ) 1642 { 1643 newExtent.x += delta.x; 1644 1645 if( newExtent.x < minExtent.x ) 1646 newExtent.x = minExtent.x; 1647 } 1648 1649 if( mSizingMode & sizingTop ) 1650 { 1651 newPosition.y += delta.y; 1652 newExtent.y -= delta.y; 1653 1654 if( newExtent.y < minExtent.y ) 1655 { 1656 newPosition.y -= minExtent.y - newExtent.y; 1657 newExtent.y = minExtent.y; 1658 } 1659 } 1660 else if( mSizingMode & sizingBottom ) 1661 { 1662 newExtent.y += delta.y; 1663 1664 if( newExtent.y < minExtent.y ) 1665 newExtent.y = minExtent.y; 1666 } 1667 1668 ctrl->resize( newPosition, newExtent ); 1669 } 1670 1671 if( mSelectedControls.size() == 1 ) 1672 onSelectionResized_callback( mSelectedControls[ 0 ] ); 1673} 1674 1675//----------------------------------------------------------------------------- 1676 1677void GuiEditCtrl::fitIntoParents( bool width, bool height ) 1678{ 1679 // Record undo. 1680 1681 onFitIntoParent_callback( width, height ); 1682 1683 // Fit. 1684 1685 for( U32 i = 0; i < mSelectedControls.size(); ++ i ) 1686 { 1687 GuiControl* ctrl = mSelectedControls[ i ]; 1688 GuiControl* parent = ctrl->getParent(); 1689 1690 Point2I position = ctrl->getPosition(); 1691 if( width ) 1692 position.x = 0; 1693 if( height ) 1694 position.y = 0; 1695 1696 Point2I extents = ctrl->getExtent(); 1697 if( width ) 1698 extents.x = parent->getWidth(); 1699 if( height ) 1700 extents.y = parent->getHeight(); 1701 1702 ctrl->resize( position, extents ); 1703 } 1704} 1705 1706//----------------------------------------------------------------------------- 1707 1708void GuiEditCtrl::selectParents( bool addToSelection ) 1709{ 1710 Vector< GuiControl*> parents; 1711 1712 // Collect all parents. 1713 1714 for( U32 i = 0; i < mSelectedControls.size(); ++ i ) 1715 { 1716 GuiControl* ctrl = mSelectedControls[ i ]; 1717 if( ctrl != mContentControl && ctrl->getParent() != mContentControl ) 1718 parents.push_back( mSelectedControls[ i ]->getParent() ); 1719 } 1720 1721 // If there's no parents to select, don't 1722 // change the selection. 1723 1724 if( parents.empty() ) 1725 return; 1726 1727 // Blast selection if need be. 1728 1729 if( !addToSelection ) 1730 clearSelection(); 1731 1732 // Add the parents. 1733 1734 for( U32 i = 0; i < parents.size(); ++ i ) 1735 addSelection( parents[ i ] ); 1736} 1737 1738//----------------------------------------------------------------------------- 1739 1740void GuiEditCtrl::selectChildren( bool addToSelection ) 1741{ 1742 Vector< GuiControl*> children; 1743 1744 // Collect all children. 1745 1746 for( U32 i = 0; i < mSelectedControls.size(); ++ i ) 1747 { 1748 GuiControl* parent = mSelectedControls[ i ]; 1749 for( GuiControl::iterator iter = parent->begin(); iter != parent->end(); ++ iter ) 1750 { 1751 GuiControl* child = dynamic_cast< GuiControl* >( *iter ); 1752 if( child ) 1753 children.push_back( child ); 1754 } 1755 } 1756 1757 // If there's no children to select, don't 1758 // change the selection. 1759 1760 if( children.empty() ) 1761 return; 1762 1763 // Blast selection if need be. 1764 1765 if( !addToSelection ) 1766 clearSelection(); 1767 1768 // Add the children. 1769 1770 for( U32 i = 0; i < children.size(); ++ i ) 1771 addSelection( children[ i ] ); 1772} 1773 1774//============================================================================= 1775// Guides. 1776//============================================================================= 1777// MARK: ---- Guides ---- 1778 1779//----------------------------------------------------------------------------- 1780 1781void GuiEditCtrl::readGuides( guideAxis axis, GuiControl* ctrl ) 1782{ 1783 // Read the guide indices from the vector stored on the respective dynamic 1784 // property of the control. 1785 1786 const char* guideIndices = ctrl->getDataField( smGuidesPropertyName[ axis ], NULL ); 1787 if( guideIndices && guideIndices[ 0 ] ) 1788 { 1789 U32 index = 0; 1790 while( true ) 1791 { 1792 const char* posStr = StringUnit::getUnit( guideIndices, index, " \t" ); 1793 if( !posStr[ 0 ] ) 1794 break; 1795 1796 mGuides[ axis ].push_back( dAtoi( posStr ) ); 1797 1798 index ++; 1799 } 1800 } 1801} 1802 1803//----------------------------------------------------------------------------- 1804 1805void GuiEditCtrl::writeGuides( guideAxis axis, GuiControl* ctrl ) 1806{ 1807 // Store the guide indices of the given axis in a vector on the respective 1808 // dynamic property of the control. 1809 1810 StringBuilder str; 1811 bool isFirst = true; 1812 for( U32 i = 0, num = mGuides[ axis ].size(); i < num; ++ i ) 1813 { 1814 if( !isFirst ) 1815 str.append( ' ' ); 1816 1817 char buffer[ 32 ]; 1818 dSprintf( buffer, sizeof( buffer ), "%i", mGuides[ axis ][ i ] ); 1819 1820 str.append( buffer ); 1821 1822 isFirst = false; 1823 } 1824 1825 String value = str.end(); 1826 ctrl->setDataField( smGuidesPropertyName[ axis ], NULL, value ); 1827} 1828 1829//----------------------------------------------------------------------------- 1830 1831S32 GuiEditCtrl::findGuide( guideAxis axis, const Point2I& point, U32 tolerance ) 1832{ 1833 const S32 p = ( point - localToGlobalCoord( Point2I( 0, 0 ) ) )[ axis ]; 1834 1835 for( U32 i = 0, num = mGuides[ axis ].size(); i < num; ++ i ) 1836 { 1837 const S32 g = mGuides[ axis ][ i ]; 1838 if( p >= ( g - tolerance ) && 1839 p <= ( g + tolerance ) ) 1840 return i; 1841 } 1842 1843 return -1; 1844} 1845 1846//============================================================================= 1847// Snapping. 1848//============================================================================= 1849// MARK: ---- Snapping ---- 1850 1851//----------------------------------------------------------------------------- 1852 1853RectI GuiEditCtrl::getSnapRegion( snappingAxis axis, const Point2I& center ) const 1854{ 1855 RectI rootBounds = getContentControl()->getBounds(); 1856 1857 RectI rect; 1858 if( axis == SnapHorizontal ) 1859 rect = RectI( rootBounds.point.x, 1860 center.y - mSnapSensitivity, 1861 rootBounds.extent.x, 1862 mSnapSensitivity * 2 ); 1863 else // SnapVertical 1864 rect = RectI( center.x - mSnapSensitivity, 1865 rootBounds.point.y, 1866 mSnapSensitivity * 2, 1867 rootBounds.extent.y ); 1868 1869 // Clip against root bounds. 1870 1871 rect.intersect( rootBounds ); 1872 1873 return rect; 1874} 1875 1876//----------------------------------------------------------------------------- 1877 1878void GuiEditCtrl::registerSnap( snappingAxis axis, const Point2I& mousePoint, const Point2I& point, snappingEdges edge, GuiControl* ctrl ) 1879{ 1880 bool takeNewSnap = false; 1881 const Point2I globalPoint = getContentControl()->localToGlobalCoord( point ); 1882 1883 // If we have no snap yet, just take this one. 1884 1885 if( !mSnapped[ axis ] ) 1886 takeNewSnap = true; 1887 1888 // Otherwise see if this snap is the better one. 1889 1890 else 1891 { 1892 // Compare deltas to pointer. 1893 1894 S32 deltaCurrent = mAbs( mSnapOffset[ axis ] - mousePoint[ axis ] ); 1895 S32 deltaNew = mAbs( globalPoint[ axis ] - mousePoint[ axis ] ); 1896 1897 if( deltaCurrent > deltaNew ) 1898 takeNewSnap = true; 1899 } 1900 1901 if( takeNewSnap ) 1902 { 1903 mSnapped[ axis ] = true; 1904 mSnapOffset[ axis ] = globalPoint[ axis ]; 1905 mSnapEdge[ axis ] = edge; 1906 mSnapTargets[ axis ] = ctrl; 1907 } 1908} 1909 1910//----------------------------------------------------------------------------- 1911 1912void GuiEditCtrl::findSnaps( snappingAxis axis, const Point2I& mousePoint, const RectI& minRegion, const RectI& midRegion, const RectI& maxRegion ) 1913{ 1914 // Find controls with edge in either minRegion, midRegion, or maxRegion 1915 // (depending on snap settings). 1916 1917 for( U32 i = 0, num = mSnapHits[ axis ].size(); i < num; ++ i ) 1918 { 1919 GuiControl* ctrl = mSnapHits[ axis ][ i ]; 1920 if( ctrl == getContentControl() && !mSnapToCanvas ) 1921 continue; 1922 1923 RectI bounds = ctrl->getGlobalBounds(); 1924 bounds.point = getContentControl()->globalToLocalCoord( bounds.point ); 1925 1926 // Compute points on min, mid, and max lines of control. 1927 1928 Point2I min = bounds.point; 1929 Point2I max = min + bounds.extent - Point2I( 1, 1 ); 1930 1931 Point2I mid = min; 1932 mid.x += bounds.extent.x / 2; 1933 mid.y += bounds.extent.y / 2; 1934 1935 // Test edge snap cases. 1936 1937 if( mSnapToEdges ) 1938 { 1939 // Min to min. 1940 1941 if( minRegion.pointInRect( min ) ) 1942 registerSnap( axis, mousePoint, min, SnapEdgeMin, ctrl ); 1943 1944 // Max to max. 1945 1946 if( maxRegion.pointInRect( max ) ) 1947 registerSnap( axis, mousePoint, max, SnapEdgeMax, ctrl ); 1948 1949 // Min to max. 1950 1951 if( minRegion.pointInRect( max ) ) 1952 registerSnap( axis, mousePoint, max, SnapEdgeMin, ctrl ); 1953 1954 // Max to min. 1955 1956 if( maxRegion.pointInRect( min ) ) 1957 registerSnap( axis, mousePoint, min, SnapEdgeMax, ctrl ); 1958 } 1959 1960 // Test center snap cases. 1961 1962 if( mSnapToCenters ) 1963 { 1964 // Mid to mid. 1965 1966 if( midRegion.pointInRect( mid ) ) 1967 registerSnap( axis, mousePoint, mid, SnapEdgeMid, ctrl ); 1968 } 1969 1970 // Test combined center+edge snap cases. 1971 1972 if( mSnapToEdges && mSnapToCenters ) 1973 { 1974 // Min to mid. 1975 1976 if( minRegion.pointInRect( mid ) ) 1977 registerSnap( axis, mousePoint, mid, SnapEdgeMin, ctrl ); 1978 1979 // Max to mid. 1980 1981 if( maxRegion.pointInRect( mid ) ) 1982 registerSnap( axis, mousePoint, mid, SnapEdgeMax, ctrl ); 1983 1984 // Mid to min. 1985 1986 if( midRegion.pointInRect( min ) ) 1987 registerSnap( axis, mousePoint, min, SnapEdgeMid, ctrl ); 1988 1989 // Mid to max. 1990 1991 if( midRegion.pointInRect( max ) ) 1992 registerSnap( axis, mousePoint, max, SnapEdgeMid, ctrl ); 1993 } 1994 } 1995} 1996 1997//----------------------------------------------------------------------------- 1998 1999void GuiEditCtrl::doControlSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta ) 2000{ 2001 if( !mSnapToControls || ( !mSnapToEdges && !mSnapToCenters ) ) 2002 return; 2003 2004 // Allow restricting to just vertical (ALT+SHIFT) or just horizontal (ALT+CTRL) 2005 // snaps. 2006 2007 bool snapAxisEnabled[ 2 ]; 2008 2009 if( event.modifier & SI_PRIMARY_ALT && event.modifier & SI_SHIFT ) 2010 snapAxisEnabled[ SnapHorizontal ] = false; 2011 else 2012 snapAxisEnabled[ SnapHorizontal ] = true; 2013 2014 if( event.modifier & SI_PRIMARY_ALT && event.modifier & SI_CTRL ) 2015 snapAxisEnabled[ SnapVertical ] = false; 2016 else 2017 snapAxisEnabled[ SnapVertical ] = true; 2018 2019 // Compute snap regions. There is one region centered on and aligned with 2020 // each of the selection bounds edges plus two regions aligned on the selection 2021 // bounds center. For the selection bounds origin, we use the point that the 2022 // selection would be at, if we had already done the mouse drag. 2023 2024 RectI snapRegions[ 2 ][ 3 ]; 2025 Point2I projectedOrigin( selectionBounds.point + delta ); 2026 dMemset( snapRegions, 0, sizeof( snapRegions ) ); 2027 2028 for( U32 axis = 0; axis < 2; ++ axis ) 2029 { 2030 if( !snapAxisEnabled[ axis ] ) 2031 continue; 2032 2033 if( mSizingMode == sizingNone || 2034 ( axis == 0 && mSizingMode & sizingLeft ) || 2035 ( axis == 1 && mSizingMode & sizingTop ) ) 2036 snapRegions[ axis ][ 0 ] = getSnapRegion( ( snappingAxis ) axis, projectedOrigin ); 2037 2038 if( mSizingMode == sizingNone ) 2039 snapRegions[ axis ][ 1 ] = getSnapRegion( ( snappingAxis ) axis, projectedOrigin + Point2I( selectionBounds.extent.x / 2, selectionBounds.extent.y / 2 ) ); 2040 2041 if( mSizingMode == sizingNone || 2042 ( axis == 0 && mSizingMode & sizingRight ) || 2043 ( axis == 1 && mSizingMode & sizingBottom ) ) 2044 snapRegions[ axis ][ 2 ] = getSnapRegion( ( snappingAxis ) axis, projectedOrigin + selectionBounds.extent - Point2I( 1, 1 ) ); 2045 } 2046 2047 // Find hit controls. 2048 2049 canHitSelectedControls( false ); 2050 2051 if( mSnapToEdges ) 2052 { 2053 for( U32 axis = 0; axis < 2; ++ axis ) 2054 if( snapAxisEnabled[ axis ] ) 2055 { 2056 getContentControl()->findHitControls( snapRegions[ axis ][ 0 ], mSnapHits[ axis ], HIT_NoCanHitNoRecurse ); 2057 getContentControl()->findHitControls( snapRegions[ axis ][ 2 ], mSnapHits[ axis ], HIT_NoCanHitNoRecurse ); 2058 } 2059 } 2060 if( mSnapToCenters && mSizingMode == sizingNone ) 2061 { 2062 for( U32 axis = 0; axis < 2; ++ axis ) 2063 if( snapAxisEnabled[ axis ] ) 2064 getContentControl()->findHitControls( snapRegions[ axis ][ 1 ], mSnapHits[ axis ], HIT_NoCanHitNoRecurse ); 2065 } 2066 2067 canHitSelectedControls( true ); 2068 2069 // Add the content control itself to the hit controls 2070 // so we can always get a snap on it. 2071 2072 if( mSnapToCanvas ) 2073 { 2074 mSnapHits[ 0 ].push_back( mContentControl ); 2075 mSnapHits[ 1 ].push_back( mContentControl ); 2076 } 2077 2078 // Find snaps. 2079 2080 for( U32 i = 0; i < 2; ++ i ) 2081 if( snapAxisEnabled[ i ] ) 2082 findSnaps( ( snappingAxis ) i, 2083 event.mousePoint, 2084 snapRegions[ i ][ 0 ], 2085 snapRegions[ i ][ 1 ], 2086 snapRegions[ i ][ 2 ] ); 2087 2088 // Clean up. 2089 2090 mSnapHits[ 0 ].clear(); 2091 mSnapHits[ 1 ].clear(); 2092} 2093 2094//----------------------------------------------------------------------------- 2095 2096void GuiEditCtrl::doGridSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta ) 2097{ 2098 delta += selectionBounds.point; 2099 2100 if( mGridSnap.x ) 2101 delta.x -= delta.x % mGridSnap.x; 2102 if( mGridSnap.y ) 2103 delta.y -= delta.y % mGridSnap.y; 2104 2105 delta -= selectionBounds.point; 2106} 2107 2108//----------------------------------------------------------------------------- 2109 2110void GuiEditCtrl::doGuideSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta ) 2111{ 2112 if( !mSnapToGuides ) 2113 return; 2114 2115 Point2I min = getContentControl()->localToGlobalCoord( selectionBounds.point + delta ); 2116 Point2I mid = min + selectionBounds.extent / 2; 2117 Point2I max = min + selectionBounds.extent - Point2I( 1, 1 ); 2118 2119 for( U32 axis = 0; axis < 2; ++ axis ) 2120 { 2121 if( mSnapToEdges ) 2122 { 2123 S32 guideMin = -1; 2124 S32 guideMax = -1; 2125 2126 if( mSizingMode == sizingNone || 2127 ( axis == 0 && mSizingMode & sizingLeft ) || 2128 ( axis == 1 && mSizingMode & sizingTop ) ) 2129 guideMin = findGuide( ( guideAxis ) axis, min, mSnapSensitivity ); 2130 if( mSizingMode == sizingNone || 2131 ( axis == 0 && mSizingMode & sizingRight ) || 2132 ( axis == 1 && mSizingMode & sizingBottom ) ) 2133 guideMax = findGuide( ( guideAxis ) axis, max, mSnapSensitivity ); 2134 2135 Point2I pos( 0, 0 ); 2136 2137 if( guideMin != -1 ) 2138 { 2139 pos[ axis ] = mGuides[ axis ][ guideMin ]; 2140 registerSnap( ( snappingAxis ) axis, event.mousePoint, pos, SnapEdgeMin ); 2141 } 2142 2143 if( guideMax != -1 ) 2144 { 2145 pos[ axis ] = mGuides[ axis ][ guideMax ]; 2146 registerSnap( ( snappingAxis ) axis, event.mousePoint, pos, SnapEdgeMax ); 2147 } 2148 } 2149 2150 if( mSnapToCenters && mSizingMode == sizingNone ) 2151 { 2152 const S32 guideMid = findGuide( ( guideAxis ) axis, mid, mSnapSensitivity ); 2153 if( guideMid != -1 ) 2154 { 2155 Point2I pos( 0, 0 ); 2156 pos[ axis ] = mGuides[ axis ][ guideMid ]; 2157 registerSnap( ( snappingAxis ) axis, event.mousePoint, pos, SnapEdgeMid ); 2158 } 2159 } 2160 } 2161} 2162 2163//----------------------------------------------------------------------------- 2164 2165void GuiEditCtrl::doSnapping( const GuiEvent& event, const RectI& selectionBounds, Point2I& delta ) 2166{ 2167 // Clear snapping. If we have snapping on, we want to find a new best snap. 2168 2169 mSnapped[ SnapVertical ] = false; 2170 mSnapped[ SnapHorizontal ] = false; 2171 2172 // Compute global bounds. 2173 2174 RectI selectionBoundsGlobal = selectionBounds; 2175 selectionBoundsGlobal.point = getContentControl()->localToGlobalCoord( selectionBoundsGlobal.point ); 2176 2177 // Apply the snaps. 2178 2179 doGridSnap( event, selectionBounds, selectionBoundsGlobal, delta ); 2180 doGuideSnap( event, selectionBounds, selectionBoundsGlobal, delta ); 2181 doControlSnap( event, selectionBounds, selectionBoundsGlobal, delta ); 2182 2183 // If we have a horizontal snap, compute a delta. 2184 2185 if( mSnapped[ SnapVertical ] ) 2186 snapDelta( SnapVertical, mSnapEdge[ SnapVertical ], mSnapOffset[ SnapVertical ], selectionBoundsGlobal, delta ); 2187 2188 // If we have a vertical snap, compute a delta. 2189 2190 if( mSnapped[ SnapHorizontal ] ) 2191 snapDelta( SnapHorizontal, mSnapEdge[ SnapHorizontal ], mSnapOffset[ SnapHorizontal ], selectionBoundsGlobal, delta ); 2192} 2193 2194//----------------------------------------------------------------------------- 2195 2196void GuiEditCtrl::snapToGrid( Point2I& point ) 2197{ 2198 if( mGridSnap.x ) 2199 point.x -= point.x % mGridSnap.x; 2200 if( mGridSnap.y ) 2201 point.y -= point.y % mGridSnap.y; 2202} 2203 2204//============================================================================= 2205// Misc. 2206//============================================================================= 2207// MARK: ---- Misc ---- 2208 2209//----------------------------------------------------------------------------- 2210 2211void GuiEditCtrl::setContentControl(GuiControl *root) 2212{ 2213 mContentControl = root; 2214 if( root != NULL ) 2215 root->mIsContainer = true; 2216 2217 setCurrentAddSet( root ); 2218} 2219 2220//----------------------------------------------------------------------------- 2221 2222void GuiEditCtrl::setEditMode(bool value) 2223{ 2224 mActive = value; 2225 2226 clearSelection(); 2227 if( mActive && mAwake ) 2228 mCurrentAddSet = NULL; 2229} 2230 2231//----------------------------------------------------------------------------- 2232 2233void GuiEditCtrl::setMouseMode( mouseModes mode ) 2234{ 2235 if( mMouseDownMode != mode ) 2236 { 2237 mMouseDownMode = mode; 2238 2239 onMouseModeChange_callback(); 2240 } 2241} 2242 2243//----------------------------------------------------------------------------- 2244 2245void GuiEditCtrl::setCurrentAddSet(GuiControl *ctrl, bool doclearSelection) 2246{ 2247 if (ctrl != mCurrentAddSet) 2248 { 2249 if(doclearSelection) 2250 clearSelection(); 2251 2252 mCurrentAddSet = ctrl; 2253 } 2254} 2255 2256//----------------------------------------------------------------------------- 2257 2258GuiControl* GuiEditCtrl::getCurrentAddSet() 2259{ 2260 if( !mCurrentAddSet ) 2261 setCurrentAddSet( mContentControl, false ); 2262 2263 return mCurrentAddSet; 2264} 2265 2266//----------------------------------------------------------------------------- 2267 2268void GuiEditCtrl::addNewControl(GuiControl *ctrl) 2269{ 2270 getCurrentAddSet()->addObject(ctrl); 2271 select( ctrl ); 2272 2273 // undo 2274 onAddNewCtrl_callback( ctrl ); 2275} 2276 2277//----------------------------------------------------------------------------- 2278 2279S32 GuiEditCtrl::getSizingHitKnobs(const Point2I &pt, const RectI &box) 2280{ 2281 S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1; 2282 S32 cx = (lx + rx) >> 1; 2283 S32 ty = box.point.y, by = box.point.y + box.extent.y - 1; 2284 S32 cy = (ty + by) >> 1; 2285 2286 // adjust nuts, so they dont straddle the controls 2287 lx -= NUT_SIZE; 2288 ty -= NUT_SIZE; 2289 rx += NUT_SIZE; 2290 by += NUT_SIZE; 2291 2292 if (inNut(pt, lx, ty)) 2293 return sizingLeft | sizingTop; 2294 if (inNut(pt, cx, ty)) 2295 return sizingTop; 2296 if (inNut(pt, rx, ty)) 2297 return sizingRight | sizingTop; 2298 if (inNut(pt, lx, by)) 2299 return sizingLeft | sizingBottom; 2300 if (inNut(pt, cx, by)) 2301 return sizingBottom; 2302 if (inNut(pt, rx, by)) 2303 return sizingRight | sizingBottom; 2304 if (inNut(pt, lx, cy)) 2305 return sizingLeft; 2306 if (inNut(pt, rx, cy)) 2307 return sizingRight; 2308 return sizingNone; 2309} 2310 2311//----------------------------------------------------------------------------- 2312 2313void GuiEditCtrl::getDragRect(RectI &box) 2314{ 2315 box.point.x = getMin(mLastMousePos.x, mSelectionAnchor.x); 2316 box.extent.x = getMax(mLastMousePos.x, mSelectionAnchor.x) - box.point.x + 1; 2317 box.point.y = getMin(mLastMousePos.y, mSelectionAnchor.y); 2318 box.extent.y = getMax(mLastMousePos.y, mSelectionAnchor.y) - box.point.y + 1; 2319} 2320 2321//----------------------------------------------------------------------------- 2322 2323void GuiEditCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent) 2324{ 2325 GuiCanvas *pRoot = getRoot(); 2326 if( !pRoot ) 2327 return; 2328 2329 showCursor = false; 2330 cursor = NULL; 2331 2332 Point2I ctOffset; 2333 Point2I cext; 2334 GuiControl *ctrl; 2335 2336 Point2I mousePos = globalToLocalCoord(lastGuiEvent.mousePoint); 2337 2338 PlatformWindow *pWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow(); 2339 AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible."); 2340 PlatformCursorController *pController = pWindow->getCursorController(); 2341 AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!"); 2342 2343 S32 desiredCursor = PlatformCursorController::curArrow; 2344 2345 // first see if we hit a sizing knob on the currently selected control... 2346 if (mSelectedControls.size() == 1 ) 2347 { 2348 ctrl = mSelectedControls.first(); 2349 cext = ctrl->getExtent(); 2350 ctOffset = globalToLocalCoord(ctrl->localToGlobalCoord(Point2I(0,0))); 2351 RectI box(ctOffset.x,ctOffset.y,cext.x, cext.y); 2352 2353 GuiEditCtrl::sizingModes sizeMode = (GuiEditCtrl::sizingModes)getSizingHitKnobs(mousePos, box); 2354 2355 if( mMouseDownMode == SizingSelection ) 2356 { 2357 if ( ( mSizingMode == ( sizingBottom | sizingRight ) ) || ( mSizingMode == ( sizingTop | sizingLeft ) ) ) 2358 desiredCursor = PlatformCursorController::curResizeNWSE; 2359 else if ( ( mSizingMode == ( sizingBottom | sizingLeft ) ) || ( mSizingMode == ( sizingTop | sizingRight ) ) ) 2360 desiredCursor = PlatformCursorController::curResizeNESW; 2361 else if ( mSizingMode == sizingLeft || mSizingMode == sizingRight ) 2362 desiredCursor = PlatformCursorController::curResizeVert; 2363 else if (mSizingMode == sizingTop || mSizingMode == sizingBottom ) 2364 desiredCursor = PlatformCursorController::curResizeHorz; 2365 } 2366 else 2367 { 2368 // Check for current mouse position after checking for actual sizing mode 2369 if ( ( sizeMode == ( sizingBottom | sizingRight ) ) || ( sizeMode == ( sizingTop | sizingLeft ) ) ) 2370 desiredCursor = PlatformCursorController::curResizeNWSE; 2371 else if ( ( sizeMode == ( sizingBottom | sizingLeft ) ) || ( sizeMode == ( sizingTop | sizingRight ) ) ) 2372 desiredCursor = PlatformCursorController::curResizeNESW; 2373 else if (sizeMode == sizingLeft || sizeMode == sizingRight ) 2374 desiredCursor = PlatformCursorController::curResizeVert; 2375 else if (sizeMode == sizingTop || sizeMode == sizingBottom ) 2376 desiredCursor = PlatformCursorController::curResizeHorz; 2377 } 2378 } 2379 2380 if( mMouseDownMode == MovingSelection && cursor == NULL ) 2381 desiredCursor = PlatformCursorController::curResizeAll; 2382 2383 if( pRoot->mCursorChanged != desiredCursor ) 2384 { 2385 // We've already changed the cursor, 2386 // so set it back before we change it again. 2387 if(pRoot->mCursorChanged != -1) 2388 pController->popCursor(); 2389 2390 // Now change the cursor shape 2391 pController->pushCursor(desiredCursor); 2392 pRoot->mCursorChanged = desiredCursor; 2393 } 2394} 2395 2396//----------------------------------------------------------------------------- 2397 2398void GuiEditCtrl::setSnapToGrid(U32 gridsize) 2399{ 2400 if( gridsize != 0 ) 2401 gridsize = getMax( gridsize, ( U32 ) MIN_GRID_SIZE ); 2402 mGridSnap.set( gridsize, gridsize ); 2403} 2404 2405//----------------------------------------------------------------------------- 2406 2407void GuiEditCtrl::controlInspectPreApply(GuiControl* object) 2408{ 2409 // undo 2410 onControlInspectPreApply_callback( object ); 2411} 2412 2413//----------------------------------------------------------------------------- 2414 2415void GuiEditCtrl::controlInspectPostApply(GuiControl* object) 2416{ 2417 // undo 2418 onControlInspectPostApply_callback( object ); 2419} 2420 2421//----------------------------------------------------------------------------- 2422 2423void GuiEditCtrl::startDragMove( const Point2I& startPoint ) 2424{ 2425 mDragMoveUndo = true; 2426 2427 // For calculating mouse delta 2428 mDragBeginPoint = globalToLocalCoord( startPoint ); 2429 2430 // Allocate enough space for our selected controls 2431 mDragBeginPoints.reserve( mSelectedControls.size() ); 2432 2433 // For snapping to origin 2434 Vector<GuiControl *>::iterator i; 2435 for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) 2436 mDragBeginPoints.push_back( (*i)->getPosition() ); 2437 2438 // Set Mouse Mode 2439 setMouseMode( MovingSelection ); 2440 2441 // undo 2442 onPreEdit_callback( getSelectedSet() ); 2443} 2444 2445//----------------------------------------------------------------------------- 2446 2447void GuiEditCtrl::startDragRectangle( const Point2I& startPoint ) 2448{ 2449 mSelectionAnchor = globalToLocalCoord( startPoint ); 2450 setMouseMode( DragSelecting ); 2451} 2452 2453//----------------------------------------------------------------------------- 2454 2455void GuiEditCtrl::startDragClone( const Point2I& startPoint ) 2456{ 2457 mDragBeginPoint = globalToLocalCoord( startPoint ); 2458 2459 setMouseMode( DragClone ); 2460} 2461 2462//----------------------------------------------------------------------------- 2463 2464void GuiEditCtrl::startMouseGuideDrag( guideAxis axis, U32 guideIndex, bool lockMouse ) 2465{ 2466 mDragGuideIndex[ axis ] = guideIndex; 2467 mDragGuide[ axis ] = true; 2468 2469 setMouseMode( DragGuide ); 2470 2471 // Grab the mouse. 2472 2473 if( lockMouse ) 2474 mouseLock(); 2475} 2476 2477//============================================================================= 2478// Console Methods. 2479//============================================================================= 2480// MARK: ---- Console Methods ---- 2481 2482//----------------------------------------------------------------------------- 2483 2484DefineEngineMethod( GuiEditCtrl, getContentControl, S32, (), , "() - Return the toplevel control edited inside the GUI editor." ) 2485{ 2486 GuiControl* ctrl = object->getContentControl(); 2487 if( ctrl ) 2488 return ctrl->getId(); 2489 else 2490 return 0; 2491} 2492 2493//----------------------------------------------------------------------------- 2494 2495DefineEngineMethod( GuiEditCtrl, setContentControl, void, (GuiControl *ctrl ), , "( GuiControl ctrl ) - Set the toplevel control to edit in the GUI editor." ) 2496{ 2497 if (ctrl) 2498 object->setContentControl(ctrl); 2499} 2500 2501//----------------------------------------------------------------------------- 2502 2503DefineEngineMethod( GuiEditCtrl, addNewCtrl, void, (GuiControl *ctrl), , "(GuiControl ctrl)") 2504{ 2505 if (ctrl) 2506 object->addNewControl(ctrl); 2507} 2508 2509//----------------------------------------------------------------------------- 2510 2511DefineEngineMethod( GuiEditCtrl, addSelection, void, (S32 id), , "selects a control.") 2512{ 2513 object->addSelection(id); 2514} 2515 2516//----------------------------------------------------------------------------- 2517 2518DefineEngineMethod( GuiEditCtrl, removeSelection, void, (S32 id), , "deselects a control.") 2519{ 2520 object->removeSelection(id); 2521} 2522 2523//----------------------------------------------------------------------------- 2524 2525DefineEngineMethod( GuiEditCtrl, clearSelection, void, (), , "Clear selected controls list.") 2526{ 2527 object->clearSelection(); 2528} 2529 2530//----------------------------------------------------------------------------- 2531 2532DefineEngineMethod( GuiEditCtrl, select, void, (GuiControl *ctrl), , "(GuiControl ctrl)") 2533{ 2534 if (ctrl) 2535 object->setSelection(ctrl, false); 2536} 2537 2538//----------------------------------------------------------------------------- 2539 2540DefineEngineMethod( GuiEditCtrl, setCurrentAddSet, void, (GuiControl *addSet), , "(GuiControl ctrl)") 2541{ 2542 if (addSet) 2543 object->setCurrentAddSet(addSet); 2544} 2545 2546//----------------------------------------------------------------------------- 2547 2548DefineEngineMethod( GuiEditCtrl, getCurrentAddSet, S32, (), , "Returns the set to which new controls will be added") 2549{ 2550 const GuiControl* add = object->getCurrentAddSet(); 2551 return add ? add->getId() : 0; 2552} 2553 2554//----------------------------------------------------------------------------- 2555 2556DefineEngineMethod( GuiEditCtrl, toggle, void, (), , "Toggle activation.") 2557{ 2558 object->setEditMode( !object->isActive() ); 2559} 2560 2561//----------------------------------------------------------------------------- 2562 2563DefineEngineMethod( GuiEditCtrl, justify, void, (U32 mode), , "(int mode)" ) 2564{ 2565 object->justifySelection( (GuiEditCtrl::Justification)mode ); 2566} 2567 2568//----------------------------------------------------------------------------- 2569 2570DefineEngineMethod( GuiEditCtrl, bringToFront, void, (), , "") 2571{ 2572 object->bringToFront(); 2573} 2574 2575//----------------------------------------------------------------------------- 2576 2577DefineEngineMethod( GuiEditCtrl, pushToBack, void, (), , "") 2578{ 2579 object->pushToBack(); 2580} 2581 2582//----------------------------------------------------------------------------- 2583 2584DefineEngineMethod( GuiEditCtrl, deleteSelection, void, (), , "() - Delete the selected controls.") 2585{ 2586 object->deleteSelection(); 2587} 2588 2589//----------------------------------------------------------------------------- 2590 2591DefineEngineMethod( GuiEditCtrl, moveSelection, void, (S32 dx, S32 dy), , "Move all controls in the selection by (dx,dy) pixels.") 2592{ 2593 object->moveAndSnapSelection(Point2I(dx, dy)); 2594} 2595 2596//----------------------------------------------------------------------------- 2597 2598DefineEngineMethod( GuiEditCtrl, saveSelection, void, (const char * filename), (nullAsType<const char*>()), "( string fileName=null ) - Save selection to file or clipboard.") 2599{ 2600 2601 object->saveSelection( filename ); 2602} 2603 2604//----------------------------------------------------------------------------- 2605 2606DefineEngineMethod( GuiEditCtrl, loadSelection, void, (const char * filename), (nullAsType<const char*>()), "( string fileName=null ) - Load selection from file or clipboard.") 2607{ 2608 2609 object->loadSelection( filename ); 2610} 2611 2612//----------------------------------------------------------------------------- 2613 2614DefineEngineMethod( GuiEditCtrl, selectAll, void, (), , "()") 2615{ 2616 object->selectAll(); 2617} 2618 2619//----------------------------------------------------------------------------- 2620 2621DefineEngineMethod( GuiEditCtrl, getSelection, SimSet*, (),, 2622 "Gets the set of GUI controls currently selected in the editor." ) 2623{ 2624 return object->getSelectedSet(); 2625} 2626 2627//----------------------------------------------------------------------------- 2628 2629DefineEngineMethod( GuiEditCtrl, getNumSelected, S32, (), , "() - Return the number of controls currently selected." ) 2630{ 2631 return object->getNumSelected(); 2632} 2633 2634//----------------------------------------------------------------------------- 2635 2636DefineEngineMethod( GuiEditCtrl, getSelectionGlobalBounds, const char*, (), , "() - Returns global bounds of current selection as vector 'x y width height'." ) 2637{ 2638 RectI bounds = object->getSelectionGlobalBounds(); 2639 String str = String::ToString( "%i %i %i %i", bounds.point.x, bounds.point.y, bounds.extent.x, bounds.extent.y ); 2640 2641 char* buffer = Con::getReturnBuffer( str.size() ); 2642 dStrcpy( buffer, str.c_str(), str.size() ); 2643 2644 return buffer; 2645} 2646 2647//----------------------------------------------------------------------------- 2648 2649DefineEngineMethod( GuiEditCtrl, selectParents, void, ( bool addToSelection ), (false), "( bool addToSelection=false ) - Select parents of currently selected controls." ) 2650{ 2651 2652 object->selectParents( addToSelection ); 2653} 2654 2655//----------------------------------------------------------------------------- 2656 2657DefineEngineMethod( GuiEditCtrl, selectChildren, void, ( bool addToSelection ), (false), "( bool addToSelection=false ) - Select children of currently selected controls." ) 2658{ 2659 2660 object->selectChildren( addToSelection ); 2661} 2662 2663//----------------------------------------------------------------------------- 2664 2665DefineEngineMethod( GuiEditCtrl, getTrash, SimGroup*, (),, 2666 "Gets the GUI controls(s) that are currently in the trash.") 2667{ 2668 return object->getTrash(); 2669} 2670 2671//----------------------------------------------------------------------------- 2672 2673DefineEngineMethod(GuiEditCtrl, setSnapToGrid, void, (U32 gridsize), , "GuiEditCtrl.setSnapToGrid(gridsize)") 2674{ 2675 object->setSnapToGrid(gridsize); 2676} 2677 2678//----------------------------------------------------------------------------- 2679 2680DefineEngineMethod( GuiEditCtrl, readGuides, void, ( GuiControl* ctrl, S32 axis ), (-1), "( GuiControl ctrl [, int axis ] ) - Read the guides from the given control." ) 2681{ 2682 // Find the control. 2683 2684 if( !ctrl ) 2685 { 2686 return; 2687 } 2688 2689 // Read the guides. 2690 2691 if( axis != -1 ) 2692 { 2693 if( axis < 0 || axis > 1 ) 2694 { 2695 Con::errorf( "GuiEditCtrl::readGuides - invalid axis '%s'", axis ); 2696 return; 2697 } 2698 2699 object->readGuides( ( GuiEditCtrl::guideAxis ) axis, ctrl ); 2700 } 2701 else 2702 { 2703 object->readGuides( GuiEditCtrl::GuideHorizontal, ctrl ); 2704 object->readGuides( GuiEditCtrl::GuideVertical, ctrl ); 2705 } 2706} 2707 2708//----------------------------------------------------------------------------- 2709 2710DefineEngineMethod( GuiEditCtrl, writeGuides, void, ( GuiControl* ctrl, S32 axis ), ( -1), "( GuiControl ctrl [, int axis ] ) - Write the guides to the given control." ) 2711{ 2712 // Find the control. 2713 2714 if( ! ctrl ) 2715 { 2716 return; 2717 } 2718 2719 // Write the guides. 2720 2721 if( axis != -1 ) 2722 { 2723 if( axis < 0 || axis > 1 ) 2724 { 2725 Con::errorf( "GuiEditCtrl::writeGuides - invalid axis '%s'", axis ); 2726 return; 2727 } 2728 2729 object->writeGuides( ( GuiEditCtrl::guideAxis ) axis, ctrl ); 2730 } 2731 else 2732 { 2733 object->writeGuides( GuiEditCtrl::GuideHorizontal, ctrl ); 2734 object->writeGuides( GuiEditCtrl::GuideVertical, ctrl ); 2735 } 2736} 2737 2738//----------------------------------------------------------------------------- 2739 2740DefineEngineMethod( GuiEditCtrl, clearGuides, void, ( S32 axis ), (-1), "( [ int axis ] ) - Clear all currently set guide lines." ) 2741{ 2742 if( axis != -1 ) 2743 { 2744 if( axis < 0 || axis > 1 ) 2745 { 2746 Con::errorf( "GuiEditCtrl::clearGuides - invalid axis '%i'", axis ); 2747 return; 2748 } 2749 2750 object->clearGuides( ( GuiEditCtrl::guideAxis ) axis ); 2751 } 2752 else 2753 { 2754 object->clearGuides( GuiEditCtrl::GuideHorizontal ); 2755 object->clearGuides( GuiEditCtrl::GuideVertical ); 2756 } 2757} 2758 2759//----------------------------------------------------------------------------- 2760 2761DefineEngineMethod( GuiEditCtrl, fitIntoParents, void, (bool width, bool height), (true, true), "( bool width=true, bool height=true ) - Fit selected controls into their parents." ) 2762{ 2763 2764 object->fitIntoParents( width, height ); 2765} 2766 2767//----------------------------------------------------------------------------- 2768 2769DefineEngineMethod( GuiEditCtrl, getMouseMode, const char*, (), , "() - Return the current mouse mode." ) 2770{ 2771 switch( object->getMouseMode() ) 2772 { 2773 case GuiEditCtrl::Selecting: 2774 return "Selecting"; 2775 2776 case GuiEditCtrl::DragSelecting: 2777 return "DragSelecting"; 2778 2779 case GuiEditCtrl::MovingSelection: 2780 return "MovingSelection"; 2781 2782 case GuiEditCtrl::SizingSelection: 2783 return "SizingSelection"; 2784 2785 case GuiEditCtrl::DragGuide: 2786 return "DragGuide"; 2787 2788 case GuiEditCtrl::DragClone: 2789 return "DragClone"; 2790 2791 default: 2792 return ""; 2793 } 2794} 2795 2796//============================================================================= 2797// GuiEditorRuler. 2798//============================================================================= 2799 2800class GuiEditorRuler : public GuiControl 2801{ 2802 public: 2803 2804 typedef GuiControl Parent; 2805 2806 protected: 2807 2808 String mRefCtrlName; 2809 String mEditCtrlName; 2810 2811 GuiScrollCtrl* mRefCtrl; 2812 GuiEditCtrl* mEditCtrl; 2813 2814 public: 2815 2816 enum EOrientation 2817 { 2818 ORIENTATION_Horizontal, 2819 ORIENTATION_Vertical 2820 }; 2821 2822 GuiEditorRuler() 2823 : mRefCtrl( 0 ), 2824 mEditCtrl( 0 ) 2825 { 2826 } 2827 2828 EOrientation getOrientation() const 2829 { 2830 if( getWidth() > getHeight() ) 2831 return ORIENTATION_Horizontal; 2832 else 2833 return ORIENTATION_Vertical; 2834 } 2835 2836 bool onWake() 2837 { 2838 if( !Parent::onWake() ) 2839 return false; 2840 2841 if( !mEditCtrlName.isEmpty() && !Sim::findObject( mEditCtrlName, mEditCtrl ) ) 2842 Con::errorf( "GuiEditorRuler::onWake() - no GuiEditCtrl '%s'", mEditCtrlName.c_str() ); 2843 2844 if( !mRefCtrlName.isEmpty() && !Sim::findObject( mRefCtrlName, mRefCtrl ) ) 2845 Con::errorf( "GuiEditorRuler::onWake() - no GuiScrollCtrl '%s'", mRefCtrlName.c_str() ); 2846 2847 return true; 2848 } 2849 2850 void onPreRender() 2851 { 2852 setUpdate(); 2853 } 2854 2855 void onMouseDown( const GuiEvent& event ) 2856 { 2857 if( !mEditCtrl ) 2858 return; 2859 2860 // Determine the guide axis. 2861 2862 GuiEditCtrl::guideAxis axis; 2863 if( getOrientation() == ORIENTATION_Horizontal ) 2864 axis = GuiEditCtrl::GuideHorizontal; 2865 else 2866 axis = GuiEditCtrl::GuideVertical; 2867 2868 // Start dragging a new guide out in the editor. 2869 2870 U32 guideIndex = mEditCtrl->addGuide( axis, 0 ); 2871 mEditCtrl->startMouseGuideDrag( axis, guideIndex ); 2872 } 2873 2874 void onRender(Point2I offset, const RectI &updateRect) 2875 { 2876 GFX->getDrawUtil()->drawRectFill(updateRect, ColorI::WHITE); 2877 2878 Point2I choffset(0,0); 2879 if( mRefCtrl != NULL ) 2880 choffset = mRefCtrl->getChildPos(); 2881 2882 if( getOrientation() == ORIENTATION_Horizontal ) 2883 { 2884 // it's horizontal. 2885 for(U32 i = 0; i < getWidth(); i++) 2886 { 2887 S32 x = offset.x + i; 2888 S32 pos = i - choffset.x; 2889 if(!(pos % 10)) 2890 { 2891 S32 start = 6; 2892 if(!(pos %20)) 2893 start = 4; 2894 if(!(pos % 100)) 2895 start = 1; 2896 GFX->getDrawUtil()->drawLine(x, offset.y + start, x, offset.y + 10, ColorI::BLACK); 2897 } 2898 } 2899 } 2900 else 2901 { 2902 // it's vertical. 2903 for(U32 i = 0; i < getHeight(); i++) 2904 { 2905 S32 y = offset.y + i; 2906 S32 pos = i - choffset.y; 2907 if(!(pos % 10)) 2908 { 2909 S32 start = 6; 2910 if(!(pos %20)) 2911 start = 4; 2912 if(!(pos % 100)) 2913 start = 1; 2914 GFX->getDrawUtil()->drawLine(offset.x + start, y, offset.x + 10, y, ColorI::BLACK); 2915 } 2916 } 2917 } 2918 } 2919 static void initPersistFields() 2920 { 2921 addField( "refCtrl", TypeRealString, Offset( mRefCtrlName, GuiEditorRuler ) ); 2922 addField( "editCtrl", TypeRealString, Offset( mEditCtrlName, GuiEditorRuler ) ); 2923 2924 Parent::initPersistFields(); 2925 } 2926 2927 DECLARE_CONOBJECT(GuiEditorRuler); 2928 DECLARE_CATEGORY( "Gui Editor" ); 2929}; 2930 2931IMPLEMENT_CONOBJECT(GuiEditorRuler); 2932 2933ConsoleDocClass( GuiEditorRuler, 2934 "@brief Visual representation of markers on top and left sides of GUI Editor\n\n" 2935 "Editor use only.\n\n" 2936 "@internal" 2937); 2938