size_t NvgFont::get_cursor_location(const std::string & text, float fontSize, int xCoord) const { std::vector<NVGglyphPosition> positions(text.size()); nvgFontSize(nvg, fontSize); nvgFontFaceId(nvg, id); nvgTextAlign(nvg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP); positions.resize(nvgTextGlyphPositions(nvg, 0, 0, text.data(), text.data() + (int) text.size(), positions.data(), (int) positions.size())); for (size_t i = 0; i<positions.size(); ++i) { if(xCoord < positions[i].maxx) return i; } return positions.size(); }
int Font::getCursorPosition(const std::string & text, float fontSize, int xCoord) const { std::vector<NVGglyphPosition> positions(text.size()); nvgFontSize(nvg, fontSize); nvgFontFaceId(nvg, handle); nvgTextAlign(nvg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP); positions.resize( nvgTextGlyphPositions(nvg, 0, 0, text.data(), text.data() + text.size(), positions.data(), (int) positions.size())); for (size_t i = 0; i < positions.size(); ++i) { if (xCoord < positions[i].maxx) { return static_cast<int>(i); } } return static_cast<int>(positions.size()); }
void Caret::updatePosFromIdx(NVGcontext *ctx) { float lineh; nvgTextMetrics(ctx, nullptr, nullptr, &lineh); const int maxGlyphs = 1024; NVGglyphPosition glyphs[maxGlyphs]; const std::string& textLine = mConsole->mBuffer[mIdx.y()]; float textBound[4]; nvgTextBounds(ctx, 0, 0, textLine.c_str(), nullptr, textBound); int nglyphs = nvgTextGlyphPositions(ctx, 0, 0, textLine.c_str(), nullptr, glyphs, maxGlyphs); //update carete position now mPos.y() = mIdx.y() * lineh; mPos.x() = cursorIndex2Position(mIdx.x(), textBound[2], glyphs, nglyphs); }
void Caret::onClick(NVGcontext *ctx, float lineh, Vector2i clickPos){ if (clickPos.x() > -1) { mIdx.y() = (int) clickPos.y()/lineh; //std::cout << "mPos = " << mConsole->mPos.x() << "," << mConsole->mPos.y() << std::endl; if(mIdx.y() < (int)mConsole->mBuffer.size()){ //std::cout << mBuffer.at(mCursorPosY)<< std::endl; double time = glfwGetTime(); if (time - mLastClick < 0.25) { /* Double-click: select a line */ int linew = mConsole->mSize.x(); mSelectionIdx = Vector2i(0,(int) clickPos.y()/lineh); mSelectionPos = Vector2i(0,mSelectionIdx.y()*lineh); mIdx = Vector2i(mConsole->mBuffer[mSelectionIdx.y()].size(), mSelectionIdx.y()); mPos = Vector2i(linew, mSelectionPos.y()); mSelectionState = true; std::cout << "Sel : " << mSelectionPos.x() << "," << mSelectionPos.y() << std::endl; std::cout << "Pos : " << mPos.x() << "," << mPos.y() << std::endl; } else { const int maxGlyphs = 1024; NVGglyphPosition glyphs[maxGlyphs]; const std::string& textLine = mConsole->mBuffer[mIdx.y()]; float textBound[4]; nvgTextBounds(ctx, 0, 0, textLine.c_str(), nullptr, textBound); int nglyphs = nvgTextGlyphPositions(ctx, 0, 0, textLine.c_str(), nullptr, glyphs, maxGlyphs); mIdx.x() = position2CursorIndex(clickPos.x(), textBound[2], glyphs, nglyphs); //update carete position now mPos.y() = mIdx.y() * lineh; mPos.x() = cursorIndex2Position(mIdx.x(), textBound[2], glyphs, nglyphs); } mLastClick = time; } } }
void drawParagraph(struct NVGcontext* vg, float x, float y, float width, float height, float mx, float my) { struct NVGtextRow rows[3]; struct NVGglyphPosition glyphs[100]; const char* text = "This is longer chunk of text.\n \n Would have used lorem ipsum but she was busy jumping over the lazy dog with the fox and all the men who came to the aid of the party."; const char* start; const char* end; int nrows, i, nglyphs, j, lnum = 0; float lineh; float caretx, px; float bounds[4]; float gx = 0.0f, gy = 0.0f; int gutter = 0; NVG_NOTUSED(height); nvgSave(vg); nvgFontSize(vg, 18.0f); nvgFontFace(vg, "sans"); nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); nvgTextMetrics(vg, NULL, NULL, &lineh); // The text break API can be used to fill a large buffer of rows, // or to iterate over the text just few lines (or just one) at a time. // The "next" variable of the last returned item tells where to continue. start = text; end = text + strlen(text); for (nrows = nvgTextBreakLines(vg, start, end, width, rows, 3); 0 != nrows; nrows = nvgTextBreakLines(vg, start, end, width, rows, 3) ) { for (i = 0; i < nrows; i++) { struct NVGtextRow* row = &rows[i]; int hit = mx > x && mx < (x+width) && my >= y && my < (y+lineh); nvgBeginPath(vg); nvgFillColor(vg, nvgRGBA(255,255,255,hit?64:8) ); nvgRect(vg, x, y, row->width, lineh); nvgFill(vg); nvgFillColor(vg, nvgRGBA(255,255,255,255) ); nvgText(vg, x, y, row->start, row->end); if (hit) { caretx = (mx < x+row->width/2) ? x : x+row->width; px = x; nglyphs = nvgTextGlyphPositions(vg, x, y, row->start, row->end, glyphs, 100); for (j = 0; j < nglyphs; j++) { float x0 = glyphs[j].x; float x1 = (j+1 < nglyphs) ? glyphs[j+1].x : x+row->width; float tgx = x0 * 0.3f + x1 * 0.7f; if (mx >= px && mx < tgx) caretx = glyphs[j].x; px = tgx; } nvgBeginPath(vg); nvgFillColor(vg, nvgRGBA(255,192,0,255) ); nvgRect(vg, caretx, y, 1, lineh); nvgFill(vg); gutter = lnum+1; gx = x - 10; gy = y + lineh/2; } lnum++; y += lineh; } // Keep going... start = rows[nrows-1].next; } if (gutter) { char txt[16]; bx::snprintf(txt, sizeof(txt), "%d", gutter); nvgFontSize(vg, 13.0f); nvgTextAlign(vg, NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); nvgTextBounds(vg, gx,gy, txt, NULL, bounds); nvgBeginPath(vg); nvgFillColor(vg, nvgRGBA(255,192,0,255) ); nvgRoundedRect(vg , bx::fround(bounds[0])-4.0f , bx::fround(bounds[1])-2.0f , bx::fround(bounds[2]-bounds[0])+8.0f , bx::fround(bounds[3]-bounds[1])+4.0f , (bx::fround(bounds[3]-bounds[1])+4.0f)/2.0f-1.0f ); nvgFill(vg); nvgFillColor(vg, nvgRGBA(32,32,32,255) ); nvgText(vg, gx,gy, txt, NULL); } y += 20.0f; nvgFontSize(vg, 13.0f); nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); nvgTextLineHeight(vg, 1.2f); nvgTextBoxBounds(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL, bounds); nvgBeginPath(vg); nvgFillColor(vg, nvgRGBA(220,220,220,255) ); nvgRoundedRect(vg , bx::fround(bounds[0]-2.0f) , bx::fround(bounds[1]-2.0f) , bx::fround(bounds[2]-bounds[0])+4.0f , bx::fround(bounds[3]-bounds[1])+4.0f , 3.0f ); px = float( (int)( (bounds[2]+bounds[0])/2) ); nvgMoveTo(vg, px,bounds[1] - 10); nvgLineTo(vg, px+7,bounds[1]+1); nvgLineTo(vg, px-7,bounds[1]+1); nvgFill(vg); nvgFillColor(vg, nvgRGBA(0,0,0,220) ); nvgTextBox(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL); nvgRestore(vg); }
void TextBox::draw(NVGcontext* ctx) { Widget::draw(ctx); NVGpaint bg = nvgBoxGradient(ctx, mPos.x() + 1, mPos.y() + 1 + 1.0f, mSize.x() - 2, mSize.y() - 2, 3, 4, Color(255, 32), Color(32, 32)); NVGpaint fg1 = nvgBoxGradient(ctx, mPos.x() + 1, mPos.y() + 1 + 1.0f, mSize.x() - 2, mSize.y() - 2, 3, 4, Color(150, 32), Color(32, 32)); NVGpaint fg2 = nvgBoxGradient(ctx, mPos.x() + 1, mPos.y() + 1 + 1.0f, mSize.x() - 2, mSize.y() - 2, 3, 4, nvgRGBA(255, 0, 0, 100), nvgRGBA(255, 0, 0, 50)); nvgBeginPath(ctx); nvgRoundedRect(ctx, mPos.x() + 1, mPos.y() + 1 + 1.0f, mSize.x() - 2, mSize.y() - 2, 3); if(mEditable && focused()) mValidFormat ? nvgFillPaint(ctx, fg1) : nvgFillPaint(ctx, fg2); else nvgFillPaint(ctx, bg); nvgFill(ctx); nvgBeginPath(ctx); nvgRoundedRect(ctx, mPos.x() + 0.5f, mPos.y() + 0.5f, mSize.x() - 1, mSize.y() - 1, 2.5f); nvgStrokeColor(ctx, Color(0, 48)); nvgStroke(ctx); nvgFontSize(ctx, fontSize()); nvgFontFace(ctx, "sans"); Vector2i drawPos(mPos.x(), mPos.y() + mSize.y() * 0.5f + 1); float xSpacing = mSize.y() * 0.3f; float unitWidth = 0; if (mUnitsImage > 0) { int w, h; nvgImageSize(ctx, mUnitsImage, &w, &h); float unitHeight = mSize.y() * 0.4f; unitWidth = w * unitHeight / h; NVGpaint imgPaint = nvgImagePattern( ctx, mPos.x() + mSize.x() - xSpacing - unitWidth, drawPos.y() - unitHeight * 0.5f, unitWidth, unitHeight, 0, mUnitsImage, mEnabled ? 0.7f : 0.35f); nvgBeginPath(ctx); nvgRect(ctx, mPos.x() + mSize.x() - xSpacing - unitWidth, drawPos.y() - unitHeight * 0.5f, unitWidth, unitHeight); nvgFillPaint(ctx, imgPaint); nvgFill(ctx); unitWidth += 2; } else if (!mUnits.empty()) { unitWidth = nvgTextBounds(ctx, 0, 0, mUnits.c_str(), nullptr, nullptr); nvgFillColor(ctx, Color(255, mEnabled ? 64 : 32)); nvgTextAlign(ctx, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE); nvgText(ctx, mPos.x() + mSize.x() - xSpacing, drawPos.y(), mUnits.c_str(), nullptr); unitWidth += 2; } switch (mAlignment) { case Alignment::Left: nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE); drawPos.x() += xSpacing; break; case Alignment::Right: nvgTextAlign(ctx, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE); drawPos.x() += mSize.x() - unitWidth - xSpacing; break; case Alignment::Center: nvgTextAlign(ctx, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); drawPos.x() += mSize.x() * 0.5f; break; } nvgFontSize(ctx, fontSize()); nvgFillColor(ctx, mEnabled ? mTheme->mTextColor : mTheme->mDisabledTextColor); // clip visible text area float clipX = mPos.x() + xSpacing - 1.0f; float clipY = mPos.y() + 1.0f; float clipWidth = mSize.x() - unitWidth - 2 * xSpacing + 2.0f; float clipHeight = mSize.y() - 3.0f; nvgScissor(ctx, clipX, clipY, clipWidth, clipHeight); Vector2i oldDrawPos(drawPos); drawPos.x() += mTextOffset; if (mCommitted) { nvgText(ctx, drawPos.x(), drawPos.y(), mValue.c_str(), nullptr); } else { const int maxGlyphs = 1024; NVGglyphPosition glyphs[maxGlyphs]; float textBound[4]; nvgTextBounds(ctx, drawPos.x(), drawPos.y(), mValueTemp.c_str(), nullptr, textBound); float lineh = textBound[3] - textBound[1]; // find cursor positions int nglyphs = nvgTextGlyphPositions(ctx, drawPos.x(), drawPos.y(), mValueTemp.c_str(), nullptr, glyphs, maxGlyphs); updateCursor(ctx, textBound[2], glyphs, nglyphs); // compute text offset int prevCPos = mCursorPos > 0 ? mCursorPos - 1 : 0; int nextCPos = mCursorPos < nglyphs ? mCursorPos + 1 : nglyphs; float prevCX = cursorIndex2Position(prevCPos, textBound[2], glyphs, nglyphs); float nextCX = cursorIndex2Position(nextCPos, textBound[2], glyphs, nglyphs); if (nextCX > clipX + clipWidth) mTextOffset -= nextCX - (clipX + clipWidth) + 1; if (prevCX < clipX) mTextOffset += clipX - prevCX + 1; drawPos.x() = oldDrawPos.x() + mTextOffset; // draw text with offset nvgText(ctx, drawPos.x(), drawPos.y(), mValueTemp.c_str(), nullptr); nvgTextBounds(ctx, drawPos.x(), drawPos.y(), mValueTemp.c_str(), nullptr, textBound); // recompute cursor positions nglyphs = nvgTextGlyphPositions(ctx, drawPos.x(), drawPos.y(), mValueTemp.c_str(), nullptr, glyphs, maxGlyphs); if (mCursorPos > -1) { if (mSelectionPos > -1) { float caretx = cursorIndex2Position(mCursorPos, textBound[2], glyphs, nglyphs); float selx = cursorIndex2Position(mSelectionPos, textBound[2], glyphs, nglyphs); if (caretx > selx) std::swap(caretx, selx); // draw selection nvgBeginPath(ctx); nvgFillColor(ctx, nvgRGBA(255, 255, 255, 80)); nvgRect(ctx, caretx, drawPos.y() - lineh * 0.5f, selx - caretx, lineh); nvgFill(ctx); } float caretx = cursorIndex2Position(mCursorPos, textBound[2], glyphs, nglyphs); // draw cursor nvgBeginPath(ctx); nvgMoveTo(ctx, caretx, drawPos.y() - lineh * 0.5f); nvgLineTo(ctx, caretx, drawPos.y() + lineh * 0.5f); nvgStrokeColor(ctx, nvgRGBA(255, 192, 0, 255)); nvgStrokeWidth(ctx, 1.0f); nvgStroke(ctx); } } nvgResetScissor(ctx); }