From e32341b24bf4874be2d8bfc9664fbd13bf96fcf5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 7 Jan 2017 20:09:44 -0500 Subject: [PATCH] More work. --- common/attrstr.c | 17 ++++++++ common/uipriv.h | 4 ++ darwin/drawtext.m | 99 ++++++++++++++++++++++++++++++++++++++++++++++- ui_attrstr.h | 24 +++++++++--- 4 files changed, 137 insertions(+), 7 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 102d56eb..76a4214b 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -312,3 +312,20 @@ size_t attrstrUTF16LEn(uiAttributedString *s) { return s->u16len; } + +size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n) +{ + return s->u8tou16[n]; +} + +size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n) +{ + size_t *out; + size_t nbytes; + + nbytes = (s->u16len + 1) * sizeof (size_t); + *n = s->u16len; + out = (size_t *) uiAlloc(nbytes, "size_t[] (uiAttributedString)"); + memmove(out, s->u16tou8, nbytes); + return out; +} diff --git a/common/uipriv.h b/common/uipriv.h index 31d941ff..f979e4f6 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -64,9 +64,13 @@ struct graphemes { extern int graphemesTakesUTF16(void); extern struct graphemes *graphemes(void *s, size_t len); +// TODO split these into a separate header file? + // attrstr.c extern const uint16_t *attrstrUTF16(uiAttributedString *s); extern size_t attrstrUTF16LEn(uiAttributedString *s); +extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); +extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); // attrlist.c struct attrlist; diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a7a400f7..45e24470 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -15,6 +15,8 @@ struct uiDrawTextLayout { CGPathRef path; CTFrameRef frame; CFArrayRef lines; + size_t *u16tou8; + size_t nu16tou8; // TODO I don't like the casing of this name }; static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) @@ -112,11 +114,15 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto tl->lines = CTFrameGetLines(tl->frame); + // and finally copy the UTF-16 to UTF-8 index conversion table + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + uiFree(tl->u16tou8); // TODO release tl->lines? CFRelease(tl->frame); CFRelease(tl->path); @@ -174,7 +180,8 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start lr = getline(tl, line); range = CTLineGetStringRange(lr); - // TODO set start and end + *start = tl->u16tou8[range.location]; + *end = tl->u16tou8[range.location + range.length]; } void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) @@ -203,8 +210,96 @@ void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, i { } -uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *byteIndex, int *line) +static CGPoint *mkLineOrigins(uiDrawTextLayout *tl) { + CGPoint *origins; + CFRange range; + CFIndex i, n; + CTLine line; + CGFloat ascent; + + n = CFArrayGetCount(tl->lines); + range.location = 0; + range.length = n; + origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[]"); + CTFrameGetLineOrigins(tl->frame, range, origins); + for (i = 0; i < n; i++) { + line = getline(tl, i); + CTLineGetTypographicBounds(line, &ascent, NULL, NULL); + origins[i].y = tl->size.height - (origins[i].y + ascent); + } + return origins; +} + +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) +{ + CGPoint *mkLineOrigins; + CFIndex i, n; + CTLineRef line; + double firstYForLine; + CGFloat width, NULL, descent, leading; + CFRange range; + + n = CFArrayGetCount(tl->lines); + if (n == 0) { + // TODO fill result + return; + } + + origins = mkLineOrigins(tl); + if (y < 0) { + line = getline(tl, 0); + width = CTLineGetTypographicBounds(line, NULL, NULL, NULL); + i = 0; + } else { + firstYForLine = 0; + for (i = 0; i < n; i++) { + line = getline(tl, i); + width = CTLineGetTypographicBounds(line, NULL, &descent, &leading); + if (y < maxYForLine) + break; + firstYForLine = origins[i].y + descent + leading; + } + } + if (i == n) { + i--; + result->Line = i; + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; + } else { + result->Line = i; + result->YPosition = uiDrawTextLayoutHitTestPositionInside; + if (i == 0 && y < 0) + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; + } + result->InTrailingWhitespace = 0; + range = CTLineGetStringRange(line); + if (x < 0) { + result->Start = tl->u16tou8[range.location]; + result->End = result->Start; + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; + } else if (x > tl->size.width) { + result->Start = tl->u16tou8[range.location + range.length]; + result->End = result->Start; + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; + } else { + CGPoint pos; + CFIndex index; + + result->XPosition = uiDrawTextLa +youtHitTestPositionInside; + pos.x = x; + // TODO this isn't set properly in any of the fast-track cases + pos.y = y - firstYForLine; + index = CTLineGetStringIn +dexForPosition(line, pos); + if (index == kCFNotFound) { + // TODO + } + result->Pos = tl->u16tou8[index]; + // TODO compute the fractional offset + result->InTrailingWhitespace = x < origins[i].x || x >= (origins[i].x + width); + } + uiFree(origins); } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) diff --git a/ui_attrstr.h b/ui_attrstr.h index 1d0c4696..a9c6ccab 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -87,6 +87,7 @@ struct uiDrawFontDescriptor { typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +typedef struct uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTestResult; typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; struct uiDrawTextLayoutLineMetrics { @@ -102,10 +103,19 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; -_UI_ENUM(uiDrawTextLayoutHitTestResult) { - uiDrawTextLayoutHitTestResultNowhere, - uiDrawTextLayoutHitTestResultOnLineTrailingWhitespace, - uiDrawTextLayoutHitTestResultOnCharacter, +_UI_ENUM(uiDrawTextLayoutHitTestPosition) { + uiDrawTextLayoutHitTestPositionBefore, + uiDrawTextLayoutHitTestPositionInside, + uiDrawTextLayoutHitTestPositionAfter, +}; + +struct uiDrawTextLayoutHitTestResult { + size_t Pos; + int Line; + uiDrawTextLayoutHitTestPosition XPosition; + uiDrawTextLayoutHitTestPosition YPosition; + int InTrailingWhitespace; + double XFraction; }; struct uiDrawTextLayoutByteRangeRectangle { @@ -122,6 +132,10 @@ struct uiDrawTextLayoutByteRangeRectangle { // - allow creating a layout out of a substring // - allow marking compositon strings // - allow marking selections, even after creation +// - add the following functions: +// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) +// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) +// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) _UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width); _UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); @@ -132,7 +146,7 @@ _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, u // TODO redo this? remove it entirely? _UI_EXTERN void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height); // TODO partial offset? -_UI_EXTERN uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *byteIndex, int *line); +_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); _UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); // TODO draw only a line? // TODO other layout-specific attributes (alignment, wrapping, etc.)?