25#if (_MSC_VER >= 1900 ) && (_MSC_VER < 1920 )
26std::locale::id std::codecvt<char16_t, char, _Mbstatet>::id;
31using StringConvert = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,
char16_t>;
34using namespace igraphics;
36#define VIRTUAL_KEY_BIT 0x80000000
37#define STB_TEXTEDIT_K_SHIFT 0x40000000
38#define STB_TEXTEDIT_K_CONTROL 0x20000000
39#define STB_TEXTEDIT_K_ALT 0x10000000
41#define STB_TEXTEDIT_K_LEFT (VIRTUAL_KEY_BIT | kVK_LEFT)
42#define STB_TEXTEDIT_K_RIGHT (VIRTUAL_KEY_BIT | kVK_RIGHT)
43#define STB_TEXTEDIT_K_UP (VIRTUAL_KEY_BIT | kVK_UP)
44#define STB_TEXTEDIT_K_DOWN (VIRTUAL_KEY_BIT | kVK_DOWN)
45#define STB_TEXTEDIT_K_LINESTART (VIRTUAL_KEY_BIT | kVK_HOME)
46#define STB_TEXTEDIT_K_LINEEND (VIRTUAL_KEY_BIT | kVK_END)
47#define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_CONTROL)
48#define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_CONTROL)
49#define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_CONTROL)
50#define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_CONTROL)
51#define STB_TEXTEDIT_K_DELETE (VIRTUAL_KEY_BIT | kVK_DELETE)
52#define STB_TEXTEDIT_K_BACKSPACE (VIRTUAL_KEY_BIT | kVK_BACK)
53#define STB_TEXTEDIT_K_UNDO (STB_TEXTEDIT_K_CONTROL | 'z')
54#define STB_TEXTEDIT_K_REDO (STB_TEXTEDIT_K_CONTROL | STB_TEXTEDIT_K_SHIFT | 'z')
55#define STB_TEXTEDIT_K_INSERT (VIRTUAL_KEY_BIT | kVK_INSERT)
56#define STB_TEXTEDIT_K_PGUP (VIRTUAL_KEY_BIT | kVK_PRIOR)
57#define STB_TEXTEDIT_K_PGDOWN (VIRTUAL_KEY_BIT | kVK_NEXT)
59#define STB_TEXTEDIT_STRINGLEN(tc) ITextEntryControl::GetLength (tc)
60#define STB_TEXTEDIT_LAYOUTROW ITextEntryControl::Layout
61#define STB_TEXTEDIT_GETWIDTH(tc, n, i) ITextEntryControl::GetCharWidth (tc, n, i)
62#define STB_TEXTEDIT_KEYTOTEXT(key) \
63((key & VIRTUAL_KEY_BIT) ? 0 : ((key & STB_TEXTEDIT_K_CONTROL) ? 0 : (key & (~0xF0000000))));
64#define STB_TEXTEDIT_GETCHAR(tc, i) ITextEntryControl::GetChar (tc, i)
65#define STB_TEXTEDIT_NEWLINE '\n'
66#define STB_TEXTEDIT_IS_SPACE(ch) isspace(ch)
67#define STB_TEXTEDIT_DELETECHARS ITextEntryControl::DeleteChars
68#define STB_TEXTEDIT_INSERTCHARS ITextEntryControl::InsertChars
70#define STB_TEXTEDIT_IMPLEMENTATION
71#include "stb_textedit.h"
74ITextEntryControl::ITextEntryControl()
77 stb_textedit_initialize_state(&mEditState,
true);
79 SetActionFunction([&](
IControl* pCaller) {
83 SetAnimation([&](
IControl* pCaller) {
92 pCaller->OnEndAnimation();
103 g.
FillRect(mText.mTextEntryBGColor, mRECT);
106 Layout(&row,
this, 0);
108 const bool hasSelection = mEditState.select_start != mEditState.select_end;
111 float selectionStart = row.x0, selectionEnd = row.x0;
112 const int start = std::min(mEditState.select_start, mEditState.select_end);
113 const int end = std::max(mEditState.select_start, mEditState.select_end);
114 for (
int i = 0; i < mCharWidths.GetSize() && i < end; ++i)
117 selectionStart += mCharWidths.Get()[i];
119 selectionEnd += mCharWidths.Get()[i];
121 IRECT selectionRect(selectionStart, mRECT.T + row.ymin, selectionEnd, mRECT.T + row.ymax);
122 selectionRect = selectionRect.
GetVPadded(-mText.mSize*0.1f);
123 IBlend blend(EBlend::Default, 0.2f);
124 g.
FillRect(mText.mTextEntryFGColor, selectionRect, &blend);
127 g.
DrawText(mText, StringConvert{}.to_bytes(mEditString).c_str(), mRECT);
129 if (mDrawCursor && !hasSelection)
131 float cursorPos = row.x0;
132 for (
int i = 0; i < mCharWidths.GetSize() && i < mEditState.cursor; ++i)
134 cursorPos += mCharWidths.Get()[i];
136 IRECT cursorRect(roundf(cursorPos-1), mRECT.T + row.ymin, roundf(cursorPos), mRECT.T + row.ymax);
137 cursorRect = cursorRect.
GetVPadded(-mText.mSize*0.1f);
138 g.
FillRect(mText.mTextEntryFGColor, cursorRect);
142template<
typename Proc>
143bool ITextEntryControl::CallSTB(Proc proc)
145 auto oldState = mEditState;
148 if(memcmp(&oldState, &mEditState,
sizeof (STB_TexteditState)) != 0)
168 stb_textedit_click(
this, &mEditState, x, y);
175 switch (pMenu->GetChosenItemIdx()) {
176 case 0: Cut();
break;
177 case 1: CopySelection();
break;
178 case 2: Paste();
break;
194 stb_textedit_drag(
this, &mEditState, x, y);
209 stb_textedit_drag(
this, &mEditState, x, y);
245 CallSTB([&]() { stb_textedit_key(
this, &mEditState, STB_TEXTEDIT_K_REDO); });
247 CallSTB([&]() { stb_textedit_key(
this, &mEditState, STB_TEXTEDIT_K_UNDO); });
258 wdl_utf8_parsechar(key.utf8, &stbKey);
262 case kVK_SPACE: stbKey =
' ';
break;
263 case kVK_TAB:
return false;
264 case kVK_DELETE: stbKey = STB_TEXTEDIT_K_DELETE;
break;
265 case kVK_BACK: stbKey = STB_TEXTEDIT_K_BACKSPACE;
break;
266 case kVK_LEFT: stbKey = STB_TEXTEDIT_K_LEFT;
break;
267 case kVK_RIGHT: stbKey = STB_TEXTEDIT_K_RIGHT;
break;
268 case kVK_UP: stbKey = STB_TEXTEDIT_K_UP;
break;
269 case kVK_DOWN: stbKey = STB_TEXTEDIT_K_DOWN;
break;
270 case kVK_PRIOR: stbKey = STB_TEXTEDIT_K_PGUP;
break;
271 case kVK_NEXT: stbKey = STB_TEXTEDIT_K_PGDOWN;
break;
272 case kVK_HOME: stbKey = STB_TEXTEDIT_K_LINESTART;
break;
273 case kVK_END: stbKey = STB_TEXTEDIT_K_LINEEND;
break;
274 case kVK_RETURN: CommitEdit();
break;
275 case kVK_ESCAPE: DismissEdit();
break;
281 if(!pControlInTextEntry)
288 switch (pParam->
Type())
290 case IParam::kTypeEnum:
291 case IParam::kTypeInt:
292 case IParam::kTypeBool:
294 if (key.VK >=
'0' && key.VK <=
'9' && !key.S)
296 if (key.VK >= kVK_NUMPAD0 && key.VK <= kVK_NUMPAD9)
298 if (stbKey ==
'+' || stbKey ==
'-')
303 case IParam::kTypeDouble:
305 if (key.VK >=
'0' && key.VK <=
'9' && !key.S)
307 if (key.VK >= kVK_NUMPAD0 && key.VK <= kVK_NUMPAD9)
309 if (stbKey ==
'+' || stbKey ==
'-' || stbKey ==
'.')
321 stbKey = (key.VK) | VIRTUAL_KEY_BIT;
328 stbKey |= STB_TEXTEDIT_K_CONTROL;
330 stbKey |= STB_TEXTEDIT_K_ALT;
332 stbKey |= STB_TEXTEDIT_K_SHIFT;
334 return CallSTB([&]() { stb_textedit_key(
this, &mEditState, stbKey); }) ?
true :
false;
337void ITextEntryControl::OnEndAnimation()
343void ITextEntryControl::CopySelection()
345 if (mEditState.select_start != mEditState.select_end)
347 const int start = std::min(mEditState.select_start, mEditState.select_end);
348 const int end = std::max(mEditState.select_start, mEditState.select_end);
349 GetUI()->
SetTextInClipboard(StringConvert{}.to_bytes(mEditString.data() + start, mEditString.data() + end).c_str());
353void ITextEntryControl::Paste()
355 WDL_String fromClipboard;
356 if (
GetUI()->GetTextFromClipboard(fromClipboard))
359 auto uText = StringConvert{}.from_bytes (fromClipboard.Get(), fromClipboard.Get() + fromClipboard.GetLength());
360 stb_textedit_paste (
this, &mEditState, uText.data(), (
int) uText.size());
365void ITextEntryControl::Cut()
369 stb_textedit_cut(
this, &mEditState);
373void ITextEntryControl::SelectAll()
376 mEditState.select_start = 0;
377 mEditState.select_end =
static_cast<int>(mEditString.length());
382int ITextEntryControl::DeleteChars(
ITextEntryControl* _this,
size_t pos,
size_t num)
384 _this->mEditString.erase(pos, num);
385 _this->SetStr(StringConvert{}.to_bytes(_this->mEditString).c_str());
386 _this->OnTextChange();
391int ITextEntryControl::InsertChars(
ITextEntryControl* _this,
size_t pos,
const char16_t* text,
size_t num)
393 _this->mEditString.insert(pos, text, num);
394 _this->SetStr(StringConvert{}.to_bytes(_this->mEditString).c_str());
395 _this->OnTextChange();
402 return _this->mEditString[pos];
408 return static_cast<int>(_this->mEditString.size());
412void ITextEntryControl::Layout(StbTexteditRow* row,
ITextEntryControl* _this,
int start_i)
414 assert (start_i == 0);
416 _this->FillCharWidthCache();
417 float textWidth = 0.;
419 for (
int i = 0; i < _this->mCharWidths.GetSize(); i++)
421 textWidth += _this->mCharWidths.Get()[i];
424 row->num_chars = GetLength(_this);
425 row->baseline_y_delta = 1.25;
427 switch (_this->
GetText().mAlign)
432 row->x1 = row->x0 + textWidth;
437 row->x0 = _this->
GetRECT().
MW() - (textWidth * 0.5f);
438 row->x1 = row->x0 + textWidth;
443 row->x0 = _this->
GetRECT().R - textWidth;
444 row->x1 = row->x0 + textWidth;
448 switch (_this->
GetText().mVAlign)
455 case EVAlign::Middle:
460 case EVAlign::Bottom:
467 row->ymax = row->ymin +
static_cast<float> (_this->
GetText().mSize);
473 _this->FillCharWidthCache();
474 return _this->mCharWidths.Get()[i];
477void ITextEntryControl::OnStateChanged()
482void ITextEntryControl::OnTextChange()
484 mCharWidths.Resize(0,
false);
485 FillCharWidthCache();
488void ITextEntryControl::FillCharWidthCache()
491 if (mCharWidths.GetSize())
494 const int len =
static_cast<int>(mEditString.size());
495 mCharWidths.Resize(len,
false);
496 for (
int i = 0; i < len; ++i)
498 mCharWidths.Get()[i] = MeasureCharWidth(mEditString[i], i == 0 ? 0 : mEditString[i - 1]);
502void ITextEntryControl::CalcCursorSizes()
513float ITextEntryControl::MeasureCharWidth(
char16_t c,
char16_t nc)
519 std::string str (StringConvert{}.to_bytes (nc));
521 str += StringConvert{}.to_bytes (c);
523 return tcWidth - ncWidth;
526 std::string str (StringConvert{}.to_bytes (c));
530void ITextEntryControl::CreateTextEntry(
int paramIdx,
const IText& text,
const IRECT& bounds,
int length,
const char* str)
534 mText.mFGColor = mText.mTextEntryFGColor;
537 mEditState.cursor = 0;
543void ITextEntryControl::DismissEdit()
551void ITextEntryControl::CommitEdit()
559void ITextEntryControl::SetStr(
const char* str)
561 mCharWidths.Resize(0,
false);
562 mEditString = StringConvert{}.from_bytes(std::string(str));
A Text entry widget drawn by IGraphics to optionally override platform text entries.
The lowest level base class of an IGraphics control.
const IRECT & GetRECT() const
Get the rectangular draw area for this control, within the graphics context.
virtual void SetText(const IText &txt)
Set the Text object typically used to determine font/layout/size etc of the main text in a control.
double GetAnimationProgress() const
Get the progress in a control's animation, in the range 0-1.
const IParam * GetParam(int valIdx=0) const
Get a const pointer to the IParam object (owned by the editor delegate class), associated with this c...
const IText & GetText() const
Get the Text object for the control.
void SetTargetAndDrawRECTs(const IRECT &bounds)
Set BOTH the draw rect and the target area, within the graphics context for this control.
virtual void SetDirty(bool triggerAction=true, int valIdx=kNoValIdx)
Mark the control as dirty, i.e.
The lowest level base class of an IGraphics context.
void DrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend=0)
Draw some text to the graphics context in a specific rectangle.
void CreatePopupMenu(IControl &control, IPopupMenu &menu, const IRECT &bounds, int valIdx=0)
Shows a pop up/contextual menu in relation to a rectangular region of the graphics context.
void ClearInTextEntryControl()
Called when the text entry is dismissed, to reset mInTextEntry.
virtual bool SetTextInClipboard(const char *str)=0
Set text in the clipboard.
virtual void FillRect(const IColor &color, const IRECT &bounds, const IBlend *pBlend=0)
Fill a rectangular region of the graphics context with a color.
void SetControlValueAfterTextEdit(const char *str)
Called by the platform class after returning from a text entry in order to update a control with a ne...
void SetAllControlsDirty()
Calls SetDirty() on every control.
IControl * GetControlInTextEntry()
virtual float MeasureText(const IText &text, const char *str, IRECT &bounds) const
Measure the rectangular region that some text will occupy.
EParamType Type() const
Get the parameter's type.
A Text entry widget drawn by IGraphics.
void OnMouseDown(float x, float y, const IMouseMod &mod) override
Implement this method to respond to a mouse down event on this control.
void Draw(IGraphics &g) override
Draw the control to the graphics context.
void OnMouseDrag(float x, float y, float dX, float dY, const IMouseMod &mod) override
Implement this method to respond to a mouse drag event on this control.
void OnMouseDblClick(float x, float y, const IMouseMod &mod) override
Implement this method to respond to a mouse double click event on this control.
bool OnKeyDown(float x, float y, const IKeyPress &key) override
Implement this method to respond to a key down event on this control.
void OnMouseUp(float x, float y, const IMouseMod &mod) override
Implement this method to respond to a mouse up event on this control.
Used to manage composite/blend operations, independent of draw class/platform.
Used for key press info, such as ASCII representation, virtual key (mapped to win32 codes) and modifi...
Used to manage mouse modifiers i.e.
Used to manage a rectangular area, independent of draw class/platform.
IRECT GetVPadded(float padding) const
Get a copy of this IRECT padded in the Y-axis N.B.
bool Contains(const IRECT &rhs) const
Returns true if this IRECT completely contains rhs.
IText is used to manage font and text/text entry style for a piece of text on the UI,...