So I am trying to write a custom NSView with text in it but not using NSTextView (since it will be more than just text). I can use Core Text to draw text, but now I also need to be able to do hit-testing and drawing of the caret and selection rects.
The naive way of drawing carets and selections, as shown by various Apple samples, is to get the typographic bounds of the various CTLines encompassing the desired string range (using CTLineGetBoundsWithOptions(0)
instead of CTLineGetTypographicBounds()
due to various errors in the latter) and filling the resultant rects. Unfortunately, as soon as there is a paragraph style of any sort (be it paragraph spacing, line spacing, or line height) this stops working because paragraph styles are not included in the typographic bounds. If you select text that spans multiple lines, you'll see that somewhat iconic white gap between the lines! And this still does not answer the question of how to know what line to use if I am clicking on the whitespace between lines.
After fighting over typographic bounds rects overlapping or having gaps between them due to how Core Text does snapping metrics points to integers by default, I noticed that if I call CTFrameGetLineOrigins()
I can determine the height of line i + 1
by computing origins[i].y - origins[i + 1].y
; the height of line 0 is the height of the frame minus the height of the combined other lines. The use of i + 1
here is important; it deals with odd scenarios caused by fonts that have the space height different from other character heights.
However, with this technique, I cannot differentiate between spacing above a line and spacing below a line, whether that space is paragraph or line space. All spacing is treated as being above the line, and if multiple spacing modes are used, they just combine. I have a small program that you can use to experiment with this for yourself; click the "Baseline Diffs" checkbox to shade in line heights.
So the question is: what am I missing that I can't figure out how tall a line really is and where its baseline is relative to its height, regardless of the paragraph style? Or do I need to reimplement the internal logic of Core Text myself if I want to do this? I do have a lot of the pieces together, but a lot of it is highly conditional...
And what happens on the last paragraph? Should I simulate the trailing space there?
The solution needs to run on OS X 10.8 or newer.
Thanks.