TextBox.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /*
  2. GWEN
  3. Copyright (c) 2010 Facepunch Studios
  4. See license in Gwen.h
  5. */
  6. #include "Gwen/Gwen.h"
  7. #include "Gwen/Controls/TextBox.h"
  8. #include "Gwen/Skin.h"
  9. #include "Gwen/Utility.h"
  10. #include "Gwen/Platform.h"
  11. #include <math.h>
  12. using namespace Gwen;
  13. using namespace Gwen::Controls;
  14. GWEN_CONTROL_CONSTRUCTOR( TextBox )
  15. {
  16. SetSize( 200, 20 );
  17. SetMouseInputEnabled( true );
  18. SetKeyboardInputEnabled( true );
  19. SetAlignment( Pos::Left | Pos::CenterV );
  20. SetTextPadding( Padding( 4, 2, 4, 2 ) );
  21. m_iCursorPos = 0;
  22. m_iCursorEnd = 0;
  23. m_bSelectAll = false;
  24. SetTextColor( Gwen::Color( 50, 50, 50, 255 ) ); // TODO: From Skin
  25. SetTabable( true );
  26. AddAccelerator( L"Ctrl + c", &TextBox::OnCopy );
  27. AddAccelerator( L"Ctrl + x", &TextBox::OnCut );
  28. AddAccelerator( L"Ctrl + v", &TextBox::OnPaste );
  29. AddAccelerator( L"Ctrl + a", &TextBox::OnSelectAll );
  30. }
  31. bool TextBox::OnChar( Gwen::UnicodeChar c )
  32. {
  33. if ( c == '\t' ) return false;
  34. Gwen::UnicodeString str;
  35. str += c;
  36. InsertText( str );
  37. return true;
  38. }
  39. void TextBox::InsertText( const Gwen::UnicodeString& strInsert )
  40. {
  41. // TODO: Make sure fits (implement maxlength)
  42. if ( HasSelection() )
  43. {
  44. EraseSelection();
  45. }
  46. if ( m_iCursorPos > TextLength() ) m_iCursorPos = TextLength();
  47. if ( !IsTextAllowed( strInsert, m_iCursorPos ) )
  48. return;
  49. UnicodeString str = GetText();
  50. str.insert( m_iCursorPos, strInsert );
  51. SetText( str );
  52. m_iCursorPos += (int) strInsert.size();
  53. m_iCursorEnd = m_iCursorPos;
  54. RefreshCursorBounds();
  55. }
  56. void TextBox::Render( Skin::Base* skin )
  57. {
  58. if ( ShouldDrawBackground() )
  59. skin->DrawTextBox( this );
  60. if ( !HasFocus() ) return;
  61. // Draw selection.. if selected..
  62. if ( m_iCursorPos != m_iCursorEnd )
  63. {
  64. skin->GetRender()->SetDrawColor( Gwen::Color( 50, 170, 255, 200 ) );
  65. skin->GetRender()->DrawFilledRect( m_rectSelectionBounds );
  66. }
  67. // Draw caret
  68. if ( fmod( Gwen::Platform::GetTimeInSeconds()-m_fLastInputTime, 1.0f ) > 0.5f )
  69. skin->GetRender()->SetDrawColor( Gwen::Color( 255, 255, 255, 255 ) );
  70. else
  71. skin->GetRender()->SetDrawColor( Gwen::Color( 0, 0, 0, 255 ) );
  72. skin->GetRender()->DrawFilledRect( m_rectCaretBounds );
  73. }
  74. void TextBox::RefreshCursorBounds()
  75. {
  76. m_fLastInputTime = Gwen::Platform::GetTimeInSeconds();
  77. MakeCaratVisible();
  78. Gwen::Point pA = GetCharacterPosition( m_iCursorPos );
  79. Gwen::Point pB = GetCharacterPosition( m_iCursorEnd );
  80. m_rectSelectionBounds.x = Utility::Min( pA.x, pB.x );
  81. m_rectSelectionBounds.y = m_Text->Y() - 1;
  82. m_rectSelectionBounds.w = Utility::Max( pA.x, pB.x ) - m_rectSelectionBounds.x;
  83. m_rectSelectionBounds.h = m_Text->Height() + 2;
  84. m_rectCaretBounds.x = pA.x;
  85. m_rectCaretBounds.y = m_Text->Y() - 1;
  86. m_rectCaretBounds.w = 1;
  87. m_rectCaretBounds.h = m_Text->Height() + 2;
  88. Redraw();
  89. }
  90. void TextBox::OnPaste( Gwen::Controls::Base* /*pCtrl*/ )
  91. {
  92. InsertText( Platform::GetClipboardText() );
  93. }
  94. void TextBox::OnCopy( Gwen::Controls::Base* /*pCtrl*/ )
  95. {
  96. if ( !HasSelection() ) return;
  97. Platform::SetClipboardText( GetSelection() );
  98. }
  99. void TextBox::OnCut( Gwen::Controls::Base* /*pCtrl*/ )
  100. {
  101. if ( !HasSelection() ) return;
  102. Platform::SetClipboardText( GetSelection() );
  103. EraseSelection();
  104. }
  105. void TextBox::OnSelectAll( Gwen::Controls::Base* /*pCtrl*/ )
  106. {
  107. m_iCursorEnd = 0;
  108. m_iCursorPos = TextLength();
  109. RefreshCursorBounds();
  110. }
  111. void TextBox::OnMouseDoubleClickLeft( int /*x*/, int /*y*/ )
  112. {
  113. OnSelectAll( this );
  114. }
  115. UnicodeString TextBox::GetSelection()
  116. {
  117. if ( !HasSelection() ) return L"";
  118. int iStart = Utility::Min( m_iCursorPos, m_iCursorEnd );
  119. int iEnd = Utility::Max( m_iCursorPos, m_iCursorEnd );
  120. const UnicodeString& str = GetText();
  121. return str.substr( iStart, iEnd - iStart );
  122. }
  123. bool TextBox::OnKeyReturn( bool bDown )
  124. {
  125. if ( bDown ) return true;
  126. OnEnter();
  127. // Try to move to the next control, as if tab had been pressed
  128. OnKeyTab( true );
  129. // If we still have focus, blur it.
  130. if ( HasFocus() )
  131. {
  132. Blur();
  133. }
  134. return true;
  135. }
  136. bool TextBox::OnKeyBackspace( bool bDown )
  137. {
  138. if ( !bDown ) return true;
  139. if ( HasSelection() )
  140. {
  141. EraseSelection();
  142. return true;
  143. }
  144. if ( m_iCursorPos == 0 ) return true;
  145. DeleteText( m_iCursorPos-1, 1 );
  146. return true;
  147. }
  148. bool TextBox::OnKeyDelete( bool bDown )
  149. {
  150. if ( !bDown ) return true;
  151. if ( HasSelection() )
  152. {
  153. EraseSelection();
  154. return true;
  155. }
  156. if ( m_iCursorPos >= TextLength() ) return true;
  157. DeleteText( m_iCursorPos, 1 );
  158. return true;
  159. }
  160. bool TextBox::OnKeyLeft( bool bDown )
  161. {
  162. if ( !bDown ) return true;
  163. if ( m_iCursorPos > 0 )
  164. m_iCursorPos--;
  165. if ( !Gwen::Input::IsShiftDown() )
  166. {
  167. m_iCursorEnd = m_iCursorPos;
  168. }
  169. RefreshCursorBounds();
  170. return true;
  171. }
  172. bool TextBox::OnKeyRight( bool bDown )
  173. {
  174. if ( !bDown ) return true;
  175. if ( m_iCursorPos < TextLength() )
  176. m_iCursorPos++;
  177. if ( !Gwen::Input::IsShiftDown() )
  178. {
  179. m_iCursorEnd = m_iCursorPos;
  180. }
  181. RefreshCursorBounds();
  182. return true;
  183. }
  184. bool TextBox::OnKeyHome( bool bDown )
  185. {
  186. if ( !bDown ) return true;
  187. m_iCursorPos = 0;
  188. if ( !Gwen::Input::IsShiftDown() )
  189. {
  190. m_iCursorEnd = m_iCursorPos;
  191. }
  192. RefreshCursorBounds();
  193. return true;
  194. }
  195. bool TextBox::OnKeyEnd( bool /*bDown*/ )
  196. {
  197. m_iCursorPos = TextLength();
  198. if ( !Gwen::Input::IsShiftDown() )
  199. {
  200. m_iCursorEnd = m_iCursorPos;
  201. }
  202. RefreshCursorBounds();
  203. return true;
  204. }
  205. void TextBox::SetCursorPos( int i )
  206. {
  207. if ( m_iCursorPos == i ) return;
  208. m_iCursorPos = i;
  209. RefreshCursorBounds();
  210. }
  211. void TextBox::SetCursorEnd( int i )
  212. {
  213. if ( m_iCursorEnd == i ) return;
  214. m_iCursorEnd = i;
  215. RefreshCursorBounds();
  216. }
  217. void TextBox::DeleteText( int iStartPos, int iLength )
  218. {
  219. UnicodeString str = GetText();
  220. str.erase( iStartPos, iLength );
  221. SetText( str );
  222. if ( m_iCursorPos > iStartPos )
  223. {
  224. SetCursorPos( m_iCursorPos - iLength );
  225. }
  226. SetCursorEnd( m_iCursorPos );
  227. }
  228. bool TextBox::HasSelection()
  229. {
  230. return m_iCursorPos != m_iCursorEnd;
  231. }
  232. void TextBox::EraseSelection()
  233. {
  234. int iStart = Utility::Min( m_iCursorPos, m_iCursorEnd );
  235. int iEnd = Utility::Max( m_iCursorPos, m_iCursorEnd );
  236. DeleteText( iStart, iEnd - iStart );
  237. // Move the cursor to the start of the selection,
  238. // since the end is probably outside of the string now.
  239. m_iCursorPos = iStart;
  240. m_iCursorEnd = iStart;
  241. }
  242. void TextBox::OnMouseClickLeft( int x, int y, bool bDown )
  243. {
  244. if ( m_bSelectAll )
  245. {
  246. OnSelectAll( this );
  247. m_bSelectAll = false;
  248. return;
  249. }
  250. int iChar = m_Text->GetClosestCharacter( m_Text->CanvasPosToLocal( Gwen::Point( x, y ) ) );
  251. if ( bDown )
  252. {
  253. SetCursorPos( iChar );
  254. if ( !Gwen::Input::IsShiftDown() )
  255. SetCursorEnd( iChar );
  256. Gwen::MouseFocus = this;
  257. }
  258. else
  259. {
  260. if ( Gwen::MouseFocus == this )
  261. {
  262. SetCursorPos( iChar );
  263. Gwen::MouseFocus = NULL;
  264. }
  265. }
  266. }
  267. void TextBox::OnMouseMoved( int x, int y, int /*deltaX*/, int /*deltaY*/ )
  268. {
  269. if ( Gwen::MouseFocus != this ) return;
  270. int iChar = m_Text->GetClosestCharacter( m_Text->CanvasPosToLocal( Gwen::Point( x, y ) ) );
  271. SetCursorPos( iChar );
  272. }
  273. void TextBox::MakeCaratVisible()
  274. {
  275. int iCaratPos = m_Text->GetCharacterPosition( m_iCursorPos ).x;
  276. // If the carat is already in a semi-good position, leave it.
  277. {
  278. int iRealCaratPos = iCaratPos + m_Text->X();
  279. if ( iRealCaratPos > Width() * 0.1f && iRealCaratPos < Width() * 0.9f )
  280. return;
  281. }
  282. // The ideal position is for the carat to be right in the middle
  283. int idealx = -iCaratPos + Width() * 0.5f;;
  284. // Don't show too much whitespace to the right
  285. if ( idealx + m_Text->Width() < Width() - m_rTextPadding.right )
  286. idealx = -m_Text->Width() + (Width() - m_rTextPadding.right );
  287. // Or the left
  288. if ( idealx > m_rTextPadding.left )
  289. idealx = m_rTextPadding.left;
  290. m_Text->SetPos( idealx, m_Text->Y() );
  291. }
  292. void TextBox::Layout( Skin::Base* skin )
  293. {
  294. BaseClass::Layout( skin );
  295. RefreshCursorBounds();
  296. }
  297. void TextBox::OnTextChanged()
  298. {
  299. if ( m_iCursorPos > TextLength() ) m_iCursorPos = TextLength();
  300. if ( m_iCursorEnd > TextLength() ) m_iCursorEnd = TextLength();
  301. onTextChanged.Call( this );
  302. }
  303. void TextBox::OnEnter()
  304. {
  305. onReturnPressed.Call( this );
  306. }