From e4ed1c337ba62a99e8eb3438c57ffba818bbe8f6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 18:38:44 -0500 Subject: [PATCH] And implemented the functions on Windows. Yeah I think I'll need cursor functions, perhaps. --- examples/drawtext/hittest.c | 6 ++- windows/drawtext.cpp | 100 +++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 8 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index efa80907..cacde315 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -160,8 +160,10 @@ static void mouse(uiAreaMouseEvent *e) &res); uiDrawFreeTextLayout(layout); - sprintf(labelText, "pos %zd line %d x position %s y position %s", - res.Pos, res.Line, + // urgh %zd is not supported by MSVC with sprintf() + // TODO get that part in test/ about having no other option + sprintf(labelText, "pos %d line %d x position %s y position %s", + (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); uiLabelSetText(caretLabel, labelText); diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 345723ce..ab63c0e1 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -5,15 +5,18 @@ // TODO // - consider the warnings about antialiasing in the PadWrite sample // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... +// - what happens if any nLines == 0? struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; UINT32 nLines; struct lineInfo *lineInfo; - // for converting DirectWrite indices to byte offsets + // for converting DirectWrite indices from/to byte offsets + size_t *u8tou16; + size_t nUTF8; size_t *u16tou8; - size_t nu16tou8; // TODO I don't like the casing of this name; is it even necessary? + size_t nUTF16; }; // TODO copy notes about DirectWrite DIPs being equal to Direct2D DIPs here @@ -43,7 +46,7 @@ static std::map dwriteStretches = { }; struct lineInfo { - size_t startPos; + size_t startPos; // in UTF-16 points size_t endPos; size_t newlineCount; double x; @@ -182,8 +185,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto computeLineInfo(tl); - // and finally copy the UTF-16 to UTF-8 index conversion table - tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + // and finally copy the UTF-8/UTF-16 index conversion tables + tl->u8tou16 = attrstrCopyUTF8ToUTF16(s, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nUTF16)); // TODO can/should this be moved elsewhere? uiFree(wDefaultFamily); @@ -193,6 +197,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); + uiFree(tl->u8tou16); uiFree(tl->lineInfo); tl->layout->Release(); tl->format->Release(); @@ -294,11 +299,94 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } +// TODO stretches space at the end of a line to the whole line void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { - // TODO + DWRITE_HIT_TEST_METRICS m; + size_t line; + double width, height; + BOOL trailing, inside; // crashes if I skip these :/ + HRESULT hr; + + hr = tl->layout->HitTestPoint(x, y, + &trailing, &inside, + &m); + if (hr != S_OK) + logHRESULT(L"error hit-testing IDWriteTextLayout", hr); + + // figure out what line this is + if (y < 0) + line = 0; + else + for (line = 0; line < tl->nLines; line++) + if (y >= tl->lineInfo[line].y && y < (tl->lineInfo[line].y + tl->lineInfo[line].height)) + break; + if (line == tl->nLines) // last position + line--; + + result->Pos = tl->u16tou8[m.textPosition]; + result->Line = line; + + uiDrawTextLayoutExtents(tl, &width, &height); + result->XPosition = uiDrawTextLayoutHitTestPositionInside; + if (x < 0) + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; + else if (x >= width) + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; + result->YPosition = uiDrawTextLayoutHitTestPositionInside; + if (y < 0) + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; + else if (y >= height) + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { + DWRITE_HIT_TEST_METRICS mstart, mend; + size_t line; + FLOAT x, y; // crashes if I skip these :/ + BOOL trailing, inside; // crashes if I skip these :/ + HRESULT hr; + + start = tl->u8tou16[start]; + end = tl->u8tou16[end]; + + // TODO explain why this is a leading hit + hr = tl->layout->HitTestTextPosition(start, FALSE, + &x, &y, + &mstart); + if (hr != S_OK) + logHRESULT(L"error getting rect of start position", hr); + + // figure out what line this is + for (line = 0; line < tl->nLines; line++) + if (start >= tl->lineInfo[line].startPos && start < tl->lineInfo[line].endPos) + break; + if (line == tl->nLines) // last position + line--; + if (end > tl->lineInfo[line].endPos) + end = tl->lineInfo[line].endPos; + + hr = tl->layout->HitTestTextPosition(end, FALSE, + &x, &y, + &mend); + if (hr != S_OK) + logHRESULT(L"error getting rect of end position", hr); + + r->X = mstart.left; + r->Y = tl->lineInfo[line].y; + r->Width = mend.left - mstart.left; + r->Height = tl->lineInfo[line].height; + + hr = tl->layout->HitTestPoint(r->X, r->Y, + &trailing, &inside, + &mstart); + if (hr != S_OK) + logHRESULT(L"TODO write this", hr); + // TODO also get the end pos just so we can have an accurate r->RealEnd + r->RealStart = mstart.textPosition; + r->RealEnd = end; + + r->RealStart = tl->u16tou8[r->RealStart]; + r->RealEnd = tl->u16tou8[r->RealEnd]; }