2

I am working on an NSTextView subclass and now need to draw text insertion points by myself during the formal NSTextView's insertion point timer stops. I succeed to draw my own, but could not find the way to obtain the time interval for the text insertion point blinking.

According to my observation on NSTextView, an NSTextView (subclass) manages the cursor blink using a private NSTimer, but this timer deactivates when the textView has only nonempty selections (because there is no insertion point to draw) or while dragging. Therefore, I need to have my own timer with a proper interval to draw my fake insertion points.

Digging the source code of the ancient OpenStep, the magic number for it seems to be 0.5 seconds. However, nowadays, users can customize the interval or even disable blinking. Likewise, the system also may change the interval for specific situations or in the future. I'd like to respect those preferences but I could not find the proper way for it.

Does anyone know how to get it (without accessing private API)?


What I do so far is:

private struct BlinkPeriod {

    var on: Int
    var off: Int
}


private extension UserDefaults {

    var textInsertionPointBlinkPeriod: BlinkPeriod {

        let onPeriod = self.integer(forKey: "NSTextInsertionPointBlinkPeriodOn")
        let offPeriod = self.integer(forKey: "NSTextInsertionPointBlinkPeriodOff")

        return BlinkPeriod(on: (onPeriod > 0) ? onPeriod : 500,
                           off: (offPeriod > 0) ? offPeriod : 500)
    }

}

But I feel it's dirty and no idea when the system changed the period :/

1024jp
  • 2,058
  • 1
  • 16
  • 25
  • Please tell us more about "need to draw text insertion points by myself during the formal NSTextView's insertion point timer stops". – Willeke Jan 05 '19 at 12:10
  • I added the second paragraph to my post to describe the timer. I need to draw fake insertion points in such situations. – 1024jp Jan 05 '19 at 13:37

1 Answers1

-1

Here's my solution:

class NSTextViewExt : NSTextView {
  public var insertionPointInterval: Double?;

  override func drawInsertionPoint(in rect: NSRect, color: NSColor, turnedOn flag: Bool) {
    super.drawInsertionPoint(in: rect, color: color, turnedOn: flag);
        
    guard let interval = insertionPointInterval else {
        insertionPointInterval = NSDate().timeIntervalSince1970;
        return;
    }
        
    if interval > 1676669420 { // seconds at time of writing this
        insertionPointInterval = NSDate().timeIntervalSince1970 - interval;
    } else {
        print(insertionPointInterval!);
    }
  }
}
  

The drawInsertionPoint method gets called on each flash of the insertion point — not just the initial drawing. If you record the intervals between them, you can reasonably determine the blink interval and store it for use on the NSTextView or in an environment variable, perhaps.

Of course, this requires that the insertion point processes through at least one cycle, but you can decide where you want to intercept that.


As a general note, I'm very new to Swift and Obj-C, so if you find something wrong with the implementation, please feel free to suggest modifications to this answer.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
stephancasas
  • 615
  • 3
  • 12