Torque3D Documentation / _generateds / guiMLTextEditCtrl.cpp

guiMLTextEditCtrl.cpp

Engine/source/gui/controls/guiMLTextEditCtrl.cpp

More...

Public Functions

ConsoleDocClass(GuiMLTextEditCtrl , "@brief A text entry <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> that accepts the Gui Markup Language ('ML') tags and multiple <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">lines.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classguimltexteditctrl/">GuiMLTextEditCtrl</a>()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		lineSpacing = \"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		allowColorChars = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		maxChars = \"-1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		deniedSound = \"DeniedSoundProfile\";\n" "		text = \"\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		escapeCommand = \"onEscapeScriptFunction();\";\n" "	  //Properties not specific <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> have been omitted from this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">example.\n</a>" "	};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiMLTextCtrl\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControl\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControls\n</a>" )

Detailed Description

Public Functions

ConsoleDocClass(GuiMLTextEditCtrl , "@brief A text entry <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> that accepts the Gui Markup Language ('ML') tags and multiple <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">lines.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classguimltexteditctrl/">GuiMLTextEditCtrl</a>()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		lineSpacing = \"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		allowColorChars = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		maxChars = \"-1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		deniedSound = \"DeniedSoundProfile\";\n" "		text = \"\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		escapeCommand = \"onEscapeScriptFunction();\";\n" "	  //Properties not specific <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> have been omitted from this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">example.\n</a>" "	};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiMLTextCtrl\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControl\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControls\n</a>" )

