Skip to content

Commit

Permalink
Textbox now sort of works
Browse files Browse the repository at this point in the history
  • Loading branch information
codecat committed Aug 5, 2017
1 parent ce0bb7e commit 8fce166
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 19 deletions.
3 changes: 3 additions & 0 deletions include/nimble/content/font.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ namespace na

int GetHandle();

float GetNextGlyphOffset(const char* pch, float size = 16.0f);

glm::vec2 Measure(const s2::string &text, float size = 16.0f);

void BeginMeasureMode(float size = 16.0f);
glm::vec2 MeasureNow(const s2::string &text);
glm::vec2 MeasureNow(const s2::string &text, int len);
void EndMeasureMode();
};
}
9 changes: 8 additions & 1 deletion include/nimble/widgets/text.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ namespace na
private:
s2::string m_text;
Font* m_font = nullptr;
float m_fontSize = 16.0f;
float m_fontSize = 20.0f;
glm::ivec2 m_size;
glm::vec4 m_color = glm::vec4(0, 0, 0, 1);
glm::vec4 m_colorText = glm::vec4(1, 1, 1, 1);
glm::vec4 m_colorSelection = glm::vec4(0.2f, 0.2f, 1, 1);
bool m_multiline = false;

STB_TexteditState m_textEditStatePrev;
STB_TexteditState m_textEditState;
bool m_dragging = false;

Expand All @@ -32,6 +34,8 @@ namespace na
virtual void DoLayout(lay_context* l, lay_id parent);
virtual void Draw(NVGcontext* vg);

virtual void CheckTextEditStateChanged();

virtual void OnMouseDown(int button, const glm::ivec2 &point);
virtual void OnMouseMove(const glm::ivec2 &point);
virtual void OnMouseUp(int button, const glm::ivec2 &point);
Expand Down Expand Up @@ -61,6 +65,9 @@ namespace na
inline const glm::vec4 &GetTextColor() { return m_colorText; }
virtual void SetTextColor(const glm::vec4 &color);

inline const glm::vec4 &GetSelectionColor() { return m_colorSelection; }
virtual void SetSelectionColor(const glm::vec4 &color);

inline bool IsMultiline() { return m_multiline; }
virtual void SetMultiline(bool multiline);

Expand Down
4 changes: 3 additions & 1 deletion src/nimble/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,9 @@ void na::Application::CallbackMouseButton(int button, int action, int mods)
if (action == GLFW_PRESS) {
w->OnMouseDown(button, w->ToRelativePoint(m_lastCursorPos));
if (w->CanHaveFocus()) {
SetFocusWidget(w);
if (!w->HasFocus()) {
SetFocusWidget(w);
}
} else {
SetFocusWidget(nullptr);
}
Expand Down
21 changes: 20 additions & 1 deletion src/nimble/content/font.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ int na::Font::GetHandle()
return m_handle;
}

float na::Font::GetNextGlyphOffset(const char* pch, float size)
{
NVGcontext* vg = m_app->GetNVG();

BeginMeasureMode(size);

NVGglyphPosition pos[2];
nvgTextGlyphPositions(vg, 0, 0, pch, nullptr, pos, 2);

EndMeasureMode();

return pos[1].x;
}

glm::vec2 na::Font::Measure(const s2::string &text, float size)
{
NVGcontext* vg = m_app->GetNVG();
Expand Down Expand Up @@ -52,10 +66,15 @@ void na::Font::BeginMeasureMode(float size)
}

glm::vec2 na::Font::MeasureNow(const s2::string &text)
{
return MeasureNow(text, text.len());
}

glm::vec2 na::Font::MeasureNow(const s2::string &text, int len)
{
NVGcontext* vg = m_app->GetNVG();
float bounds[4];
nvgTextBounds(vg, 0, 0, text, nullptr, bounds);
nvgTextBounds(vg, 0, 0, text, (const char*)text + len, bounds);
return glm::vec2(bounds[2], bounds[3]);
}

Expand Down
112 changes: 96 additions & 16 deletions src/nimble/widgets/text.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,12 @@ static float TES_GetWidth(na::TextWidget* obj, int n, int i)
// Here, `n` is the position within the string that this row starts at
// We return the pixel distance between char (n + i) and (n + i + 1)

//TODO: Current implementation is probably inaccurate
//TODO: If this is a monospaced font, we could just return some constant

