guiDragAndDropCtrl.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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 "gui/containers/guiDragAndDropCtrl.h"
  23. #include "gui/core/guiCanvas.h"
  24. #include "console/engineAPI.h"
  25. IMPLEMENT_CONOBJECT( GuiDragAndDropControl );
  26. ConsoleDocClass( GuiDragAndDropControl,
  27. "@brief A container control that can be used to implement drag&drop behavior.\n\n"
  28. "GuiDragAndDropControl is a special control that can be used to allow drag&drop behavior to be implemented where "
  29. "GuiControls may be dragged across the canvas and the dropped on other GuiControls.\n\n"
  30. "To start a drag operation, construct a GuiDragAndDropControl and add the control that should be drag&dropped "
  31. "as a child to it. Note that this must be a single child control. To drag multiple controls, wrap them in a new "
  32. "GuiControl object as a temporary container.\n\n"
  33. "Then, to initiate the drag, add the GuiDragAndDropControl to the canvas and call startDragging(). You can optionally "
  34. "supply an offset to better position the GuiDragAndDropControl on the mouse cursor.\n\n"
  35. "As the GuiDragAndDropControl is then moved across the canvas, it will call the onControlDragEnter(), onControlDragExit(), "
  36. "onControlDragged(), and finally onControlDropped() callbacks on the visible topmost controls that it moves across. "
  37. "onControlDropped() is called when the mouse button is released and the drag operation thus finished.\n\n"
  38. "@tsexample\n"
  39. "// The following example implements drag&drop behavior for GuiSwatchButtonCtrl so that\n"
  40. "// one color swatch may be dragged over the other to quickly copy its color.\n"
  41. "//\n"
  42. "// This code is taken from the stock scripts.\n"
  43. "\n"
  44. "//---------------------------------------------------------------------------------------------\n"
  45. "\n"
  46. "// With this method, we start the operation when the mouse is click-dragged away from a color swatch.\n"
  47. "function GuiSwatchButtonCtrl::onMouseDragged( %this )\n"
  48. "{\n"
  49. " // First we construct a new temporary swatch button that becomes the payload for our\n"
  50. " // drag operation and give it the properties of the swatch button we want to copy.\n"
  51. "\n"
  52. " %payload = new GuiSwatchButtonCtrl();\n"
  53. " %payload.assignFieldsFrom( %this );\n"
  54. " %payload.position = \"0 0\";\n"
  55. " %payload.dragSourceControl = %this; // Remember where the drag originated from so that we don't copy a color swatch onto itself.\n"
  56. "\n"
  57. " // Calculate the offset of the GuiDragAndDropControl from the mouse cursor. Here we center\n"
  58. " // it on the cursor.\n"
  59. "\n"
  60. " %xOffset = getWord( %payload.extent, 0 ) / 2;\n"
  61. " %yOffset = getWord( %payload.extent, 1 ) / 2;\n"
  62. "\n"
  63. " // Compute the initial position of the GuiDragAndDrop control on the cavas based on the current\n"
  64. " // mouse cursor position.\n"
  65. "\n"
  66. " %cursorpos = Canvas.getCursorPos();\n"
  67. " %xPos = getWord( %cursorpos, 0 ) - %xOffset;\n"
  68. " %yPos = getWord( %cursorpos, 1 ) - %yOffset;\n"
  69. "\n"
  70. " // Create the drag control.\n"
  71. "\n"
  72. " %ctrl = new GuiDragAndDropControl()\n"
  73. " {\n"
  74. " canSaveDynamicFields = \"0\";\n"
  75. " Profile = \"GuiSolidDefaultProfile\";\n"
  76. " HorizSizing = \"right\";\n"
  77. " VertSizing = \"bottom\";\n"
  78. " Position = %xPos SPC %yPos;\n"
  79. " extent = %payload.extent;\n"
  80. " MinExtent = \"4 4\";\n"
  81. " canSave = \"1\";\n"
  82. " Visible = \"1\";\n"
  83. " hovertime = \"1000\";\n"
  84. "\n"
  85. " // Let the GuiDragAndDropControl delete itself on mouse-up. When the drag is aborted,\n"
  86. " // this not only deletes the drag control but also our payload.\n"
  87. " deleteOnMouseUp = true;\n"
  88. "\n"
  89. " // To differentiate drags, use the namespace hierarchy to classify them.\n"
  90. " // This will allow a color swatch drag to tell itself apart from a file drag, for example.\n"
  91. " class = \"GuiDragAndDropControlType_ColorSwatch\";\n"
  92. " };\n"
  93. "\n"
  94. " // Add the temporary color swatch to the drag control as the payload.\n"
  95. " %ctrl.add( %payload );\n"
  96. "\n"
  97. " // Start drag by adding the drag control to the canvas and then calling startDragging().\n"
  98. "\n"
  99. " Canvas.getContent().add( %ctrl );\n"
  100. " %ctrl.startDragging( %xOffset, %yOffset );\n"
  101. "}\n"
  102. "\n"
  103. "//---------------------------------------------------------------------------------------------\n"
  104. "\n"
  105. "// This method receives the drop when the mouse button is released over a color swatch control\n"
  106. "// during a drag operation.\n"
  107. "function GuiSwatchButtonCtrl::onControlDropped( %this, %payload, %position )\n"
  108. "{\n"
  109. " // Make sure this is a color swatch drag operation.\n"
  110. " if( !%payload.parentGroup.isInNamespaceHierarchy( \"GuiDragAndDropControlType_ColorSwatch\" ) )\n"
  111. " return;\n"
  112. "\n"
  113. " // If dropped on same button whence we came from,\n"
  114. " // do nothing.\n"
  115. "\n"
  116. " if( %payload.dragSourceControl == %this )\n"
  117. " return;\n"
  118. "\n"
  119. " // If a swatch button control is dropped onto this control,\n"
  120. " // copy it's color.\n"
  121. "\n"
  122. " if( %payload.isMemberOfClass( \"GuiSwatchButtonCtrl\" ) )\n"
  123. " {\n"
  124. " // If the swatch button is part of a color-type inspector field,\n"
  125. " // remember the inspector field so we can later set the color\n"
  126. " // through it.\n"
  127. "\n"
  128. " if( %this.parentGroup.isMemberOfClass( \"GuiInspectorTypeColorI\" ) )\n"
  129. " %this.parentGroup.apply( ColorFloatToInt( %payload.color ) );\n"
  130. " else if( %this.parentGroup.isMemberOfClass( \"GuiInspectorTypeColorF\" ) )\n"
  131. " %this.parentGroup.apply( %payload.color );\n"
  132. " else\n"
  133. " %this.setColor( %payload.color );\n"
  134. " }\n"
  135. "}\n"
  136. "@endtsexample\n\n"
  137. "@see GuiControl::onControlDragEnter\n"
  138. "@see GuiControl::onControlDragExit\n"
  139. "@see GuiControl::onControlDragged\n"
  140. "@see GuiControl::onControlDropped\n\n"
  141. "@ingroup GuiUtil"
  142. );
  143. IMPLEMENT_CALLBACK(GuiDragAndDropControl, onControlDragCancelled, void, (), (),
  144. "Called when the we cancel out of the drag and drop action.\n"
  145. "@see GuiDragAndDropControl::onControlDragCancelled");
  146. //-----------------------------------------------------------------------------
  147. GuiDragAndDropControl::GuiDragAndDropControl() : mDeleteOnMouseUp(true), mUseWholeCanvas(false)
  148. {
  149. }
  150. void GuiDragAndDropControl::initPersistFields()
  151. {
  152. docsURL;
  153. addField( "deleteOnMouseUp", TypeBool, Offset( mDeleteOnMouseUp, GuiDragAndDropControl ),
  154. "If true, the control deletes itself when the left mouse button is released.\n\n"
  155. "If at this point, the drag&drop control still contains its payload, it will be deleted along with the control." );
  156. addField("useWholeCanvas", TypeBool, Offset(mUseWholeCanvas, GuiDragAndDropControl),
  157. "If true, the control can be tested against ANY control active on the canvas instead of just the direct parent.\n\n");
  158. Parent::initPersistFields();
  159. }
  160. //-----------------------------------------------------------------------------
  161. void GuiDragAndDropControl::startDragging( Point2I offset )
  162. {
  163. GuiCanvas* canvas = getRoot();
  164. if( !canvas )
  165. {
  166. Con::errorf( "GuiDragAndDropControl::startDragging - GuiDragAndDropControl wasn't added to the gui before the drag started." );
  167. if( mDeleteOnMouseUp )
  168. deleteObject();
  169. return;
  170. }
  171. if( canvas->getMouseLockedControl() )
  172. {
  173. GuiEvent event;
  174. canvas->getMouseLockedControl()->onMouseLeave(event);
  175. canvas->mouseUnlock( canvas->getMouseLockedControl() );
  176. }
  177. canvas->mouseLock(this);
  178. canvas->setFirstResponder(this);
  179. mOffset = offset;
  180. mLastTarget=NULL;
  181. }
  182. //-----------------------------------------------------------------------------
  183. void GuiDragAndDropControl::onMouseDown( const GuiEvent& event )
  184. {
  185. startDragging( event.mousePoint - getPosition() );
  186. }
  187. //-----------------------------------------------------------------------------
  188. void GuiDragAndDropControl::onMouseDragged( const GuiEvent& event )
  189. {
  190. setPosition( event.mousePoint - mOffset );
  191. // Allow the control under the drag to react to a potential drop
  192. GuiControl* enterTarget = findDragTarget( event.mousePoint, "onControlDragEnter" );
  193. if( mLastTarget != enterTarget )
  194. {
  195. if( mLastTarget )
  196. mLastTarget->onControlDragExit_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() );
  197. if( enterTarget )
  198. enterTarget->onControlDragEnter_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() );
  199. mLastTarget = enterTarget;
  200. }
  201. GuiControl* dragTarget = findDragTarget( event.mousePoint, "onControlDragged" );
  202. if( dragTarget )
  203. dragTarget->onControlDragged_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() );
  204. }
  205. //-----------------------------------------------------------------------------
  206. void GuiDragAndDropControl::onMouseUp(const GuiEvent& event)
  207. {
  208. mouseUnlock();
  209. GuiControl* target = findDragTarget( event.mousePoint, "onControlDropped" );
  210. if (target)
  211. target->onControlDropped_callback(dynamic_cast<GuiControl*>(at(0)), getDropPoint());
  212. else
  213. onControlDragCancelled_callback();
  214. if( mDeleteOnMouseUp )
  215. deleteObject();
  216. }
  217. //-----------------------------------------------------------------------------
  218. GuiControl* GuiDragAndDropControl::findDragTarget( Point2I mousePoint, const char* method )
  219. {
  220. // If there are any children and we have a parent.
  221. GuiControl* parent = getParent();
  222. if (mUseWholeCanvas)
  223. {
  224. parent->setVisible(false);
  225. parent = getRoot();
  226. }
  227. if (size() && parent)
  228. {
  229. mVisible = false;
  230. GuiControl* dropControl = parent->findHitControl(mousePoint);
  231. mVisible = true;
  232. while( dropControl )
  233. {
  234. if (dropControl->isMethod(method))
  235. return dropControl;
  236. else
  237. dropControl = dropControl->getParent();
  238. }
  239. }
  240. if(mUseWholeCanvas)
  241. parent->setVisible(true);
  242. return NULL;
  243. }
  244. //=============================================================================
  245. // Console Methods.
  246. //=============================================================================
  247. // MARK: ---- Console Methods ----
  248. //-----------------------------------------------------------------------------
  249. DefineEngineMethod( GuiDragAndDropControl, startDragging, void, ( S32 x, S32 y ), ( 0, 0 ),
  250. "Start the drag operation.\n\n"
  251. "@param x X coordinate for the mouse pointer offset which the drag control should position itself.\n"
  252. "@param y Y coordinate for the mouse pointer offset which the drag control should position itself.")
  253. {
  254. object->startDragging( Point2I( x, y ) );
  255. }