// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "ctextedit.h" #include "itexteditlistener.h" #include "../cframe.h" #include "../cdrawcontext.h" #include "../events.h" #include "../platform/iplatformframe.h" #include namespace VSTGUI { //------------------------------------------------------------------------ // CTextEdit //------------------------------------------------------------------------ /*! @class CTextEdit Define a rectangle view where a text-value can be displayed and edited with a given font and color. The user can specify its convert function (from char to char). The text-value is centered in the given rect. A bitmap can be used as background. */ //------------------------------------------------------------------------ /** * CTextEdit constructor. * @param size the size of this view * @param listener the listener * @param tag the control tag * @param txt the initial text as c string (UTF-8 encoded) * @param background the background bitmap * @param style the display style (see CParamDisplay for styles) */ //------------------------------------------------------------------------ CTextEdit::CTextEdit (const CRect& size, IControlListener* listener, int32_t tag, UTF8StringPtr txt, CBitmap* background, const int32_t style) : CTextLabel (size, txt, background, style) { this->listener = listener; this->tag = tag; setWantsFocus (true); } //------------------------------------------------------------------------ CTextEdit::CTextEdit (const CTextEdit& v) : CTextLabel (v) , bWasReturnPressed (false) , stringToValueFunction (v.stringToValueFunction) , immediateTextChange (v.immediateTextChange) , secureStyle (v.secureStyle) , platformFont (v.platformFont) , placeholderString (v.placeholderString) { setWantsFocus (true); } //------------------------------------------------------------------------ CTextEdit::~CTextEdit () noexcept { listener = nullptr; vstgui_assert (platformControl == nullptr); } //------------------------------------------------------------------------ void CTextEdit::setStringToValueFunction (const StringToValueFunction& stringToValueFunc) { stringToValueFunction = stringToValueFunc; } //------------------------------------------------------------------------ void CTextEdit::setStringToValueFunction (StringToValueFunction&& stringToValueFunc) { stringToValueFunction = std::move (stringToValueFunc); } //------------------------------------------------------------------------ void CTextEdit::setImmediateTextChange (bool state) { immediateTextChange = state; } //------------------------------------------------------------------------ void CTextEdit::setSecureStyle (bool state) { if (secureStyle != state) { secureStyle = state; if (platformControl) { } } } //------------------------------------------------------------------------ bool CTextEdit::getSecureStyle () const { return secureStyle; } //------------------------------------------------------------------------ void CTextEdit::registerTextEditListener (ITextEditListener* listener) { textEditListeners.add (listener); } //------------------------------------------------------------------------ void CTextEdit::unregisterTextEditListener (ITextEditListener* listener) { textEditListeners.remove (listener); } //------------------------------------------------------------------------ void CTextEdit::setValue (float val) { CTextLabel::setValue (val); bool converted = false; std::string string; if (valueToStringFunction) converted = valueToStringFunction (getValue (), string, this); if (!converted) { char tmp[255]; char precisionStr[10]; snprintf (precisionStr, 10, "%%.%hhuf", valuePrecision); snprintf (tmp, 255, precisionStr, getValue ()); string = tmp; } if (converted) { CTextLabel::setText (UTF8String (std::move (string))); if (platformControl) platformControl->setText (getText ()); } else setText (UTF8String (std::move (string))); } //------------------------------------------------------------------------ void CTextEdit::setText (const UTF8String& txt) { if (stringToValueFunction) { float val = getValue (); if (stringToValueFunction (txt, val, this)) { CTextLabel::setValue (val); if (valueToStringFunction) { std::string string; valueToStringFunction (getValue (), string, this); CTextLabel::setText (UTF8String (std::move (string))); if (platformControl) platformControl->setText (getText ()); return; } } } CTextLabel::setText (txt); if (platformControl) platformControl->setText (getText ()); } //------------------------------------------------------------------------ void CTextEdit::valueChanged () { if (stringToValueFunction) CTextLabel::valueChanged (); CParamDisplay::valueChanged (); } //------------------------------------------------------------------------ void CTextEdit::setPlaceholderString (const UTF8String& str) { placeholderString = str; } //------------------------------------------------------------------------ void CTextEdit::draw (CDrawContext *pContext) { if (platformControl) { drawBack (pContext); if (!platformControl->drawsPlaceholder () && !placeholderString.empty () && platformControl->getText ().empty ()) { pContext->saveGlobalState (); pContext->setGlobalAlpha (pContext->getGlobalAlpha () * 0.5f); drawPlatformText (pContext, placeholderString); pContext->restoreGlobalState (); } setDirty (false); return; } drawBack (pContext); if (text.empty ()) { if (!placeholderString.empty ()) { pContext->saveGlobalState (); pContext->setGlobalAlpha (pContext->getGlobalAlpha () * 0.5f); drawPlatformText (pContext, placeholderString); pContext->restoreGlobalState (); } } else if (getSecureStyle ()) { constexpr auto bulletCharacter = "\xE2\x80\xA2"; UTF8String str; for (auto i = 0u; i < text.length (); ++i) str += bulletCharacter; drawPlatformText (pContext, str); } else CTextLabel::draw (pContext); setDirty (false); } //------------------------------------------------------------------------ CMouseEventResult CTextEdit::onMouseDown (CPoint& where, const CButtonState& buttons) { if (buttons & kLButton) { if (getFrame ()->getFocusView () != this) { if (isDoubleClickStyle ()) { if (!(buttons & kDoubleClick)) return kMouseEventNotHandled; } takeFocus (); return kMouseDownEventHandledButDontNeedMovedOrUpEvents; } } return kMouseEventNotHandled; } //------------------------------------------------------------------------ void CTextEdit::onKeyboardEvent (KeyboardEvent& event) { if (!platformControl || event.type != EventType::KeyDown) return; if (event.virt == VirtualKey::Escape) { bWasReturnPressed = false; platformControl->setText (text); getFrame ()->setFocusView (nullptr); looseFocus (); event.consumed = true; } else if (event.virt == VirtualKey::Return) { bWasReturnPressed = true; getFrame ()->setFocusView (nullptr); looseFocus (); event.consumed = true; } } //------------------------------------------------------------------------ CFontRef CTextEdit::platformGetFont () const { CFontRef font = getFont (); CCoord fontSize = font->getSize (); fontSize *= getGlobalTransform ().m11; if (fontSize == font->getSize ()) return font; platformFont = makeOwned (*font); platformFont->setSize (fontSize); return platformFont; } //------------------------------------------------------------------------ CRect CTextEdit::platformGetSize () const { return translateToGlobal (getViewSize ()); } //------------------------------------------------------------------------ CRect CTextEdit::platformGetVisibleSize () const { return translateToGlobal (getVisibleViewSize ()); } //------------------------------------------------------------------------ void CTextEdit::platformLooseFocus (bool returnPressed) { remember (); bWasReturnPressed = returnPressed; if (getFrame ()->getFocusView () == this) getFrame ()->setFocusView (nullptr); forget (); } //------------------------------------------------------------------------ void CTextEdit::platformOnKeyboardEvent (KeyboardEvent& event) { dynamic_cast (getFrame ())->platformOnEvent (event); if (event.consumed) return; if (event.virt == VirtualKey::Return) { platformLooseFocus (true); event.consumed = true; } else if (event.virt == VirtualKey::Escape) { platformLooseFocus (false); event.consumed = true; } } //------------------------------------------------------------------------ void CTextEdit::platformTextDidChange () { if (platformControl && immediateTextChange) updateText (platformControl); } //------------------------------------------------------------------------ bool CTextEdit::platformIsSecureTextEdit () { return getSecureStyle (); } //------------------------------------------------------------------------ void CTextEdit::parentSizeChanged () { if (platformControl) platformControl->updateSize (); } //------------------------------------------------------------------------ void CTextEdit::setViewSize (const CRect& newSize, bool invalid) { CTextLabel::setViewSize (newSize, invalid); if (platformControl) platformControl->updateSize (); } //------------------------------------------------------------------------ void CTextEdit::createPlatformTextEdit () { if (platformControl) return; bWasReturnPressed = false; platformControl = getFrame ()->getPlatformFrame ()->createPlatformTextEdit (this); textEditListeners.forEach ( [this] (ITextEditListener* l) { l->onTextEditPlatformControlTookFocus (this); }); } //------------------------------------------------------------------------ bool CTextEdit::wantsFocus () const { if (isDoubleClickStyle () && !platformControl) return false; return CTextLabel::wantsFocus (); } //------------------------------------------------------------------------ void CTextEdit::takeFocus () { if (!getFrame ()) return; createPlatformTextEdit (); if (getFrame()->getFocusView () != this) getFrame()->setFocusView (this); CTextLabel::takeFocus (); invalid (); } //------------------------------------------------------------------------ void CTextEdit::looseFocus () { if (platformControl == nullptr) return; CBaseObjectGuard guard (this); auto _platformControl = std::move (platformControl); updateText (_platformControl); _platformControl = nullptr; textEditListeners.forEach ( [this] (ITextEditListener* l) { l->onTextEditPlatformControlLostFocus (this); }); // if you want to destroy the text edit do it with the loose focus message CView* receiver = getParentView () ? getParentView () : getFrame (); while (receiver) { if (receiver->notify (this, kMsgLooseFocus) == kMessageNotified) break; receiver = receiver->getParentView (); } CTextLabel::looseFocus (); invalid (); } //------------------------------------------------------------------------ void CTextEdit::updateText (IPlatformTextEdit* pte) { auto newText = pte->getText (); if (newText != getText ()) { beginEdit (); setText (newText); CParamDisplay::valueChanged (); endEdit (); } } } // VSTGUI