na::Font* font = obj->GetFont();
s2::string text = obj->GetText().substr(n, i + 1);
const char* sz = obj->GetText();

assert(font != nullptr);

glm::vec2 sizeSecond = font->MeasureNow(text);
text[text.len() - 1] = '\0';
glm::vec2 sizeFirst = font->MeasureNow(text);

return sizeSecond.x - sizeFirst.x;
return font->GetNextGlyphOffset(sz + n + i, obj->GetFontSize());
}

static bool TES_InsertChars(na::TextWidget* obj, int i, const char* sz, int n)
Expand Down Expand Up @@ -85,7 +78,7 @@ static bool TES_InsertChars(na::TextWidget* obj, int i, const char* sz, int n)
#define STB_TEXTEDIT_K_BACKSPACE STB_TEXTEDIT_K_KEYDOWN | GLFW_KEY_BACKSPACE
#define STB_TEXTEDIT_K_UNDO STB_TEXTEDIT_K_KEYDOWN | STB_TEXTEDIT_K_CONTROL | GLFW_KEY_Z
#define STB_TEXTEDIT_K_REDO STB_TEXTEDIT_K_KEYDOWN | STB_TEXTEDIT_K_CONTROL | GLFW_KEY_Y
//#define STB_TEXTEDIT_K_INSERT STB_TEXTEDIT_K_KEYDOWN | GLFW_KEY_INSERT
#define STB_TEXTEDIT_K_INSERT STB_TEXTEDIT_K_KEYDOWN | GLFW_KEY_INSERT
#define STB_TEXTEDIT_K_WORDLEFT STB_TEXTEDIT_K_KEYDOWN | STB_TEXTEDIT_K_CONTROL | GLFW_KEY_LEFT
#define STB_TEXTEDIT_K_WORDRIGHT STB_TEXTEDIT_K_KEYDOWN | STB_TEXTEDIT_K_CONTROL | GLFW_KEY_RIGHT

Expand Down Expand Up @@ -128,6 +121,7 @@ void na::TextWidget::Load(ContentNode &node)

//TODO: Invalidate text edit state when SetMultiline is called?
stb_textedit_initialize_state(&m_textEditState, m_multiline ? 1 : 0);
memcpy(&m_textEditStatePrev, &m_textEditState, sizeof(STB_TexteditState));
}

void na::TextWidget::DoLayout(lay_context* l, lay_id parent)
Expand Down Expand Up @@ -161,28 +155,100 @@ void na::TextWidget::Draw(NVGcontext* vg)
nvgStrokeColor(vg, nvgRGBAf(0.2f, 0.2f, 1.0f, 1.0f));
nvgStroke(vg);
}

nvgClosePath(vg);
}

if (m_colorText.a > 0.0f) {
nvgScissor(vg, (float)rect.x, (float)rect.y, (float)rect.z, (float)rect.w);

nvgFontFaceId(vg, m_font->GetHandle());
nvgFontSize(vg, m_fontSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);

float cursorX = 0;
float cursorY = 0;
float cursorWidth = 0;
float selStartX = 0;
float selEndX = 0;

float lastX = 0;
const int numPos = 10;
NVGglyphPosition pos[numPos + 1];
for (int i = 0; i < (int)m_text.len(); i += numPos) {
int npos = nvgTextGlyphPositions(vg, lastX, 0, (const char*)m_text + i, nullptr, pos, numPos + 1);

int cursorIndex = m_textEditState.cursor - i;
if (cursorIndex <= numPos && cursorIndex >= 0) {
if (cursorIndex == npos) {
cursorX = pos[cursorIndex - 1].maxx;
cursorWidth = 0;
} else {
cursorX = pos[cursorIndex].x;
cursorWidth = pos[cursorIndex].maxx - cursorX;
}
}

int selStartIndex = m_textEditState.select_start - i;
if (selStartIndex <= numPos && selStartIndex >= 0) {
if (selStartIndex == npos) {
selStartX = pos[selStartIndex - 1].maxx;
} else {
selStartX = pos[selStartIndex].x;
}
}

int selEndIndex = m_textEditState.select_end - i;
if (selEndIndex <= numPos && selEndIndex >= 0) {
if (selEndIndex == npos) {
selEndX = pos[selEndIndex - 1].maxx;
} else {
selEndX = pos[selEndIndex].x;
}
}

lastX = pos[numPos].x;
}

nvgFillColor(vg, nvgRGBAf(m_colorSelection.r, m_colorSelection.g, m_colorSelection.b, m_colorSelection.a));
nvgBeginPath(vg);
nvgRect(vg, (float)rect.x + selStartX, (float)rect.y, selEndX - selStartX, m_fontSize);
nvgFill(vg);
nvgClosePath(vg);

nvgFillColor(vg, nvgRGBAf(m_colorText.r, m_colorText.g, m_colorText.b, m_colorText.a));
nvgText(vg, (float)rect.x, (float)rect.y, m_text, nullptr);

float breakWidth = 99999.0f;
if (m_multiline) {
breakWidth = (float)rect.z;
if (HasFocus()) {
nvgBeginPath(vg);

if (m_textEditState.insert_mode == 0 || cursorWidth == 0) {
// Insert mode
nvgRect(vg, rect.x + cursorX, rect.y + cursorY, 1, m_fontSize);
} else {
// Overwrite mode
nvgRect(vg, rect.x + cursorX, rect.y + cursorY + m_fontSize, cursorWidth, 1);
}

nvgFill(vg);
nvgClosePath(vg);
}
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
nvgTextBox(vg, (float)rect.x, (float)rect.y, breakWidth, m_text, nullptr);
}

nvgRestore(vg);

Widget::Draw(vg);
}

