guiEditCtrl.cpp 88 KB

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