Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSTextView drag text with custom attributes

I am having an issue regarding my NSTextView subclass. My view contains attributed strings with custom attributes, therefore I had to implement the following pasteboard methods to ensure that my custom attributes get copied on to the pasteboard.

writeSelection(to:type:)
readSelection(from:type:)

In readSelection, I manually read the string on the pasteboard, and write it into the NSTextView's textStorage at rangeForUserTextChange.

override func readSelection(from pboard: NSPasteboard, type: String) -> Bool {


    // Manually reads the text on the pasteboard, and write it to textStorage

    if type == astroBoardAttributedString {

        if let string = pboard.string(forType: astroBoardAttributedString) {

            let attrString = NSAttributedString(string: string)

            self.textStorage?.replaceCharacters(in: self.rangeForUserTextChange, with: attrString)


            return true

        }


    }

    return super.readSelection(from: pboard, type: type)

}

Now the problem is, when I select and drag texts up say from line 5 to under line 1, the text is inserted under line 1 correctly, however the system then attempts to remove text from where line 5 used to be, which now contains line 4 because an extra line has been added below line 1.

enter image description here

/*
line 1 <-- Drop after this line
line 2
line 3
line 4
line 5 <-- Drag from this line

The expected outcome is 

line 1
line 5
line 2
line 3
line 4

The actual resulting outcome is 

line 1
line 5
line 2
line 3
line 5

What happens is

line 1
line 5  <-- Line inserted here (correct)
line 2
line 3
line 4 <-- This line is removed instead :(
line 5 <-- This is the line that should be removed.
*/

As you can see, the system is removing the line after the length has been altered. I couldn't find any delegate method for intercepting when the drag and drop method removes text in the textStorage that was dragged from the textview.

like image 314
Joseph Williamson Avatar asked Aug 16 '17 11:08

Joseph Williamson


1 Answers

After contacting Apple for support, I got the answer, here it is in quote.

The code snippet you provided shows that when you read from the pasteboard and change the text storage, you don’t notifiy the other components by calling shouldChangeText(...) and didChangeText(), which causes an inconsistency between the text storage and rendering. You should be able to fix the issue by wrapping the code like below:

self.shouldChangeText(in:self.rangeForUserTextChange, replacementString: string)
self.textStorage?.replaceCharacters(in: self.rangeForUserTextChange, with: string)
self.didChangeText()
like image 50
Joseph Williamson Avatar answered Sep 28 '22 19:09

Joseph Williamson