1 // This file is part of VSTGUI. It is subject to the license terms 2 // in the LICENSE file found in the top-level directory of this 3 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE 4 5 #include "generictextedit.h" 6 #include "../iplatformfont.h" 7 #include "../iplatformframe.h" 8 #include "../../controls/ctextlabel.h" 9 #include "../../cframe.h" 10 #include "../../cvstguitimer.h" 11 #include "../../cdropsource.h" 12 #include <numeric> 13 #include <string> 14 #include <codecvt> 15 #include <locale> 16 17 //----------------------------------------------------------------------------- 18 namespace VSTGUI { 19 20 #define VSTGUI_STB_TEXTEDIT_USE_UNICODE 1 21 22 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 23 using STB_CharT = char16_t; 24 using StringConvert = std::wstring_convert<std::codecvt_utf8_utf16<STB_CharT>, STB_CharT>; 25 #else 26 using STB_CharT = char; 27 #endif 28 #define STB_TEXTEDIT_CHARTYPE STB_CharT 29 #define STB_TEXTEDIT_POSITIONTYPE int 30 #define STB_TEXTEDIT_STRING STBTextEditView 31 #define STB_TEXTEDIT_KEYTYPE uint32_t 32 33 #include "stb_textedit.h" 34 35 //----------------------------------------------------------------------------- 36 class STBTextEditView 37 : public CTextLabel 38 , public IKeyboardHook 39 , public IMouseObserver 40 { 41 public: 42 STBTextEditView (IPlatformTextEditCallback* callback); 43 44 void draw (CDrawContext* pContext) override; 45 void drawBack (CDrawContext* pContext, CBitmap* newBack = nullptr) override; 46 void setText (const UTF8String& txt) override; 47 48 int32_t onKeyDown (const VstKeyCode& code, CFrame* frame) override; 49 int32_t onKeyUp (const VstKeyCode& code, CFrame* frame) override; 50 51 void onMouseEntered (CView* view, CFrame* frame) override; 52 void onMouseExited (CView* view, CFrame* frame) override; 53 CMouseEventResult onMouseMoved (CFrame* frame, 54 const CPoint& where, 55 const CButtonState& buttons) override; 56 CMouseEventResult onMouseDown (CFrame* frame, 57 const CPoint& where, 58 const CButtonState& buttons) override; 59 60 bool attached (CView* parent) override; 61 bool removed (CView* parent) override; 62 void drawStyleChanged () override; 63 64 void selectAll (); 65 bool doCut (); 66 bool doCopy (); 67 bool doPaste (); 68 69 static int deleteChars (STBTextEditView* self, size_t pos, size_t num); 70 static int insertChars (STBTextEditView* self, size_t pos, const STB_CharT* text, size_t num); 71 static void layout (StbTexteditRow* row, STBTextEditView* self, int start_i); 72 static float getCharWidth (STBTextEditView* self, int n, int i); 73 static STB_CharT getChar (STBTextEditView* self, int pos); 74 static int getLength (STBTextEditView* self); 75 76 private: 77 using CTextLabel::onKeyDown; 78 using CTextLabel::onKeyUp; 79 using CTextLabel::onMouseEntered; 80 using CTextLabel::onMouseExited; 81 using CTextLabel::onMouseMoved; 82 using CTextLabel::onMouseDown; 83 84 template<typename Proc> 85 bool callSTB (Proc proc); 86 void onStateChanged (); 87 void onTextChange (); 88 void fillCharWidthCache (); 89 void calcCursorSizes (); 90 CCoord getCharWidth (STB_CharT c, STB_CharT pc) const; 91 92 static constexpr auto BitRecursiveKeyGuard = 1 << 0; 93 static constexpr auto BitBlinkToggle = 1 << 1; 94 static constexpr auto BitCursorIsSet = 1 << 2; 95 static constexpr auto BitCursorSizesValid = 1 << 3; 96 static constexpr auto BitNotifyTextChange = 1 << 4; 97 98 bool isRecursiveKeyEventGuard () const { return hasBit (flags, BitRecursiveKeyGuard); } 99 bool isBlinkToggle () const { return hasBit (flags, BitBlinkToggle); } 100 bool isCursorSet () const { return hasBit (flags, BitCursorIsSet); } 101 bool cursorSizesValid () const { return hasBit (flags, BitCursorSizesValid); } 102 bool notifyTextChange () const { return hasBit (flags, BitNotifyTextChange); } 103 104 void setRecursiveKeyEventGuard (bool state) { setBit (flags, BitRecursiveKeyGuard, state); } 105 void setBlinkToggle (bool state) { setBit (flags, BitBlinkToggle, state); } 106 void setCursorIsSet (bool state) { setBit (flags, BitCursorIsSet, state); } 107 void setCursorSizesValid (bool state) { setBit (flags, BitCursorSizesValid, state); } 108 void setNotifyTextChange (bool state) { setBit (flags, BitNotifyTextChange, state); } 109 110 SharedPointer<CVSTGUITimer> blinkTimer; 111 IPlatformTextEditCallback* callback; 112 STB_TexteditState editState; 113 std::vector<CCoord> charWidthCache; 114 CColor selectionColor{kBlueCColor}; 115 CCoord cursorOffset{0.}; 116 CCoord cursorHeight{0.}; 117 uint32_t flags{0}; 118 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 119 std::u16string uString; 120 #endif 121 }; 122 123 //----------------------------------------------------------------------------- 124 #define VIRTUAL_KEY_BIT 0x80000000 125 #define STB_TEXTEDIT_K_SHIFT 0x40000000 126 #define STB_TEXTEDIT_K_CONTROL 0x20000000 127 #define STB_TEXTEDIT_K_ALT 0x10000000 128 // key-bindings 129 #define STB_TEXTEDIT_K_LEFT (VIRTUAL_KEY_BIT | VKEY_LEFT) 130 #define STB_TEXTEDIT_K_RIGHT (VIRTUAL_KEY_BIT | VKEY_RIGHT) 131 #define STB_TEXTEDIT_K_UP (VIRTUAL_KEY_BIT | VKEY_UP) 132 #define STB_TEXTEDIT_K_DOWN (VIRTUAL_KEY_BIT | VKEY_DOWN) 133 #if MAC 134 # define STB_TEXTEDIT_K_LINESTART (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_LEFT) 135 # define STB_TEXTEDIT_K_LINEEND (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_RIGHT) 136 # define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_ALT | STB_TEXTEDIT_K_LEFT) 137 # define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_ALT | STB_TEXTEDIT_K_RIGHT) 138 # define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_UP) 139 # define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_DOWN) 140 #else 141 # define STB_TEXTEDIT_K_LINESTART (VIRTUAL_KEY_BIT | VKEY_HOME) 142 # define STB_TEXTEDIT_K_LINEEND (VIRTUAL_KEY_BIT | VKEY_END) 143 # define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_CONTROL) 144 # define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_CONTROL) 145 # define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_CONTROL) 146 # define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_CONTROL) 147 #endif 148 #define STB_TEXTEDIT_K_DELETE (VIRTUAL_KEY_BIT | VKEY_DELETE) 149 #define STB_TEXTEDIT_K_BACKSPACE (VIRTUAL_KEY_BIT | VKEY_BACK) 150 #define STB_TEXTEDIT_K_UNDO (STB_TEXTEDIT_K_CONTROL | 'z') 151 #define STB_TEXTEDIT_K_REDO (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_SHIFT | 'z') 152 #define STB_TEXTEDIT_K_INSERT (VIRTUAL_KEY_BIT | VKEY_INSERT) 153 #define STB_TEXTEDIT_K_PGUP (VIRTUAL_KEY_BIT | VKEY_PAGEUP) 154 #define STB_TEXTEDIT_K_PGDOWN (VIRTUAL_KEY_BIT | VKEY_PAGEDOWN) 155 // functions 156 #define STB_TEXTEDIT_STRINGLEN(tc) STBTextEditView::getLength (tc) 157 #define STB_TEXTEDIT_LAYOUTROW STBTextEditView::layout 158 #define STB_TEXTEDIT_GETWIDTH(tc, n, i) STBTextEditView::getCharWidth (tc, n, i) 159 #define STB_TEXTEDIT_KEYTOTEXT(key) \ 160 ((key & VIRTUAL_KEY_BIT) ? 0 : ((key & STB_TEXTEDIT_K_CONTROL) ? 0 : (key & (~0xF0000000)))); 161 #define STB_TEXTEDIT_GETCHAR(tc, i) STBTextEditView::getChar (tc, i) 162 #define STB_TEXTEDIT_NEWLINE '\n' 163 #define STB_TEXTEDIT_IS_SPACE(ch) isSpace (ch) 164 #define STB_TEXTEDIT_DELETECHARS STBTextEditView::deleteChars 165 #define STB_TEXTEDIT_INSERTCHARS STBTextEditView::insertChars 166 167 #define STB_TEXTEDIT_IMPLEMENTATION 168 #include "stb_textedit.h" 169 170 //----------------------------------------------------------------------------- 171 struct GenericTextEdit::Impl 172 { 173 STBTextEditView* view; 174 }; 175 176 //----------------------------------------------------------------------------- 177 GenericTextEdit::GenericTextEdit (IPlatformTextEditCallback* callback) 178 : IPlatformTextEdit (callback) 179 { 180 impl = std::unique_ptr<Impl> (new Impl); 181 impl->view = new STBTextEditView (callback); 182 auto view = dynamic_cast<CView*> (callback); 183 vstgui_assert (view); 184 view->getParentView ()->asViewContainer ()->addView (impl->view); 185 186 auto font = shared (callback->platformGetFont ()); 187 auto fontSize = font->getSize () / impl->view->getGlobalTransform ().m11; 188 if (fontSize != font->getSize ()) 189 { 190 font = makeOwned<CFontDesc> (*font); 191 font->setSize (fontSize); 192 } 193 impl->view->setFont (font); 194 impl->view->setFontColor (callback->platformGetFontColor ()); 195 impl->view->setTextInset (callback->platformGetTextInset ()); 196 impl->view->setHoriAlign (callback->platformGetHoriTxtAlign ()); 197 impl->view->setText (callback->platformGetText ()); 198 impl->view->selectAll (); 199 200 updateSize (); 201 } 202 203 //----------------------------------------------------------------------------- 204 GenericTextEdit::~GenericTextEdit () noexcept 205 { 206 if (impl->view->isAttached ()) 207 impl->view->getParentView ()->asViewContainer ()->removeView (impl->view); 208 else 209 impl->view->forget (); 210 } 211 212 //----------------------------------------------------------------------------- 213 UTF8String GenericTextEdit::getText () 214 { 215 return impl->view->getText (); 216 } 217 218 //----------------------------------------------------------------------------- 219 bool GenericTextEdit::setText (const UTF8String& text) 220 { 221 impl->view->setText (text); 222 return true; 223 } 224 225 //----------------------------------------------------------------------------- 226 bool GenericTextEdit::updateSize () 227 { 228 auto r = textEdit->platformGetVisibleSize (); 229 r = impl->view->translateToLocal (r); 230 impl->view->setViewSize (r); 231 impl->view->setMouseableArea (r); 232 return true; 233 } 234 235 //----------------------------------------------------------------------------- 236 STBTextEditView::STBTextEditView (IPlatformTextEditCallback* callback) 237 : CTextLabel ({}), callback (callback) 238 { 239 stb_textedit_initialize_state (&editState, true); 240 setTransparency (true); 241 } 242 243 //----------------------------------------------------------------------------- 244 template<typename Proc> 245 bool STBTextEditView::callSTB (Proc proc) 246 { 247 auto oldState = editState; 248 proc (); 249 if (memcmp (&oldState, &editState, sizeof (STB_TexteditState)) != 0) 250 { 251 onStateChanged (); 252 return true; 253 } 254 return false; 255 } 256 257 //----------------------------------------------------------------------------- 258 int32_t STBTextEditView::onKeyDown (const VstKeyCode& code, CFrame* frame) 259 { 260 if (isRecursiveKeyEventGuard ()) 261 return -1; 262 auto selfGuard = SharedPointer<CBaseObject> (this); 263 BitScopeToggleT<uint32_t, uint32_t> br (flags, BitRecursiveKeyGuard); 264 if (callback->platformOnKeyDown (code)) 265 return 1; 266 267 if (code.character == 0 && code.virt == 0) 268 return -1; 269 270 if (code.modifier == MODIFIER_CONTROL) 271 { 272 switch (code.character) 273 { 274 case 'a': 275 { 276 selectAll (); 277 return 1; 278 } 279 case 'x': 280 { 281 if (doCut ()) 282 return 1; 283 return -1; 284 } 285 case 'c': 286 { 287 if (doCopy ()) 288 return 1; 289 return -1; 290 } 291 case 'v': 292 { 293 if (doPaste ()) 294 return 1; 295 return -1; 296 } 297 } 298 } 299 300 auto key = code.character; 301 if (key) 302 { 303 if (auto text = getFrame ()->getPlatformFrame ()->convertCurrentKeyEventToText ()) 304 { 305 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 306 auto tmp = StringConvert{}.from_bytes (text->getString ()); 307 key = tmp[0]; 308 #else 309 if (text->length () != 1) 310 return -1; 311 key = text->getString ()[0]; 312 #endif 313 } 314 } 315 if (code.virt) 316 { 317 switch (code.virt) 318 { 319 case VKEY_SPACE: 320 { 321 key = 0x20; 322 break; 323 } 324 case VKEY_TAB: 325 { 326 return -1; 327 } 328 default: 329 { 330 key = code.virt | VIRTUAL_KEY_BIT; 331 break; 332 } 333 } 334 } 335 if (code.modifier & MODIFIER_CONTROL) 336 key |= STB_TEXTEDIT_K_CONTROL; 337 if (code.modifier & MODIFIER_ALTERNATE) 338 key |= STB_TEXTEDIT_K_ALT; 339 if (code.modifier & MODIFIER_SHIFT) 340 key |= STB_TEXTEDIT_K_SHIFT; 341 return callSTB ([&]() { stb_textedit_key (this, &editState, key); }) ? 1 : -1; 342 } 343 344 //----------------------------------------------------------------------------- 345 int32_t STBTextEditView::onKeyUp (const VstKeyCode& code, CFrame* frame) 346 { 347 return -1; 348 } 349 350 //----------------------------------------------------------------------------- 351 CMouseEventResult STBTextEditView::onMouseDown (CFrame* frame, 352 const CPoint& _where, 353 const CButtonState& buttons) 354 { 355 auto where = _where; 356 if (auto parent = getParentView ()) 357 { 358 parent->translateToLocal (where); 359 if (buttons.isLeftButton () && hitTest (where, buttons)) 360 { 361 CPoint where2 (where); 362 where2.x -= getViewSize ().left; 363 where2.y -= getViewSize ().top; 364 callSTB ([&]() { 365 stb_textedit_click (this, &editState, static_cast<float> (where2.x), 366 static_cast<float> (where2.y)); 367 }); 368 return kMouseEventHandled; 369 } 370 } 371 return kMouseEventNotHandled; 372 } 373 374 //----------------------------------------------------------------------------- 375 CMouseEventResult STBTextEditView::onMouseMoved (CFrame* frame, 376 const CPoint& _where, 377 const CButtonState& buttons) 378 { 379 auto where = _where; 380 if (auto parent = getParentView ()) 381 { 382 parent->translateToLocal (where); 383 if (buttons.isLeftButton () && hitTest (where, buttons)) 384 { 385 CPoint where2 (where); 386 where2.x -= getViewSize ().left; 387 where2.y -= getViewSize ().top; 388 callSTB ([&]() { 389 stb_textedit_drag (this, &editState, static_cast<float> (where2.x), 390 static_cast<float> (where2.y)); 391 }); 392 return kMouseEventHandled; 393 } 394 } 395 return kMouseEventNotHandled; 396 } 397 398 //----------------------------------------------------------------------------- 399 void STBTextEditView::onMouseEntered (CView* view, CFrame* frame) 400 { 401 if (view == this) 402 { 403 setCursorIsSet (true); 404 getFrame ()->setCursor (kCursorIBeam); 405 } 406 } 407 408 //----------------------------------------------------------------------------- 409 void STBTextEditView::onMouseExited (CView* view, CFrame* frame) 410 { 411 if (view == this) 412 { 413 setCursorIsSet (false); 414 getFrame ()->setCursor (kCursorDefault); 415 } 416 } 417 418 //----------------------------------------------------------------------------- 419 bool STBTextEditView::attached (CView* parent) 420 { 421 if (auto frame = parent->getFrame ()) 422 { 423 frame->registerMouseObserver (this); 424 frame->registerKeyboardHook (this); 425 selectionColor = frame->getFocusColor (); 426 drawStyleChanged (); 427 } 428 return CTextLabel::attached (parent); 429 } 430 431 //----------------------------------------------------------------------------- 432 bool STBTextEditView::removed (CView* parent) 433 { 434 if (auto frame = getFrame ()) 435 { 436 blinkTimer = nullptr; 437 frame->unregisterMouseObserver (this); 438 frame->unregisterKeyboardHook (this); 439 if (isCursorSet ()) 440 frame->setCursor (kCursorDefault); 441 } 442 return CTextLabel::removed (parent); 443 } 444 445 //----------------------------------------------------------------------------- 446 void STBTextEditView::drawStyleChanged () 447 { 448 setCursorSizesValid (false); 449 charWidthCache.clear (); 450 CTextLabel::drawStyleChanged (); 451 } 452 453 //----------------------------------------------------------------------------- 454 void STBTextEditView::selectAll () 455 { 456 editState.select_start = 0; 457 editState.select_end = static_cast<int> (getText ().length ()); 458 onStateChanged (); 459 } 460 461 //----------------------------------------------------------------------------- 462 bool STBTextEditView::doCut () 463 { 464 if (doCopy ()) 465 { 466 callSTB ([&]() { stb_textedit_cut (this, &editState); }); 467 return true; 468 } 469 return false; 470 } 471 472 //----------------------------------------------------------------------------- 473 bool STBTextEditView::doCopy () 474 { 475 if (editState.select_start == editState.select_end) 476 return false; 477 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 478 auto txt = StringConvert{}.to_bytes (uString.data () + editState.select_start, 479 uString.data () + editState.select_end); 480 auto dataPackage = CDropSource::create (txt.data (), txt.size (), IDataPackage::kText); 481 #else 482 auto dataPackage = 483 CDropSource::create (getText ().data (), getText ().length (), IDataPackage::kText); 484 #endif 485 getFrame ()->getPlatformFrame ()->setClipboard (dataPackage); 486 return true; 487 } 488 489 //----------------------------------------------------------------------------- 490 bool STBTextEditView::doPaste () 491 { 492 if (auto clipboard = getFrame ()->getPlatformFrame ()->getClipboard ()) 493 { 494 auto count = clipboard->getCount (); 495 for (auto i = 0u; i < count; ++i) 496 { 497 const void* buffer; 498 IDataPackage::Type dataType; 499 auto size = clipboard->getData (i, buffer, dataType); 500 if (dataType == IDataPackage::kText) 501 { 502 auto text = reinterpret_cast<const char*> (buffer); 503 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 504 auto uText = StringConvert{}.from_bytes (text, text + size); 505 callSTB ( 506 [&]() { stb_textedit_paste (this, &editState, uText.data (), uText.size ()); }); 507 #else 508 callSTB ([&]() { stb_textedit_paste (this, &editState, text, size); }); 509 #endif 510 return true; 511 } 512 } 513 } 514 return false; 515 } 516 517 //----------------------------------------------------------------------------- 518 void STBTextEditView::onStateChanged () 519 { 520 setBlinkToggle (true); 521 if (isAttached ()) 522 { 523 blinkTimer = makeOwned<CVSTGUITimer> ( 524 [&](CVSTGUITimer* timer) { 525 setBlinkToggle (!isBlinkToggle ()); 526 if (editState.select_start == editState.select_end) 527 invalid (); 528 }, 529 500); 530 } 531 invalid (); 532 } 533 534 //----------------------------------------------------------------------------- 535 void STBTextEditView::setText (const UTF8String& txt) 536 { 537 charWidthCache.clear (); 538 CTextLabel::setText (txt); 539 if (editState.select_start != editState.select_end) 540 selectAll (); 541 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 542 uString = StringConvert{}.from_bytes (CTextLabel::getText ().getString ()); 543 #endif 544 } 545 546 //----------------------------------------------------------------------------- 547 CCoord STBTextEditView::getCharWidth (STB_CharT c, STB_CharT pc) const 548 { 549 auto platformFont = getFont ()->getPlatformFont (); 550 vstgui_assert (platformFont); 551 auto fontPainter = platformFont->getPainter (); 552 vstgui_assert (fontPainter); 553 554 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 555 if (pc) 556 { 557 UTF8String str (StringConvert{}.to_bytes (pc)); 558 auto pcWidth = fontPainter->getStringWidth (nullptr, str.getPlatformString (), true); 559 str += StringConvert{}.to_bytes (c); 560 auto tcWidth = fontPainter->getStringWidth (nullptr, str.getPlatformString (), true); 561 return tcWidth - pcWidth; 562 } 563 UTF8String str (StringConvert{}.to_bytes (c)); 564 return fontPainter->getStringWidth (nullptr, str.getPlatformString (), true); 565 #else 566 if (pc) 567 { 568 UTF8String str (std::string (1, pc)); 569 auto pcWidth = fontPainter->getStringWidth (nullptr, str.getPlatformString (), true); 570 str += std::string (1, c); 571 auto tcWidth = fontPainter->getStringWidth (nullptr, str.getPlatformString (), true); 572 return tcWidth - pcWidth; 573 } 574 575 UTF8String str (std::string (1, c)); 576 return fontPainter->getStringWidth (nullptr, str.getPlatformString (), true); 577 #endif 578 } 579 580 //----------------------------------------------------------------------------- 581 void STBTextEditView::fillCharWidthCache () 582 { 583 if (!charWidthCache.empty ()) 584 return; 585 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 586 auto num = uString.size (); 587 charWidthCache.resize (num); 588 for (auto i = 0u; i < num; ++i) 589 charWidthCache[i] = getCharWidth (uString[i], i == 0 ? 0 : uString[i - 1]); 590 #else 591 auto num = getText ().length (); 592 charWidthCache.resize (num); 593 const auto& str = getText ().getString (); 594 for (auto i = 0u; i < num; ++i) 595 charWidthCache[i] = getCharWidth (str[i], i == 0 ? 0 : str[i - 1]); 596 #endif 597 } 598 599 //----------------------------------------------------------------------------- 600 void STBTextEditView::calcCursorSizes () 601 { 602 if (cursorSizesValid ()) 603 return; 604 605 auto platformFont = getFont ()->getPlatformFont (); 606 vstgui_assert (platformFont); 607 608 cursorHeight = platformFont->getAscent () + platformFont->getDescent (); 609 auto viewHeight = getViewSize ().getHeight (); 610 cursorOffset = (viewHeight / 2. - cursorHeight / 2.); 611 setCursorSizesValid (true); 612 } 613 614 //----------------------------------------------------------------------------- 615 void STBTextEditView::draw (CDrawContext* context) 616 { 617 fillCharWidthCache (); 618 calcCursorSizes (); 619 620 drawBack (context, nullptr); 621 drawPlatformText (context, getText ().getPlatformString ()); 622 623 if (!isBlinkToggle () || editState.select_start != editState.select_end) 624 return; 625 626 // draw cursor 627 StbTexteditRow row{}; 628 layout (&row, this, 0); 629 630 context->setFillColor (getFontColor ()); 631 context->setDrawMode (kAntiAliasing); 632 CRect r = getViewSize (); 633 r.setHeight (cursorHeight); 634 r.offset (row.x0, cursorOffset); 635 r.setWidth (1); 636 for (auto i = 0; i < editState.cursor; ++i) 637 r.offset (charWidthCache[i], 0); 638 r.offset (-0.5, 0); 639 context->drawRect (r, kDrawFilled); 640 } 641 642 //----------------------------------------------------------------------------- 643 void STBTextEditView::drawBack (CDrawContext* context, CBitmap* newBack) 644 { 645 CTextLabel::drawBack (context, newBack); 646 647 auto selStart = editState.select_start; 648 auto selEnd = editState.select_end; 649 if (selStart > selEnd) 650 std::swap (selStart, selEnd); 651 652 if (selStart != selEnd) 653 { 654 StbTexteditRow row{}; 655 layout (&row, this, 0); 656 657 // draw selection 658 CRect selection = getViewSize (); 659 selection.setHeight (cursorHeight); 660 selection.offset (row.x0, cursorOffset); 661 selection.setWidth (0); 662 auto index = 0; 663 for (; index < selStart; ++index) 664 selection.offset (charWidthCache[index], 0); 665 for (; index < selEnd; ++index) 666 selection.right += charWidthCache[index]; 667 context->setFillColor (selectionColor); 668 context->drawRect (selection, kDrawFilled); 669 } 670 } 671 672 //----------------------------------------------------------------------------- 673 void STBTextEditView::onTextChange () 674 { 675 if (notifyTextChange ()) 676 return; 677 if (auto frame = getFrame ()) 678 { 679 if (frame->inEventProcessing ()) 680 { 681 setNotifyTextChange (true); 682 auto self = shared (this); 683 frame->doAfterEventProcessing ([self]() { 684 self->setNotifyTextChange (false); 685 self->callback->platformTextDidChange (); 686 }); 687 } 688 } 689 } 690 691 //----------------------------------------------------------------------------- 692 int STBTextEditView::deleteChars (STBTextEditView* self, size_t pos, size_t num) 693 { 694 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 695 self->uString.erase (pos, num); 696 self->setText (StringConvert{}.to_bytes (self->uString)); 697 self->onTextChange (); 698 return true; 699 #else 700 auto str = self->text.getString (); 701 str.erase (pos, num); 702 self->setText (str.data ()); 703 self->onTextChange (); 704 return true; // success 705 #endif 706 } 707 708 //----------------------------------------------------------------------------- 709 int STBTextEditView::insertChars (STBTextEditView* self, 710 size_t pos, 711 const STB_CharT* text, 712 size_t num) 713 { 714 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 715 self->uString.insert (pos, text, num); 716 self->setText (StringConvert{}.to_bytes (self->uString)); 717 self->onTextChange (); 718 return true; 719 #else 720 auto str = self->text.getString (); 721 str.insert (pos, text, num); 722 self->setText (str.data ()); 723 self->onTextChange (); 724 return true; // success 725 #endif 726 } 727 728 //----------------------------------------------------------------------------- 729 STB_CharT STBTextEditView::getChar (STBTextEditView* self, int pos) 730 { 731 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 732 return self->uString[pos]; 733 #else 734 return self->getText ().getString ()[pos]; 735 #endif 736 } 737 738 //----------------------------------------------------------------------------- 739 int STBTextEditView::getLength (STBTextEditView* self) 740 { 741 #if VSTGUI_STB_TEXTEDIT_USE_UNICODE 742 return self->uString.size (); 743 #else 744 return static_cast<int> (self->getText ().length ()); 745 #endif 746 } 747 748 //----------------------------------------------------------------------------- 749 void STBTextEditView::layout (StbTexteditRow* row, STBTextEditView* self, int start_i) 750 { 751 vstgui_assert (start_i == 0); 752 753 self->fillCharWidthCache (); 754 auto textWidth = static_cast<float> ( 755 std::accumulate (self->charWidthCache.begin (), self->charWidthCache.end (), 0.)); 756 757 row->num_chars = static_cast<int> (self->getText ().length ()); 758 row->baseline_y_delta = 1.25; 759 row->ymin = 0.f; 760 row->ymax = static_cast<float> (self->getFont ()->getSize ()); 761 switch (self->getHoriAlign ()) 762 { 763 case kLeftText: 764 { 765 row->x0 = static_cast<float> (self->getTextInset ().x); 766 row->x1 = row->x0 + textWidth; 767 break; 768 } 769 case kCenterText: 770 { 771 row->x0 = 772 static_cast<float> ((self->getViewSize ().getWidth () / 2.) - (textWidth / 2.)); 773 row->x1 = row->x0 + textWidth; 774 break; 775 } 776 default: 777 { 778 vstgui_assert (false, "Not Implemented !"); 779 break; 780 } 781 } 782 } 783 784 //----------------------------------------------------------------------------- 785 float STBTextEditView::getCharWidth (STBTextEditView* self, int n, int i) 786 { 787 self->fillCharWidthCache (); 788 return static_cast<float> (self->charWidthCache[i]); 789 } 790 791 //----------------------------------------------------------------------------- 792 } // VSTGUI 793