void na::TextWidget::CheckTextEditStateChanged()
{
if (memcmp(&m_textEditStatePrev, &m_textEditState, sizeof(STB_TexteditState) - sizeof(StbUndoState)) == 0) {
return;
}
memcpy(&m_textEditStatePrev, &m_textEditState, sizeof(STB_TexteditState) - sizeof(StbUndoState));
InvalidateRendering();
}

void na::TextWidget::OnMouseDown(int button, const glm::ivec2 &point)
{
Widget::OnMouseDown(button, point);
Expand All @@ -194,6 +260,7 @@ void na::TextWidget::OnMouseDown(int button, const glm::ivec2 &point)
stb_textedit_click(this, &m_textEditState, (float)point.x, (float)point.y);

m_font->EndMeasureMode();
CheckTextEditStateChanged();
}
}

Expand All @@ -207,6 +274,7 @@ void na::TextWidget::OnMouseMove(const glm::ivec2 &point)
stb_textedit_drag(this, &m_textEditState, (float)point.x, (float)point.y);

m_font->EndMeasureMode();
CheckTextEditStateChanged();
}
}

Expand Down Expand Up @@ -251,6 +319,7 @@ void na::TextWidget::OnKeyPress(int key, int scancode, int mods)
stb_textedit_key(this, &m_textEditState, key);

m_font->EndMeasureMode();
CheckTextEditStateChanged();
}

void na::TextWidget::OnChar(unsigned int ch, int mods)
Expand All @@ -264,6 +333,7 @@ void na::TextWidget::OnChar(unsigned int ch, int mods)
stb_textedit_key(this, &m_textEditState, (int)ch);

m_font->EndMeasureMode();
CheckTextEditStateChanged();
}

void na::TextWidget::SetText(const s2::string &text)
Expand All @@ -272,6 +342,9 @@ void na::TextWidget::SetText(const s2::string &text)
InvalidateRendering();
}
m_text = text;

stb_textedit_clamp(this, &m_textEditState);
CheckTextEditStateChanged();
}

void na::TextWidget::SetFont(const s2::string &name)
Expand Down Expand Up @@ -315,6 +388,14 @@ void na::TextWidget::SetTextColor(const glm::vec4 &color)
m_colorText = color;
}

void na::TextWidget::SetSelectionColor(const glm::vec4 &color)
{
if (m_colorSelection != color) {
InvalidateRendering();
}
m_colorSelection = color;
}

void na::TextWidget::SetMultiline(bool multiline)
{
if (m_multiline != multiline) {
Expand All @@ -334,6 +415,5 @@ bool na::TextWidget::InsertChars(int i, const char* sz, int num)
{
m_text.insert(sz, i, num);
InvalidateRendering();
printf("Text: \"%s\"", m_text.c_str());
return true;
}

0 comments on commit 8fce166

Please sign in to comment.