IMPLEMENT_CONOBJECT(GuiMLTextEditCtrl )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "gui/controls/guiMLTextEditCtrl.h"
 25#include "gui/containers/guiScrollCtrl.h"
 26#include "gui/core/guiCanvas.h"
 27#include "console/consoleTypes.h"
 28#include "core/frameAllocator.h"
 29#include "core/stringBuffer.h"
 30#include "gfx/gfxDrawUtil.h"
 31#include "console/engineAPI.h"
 32
 33IMPLEMENT_CONOBJECT(GuiMLTextEditCtrl);
 34
 35ConsoleDocClass( GuiMLTextEditCtrl,
 36   "@brief A text entry control that accepts the Gui Markup Language ('ML') tags and multiple lines.\n\n"
 37
 38   "@tsexample\n"
 39   "new GuiMLTextEditCtrl()\n"
 40   "  {\n"
 41   "     lineSpacing = \"2\";\n"
 42   "     allowColorChars = \"0\";\n"
 43   "     maxChars = \"-1\";\n"
 44   "     deniedSound = \"DeniedSoundProfile\";\n"
 45   "     text = \"\";\n"
 46   "     escapeCommand = \"onEscapeScriptFunction();\";\n"
 47   "    //Properties not specific to this control have been omitted from this example.\n"
 48   "  };\n"
 49   "@endtsexample\n\n"
 50
 51   "@see GuiMLTextCtrl\n"
 52   "@see GuiControl\n\n"
 53
 54   "@ingroup GuiControls\n"
 55);
 56
 57//--------------------------------------------------------------------------
 58GuiMLTextEditCtrl::GuiMLTextEditCtrl()
 59{
 60   mEscapeCommand = StringTable->insert( "" );
 61
 62   mIsEditCtrl = true;
 63
 64   mActive = true;
 65
 66   mVertMoveAnchorValid = false;
 67}
 68
 69
 70//--------------------------------------------------------------------------
 71GuiMLTextEditCtrl::~GuiMLTextEditCtrl()
 72{
 73
 74}
 75
 76
 77//--------------------------------------------------------------------------
 78bool GuiMLTextEditCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
 79{
 80   // We don't want to get any smaller than our parent:
 81   Point2I newExt = newExtent;
 82   GuiControl* parent = getParent();
 83   if ( parent )
 84      newExt.y = getMax( parent->getHeight(), newExt.y );
 85
 86   return Parent::resize( newPosition, newExt );
 87}
 88
 89
 90//--------------------------------------------------------------------------
 91void GuiMLTextEditCtrl::initPersistFields()
 92{
 93   addField( "escapeCommand", TypeString, Offset( mEscapeCommand, GuiMLTextEditCtrl ), "Script function to run whenever the 'escape' key is pressed when this control is in focus.\n");
 94   Parent::initPersistFields();
 95}
 96
 97//--------------------------------------------------------------------------
 98void GuiMLTextEditCtrl::setFirstResponder()
 99{
100   Parent::setFirstResponder();
101
102   GuiCanvas *root = getRoot();
103   if (root != NULL)
104   {
105      root->enableKeyboardTranslation();
106
107      // If the native OS accelerator keys are not disabled
108      // then some key events like Delete, ctrl+V, etc may
109      // not make it down to us.
110      root->setNativeAcceleratorsEnabled( false );
111   }
112}
113
114void GuiMLTextEditCtrl::onLoseFirstResponder()
115{
116   GuiCanvas *root = getRoot();
117   if (root != NULL)
118   {
119      root->setNativeAcceleratorsEnabled( true );
120      root->disableKeyboardTranslation();
121   }
122
123   // Redraw the control:
124   setUpdate();
125}
126
127//--------------------------------------------------------------------------
128bool GuiMLTextEditCtrl::onWake()
129{
130   if( !Parent::onWake() )
131      return false;
132
133   getRoot()->enableKeyboardTranslation();
134   return true;
135}
136
137//--------------------------------------------------------------------------
138// Key events...
139bool GuiMLTextEditCtrl::onKeyDown(const GuiEvent& event)
140{
141   if ( !isActive() )
142      return false;
143
144   setUpdate();
145   //handle modifiers first...
146   if (event.modifier & SI_PRIMARY_CTRL)
147   {
148      switch(event.keyCode)
149      {
150         //copy/cut
151         case KEY_C:
152         case KEY_X:
153         {
154            //make sure we actually have something selected
155            if (mSelectionActive)
156            {
157               copyToClipboard(mSelectionStart, mSelectionEnd);
158
159               //if we're cutting, also delete the selection
160               if (event.keyCode == KEY_X)
161               {
162                  mSelectionActive = false;
163                  deleteChars(mSelectionStart, mSelectionEnd);
164                  mCursorPosition = mSelectionStart;
165               }
166               else
167                  mCursorPosition = mSelectionEnd + 1;
168            }
169            return true;
170         }
171
172         //paste
173         case KEY_V:
174         {
175            const char *clipBuf = Platform::getClipboard();
176            if (dStrlen(clipBuf) > 0)
177            {
178               // Normal ascii keypress.  Go ahead and add the chars...
179               if (mSelectionActive == true)
180               {
181                  mSelectionActive = false;
182                  deleteChars(mSelectionStart, mSelectionEnd);
183                  mCursorPosition = mSelectionStart;
184               }
185
186               insertChars(clipBuf, dStrlen(clipBuf), mCursorPosition);
187            }
188            return true;
189         }
190         
191         default:
192            break;
193      }
194   }
195   else if ( event.modifier & SI_SHIFT )
196   {
197      switch ( event.keyCode )
198      {
199         case KEY_TAB:
200            return( Parent::onKeyDown( event ) );
201         default:
202            break;
203      }
204   }
205   else if ( event.modifier == 0 )
206   {
207      switch (event.keyCode)
208      {
209         // Escape:
210         case KEY_ESCAPE:
211            if ( mEscapeCommand[0] )
212            {
213               Con::evaluate( mEscapeCommand );
214               return( true );
215            }
216            return( Parent::onKeyDown( event ) );
217
218         // Deletion
219         case KEY_BACKSPACE:
220         case KEY_DELETE:
221            handleDeleteKeys(event);
222            return true;
223
224         // Cursor movement
225         case KEY_LEFT:
226         case KEY_RIGHT:
227         case KEY_UP:
228         case KEY_DOWN:
229         case KEY_HOME:
230         case KEY_END:
231            handleMoveKeys(event);
232            return true;
233
234         // Special chars...
235         case KEY_TAB:
236            // insert 3 spaces
237            if (mSelectionActive == true)
238            {
239               mSelectionActive = false;
240               deleteChars(mSelectionStart, mSelectionEnd);
241               mCursorPosition = mSelectionStart;
242            }
243            insertChars( "\t", 1, mCursorPosition );
244            return true;
245
246         case KEY_RETURN:
247            // insert carriage return
248            if (mSelectionActive == true)
249            {
250               mSelectionActive = false;
251               deleteChars(mSelectionStart, mSelectionEnd);
252               mCursorPosition = mSelectionStart;
253            }
254            insertChars( "\n", 1, mCursorPosition );
255            return true;
256            
257         default:
258            break;
259      }
260   }
261
262   if ( (mFont && mFont->isValidChar(event.ascii)) || (!mFont && event.ascii != 0) )
263   {
264      // Normal ascii keypress.  Go ahead and add the chars...
265      if (mSelectionActive == true)
266      {
267         mSelectionActive = false;
268         deleteChars(mSelectionStart, mSelectionEnd);
269         mCursorPosition = mSelectionStart;
270      }
271
272      UTF8 *outString = NULL;
273      U32 outStringLen = 0;
274
275#ifdef TORQUE_UNICODE
276
277      UTF16 inData[2] = { event.ascii, 0 };
278      StringBuffer inBuff(inData);
279
280      FrameTemp<UTF8> outBuff(4);
281      inBuff.getCopy8(outBuff, 4);
282
283      outString = outBuff;
284      outStringLen = dStrlen(outBuff);
285#else
286      char ascii = char(event.ascii);
287      outString = &ascii;
288      outStringLen = 1;
289#endif
290
291      insertChars(outString, outStringLen, mCursorPosition);
292      mVertMoveAnchorValid = false;
293      return true;
294   }
295
296   // Otherwise, let the parent have the event...
297   return Parent::onKeyDown(event);
298}
299
300
301//--------------------------------------
302void GuiMLTextEditCtrl::handleDeleteKeys(const GuiEvent& event)
303{
304   if ( isSelectionActive() )
305   {
306      mSelectionActive = false;
307      deleteChars(mSelectionStart, mSelectionEnd+1);
308      mCursorPosition = mSelectionStart;
309   }
310   else
311   {
312      switch ( event.keyCode )
313      {
314         case KEY_BACKSPACE:
315            if (mCursorPosition != 0)
316            {
317               // delete one character left
318               deleteChars(mCursorPosition-1, mCursorPosition);
319               setUpdate();
320            }
321            break;
322
323         case KEY_DELETE:
324            if (mCursorPosition != mTextBuffer.length())
325            {
326               // delete one character right
327               deleteChars(mCursorPosition, mCursorPosition+1);
328               setUpdate();
329            }
330            break;
331
332        default:
333            AssertFatal(false, "Unknown key code received!");
334      }
335   }
336}
337
338
339//--------------------------------------
340void GuiMLTextEditCtrl::handleMoveKeys(const GuiEvent& event)
341{
342   if ( event.modifier & SI_SHIFT )
343      return;
344
345   mSelectionActive = false;
346
347   switch ( event.keyCode )
348   {
349      case KEY_LEFT:
350         mVertMoveAnchorValid = false;
351         // move one left
352         if ( mCursorPosition != 0 )
353         {
354            mCursorPosition--;
355            setUpdate();
356         }
357         break;
358
359      case KEY_RIGHT:
360         mVertMoveAnchorValid = false;
361         // move one right
362         if ( mCursorPosition != mTextBuffer.length() )
363         {
364            mCursorPosition++;
365            setUpdate();
366         }
367         break;
368
369      case KEY_UP:
370      case KEY_DOWN:
371      {
372         Line* walk;
373         for ( walk = mLineList; walk->next; walk = walk->next )
374         {
375            if ( mCursorPosition <= ( walk->textStart + walk->len ) )
376               break;
377         }
378
379         if ( !walk )
380            return;
381
382         if ( event.keyCode == KEY_UP )
383         {
384            if ( walk == mLineList )
385               return;
386         }
387         else if ( walk->next == NULL )
388            return;
389
390         Point2I newPos;
391         newPos.set( 0, walk->y );
392
393         // Find the x-position:
394         if ( !mVertMoveAnchorValid )
395         {
396            Point2I cursorTopP, cursorBottomP;
397            ColorI color;
398            getCursorPositionAndColor(cursorTopP, cursorBottomP, color);
399            mVertMoveAnchor = cursorTopP.x;
400            mVertMoveAnchorValid = true;
401         }
402
403         newPos.x = mVertMoveAnchor;
404
405         // Set the new y-position:
406         if (event.keyCode == KEY_UP)
407            newPos.y--;
408         else
409            newPos.y += (walk->height + 1);
410
411         if (setCursorPosition(getTextPosition(newPos)))
412            mVertMoveAnchorValid = false;
413         break;
414      }
415
416      case KEY_HOME:
417      case KEY_END:
418      {
419         mVertMoveAnchorValid = false;
420         Line* walk;
421         for (walk = mLineList; walk->next; walk = walk->next)
422         {
423            if (mCursorPosition <= (walk->textStart + walk->len))
424               break;
425         }
426
427         if (walk)
428         {
429            if (event.keyCode == KEY_HOME)
430            {
431               //place the cursor at the beginning of the first atom if there is one
432               if (walk->atomList)
433                  mCursorPosition = walk->atomList->textStart;
434               else
435                  mCursorPosition = walk->textStart;
436            }
437            else
438            {
439               mCursorPosition = walk->textStart;
440               mCursorPosition += walk->len;
441            }
442            setUpdate();
443         }
444         break;
445      }
446
447      default:
448         AssertFatal(false, "Unknown move key code was received!");
449   }
450
451   ensureCursorOnScreen();
452}
453
454//--------------------------------------------------------------------------
455void GuiMLTextEditCtrl::onRender(Point2I offset, const RectI& updateRect)
456{
457   Parent::onRender(offset, updateRect);
458
459   // We are the first responder, draw our cursor in the appropriate position...
460   if (isFirstResponder()) 
461   {
462      Point2I top, bottom;
463      ColorI color;
464      getCursorPositionAndColor(top, bottom, color);
465      GFX->getDrawUtil()->drawLine(top + offset, bottom + offset, mProfile->mCursorColor);
466   }
467}
468
469