// Public method to create a rect for a given range in the text contents // Called by our EditableTextRange to implement the required // UITextInput:firstRectForRange method public CGRect FirstRect(NSRange range) { int index = (int)range.Location; // Iterate over our CTLines, looking for the line that encompasses the given range var lines = frame.GetLines(); for (int i = 0; i < lines.Length; i++) { CTLine line = lines [i]; NSRange lineRange = line.StringRange; int localIndex = index - (int)lineRange.Location; if (localIndex >= 0 && localIndex < lineRange.Length) { // For this sample, we use just the first line that intersects range int finalIndex = (int)Math.Min(lineRange.Location + lineRange.Length, range.Location + range.Length); // Create a rect for the given range within this line nfloat xStart = line.GetOffsetForStringIndex(index); nfloat xEnd = line.GetOffsetForStringIndex(finalIndex); var origins = new CGPoint [lines.Length]; frame.GetLineOrigins(new NSRange(i, 0), origins); nfloat ascent, descent, leading; line.GetTypographicBounds(out ascent, out descent, out leading); return(new CGRect(xStart, origins [0].Y - descent, xEnd - xStart, ascent + descent)); } } return(CGRect.Empty); }
// Helper method for drawing the current selection range (as a simple filled rect) void DrawRangeAsSelection(NSRange selectionRange) { // If not in editing mode, we do not draw selection rects if (!IsEditing) { return; } // If selection range empty, do not draw if (selectionRange.Length == 0 || selectionRange.Location == NSRange.NotFound) { return; } // set the fill color to the selection color SelectionColor.SetFill(); // Iterate over the lines in our CTFrame, looking for lines that intersect // with the given selection range, and draw a selection rect for each intersection var lines = frame.GetLines(); for (int i = 0; i < lines.Length; i++) { CTLine line = lines [i]; NSRange lineRange = line.StringRange; NSRange range = new NSRange(lineRange.Location, lineRange.Length); NSRange intersection = RangeIntersection(range, selectionRange); if (intersection.Location != NSRange.NotFound && intersection.Length > 0) { // The text range for this line intersects our selection range nfloat xStart = line.GetOffsetForStringIndex(intersection.Location); nfloat xEnd = line.GetOffsetForStringIndex(intersection.Location + intersection.Length); var origin = new CGPoint [lines.Length]; // Get coordinate and bounds information for the intersection text range frame.GetLineOrigins(new NSRange(i, 0), origin); nfloat ascent, descent, leading; line.GetTypographicBounds(out ascent, out descent, out leading); // Create a rect for the intersection and draw it with selection color CGRect selectionRect = new CGRect(xStart, origin [0].Y - descent, xEnd - xStart, ascent + descent); UIGraphics.RectFill(selectionRect); } } }
// Public method to determine the CGRect for the insertion point or selection, used // when creating or updating our SimpleCaretView instance public CGRect CaretRect(int index) { var lines = frame.GetLines(); // Special case, no text if (text.Length == 0) { CGPoint origin = new CGPoint(Bounds.GetMinX(), Bounds.GetMinY() - font.Leading); // Note: using fabs() for typically negative descender from fonts return(new CGRect(origin.X, origin.Y - (nfloat)Math.Abs(font.Descender), 3, font.Ascender + (nfloat)Math.Abs(font.Descender))); } // Special case, insertion point at final position in text after newline if (index == text.Length && text.EndsWith("\n")) { CTLine line = lines [lines.Length - 1]; NSRange range = line.StringRange; nfloat xPos = line.GetOffsetForStringIndex(range.Location); var origins = new CGPoint [lines.Length]; nfloat ascent, descent, leading; line.GetTypographicBounds(out ascent, out descent, out leading); frame.GetLineOrigins(new NSRange(lines.Length - 1, 0), origins); // Place point after last line, including any font leading spacing if applicable origins[0].Y -= font.Leading; return(new CGRect(xPos, origins [0].Y - descent, 3, ascent + descent)); } // Regular case, caret somewhere within our text content range for (int i = 0; i < lines.Length; i++) { CTLine line = lines [i]; NSRange range = line.StringRange; int localIndex = index - (int)range.Location; if (localIndex >= 0 && localIndex <= range.Length) { // index is in the range for this line nfloat xPos = line.GetOffsetForStringIndex(index); var origins = new CGPoint [lines.Length]; nfloat ascent, descent, leading; line.GetTypographicBounds(out ascent, out descent, out leading); frame.GetLineOrigins(new NSRange(i, 0), origins); // Make a small "caret" rect at the index position return(new CGRect(xPos, origins [0].Y - descent, 3, ascent + descent)); } } return(CGRect.Empty); }