diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-20 13:58:11 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-20 13:58:11 +0300 |
commit | 10f44efd277855761f69687193d6d5250650186d (patch) | |
tree | f1adf38c4d215b7466952c2ba1c18692675cf191 /src/ui | |
parent | dc1528e89d48947780e00fc1a49ce57cccdfbfe5 (diff) |
Text: Hit testing during a text run
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/text.c | 47 | ||||
-rw-r--r-- | src/ui/text.h | 4 |
2 files changed, 41 insertions, 10 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index 49b3f0cd..0d101813 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -1376,12 +1376,15 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1376 | iAttributedText attrText; | 1376 | iAttributedText attrText; |
1377 | init_AttributedText(&attrText, args->text, args->maxLen, d, get_Color(args->color), | 1377 | init_AttributedText(&attrText, args->text, args->maxLen, d, get_Color(args->color), |
1378 | args->baseDir); | 1378 | args->baseDir); |
1379 | if (args->wrap) { | 1379 | iWrapText *wrap = args->wrap; |
1380 | args->wrap->baseDir = attrText.isBaseRTL ? -1 : +1; | 1380 | if (wrap) { |
1381 | wrap->baseDir = attrText.isBaseRTL ? -1 : +1; | ||
1381 | /* TODO: Duplicated args? */ | 1382 | /* TODO: Duplicated args? */ |
1382 | iAssert(equalRange_Rangecc(args->wrap->text, args->text)); | 1383 | iAssert(equalRange_Rangecc(wrap->text, args->text)); |
1383 | /* Initialize the wrap range. */ | 1384 | /* Initialize the wrap range. */ |
1384 | args->wrap->wrapRange_ = args->text; | 1385 | wrap->wrapRange_ = args->text; |
1386 | wrap->hitChar_out = NULL; | ||
1387 | wrap->hitGlyphNormX_out = 0.0f; | ||
1385 | } | 1388 | } |
1386 | const iChar *logicalText = constData_Array(&attrText.logical); | 1389 | const iChar *logicalText = constData_Array(&attrText.logical); |
1387 | const iChar *visualText = constData_Array(&attrText.visual); | 1390 | const iChar *visualText = constData_Array(&attrText.visual); |
@@ -1423,8 +1426,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1423 | iRangei wrapPosRange = { 0, textLen }; | 1426 | iRangei wrapPosRange = { 0, textLen }; |
1424 | int wrapResumePos = textLen; /* logical position where next line resumes */ | 1427 | int wrapResumePos = textLen; /* logical position where next line resumes */ |
1425 | size_t wrapResumeRunIndex = runCount; /* index of run where next line resumes */ | 1428 | size_t wrapResumeRunIndex = runCount; /* index of run where next line resumes */ |
1426 | const int layoutBound = (args->wrap ? args->wrap->maxWidth : 0); | 1429 | const int layoutBound = (wrap ? wrap->maxWidth : 0); |
1427 | iBool isFirst = iTrue; | 1430 | iBool isFirst = iTrue; |
1431 | const iBool checkHitPoint = wrap && !isEqual_I2(wrap->hitPoint, zero_I2()); | ||
1428 | while (!isEmpty_Range(&wrapRuns)) { | 1432 | while (!isEmpty_Range(&wrapRuns)) { |
1429 | if (isFirst) { | 1433 | if (isFirst) { |
1430 | isFirst = iFalse; | 1434 | isFirst = iFalse; |
@@ -1434,7 +1438,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1434 | } | 1438 | } |
1435 | float wrapAdvance = 0.0f; | 1439 | float wrapAdvance = 0.0f; |
1436 | /* First we need to figure out how much text fits on the current line. */ | 1440 | /* First we need to figure out how much text fits on the current line. */ |
1437 | if (args->wrap && args->wrap->maxWidth > 0) { | 1441 | if (wrap && (wrap->maxWidth > 0 || checkHitPoint)) { |
1442 | const iBool isHitPointOnThisLine = (checkHitPoint && wrap->hitPoint.y >= yCursor && | ||
1443 | wrap->hitPoint.y < yCursor + d->height); | ||
1438 | float breakAdvance = -1.0f; | 1444 | float breakAdvance = -1.0f; |
1439 | iAssert(wrapPosRange.end == textLen); | 1445 | iAssert(wrapPosRange.end == textLen); |
1440 | /* Determine ends of wrapRuns and wrapVisRange. */ | 1446 | /* Determine ends of wrapRuns and wrapVisRange. */ |
@@ -1463,6 +1469,11 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1463 | if (logPos < wrapPosRange.start || logPos >= wrapPosRange.end) { | 1469 | if (logPos < wrapPosRange.start || logPos >= wrapPosRange.end) { |
1464 | continue; | 1470 | continue; |
1465 | } | 1471 | } |
1472 | /* Check if the hit point is on the left side of this line. */ | ||
1473 | if (isHitPointOnThisLine && !wrap->hitChar_out && wrap->hitPoint.x < orig.x) { | ||
1474 | wrap->hitChar_out = sourcePtr_AttributedText_(&attrText, logPos); | ||
1475 | wrap->hitGlyphNormX_out = 0.0f; | ||
1476 | } | ||
1466 | const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId); | 1477 | const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId); |
1467 | const int glyphFlags = hb_glyph_info_get_glyph_flags(info); | 1478 | const int glyphFlags = hb_glyph_info_get_glyph_flags(info); |
1468 | const float xOffset = run->font->xScale * buf->glyphPos[i].x_offset; | 1479 | const float xOffset = run->font->xScale * buf->glyphPos[i].x_offset; |
@@ -1489,8 +1500,16 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1489 | breakAdvance = wrapAdvance; | 1500 | breakAdvance = wrapAdvance; |
1490 | //} | 1501 | //} |
1491 | } | 1502 | } |
1503 | if (isHitPointOnThisLine) { | ||
1504 | if (wrap->hitPoint.x >= orig.x + wrapAdvance && | ||
1505 | wrap->hitPoint.x < orig.x + wrapAdvance + xAdvance) { | ||
1506 | wrap->hitChar_out = sourcePtr_AttributedText_(&attrText, logPos); | ||
1507 | wrap->hitGlyphNormX_out = (wrap->hitPoint.x - wrapAdvance) / xAdvance; | ||
1508 | } | ||
1509 | } | ||
1492 | /* Out of room? */ | 1510 | /* Out of room? */ |
1493 | if (wrapAdvance + xOffset + glyph->d[0].x + glyph->rect[0].size.x > | 1511 | if (wrap->maxWidth > 0 && |
1512 | wrapAdvance + xOffset + glyph->d[0].x + glyph->rect[0].size.x > | ||
1494 | args->wrap->maxWidth) { | 1513 | args->wrap->maxWidth) { |
1495 | if (safeBreakPos >= 0) { | 1514 | if (safeBreakPos >= 0) { |
1496 | wrapPosRange.end = safeBreakPos; | 1515 | wrapPosRange.end = safeBreakPos; |
@@ -1509,8 +1528,10 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1509 | breakAdvance = wrapAdvance; | 1528 | breakAdvance = wrapAdvance; |
1510 | } | 1529 | } |
1511 | wrapResumePos = wrapPosRange.end; | 1530 | wrapResumePos = wrapPosRange.end; |
1512 | while (wrapResumePos < textLen && isSpace_Char(logicalText[wrapResumePos])) { | 1531 | if (args->wrap->mode != anyCharacter_WrapTextMode) { |
1513 | wrapResumePos++; /* skip space */ | 1532 | while (wrapResumePos < textLen && isSpace_Char(logicalText[wrapResumePos])) { |
1533 | wrapResumePos++; /* skip space */ | ||
1534 | } | ||
1514 | } | 1535 | } |
1515 | wrapRuns.end = runIndex + 1; /* still includes this run */ | 1536 | wrapRuns.end = runIndex + 1; /* still includes this run */ |
1516 | wrapResumeRunIndex = runIndex; /* ...but continue from the same one */ | 1537 | wrapResumeRunIndex = runIndex; /* ...but continue from the same one */ |
@@ -1527,6 +1548,11 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1527 | } | 1548 | } |
1528 | } | 1549 | } |
1529 | } | 1550 | } |
1551 | if (isHitPointOnThisLine && wrap->hitPoint.x >= orig.x + wrapAdvance) { | ||
1552 | /* On the right side. */ | ||
1553 | wrap->hitChar_out = sourcePtr_AttributedText_(&attrText, iMax(0, wrapResumePos - 1)); | ||
1554 | wrap->hitGlyphNormX_out = 1.0f; | ||
1555 | } | ||
1530 | } | 1556 | } |
1531 | else { | 1557 | else { |
1532 | /* Not wrapped so everything fits! Calculate total advance without wrapping. */ | 1558 | /* Not wrapped so everything fits! Calculate total advance without wrapping. */ |
@@ -1589,7 +1615,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1589 | // } | 1615 | // } |
1590 | } | 1616 | } |
1591 | /* Make a callback for each wrapped line. */ | 1617 | /* Make a callback for each wrapped line. */ |
1592 | if (!notify_WrapText_(args->wrap, | 1618 | if (wrap && wrap->wrapFunc && |
1619 | !notify_WrapText_(args->wrap, | ||
1593 | sourcePtr_AttributedText_(&attrText, wrapResumePos), | 1620 | sourcePtr_AttributedText_(&attrText, wrapResumePos), |
1594 | origin, | 1621 | origin, |
1595 | iRound(wrapAdvance), | 1622 | iRound(wrapAdvance), |
diff --git a/src/ui/text.h b/src/ui/text.h index d4f820d4..a82da22c 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -212,6 +212,10 @@ struct Impl_WrapText { | |||
212 | iBool (*wrapFunc)(iWrapText *, iRangecc wrappedText, int origin, int advance, iBool isBaseRTL); | 212 | iBool (*wrapFunc)(iWrapText *, iRangecc wrappedText, int origin, int advance, iBool isBaseRTL); |
213 | void * context; | 213 | void * context; |
214 | int baseDir; /* set to +1 for LTR, -1 for RTL */ | 214 | int baseDir; /* set to +1 for LTR, -1 for RTL */ |
215 | iInt2 hitPoint; | ||
216 | /* output */ | ||
217 | const char *hitChar_out; | ||
218 | float hitGlyphNormX_out; /* normalized X inside the glyph */ | ||
215 | /* internal */ | 219 | /* internal */ |
216 | iRangecc wrapRange_; | 220 | iRangecc wrapRange_; |
217 | }; | 221 | }; |