2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
# include "platform/platform.h"
# include "gui/controls/guiTextEditCtrl.h"
# include "console/consoleTypes.h"
# include "console/console.h"
# include "gui/core/guiCanvas.h"
# include "gui/controls/guiMLTextCtrl.h"
# include "gui/core/guiDefaultControlRender.h"
# include "gfx/gfxDevice.h"
# include "gfx/gfxDrawUtil.h"
# include "core/frameAllocator.h"
# include "sfx/sfxTrack.h"
# include "sfx/sfxTypes.h"
# include "sfx/sfxSystem.h"
# include "core/strings/unicode.h"
# include "console/engineAPI.h"
IMPLEMENT_CONOBJECT ( GuiTextEditCtrl ) ;
ConsoleDocClass ( GuiTextEditCtrl ,
" @brief A component that places a text entry box on the screen. \n \n "
" Fonts and sizes are changed using profiles. The text value can be set or entered by a user. \n \n "
" @tsexample \n "
" new GuiTextEditCtrl(MessageHud_Edit) \n "
" { \n "
" text = \" Hello World \" ; \n "
" validate = \" validateCommand(); \" \n "
" escapeCommand = \" escapeCommand(); \" ; \n "
" historySize = \" 5 \" ; \n "
" tabComplete = \" true \" ; \n "
" deniedSound = \" DeniedSoundProfile \" ; \n "
" sinkAllKeyEvents = \" true \" ; \n "
" password = \" true \" ; \n "
" passwordMask = \" * \" ; \n "
" //Properties not specific to this control have been omitted from this example. \n "
" }; \n "
" @endtsexample \n \n "
" @see GuiTextCtrl \n "
" @see GuiControl \n \n "
" @ingroup GuiControls \n "
) ;
IMPLEMENT_CALLBACK ( GuiTextEditCtrl , onTabComplete , void , ( const char * val ) , ( val ) ,
" @brief Called if tabComplete is true, and the 'tab' key is pressed. \n \n "
" @param val Input to mimick the '1' sent by the actual tab key button press. \n "
" @tsexample \n "
" // Tab key has been pressed, causing the callback to occur. \n "
" GuiTextEditCtrl::onTabComplete(%this,%val) \n "
" { \n "
" //Code to run when the onTabComplete callback occurs \n "
" } \n "
" @endtsexample \n \n "
" @see GuiTextCtrl \n "
" @see GuiControl \n \n "
) ;
IMPLEMENT_CALLBACK ( GuiTextEditCtrl , onReturn , void , ( ) , ( ) ,
" @brief Called when the 'Return' or 'Enter' key is pressed. \n \n "
" @tsexample \n "
" // Return or Enter key was pressed, causing the callback to occur. \n "
" GuiTextEditCtrl::onReturn(%this) \n "
" { \n "
" // Code to run when the onReturn callback occurs \n "
" } \n "
" @endtsexample \n \n "
" @see GuiTextCtrl \n "
" @see GuiControl \n \n "
) ;
IMPLEMENT_CALLBACK ( GuiTextEditCtrl , onValidate , void , ( ) , ( ) ,
" @brief Called whenever the control is validated. \n \n "
" @tsexample \n "
" // The control gets validated, causing the callback to occur \n "
" GuiTextEditCtrl::onValidated(%this) \n "
" { \n "
" // Code to run when the control is validated \n "
" } \n "
" @endtsexample \n \n "
" @see GuiTextCtrl \n "
" @see GuiControl \n \n "
) ;
GuiTextEditCtrl : : GuiTextEditCtrl ( )
{
mInsertOn = true ;
mBlockStart = 0 ;
mBlockEnd = 0 ;
mCursorPos = 0 ;
mCursorOn = false ;
mNumFramesElapsed = 0 ;
mDragHit = false ;
mTabComplete = false ;
mScrollDir = 0 ;
mUndoBlockStart = 0 ;
mUndoBlockEnd = 0 ;
mUndoCursorPos = 0 ;
mPasswordText = false ;
mSinkAllKeyEvents = false ;
mActive = true ;
2015-01-21 22:14:53 +00:00
mTextValid = true ;
2012-09-19 15:15:01 +00:00
mTextOffsetReset = true ;
mHistoryDirty = false ;
mHistorySize = 0 ;
mHistoryLast = - 1 ;
mHistoryIndex = 0 ;
mHistoryBuf = NULL ;
# if defined(__MACOSX__)
UTF8 bullet [ 4 ] = { 0xE2 , 0x80 , 0xA2 , 0 } ;
mPasswordMask = StringTable - > insert ( bullet ) ;
# else
mPasswordMask = StringTable - > insert ( " * " ) ;
# endif
Sim : : findObject ( " InputDeniedSound " , mDeniedSound ) ;
}
GuiTextEditCtrl : : ~ GuiTextEditCtrl ( )
{
//delete the history buffer if it exists
if ( mHistoryBuf )
{
for ( S32 i = 0 ; i < mHistorySize ; i + + )
delete [ ] mHistoryBuf [ i ] ;
delete [ ] mHistoryBuf ;
}
}
void GuiTextEditCtrl : : initPersistFields ( )
{
addGroup ( " Text Input " ) ;
addField ( " validate " , TypeRealString , Offset ( mValidateCommand , GuiTextEditCtrl ) , " Script command to be called when the first validater is lost. \n " ) ;
addField ( " escapeCommand " , TypeRealString , Offset ( mEscapeCommand , GuiTextEditCtrl ) , " Script command to be called when the Escape key is pressed. \n " ) ;
addField ( " historySize " , TypeS32 , Offset ( mHistorySize , GuiTextEditCtrl ) , " How large of a history buffer to maintain. \n " ) ;
addField ( " tabComplete " , TypeBool , Offset ( mTabComplete , GuiTextEditCtrl ) , " If true, when the 'tab' key is pressed, it will act as if the Enter key was pressed on the control. \n " ) ;
addField ( " deniedSound " , TypeSFXTrackName , Offset ( mDeniedSound , GuiTextEditCtrl ) , " If the attempted text cannot be entered, this sound effect will be played. \n " ) ;
addField ( " sinkAllKeyEvents " , TypeBool , Offset ( mSinkAllKeyEvents , GuiTextEditCtrl ) , " If true, every key event will act as if the Enter key was pressed. \n " ) ;
addField ( " password " , TypeBool , Offset ( mPasswordText , GuiTextEditCtrl ) , " If true, all characters entered will be stored in the control, however will display as the character stored in passwordMask. \n " ) ;
addField ( " passwordMask " , TypeString , Offset ( mPasswordMask , GuiTextEditCtrl ) , " If 'password' is true, this is the character that will be used to mask the characters in the control. \n " ) ;
endGroup ( " Text Input " ) ;
Parent : : initPersistFields ( ) ;
}
bool GuiTextEditCtrl : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) )
return false ;
//create the history buffer
if ( mHistorySize > 0 )
{
mHistoryBuf = new UTF16 * [ mHistorySize ] ;
for ( S32 i = 0 ; i < mHistorySize ; i + + )
{
mHistoryBuf [ i ] = new UTF16 [ GuiTextCtrl : : MAX_STRING_LENGTH + 1 ] ;
mHistoryBuf [ i ] [ 0 ] = ' \0 ' ;
}
}
if ( mText & & mText [ 0 ] )
{
setText ( mText ) ;
}
return true ;
}
void GuiTextEditCtrl : : onStaticModified ( const char * slotName , const char * newValue )
{
if ( ! dStricmp ( slotName , " text " ) )
setText ( mText ) ;
}
void GuiTextEditCtrl : : execConsoleCallback ( )
{
// Execute the console command!
Parent : : execConsoleCallback ( ) ;
// Update the console variable:
if ( mConsoleVariable [ 0 ] )
Con : : setVariable ( mConsoleVariable , mTextBuffer . getPtr8 ( ) ) ;
}
void GuiTextEditCtrl : : updateHistory ( StringBuffer * inTxt , bool moveIndex )
{
if ( ! mHistorySize )
return ;
const UTF16 * txt = inTxt - > getPtr ( ) ;
// Reject empty strings.
if ( ! txt | | ! txt [ 0 ] )
return ;
// see if it's already in
if ( mHistoryLast = = - 1 | | dStrcmp ( txt , mHistoryBuf [ mHistoryLast ] ) )
{
if ( mHistoryLast = = mHistorySize - 1 ) // we're at the history limit... shuffle the pointers around:
{
UTF16 * first = mHistoryBuf [ 0 ] ;
for ( U32 i = 0 ; i < mHistorySize - 1 ; i + + )
mHistoryBuf [ i ] = mHistoryBuf [ i + 1 ] ;
mHistoryBuf [ mHistorySize - 1 ] = first ;
if ( mHistoryIndex > 0 )
mHistoryIndex - - ;
}
else
mHistoryLast + + ;
inTxt - > getCopy ( mHistoryBuf [ mHistoryLast ] , GuiTextCtrl : : MAX_STRING_LENGTH ) ;
mHistoryBuf [ mHistoryLast ] [ GuiTextCtrl : : MAX_STRING_LENGTH ] = ' \0 ' ;
}
if ( moveIndex )
mHistoryIndex = mHistoryLast + 1 ;
}
void GuiTextEditCtrl : : getText ( char * dest )
{
if ( dest )
mTextBuffer . getCopy8 ( ( UTF8 * ) dest , GuiTextCtrl : : MAX_STRING_LENGTH + 1 ) ;
}
void GuiTextEditCtrl : : getRenderText ( char * dest )
{
getText ( dest ) ;
}
void GuiTextEditCtrl : : setText ( const UTF8 * txt )
{
if ( txt & & txt [ 0 ] ! = 0 )
{
Parent : : setText ( txt ) ;
mTextBuffer . set ( txt ) ;
}
else
mTextBuffer . set ( " " ) ;
mCursorPos = mTextBuffer . length ( ) ;
}
void GuiTextEditCtrl : : setText ( const UTF16 * txt )
{
if ( txt & & txt [ 0 ] ! = 0 )
{
2015-01-23 21:09:01 +00:00
UTF8 * txt8 = createUTF8string ( txt ) ;
2012-09-19 15:15:01 +00:00
Parent : : setText ( txt8 ) ;
delete [ ] txt8 ;
mTextBuffer . set ( txt ) ;
}
else
{
Parent : : setText ( " " ) ;
mTextBuffer . set ( " " ) ;
}
mCursorPos = mTextBuffer . length ( ) ;
}
bool GuiTextEditCtrl : : isAllTextSelected ( )
{
if ( mBlockStart = = 0 & & mBlockEnd = = mTextBuffer . length ( ) )
return true ;
else
return false ;
}
void GuiTextEditCtrl : : selectAllText ( )
{
mBlockStart = 0 ;
mBlockEnd = mTextBuffer . length ( ) ;
setUpdate ( ) ;
}
void GuiTextEditCtrl : : clearSelectedText ( )
{
mBlockStart = 0 ;
mBlockEnd = 0 ;
setUpdate ( ) ;
}
void GuiTextEditCtrl : : forceValidateText ( )
{
if ( mValidateCommand . isNotEmpty ( ) )
evaluate ( mValidateCommand ) ;
}
void GuiTextEditCtrl : : setCursorPos ( const S32 newPos )
{
S32 charCount = mTextBuffer . length ( ) ;
S32 realPos = newPos > charCount ? charCount : newPos < 0 ? 0 : newPos ;
if ( realPos ! = mCursorPos )
{
mCursorPos = realPos ;
setUpdate ( ) ;
}
}
S32 GuiTextEditCtrl : : calculateCursorPos ( const Point2I & globalPos )
{
Point2I ctrlOffset = localToGlobalCoord ( Point2I ( 0 , 0 ) ) ;
S32 charLength = 0 ;
S32 curX ;
curX = globalPos . x - ctrlOffset . x ;
setUpdate ( ) ;
//if the cursor is too far to the left
if ( curX < 0 )
return - 1 ;
//if the cursor is too far to the right
if ( curX > = ctrlOffset . x + getExtent ( ) . x )
return - 2 ;
curX = globalPos . x - mTextOffset . x ;
S32 count = 0 ;
if ( mTextBuffer . length ( ) = = 0 )
return 0 ;
for ( count = 0 ; count < mTextBuffer . length ( ) ; count + + )
{
UTF16 c = mTextBuffer . getChar ( count ) ;
if ( ! mPasswordText & & ! mProfile - > mFont - > isValidChar ( c ) )
continue ;
if ( mPasswordText )
charLength + = mProfile - > mFont - > getCharXIncrement ( mPasswordMask [ 0 ] ) ;
else
charLength + = mProfile - > mFont - > getCharXIncrement ( c ) ;
if ( charLength > curX )
break ;
}
return count ;
}
void GuiTextEditCtrl : : onMouseDown ( const GuiEvent & event )
{
mDragHit = false ;
// If we have a double click, select all text. Otherwise
// act as before by clearing any selection.
bool doubleClick = ( event . mouseClickCount > 1 ) ;
if ( doubleClick )
{
selectAllText ( ) ;
} else
{
//undo any block function
mBlockStart = 0 ;
mBlockEnd = 0 ;
}
//find out where the cursor should be
S32 pos = calculateCursorPos ( event . mousePoint ) ;
// if the position is to the left
if ( pos = = - 1 )
mCursorPos = 0 ;
else if ( pos = = - 2 ) //else if the position is to the right
mCursorPos = mTextBuffer . length ( ) ;
else //else set the mCursorPos
mCursorPos = pos ;
//save the mouseDragPos
mMouseDragStart = mCursorPos ;
// lock the mouse
mouseLock ( ) ;
//set the drag var
mDragHit = true ;
//let the parent get the event
setFirstResponder ( ) ;
}
void GuiTextEditCtrl : : onMouseDragged ( const GuiEvent & event )
{
S32 pos = calculateCursorPos ( event . mousePoint ) ;
// if the position is to the left
if ( pos = = - 1 )
mScrollDir = - 1 ;
else if ( pos = = - 2 ) // the position is to the right
mScrollDir = 1 ;
else // set the new cursor position
{
mScrollDir = 0 ;
mCursorPos = pos ;
}
// update the block:
mBlockStart = getMin ( mCursorPos , mMouseDragStart ) ;
mBlockEnd = getMax ( mCursorPos , mMouseDragStart ) ;
if ( mBlockStart < 0 )
mBlockStart = 0 ;
if ( mBlockStart = = mBlockEnd )
mBlockStart = mBlockEnd = 0 ;
//let the parent get the event
Parent : : onMouseDragged ( event ) ;
}
void GuiTextEditCtrl : : onMouseUp ( const GuiEvent & event )
{
TORQUE_UNUSED ( event ) ;
mDragHit = false ;
mScrollDir = 0 ;
mouseUnlock ( ) ;
}
void GuiTextEditCtrl : : saveUndoState ( )
{
//save the current state
mUndoText . set ( & mTextBuffer ) ;
mUndoBlockStart = mBlockStart ;
mUndoBlockEnd = mBlockEnd ;
mUndoCursorPos = mCursorPos ;
}
void GuiTextEditCtrl : : onCopy ( bool andCut )
{
// Don't copy/cut password field!
if ( mPasswordText )
return ;
if ( mBlockEnd > 0 )
{
//save the current state
saveUndoState ( ) ;
//copy the text to the clipboard
UTF8 * clipBuff = mTextBuffer . createSubstring8 ( mBlockStart , mBlockEnd - mBlockStart ) ;
Platform : : setClipboard ( clipBuff ) ;
delete [ ] clipBuff ;
//if we pressed the cut shortcut, we need to cut the selected text from the control...
if ( andCut )
{
mTextBuffer . cut ( mBlockStart , mBlockEnd - mBlockStart ) ;
mCursorPos = mBlockStart ;
}
mBlockStart = 0 ;
mBlockEnd = 0 ;
}
}
void GuiTextEditCtrl : : onPaste ( )
{
//first, make sure there's something in the clipboard to copy...
const UTF8 * clipboard = Platform : : getClipboard ( ) ;
if ( dStrlen ( clipboard ) < = 0 )
return ;
//save the current state
saveUndoState ( ) ;
//delete anything hilited
if ( mBlockEnd > 0 )
{
mTextBuffer . cut ( mBlockStart , mBlockEnd - mBlockStart ) ;
mCursorPos = mBlockStart ;
mBlockStart = 0 ;
mBlockEnd = 0 ;
}
// We'll be converting to UTF16, and maybe trimming the string,
// so let's use a StringBuffer, for convinience.
StringBuffer pasteText ( clipboard ) ;
// Space left after we remove the highlighted text
S32 stringLen = mTextBuffer . length ( ) ;
// Trim down to fit in a buffer of size mMaxStrLen
S32 pasteLen = pasteText . length ( ) ;
if ( stringLen + pasteLen > mMaxStrLen )
{
pasteLen = mMaxStrLen - stringLen ;
pasteText . cut ( pasteLen , pasteText . length ( ) - pasteLen ) ;
}
if ( mCursorPos = = stringLen )
{
mTextBuffer . append ( pasteText ) ;
}
else
{
mTextBuffer . insert ( mCursorPos , pasteText ) ;
}
mCursorPos + = pasteLen ;
}
void GuiTextEditCtrl : : onUndo ( )
{
StringBuffer tempBuffer ;
S32 tempBlockStart ;
S32 tempBlockEnd ;
S32 tempCursorPos ;
//save the current
tempBuffer . set ( & mTextBuffer ) ;
tempBlockStart = mBlockStart ;
tempBlockEnd = mBlockEnd ;
tempCursorPos = mCursorPos ;
//restore the prev
mTextBuffer . set ( & mUndoText ) ;
mBlockStart = mUndoBlockStart ;
mBlockEnd = mUndoBlockEnd ;
mCursorPos = mUndoCursorPos ;
//update the undo
mUndoText . set ( & tempBuffer ) ;
mUndoBlockStart = tempBlockStart ;
mUndoBlockEnd = tempBlockEnd ;
mUndoCursorPos = tempCursorPos ;
}
bool GuiTextEditCtrl : : onKeyDown ( const GuiEvent & event )
{
if ( ! isActive ( ) | | ! isAwake ( ) )
return false ;
S32 stringLen = mTextBuffer . length ( ) ;
setUpdate ( ) ;
// Ugly, but now I'm cool like MarkF.
if ( event . keyCode = = KEY_BACKSPACE )
goto dealWithBackspace ;
if ( event . modifier & SI_SHIFT )
{
// Added support for word jump selection.
if ( event . modifier & SI_CTRL )
{
switch ( event . keyCode )
{
case KEY_LEFT :
{
S32 newpos = findPrevWord ( ) ;
if ( mBlockStart = = mBlockEnd )
{
// There was not already a selection so start a new one.
mBlockStart = newpos ;
mBlockEnd = mCursorPos ;
}
else
{
// There was a selection already...
// In this case the cursor MUST be at either the
// start or end of that selection.
if ( mCursorPos = = mBlockStart )
{
// We are at the start block and traveling left so
// just extend the start block farther left.
mBlockStart = newpos ;
}
else
{
// We are at the end block BUT traveling left
// back towards the start block...
if ( newpos > mBlockStart )
{
// We haven't overpassed the existing start block
// so just trim back the end block.
mBlockEnd = newpos ;
}
else if ( newpos = = mBlockStart )
{
// We are back at the start, so no more selection.
mBlockEnd = mBlockStart = 0 ;
}
else
{
// Only other option, we just backtracked PAST
// our original start block.
// So the new position becomes the start block
// and the old start block becomes the end block.
mBlockEnd = mBlockStart ;
mBlockStart = newpos ;
}
}
}
mCursorPos = newpos ;
return true ;
}
case KEY_RIGHT :
{
S32 newpos = findNextWord ( ) ;
if ( mBlockStart = = mBlockEnd )
{
// There was not already a selection so start a new one.
mBlockStart = mCursorPos ;
mBlockEnd = newpos ;
}
else
{
// There was a selection already...
// In this case the cursor MUST be at either the
// start or end of that selection.
if ( mCursorPos = = mBlockEnd )
{
// We are at the end block and traveling right so
// just extend the end block farther right.
mBlockEnd = newpos ;
}
else
{
// We are at the start block BUT traveling right
// back towards the end block...
if ( newpos < mBlockEnd )
{
// We haven't overpassed the existing end block
// so just trim back the start block.
mBlockStart = newpos ;
}
else if ( newpos = = mBlockEnd )
{
// We are back at the end, so no more selection.
mBlockEnd = mBlockStart = 0 ;
}
else
{
// Only other option, we just backtracked PAST
// our original end block.
// So the new position becomes the end block
// and the old end block becomes the start block.
mBlockStart = mBlockEnd ;
mBlockEnd = newpos ;
}
}
}
mCursorPos = newpos ;
return true ;
}
default :
break ;
}
}
// End support for word jump selection.
switch ( event . keyCode )
{
case KEY_TAB :
if ( mTabComplete )
{
onTabComplete_callback ( " 1 " ) ;
return true ;
}
break ; // We don't want to fall through if we don't handle the TAB here.
case KEY_HOME :
mBlockStart = 0 ;
mBlockEnd = mCursorPos ;
mCursorPos = 0 ;
return true ;
case KEY_END :
mBlockStart = mCursorPos ;
mBlockEnd = stringLen ;
mCursorPos = stringLen ;
return true ;
case KEY_LEFT :
if ( ( mCursorPos > 0 ) & ( stringLen > 0 ) )
{
//if we already have a selected block
if ( mCursorPos = = mBlockEnd )
{
mCursorPos - - ;
mBlockEnd - - ;
if ( mBlockEnd = = mBlockStart )
{
mBlockStart = 0 ;
mBlockEnd = 0 ;
}
}
else {
mCursorPos - - ;
mBlockStart = mCursorPos ;
if ( mBlockEnd = = 0 )
{
mBlockEnd = mCursorPos + 1 ;
}
}
}
return true ;
case KEY_RIGHT :
if ( mCursorPos < stringLen )
{
if ( ( mCursorPos = = mBlockStart ) & & ( mBlockEnd > 0 ) )
{
mCursorPos + + ;
mBlockStart + + ;
if ( mBlockStart = = mBlockEnd )
{
mBlockStart = 0 ;
mBlockEnd = 0 ;
}
}
else
{
if ( mBlockEnd = = 0 )
{
mBlockStart = mCursorPos ;
mBlockEnd = mCursorPos ;
}
mCursorPos + + ;
mBlockEnd + + ;
}
}
return true ;
case KEY_RETURN :
case KEY_NUMPADENTER :
return dealWithEnter ( false ) ;
default :
break ;
}
}
else if ( event . modifier & SI_CTRL )
{
switch ( event . keyCode )
{
# if defined(TORQUE_OS_MAC)
// Added UNIX emacs key bindings - just a little hack here...
// Ctrl-B - move one character back
case KEY_B :
{
GuiEvent new_event ;
new_event . modifier = 0 ;
new_event . keyCode = KEY_LEFT ;
return ( onKeyDown ( new_event ) ) ;
}
// Ctrl-F - move one character forward
case KEY_F :
{
GuiEvent new_event ;
new_event . modifier = 0 ;
new_event . keyCode = KEY_RIGHT ;
return ( onKeyDown ( new_event ) ) ;
}
// Ctrl-A - move to the beginning of the line
case KEY_A :
{
GuiEvent new_event ;
new_event . modifier = 0 ;
new_event . keyCode = KEY_HOME ;
return ( onKeyDown ( new_event ) ) ;
}
// Ctrl-E - move to the end of the line
case KEY_E :
{
GuiEvent new_event ;
new_event . modifier = 0 ;
new_event . keyCode = KEY_END ;
return ( onKeyDown ( new_event ) ) ;
}
// Ctrl-P - move backward in history
case KEY_P :
{
GuiEvent new_event ;
new_event . modifier = 0 ;
new_event . keyCode = KEY_UP ;
return ( onKeyDown ( new_event ) ) ;
}
// Ctrl-N - move forward in history
case KEY_N :
{
GuiEvent new_event ;
new_event . modifier = 0 ;
new_event . keyCode = KEY_DOWN ;
return ( onKeyDown ( new_event ) ) ;
}
// Ctrl-D - delete under cursor
case KEY_D :
{
GuiEvent new_event ;
new_event . modifier = 0 ;
new_event . keyCode = KEY_DELETE ;
return ( onKeyDown ( new_event ) ) ;
}
case KEY_U :
{
GuiEvent new_event ;
new_event . modifier = SI_CTRL ;
new_event . keyCode = KEY_DELETE ;
return ( onKeyDown ( new_event ) ) ;
}
// End added UNIX emacs key bindings
# endif
// Adding word jump navigation.
case KEY_LEFT :
{
mCursorPos = findPrevWord ( ) ;
mBlockStart = 0 ;
mBlockEnd = 0 ;
return true ;
}
case KEY_RIGHT :
{
mCursorPos = findNextWord ( ) ;
mBlockStart = 0 ;
mBlockEnd = 0 ;
return true ;
}
# if !defined(TORQUE_OS_MAC)
// Select all
case KEY_A :
{
selectAllText ( ) ;
return true ;
}
// windows style cut / copy / paste / undo keybinds
case KEY_C :
case KEY_X :
{
// copy, and cut the text if we hit ctrl-x
onCopy ( event . keyCode = = KEY_X ) ;
return true ;
}
case KEY_V :
{
onPaste ( ) ;
// Execute the console command!
execConsoleCallback ( ) ;
return true ;
}
case KEY_Z :
if ( ! mDragHit )
{
onUndo ( ) ;
return true ;
}
# endif
case KEY_DELETE :
case KEY_BACKSPACE :
//save the current state
saveUndoState ( ) ;
//delete everything in the field
mTextBuffer . set ( " " ) ;
mCursorPos = 0 ;
mBlockStart = 0 ;
mBlockEnd = 0 ;
execConsoleCallback ( ) ;
return true ;
default :
break ;
}
}
# if defined(TORQUE_OS_MAC)
// mac style cut / copy / paste / undo keybinds
else if ( event . modifier & SI_ALT )
{
// Mac command key maps to alt in torque.
// Added Mac cut/copy/paste/undo keys
switch ( event . keyCode )
{
// Select all
case KEY_A :
{
selectAllText ( ) ;
return true ;
}
case KEY_C :
case KEY_X :
{
// copy, and cut the text if we hit cmd-x
onCopy ( event . keyCode = = KEY_X ) ;
return true ;
}
case KEY_V :
{
onPaste ( ) ;
// Execute the console command!
execConsoleCallback ( ) ;
return true ;
}
case KEY_Z :
if ( ! mDragHit )
{
onUndo ( ) ;
return true ;
}
default :
break ;
}
}
# endif
else
{
switch ( event . keyCode )
{
case KEY_ESCAPE :
if ( mEscapeCommand . isNotEmpty ( ) )
{
evaluate ( mEscapeCommand ) ;
return ( true ) ;
}
return ( Parent : : onKeyDown ( event ) ) ;
case KEY_RETURN :
case KEY_NUMPADENTER :
return dealWithEnter ( true ) ;
case KEY_UP :
{
if ( mHistorySize > 0 )
{
if ( mHistoryDirty )
{
updateHistory ( & mTextBuffer , false ) ;
mHistoryDirty = false ;
}
mHistoryIndex - - ;
if ( mHistoryIndex > = 0 & & mHistoryIndex < = mHistoryLast )
setText ( mHistoryBuf [ mHistoryIndex ] ) ;
else if ( mHistoryIndex < 0 )
mHistoryIndex = 0 ;
}
return true ;
}
case KEY_DOWN :
{
if ( mHistorySize > 0 )
{
if ( mHistoryDirty )
{
updateHistory ( & mTextBuffer , false ) ;
mHistoryDirty = false ;
}
mHistoryIndex + + ;
if ( mHistoryIndex > mHistoryLast )
{
mHistoryIndex = mHistoryLast + 1 ;
setText ( " " ) ;
}
else
setText ( mHistoryBuf [ mHistoryIndex ] ) ;
}
return true ;
}
case KEY_LEFT :
// If we have a selection put the cursor to the left side of it.
if ( mBlockStart ! = mBlockEnd )
{
mCursorPos = mBlockStart ;
mBlockStart = mBlockEnd = 0 ;
}
else
{
mBlockStart = mBlockEnd = 0 ;
mCursorPos = getMax ( mCursorPos - 1 , 0 ) ;
}
return true ;
case KEY_RIGHT :
// If we have a selection put the cursor to the right side of it.
if ( mBlockStart ! = mBlockEnd )
{
mCursorPos = mBlockEnd ;
mBlockStart = mBlockEnd = 0 ;
}
else
{
mBlockStart = mBlockEnd = 0 ;
mCursorPos = getMin ( mCursorPos + 1 , stringLen ) ;
}
return true ;
case KEY_BACKSPACE :
dealWithBackspace :
//save the current state
saveUndoState ( ) ;
if ( mBlockEnd > 0 )
{
mTextBuffer . cut ( mBlockStart , mBlockEnd - mBlockStart ) ;
mCursorPos = mBlockStart ;
mBlockStart = 0 ;
mBlockEnd = 0 ;
mHistoryDirty = true ;
// Execute the console command!
execConsoleCallback ( ) ;
}
else if ( mCursorPos > 0 )
{
mTextBuffer . cut ( mCursorPos - 1 , 1 ) ;
mCursorPos - - ;
mHistoryDirty = true ;
// Execute the console command!
execConsoleCallback ( ) ;
}
return true ;
case KEY_DELETE :
//save the current state
saveUndoState ( ) ;
if ( mBlockEnd > 0 )
{
mHistoryDirty = true ;
mTextBuffer . cut ( mBlockStart , mBlockEnd - mBlockStart ) ;
mCursorPos = mBlockStart ;
mBlockStart = 0 ;
mBlockEnd = 0 ;
// Execute the console command!
execConsoleCallback ( ) ;
}
else if ( mCursorPos < stringLen )
{
mHistoryDirty = true ;
mTextBuffer . cut ( mCursorPos , 1 ) ;
// Execute the console command!
execConsoleCallback ( ) ;
}
return true ;
case KEY_INSERT :
mInsertOn = ! mInsertOn ;
return true ;
case KEY_HOME :
mBlockStart = 0 ;
mBlockEnd = 0 ;
mCursorPos = 0 ;
return true ;
case KEY_END :
mBlockStart = 0 ;
mBlockEnd = 0 ;
mCursorPos = stringLen ;
return true ;
default :
break ;
}
}
switch ( event . keyCode )
{
case KEY_TAB :
if ( mTabComplete )
{
onTabComplete_callback ( " 0 " ) ;
return ( true ) ;
}
case KEY_UP :
case KEY_DOWN :
case KEY_ESCAPE :
return Parent : : onKeyDown ( event ) ;
default :
break ;
}
// Handle character input events.
if ( mProfile - > mFont - > isValidChar ( event . ascii ) )
{
handleCharInput ( event . ascii ) ;
return true ;
}
// Or eat it if that's appropriate.
if ( mSinkAllKeyEvents )
return true ;
// Not handled - pass the event to it's parent.
return Parent : : onKeyDown ( event ) ;
}
bool GuiTextEditCtrl : : dealWithEnter ( bool clearResponder )
{
//first validate
if ( mProfile - > mReturnTab )
{
onLoseFirstResponder ( ) ;
}
updateHistory ( & mTextBuffer , true ) ;
mHistoryDirty = false ;
//next exec the alt console command
execAltConsoleCallback ( ) ;
// Notify of Return
onReturn_callback ( ) ;
if ( mProfile - > mReturnTab )
{
GuiCanvas * root = getRoot ( ) ;
if ( root )
{
root - > tabNext ( ) ;
return true ;
}
}
if ( clearResponder )
clearFirstResponder ( ) ;
return true ;
}
void GuiTextEditCtrl : : setFirstResponder ( )
{
Parent : : setFirstResponder ( ) ;
GuiCanvas * root = getRoot ( ) ;
if ( root ! = NULL )
{
root - > enableKeyboardTranslation ( ) ;
// If the native OS accelerator keys are not disabled
// then some key events like Delete, ctrl+V, etc may
// not make it down to us.
root - > setNativeAcceleratorsEnabled ( false ) ;
}
}
void GuiTextEditCtrl : : onLoseFirstResponder ( )
{
GuiCanvas * root = getRoot ( ) ;
if ( root )
{
root - > setNativeAcceleratorsEnabled ( true ) ;
root - > disableKeyboardTranslation ( ) ;
}
//execute the validate command
if ( mValidateCommand . isNotEmpty ( ) )
evaluate ( mValidateCommand ) ;
onValidate_callback ( ) ;
// Redraw the control:
setUpdate ( ) ;
// Lost Responder
Parent : : onLoseFirstResponder ( ) ;
}
2016-02-21 21:55:45 +00:00
void GuiTextEditCtrl : : onRender ( Point2I offset , const RectI & updateRect )
2012-09-19 15:15:01 +00:00
{
RectI ctrlRect ( offset , getExtent ( ) ) ;
//if opaque, fill the update rect with the fill color
if ( mProfile - > mOpaque )
{
2016-02-21 21:55:45 +00:00
if ( ! mTextValid )
GFX - > getDrawUtil ( ) - > drawRectFill ( ctrlRect , mProfile - > mFillColorERR ) ;
else if ( isFirstResponder ( ) )
GFX - > getDrawUtil ( ) - > drawRectFill ( ctrlRect , mProfile - > mFillColorHL ) ;
else
GFX - > getDrawUtil ( ) - > drawRectFill ( ctrlRect , mProfile - > mFillColor ) ;
2012-09-19 15:15:01 +00:00
}
//if there's a border, draw the border
2016-02-21 21:55:45 +00:00
if ( mProfile - > mBorder )
2015-01-21 22:14:53 +00:00
{
2016-02-21 21:55:45 +00:00
renderBorder ( ctrlRect , mProfile ) ;
if ( ! mTextValid )
GFX - > getDrawUtil ( ) - > drawRectFill ( ctrlRect , mProfile - > mFillColorERR ) ;
2015-01-21 22:14:53 +00:00
}
2012-09-19 15:15:01 +00:00
drawText ( ctrlRect , isFirstResponder ( ) ) ;
}
void GuiTextEditCtrl : : onPreRender ( )
{
if ( isFirstResponder ( ) )
{
U32 timeElapsed = Platform : : getVirtualMilliseconds ( ) - mTimeLastCursorFlipped ;
mNumFramesElapsed + + ;
if ( ( timeElapsed > 500 ) & & ( mNumFramesElapsed > 3 ) )
{
mCursorOn = ! mCursorOn ;
mTimeLastCursorFlipped = Platform : : getVirtualMilliseconds ( ) ;
mNumFramesElapsed = 0 ;
setUpdate ( ) ;
}
//update the cursor if the text is scrolling
if ( mDragHit )
{
if ( ( mScrollDir < 0 ) & & ( mCursorPos > 0 ) )
mCursorPos - - ;
else if ( ( mScrollDir > 0 ) & & ( mCursorPos < ( S32 ) mTextBuffer . length ( ) ) )
mCursorPos + + ;
}
}
}
void GuiTextEditCtrl : : drawText ( const RectI & drawRect , bool isFocused )
{
StringBuffer textBuffer ;
Point2I drawPoint = drawRect . point ;
Point2I paddingLeftTop , paddingRightBottom ;
// Or else just copy it over.
char * renderText = Con : : getReturnBuffer ( GuiTextEditCtrl : : MAX_STRING_LENGTH ) ;
getRenderText ( renderText ) ;
// Apply password masking (make the masking char optional perhaps?)
if ( mPasswordText )
{
const U32 renderLen = dStrlen ( renderText ) ;
for ( U32 i = 0 ; i < renderLen ; i + + )
textBuffer . append ( mPasswordMask ) ;
}
else
{
textBuffer . set ( renderText ) ;
}
// Just a little sanity.
if ( mCursorPos > textBuffer . length ( ) )
mCursorPos = textBuffer . length ( ) ;
paddingLeftTop . set ( ( mProfile - > mTextOffset . x ! = 0 ? mProfile - > mTextOffset . x : 3 ) , mProfile - > mTextOffset . y ) ;
paddingRightBottom = paddingLeftTop ;
// Center vertically:
drawPoint . y + = ( ( drawRect . extent . y - paddingLeftTop . y - paddingRightBottom . y - S32 ( mProfile - > mFont - > getHeight ( ) ) ) / 2 ) + paddingLeftTop . y ;
// Align horizontally:
S32 textWidth = mProfile - > mFont - > getStrNWidth ( textBuffer . getPtr ( ) , textBuffer . length ( ) ) ;
switch ( mProfile - > mAlignment )
{
case GuiControlProfile : : RightJustify :
drawPoint . x + = ( drawRect . extent . x - textWidth - paddingRightBottom . x ) ;
break ;
case GuiControlProfile : : CenterJustify :
drawPoint . x + = ( ( drawRect . extent . x - textWidth ) / 2 ) ;
break ;
default :
case GuiControlProfile : : LeftJustify :
drawPoint . x + = paddingLeftTop . x ;
break ;
}
ColorI fontColor = mActive ? mProfile - > mFontColor : mProfile - > mFontColorNA ;
// now draw the text
Point2I cursorStart , cursorEnd ;
mTextOffset . y = drawPoint . y ;
mTextOffset . x = drawPoint . x ;
if ( drawRect . extent . x - paddingLeftTop . x > textWidth )
mTextOffset . x = drawPoint . x ;
else
{
// Alignment affects large text
if ( mProfile - > mAlignment = = GuiControlProfile : : RightJustify
| | mProfile - > mAlignment = = GuiControlProfile : : CenterJustify )
{
if ( mTextOffset . x + textWidth < ( drawRect . point . x + drawRect . extent . x ) - paddingRightBottom . x )
mTextOffset . x = ( drawRect . point . x + drawRect . extent . x ) - paddingRightBottom . x - textWidth ;
}
}
// calculate the cursor
if ( isFocused & & mActive )
{
// Where in the string are we?
S32 cursorOffset = 0 , charWidth = 0 ;
UTF16 tempChar = textBuffer . getChar ( mCursorPos ) ;
// Alright, we want to terminate things momentarily.
if ( mCursorPos > 0 )
{
cursorOffset = mProfile - > mFont - > getStrNWidth ( textBuffer . getPtr ( ) , mCursorPos ) ;
}
else
cursorOffset = 0 ;
if ( tempChar & & mProfile - > mFont - > isValidChar ( tempChar ) )
charWidth = mProfile - > mFont - > getCharWidth ( tempChar ) ;
else
charWidth = paddingRightBottom . x ;
if ( mTextOffset . x + cursorOffset + 1 > = ( drawRect . point . x + drawRect . extent . x ) ) // +1 is for the cursor width
{
// Cursor somewhere beyond the textcontrol,
// skip forward roughly 25% of the total width (if possible)
S32 skipForward = drawRect . extent . x / 4 * 3 ;
if ( cursorOffset + skipForward > textWidth )
mTextOffset . x = ( drawRect . point . x + drawRect . extent . x ) - paddingRightBottom . x - textWidth ;
else
{
//mTextOffset.x -= skipForward;
S32 mul = ( S32 ) ( mFloor ( ( cursorOffset - drawRect . extent . x ) / skipForward ) ) ;
mTextOffset . x - = skipForward * mul + drawRect . extent . x - 1 ; // -1 is for the cursor width
}
}
else if ( mTextOffset . x + cursorOffset < drawRect . point . x + paddingLeftTop . x )
{
// Cursor somewhere before the textcontrol
// skip backward roughly 25% of the total width (if possible)
S32 skipBackward = drawRect . extent . x / 4 * 3 ;
if ( cursorOffset - skipBackward < 0 )
mTextOffset . x = drawRect . point . x + paddingLeftTop . x ;
else
{
S32 mul = ( S32 ) ( mFloor ( cursorOffset / skipBackward ) ) ;
mTextOffset . x + = drawRect . point . x - mTextOffset . x - skipBackward * mul ;
}
}
cursorStart . x = mTextOffset . x + cursorOffset ;
# ifdef TORQUE_OS_MAC
cursorStart . x + = charWidth / 2 ;
# endif
cursorEnd . x = cursorStart . x ;
S32 cursorHeight = mProfile - > mFont - > getHeight ( ) ;
if ( cursorHeight < drawRect . extent . y )
{
cursorStart . y = drawPoint . y ;
cursorEnd . y = cursorStart . y + cursorHeight ;
}
else
{
cursorStart . y = drawRect . point . y ;
cursorEnd . y = cursorStart . y + drawRect . extent . y ;
}
}
//draw the text
if ( ! isFocused )
mBlockStart = mBlockEnd = 0 ;
//also verify the block start/end
if ( ( mBlockStart > textBuffer . length ( ) | | ( mBlockEnd > textBuffer . length ( ) ) | | ( mBlockStart > mBlockEnd ) ) )
mBlockStart = mBlockEnd = 0 ;
Point2I tempOffset = mTextOffset ;
//draw the portion before the highlight
if ( mBlockStart > 0 )
{
GFX - > getDrawUtil ( ) - > setBitmapModulation ( fontColor ) ;
const UTF16 * preString2 = textBuffer . getPtr ( ) ;
GFX - > getDrawUtil ( ) - > drawText ( mProfile - > mFont , tempOffset , preString2 , mProfile - > mFontColors ) ;
tempOffset . x + = mProfile - > mFont - > getStrNWidth ( preString2 , mBlockStart ) ;
}
//draw the highlighted portion
if ( mBlockEnd > 0 )
{
const UTF16 * highlightBuff = textBuffer . getPtr ( ) + mBlockStart ;
U32 highlightBuffLen = mBlockEnd - mBlockStart ;
S32 highlightWidth = mProfile - > mFont - > getStrNWidth ( highlightBuff , highlightBuffLen ) ;
GFX - > getDrawUtil ( ) - > drawRectFill ( Point2I ( tempOffset . x , drawRect . point . y ) ,
Point2I ( tempOffset . x + highlightWidth , drawRect . point . y + drawRect . extent . y - 1 ) ,
mProfile - > mFontColorSEL ) ;
GFX - > getDrawUtil ( ) - > setBitmapModulation ( mProfile - > mFontColorHL ) ;
GFX - > getDrawUtil ( ) - > drawTextN ( mProfile - > mFont , tempOffset , highlightBuff , highlightBuffLen , mProfile - > mFontColors ) ;
tempOffset . x + = highlightWidth ;
}
//draw the portion after the highlight
if ( mBlockEnd < textBuffer . length ( ) )
{
const UTF16 * finalBuff = textBuffer . getPtr ( ) + mBlockEnd ;
U32 finalBuffLen = textBuffer . length ( ) - mBlockEnd ;
GFX - > getDrawUtil ( ) - > setBitmapModulation ( fontColor ) ;
GFX - > getDrawUtil ( ) - > drawTextN ( mProfile - > mFont , tempOffset , finalBuff , finalBuffLen , mProfile - > mFontColors ) ;
}
//draw the cursor
if ( isFocused & & mCursorOn )
GFX - > getDrawUtil ( ) - > drawLine ( cursorStart , cursorEnd , mProfile - > mCursorColor ) ;
}
bool GuiTextEditCtrl : : hasText ( )
{
2016-02-21 21:55:45 +00:00
return ( mTextBuffer . length ( ) ) ;
2012-09-19 15:15:01 +00:00
}
2015-01-21 22:14:53 +00:00
void GuiTextEditCtrl : : invalidText ( bool playSound )
{
2016-02-21 21:55:45 +00:00
mTextValid = false ;
2015-01-21 22:14:53 +00:00
2016-02-21 21:55:45 +00:00
if ( playSound )
playDeniedSound ( ) ;
2015-01-21 22:14:53 +00:00
}
void GuiTextEditCtrl : : validText ( )
{
2016-02-21 21:55:45 +00:00
mTextValid = true ;
2015-01-21 22:14:53 +00:00
}
bool GuiTextEditCtrl : : isValidText ( )
{
2016-02-21 21:55:45 +00:00
return mTextValid ;
2015-01-21 22:14:53 +00:00
}
2012-09-19 15:15:01 +00:00
void GuiTextEditCtrl : : playDeniedSound ( )
{
if ( mDeniedSound )
SFX - > playOnce ( mDeniedSound ) ;
}
const char * GuiTextEditCtrl : : getScriptValue ( )
{
return StringTable - > insert ( mTextBuffer . getPtr8 ( ) ) ;
}
void GuiTextEditCtrl : : setScriptValue ( const char * value )
{
mTextBuffer . set ( value ) ;
mCursorPos = mTextBuffer . length ( ) ;
}
void GuiTextEditCtrl : : handleCharInput ( U16 ascii )
{
S32 stringLen = mTextBuffer . length ( ) ;
// Get the character ready to add to a UTF8 string.
UTF16 convertedChar [ 2 ] = { ascii , 0 } ;
//see if it's a number field
if ( mProfile - > mNumbersOnly )
{
2015-01-21 22:14:53 +00:00
if ( ascii = = ' - ' )
{
//a minus sign only exists at the beginning, and only a single minus sign
if ( mCursorPos ! = 0 & & ! isAllTextSelected ( ) )
{
invalidText ( ) ;
return ;
}
if ( mInsertOn & & ( mTextBuffer . getChar ( 0 ) = = ' - ' ) )
{
invalidText ( ) ;
return ;
}
}
// BJTODO: This is probably not unicode safe.
else if ( ascii ! = ' . ' & & ( ascii < ' 0 ' | | ascii > ' 9 ' ) )
{
invalidText ( ) ;
return ;
}
else
validText ( ) ;
2012-09-19 15:15:01 +00:00
}
//save the current state
saveUndoState ( ) ;
bool alreadyCut = false ;
//delete anything highlighted
if ( mBlockEnd > 0 )
{
mTextBuffer . cut ( mBlockStart , mBlockEnd - mBlockStart ) ;
mCursorPos = mBlockStart ;
mBlockStart = 0 ;
mBlockEnd = 0 ;
// We just changed the string length!
// Get its new value.
stringLen = mTextBuffer . length ( ) ;
// If we already had text highlighted, we just want to cut that text.
// Don't cut the next character even if insert is not on.
alreadyCut = true ;
}
if ( ( mInsertOn & & ( stringLen < mMaxStrLen ) ) | |
( ! mInsertOn & & ( mCursorPos < mMaxStrLen ) ) )
{
if ( mCursorPos = = stringLen )
{
mTextBuffer . append ( convertedChar ) ;
mCursorPos + + ;
}
else
{
if ( mInsertOn | | alreadyCut )
{
mTextBuffer . insert ( mCursorPos , convertedChar ) ;
mCursorPos + + ;
}
else
{
mTextBuffer . cut ( mCursorPos , 1 ) ;
mTextBuffer . insert ( mCursorPos , convertedChar ) ;
mCursorPos + + ;
}
}
}
else
playDeniedSound ( ) ;
//reset the history index
mHistoryDirty = true ;
//execute the console command if it exists
execConsoleCallback ( ) ;
}
S32 GuiTextEditCtrl : : findPrevWord ( )
{
// First the first word to the left of the current cursor position
// and return the positional index of its starting character.
// We define the first character of a word as any non-whitespace
// character which has a non-alpha-numeric character to its immediate left.
const UTF8 * text = mTextBuffer . getPtr8 ( ) ;
for ( S32 i = mCursorPos - 1 ; i > 0 ; i - - )
{
if ( ! dIsspace ( text [ i ] ) )
{
if ( ! dIsalnum ( text [ i - 1 ] ) )
{
return i ;
}
}
}
return 0 ;
}
S32 GuiTextEditCtrl : : findNextWord ( )
{
// First the first word to the right of the current cursor position
// and return the positional index of its starting character.
// We define the first character of a word as any non-whitespace
// character which has a non-alpha-numeric character to its immediate left.
const UTF8 * text = mTextBuffer . getPtr8 ( ) ;
for ( S32 i = mCursorPos + 1 ; i < mTextBuffer . length ( ) ; i + + )
{
if ( ! dIsspace ( text [ i ] ) )
{
if ( ! dIsalnum ( text [ i - 1 ] ) )
{
return i ;
}
}
}
return mTextBuffer . length ( ) ;
}
DefineEngineMethod ( GuiTextEditCtrl , getText , const char * , ( ) , ,
" @brief Acquires the current text displayed in this control. \n \n "
" @tsexample \n "
" // Acquire the value of the text control. \n "
" %text = %thisGuiTextEditCtrl.getText(); \n "
" @endtsexample \n \n "
" @return The current text within the control. \n \n "
" @see GuiControl " )
{
if ( ! object - > hasText ( ) )
return StringTable - > insert ( " " ) ;
char * retBuffer = Con : : getReturnBuffer ( GuiTextEditCtrl : : MAX_STRING_LENGTH ) ;
object - > getText ( retBuffer ) ;
return retBuffer ;
}
DefineEngineMethod ( GuiTextEditCtrl , setText , void , ( const char * text ) , ,
" @brief Sets the text in the control. \n \n "
" @param text Text to place in the control. \n "
" @tsexample \n "
" // Define the text to display \n "
" %text = \" Text! \" \n \n "
" // Inform the GuiTextEditCtrl to display the defined text \n "
" %thisGuiTextEditCtrl.setText(%text); \n "
" @endtsexample \n \n "
" @see GuiControl " )
{
object - > setText ( text ) ;
}
DefineEngineMethod ( GuiTextEditCtrl , getCursorPos , S32 , ( ) , ,
" @brief Returns the current position of the text cursor in the control. \n \n "
" @tsexample \n "
" // Acquire the cursor position in the control \n "
" %position = %thisGuiTextEditCtrl.getCursorPost(); \n "
" @endtsexample \n \n "
" @return Text cursor position within the control. \n \n "
" @see GuiControl " )
{
return ( object - > getCursorPos ( ) ) ;
}
2013-08-04 21:26:01 +00:00
DefineEngineMethod ( GuiTextEditCtrl , setCursorPos , void , ( S32 position ) , ,
2012-09-19 15:15:01 +00:00
" @brief Sets the text cursor at the defined position within the control. \n \n "
" @param position Text position to set the text cursor. \n "
" @tsexample \n "
" // Define the cursor position \n "
" %position = \" 12 \" ; \n \n "
" // Inform the GuiTextEditCtrl control to place the text cursor at the defined position \n "
" %thisGuiTextEditCtrl.setCursorPos(%position); \n "
" @endtsexample \n \n "
" @see GuiControl " )
{
object - > setCursorPos ( position ) ;
}
DefineEngineMethod ( GuiTextEditCtrl , isAllTextSelected , bool , ( ) , ,
" @brief Checks to see if all text in the control has been selected. \n \n "
" @tsexample \n "
" // Check to see if all text has been selected or not. \n "
" %allSelected = %thisGuiTextEditCtrl.isAllTextSelected(); \n "
" @endtsexample \n \n "
" @return True if all text in the control is selected, otherwise false. \n \n "
" @see GuiControl " )
{
return object - > isAllTextSelected ( ) ;
}
DefineEngineMethod ( GuiTextEditCtrl , selectAllText , void , ( ) , ,
" @brief Selects all text within the control. \n \n "
" @tsexample \n "
" // Inform the control to select all of its text. \n "
" %thisGuiTextEditCtrl.selectAllText(); \n "
" @endtsexample \n \n "
" @see GuiControl " )
{
object - > selectAllText ( ) ;
}
DefineEngineMethod ( GuiTextEditCtrl , clearSelectedText , void , ( ) , ,
" @brief Unselects all selected text in the control. \n \n "
" @tsexample \n "
" // Inform the control to unselect all of its selected text \n "
" %thisGuiTextEditCtrl.clearSelectedText(); \n "
" @endtsexample \n \n "
" @see GuiControl " )
{
object - > clearSelectedText ( ) ;
}
DefineEngineMethod ( GuiTextEditCtrl , forceValidateText , void , ( ) , ,
" @brief Force a validation to occur. \n \n "
" @tsexample \n "
" // Inform the control to force a validation of its text. \n "
" %thisGuiTextEditCtrl.forceValidateText(); \n "
" @endtsexample \n \n "
" @see GuiControl " )
{
object - > forceValidateText ( ) ;
}
2015-01-21 22:14:53 +00:00
DefineEngineMethod ( GuiTextEditCtrl , invalidText , void , ( bool playSound ) , ( true ) ,
" @brief Trigger the invalid sound and make the box red.nn "
" @param playSound Play the invalid text sound or not.n " )
{
object - > invalidText ( playSound ) ;
}
DefineEngineMethod ( GuiTextEditCtrl , validText , void , ( ) , ,
" @brief Restores the box to normal color.nn " )
{
object - > validText ( ) ;
}
DefineEngineMethod ( GuiTextEditCtrl , isValidText , bool , ( ) , ,
" @brief Returns if the text is set to valid or not.n "
" @Return true if text is set to valid, false if not.nn " )
{
return object - > isValidText ( ) ;
}