insert GlyphProperty.null in shouldGenerateGlyphs causes cursor misplacement problem

141 Views Asked by At

I am hiding the character * by inserting GlyphProperty.null then calling setGlyphs(_:properties:characterIndexes:font:forGlyphRange:) inside layoutManager(_:shouldGenerateGlyphs:properties:characterIndexes:font:forGlyphRange:) as described in the answer in https://stackoverflow.com/a/57697139/9568961, it works but after a certain sequence of text editing, the cursor starts to misplace, as shown in the gif https://github.com/dzAtZJU/Demos/blob/master/cursor_misplace.GIF?raw=true

GIF Description: When the second line becomes qq, I try to move the cursor to the end of first line, but it move directly to the beginning. I then try to delete characters, then the cursor move to the end correctly.

The sample project is here https://github.com/dzAtZJU/HideText.

Is it a bug? Have anyone run into this? How can I work around this?

1

There are 1 best solutions below

0
On

This is a year-old question, but for anyone struggling with the same problem, here's a solution.

My Markdown-like parser knows the ranges for both the line on which the cursor is now, and where it was previously. I'm calling hideAndShowMarkup whenever text view selection changes.

You need to invalidate current glyphs for both of the ranges, and then redraw them synchronously before updating insertion point. The most crucial step is invalidating layout for the range where you want to show Markdown symbols. Without that, your caret gets drawn into the wrong position.

Code is in Objective C, but should be easy to implement in Swift, too.

-(void)hideAndShowMarkup {  
    // Just for reference
    Line* line = currentLine;
    Line* prevLine = previouslySelectedLine;
    
    [self.layoutManager invalidateGlyphsForCharacterRange:line.range changeInLength:0 actualCharacterRange:nil];
    [self.layoutManager invalidateGlyphsForCharacterRange:prevLine.range changeInLength:0 actualCharacterRange:nil];
    [self.layoutManager invalidateLayoutForCharacterRange:line.range actualCharacterRange:nil];
        
    if (NSGraphicsContext.currentContext) {
        [self.layoutManager drawGlyphsForGlyphRange:line.range atPoint:self.frame.origin];
        [self.layoutManager drawGlyphsForGlyphRange:prevLine.range atPoint:self.frame.origin];
    }
    
    [self updateInsertionPointStateAndRestartTimer:NO];
}