Crash in dragImageForSelection


John Brownie
 

I have what seems to be a simple operation to perform, but it crashes. I have a custom view which includes an NSTextView called textView. In the mouseDragged(with:) function in my view subclass, I have the following code (leaving out the logic deciding whether it is appropriate as a drag source):

            textView?.setSelectedRange(NSMakeRange(0, textView!.textStorage!.length))
            let dragImage = textView?.dragImageForSelection(with: event, origin: nil)

I've tried a variety of ways to select the whole text, but nothing changes the result. I get a crash in dragImageForSelection, with the message:

Cannot lock focus on image <NSImage 0x6000017f5640 Size={0, 0} Reps=() flipped:YES (flippedness is deprecated)>, because it is size zero.

The text is most often of length one (always in testing), and is often plain ASCII, but is always valid Unicode. Here's a sample of the textView and its textStorage:

(lldb) po textView
▿ Optional<NSTextView>
  - some : <NSTextView: 0x600003327200>
    Frame = {{2.50, 2.50}, {42.50, 42.50}}, Bounds = {{0.00, 0.00}, {42.50, 42.50}}
    Horizontally resizable: NO, Vertically resizable: NO
    MinSize = {34.00, 34.00}, MaxSize = {42.50, 10000000.00}


(lldb) po textView?.textStorage
▿ Optional<NSTextStorage>
  - some : 2{
    NSColor = "Generic Gray Gamma 2.2 Profile colorspace 1 1";
    NSFont = "\"LucidaGrande 22.50 pt. P [] (0x600000d719e0) fobj=0x100f6a270, spc=7.12\"";
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 1.1, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n 140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n 308L,\n    336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation YES, HeaderLevel 0";
}

If I leave out the selection line, I don't get a crash, but then I don't get an image to drag.

Searching hasn't turned up anything of use. This was working in an earlier version of the app, written in Objective-C, and I'm unable to spot a significant difference in the code.

Anybody have an idea what is going wrong?

John
--
John Brownie
Mussau-Emira language, New Ireland Province, Papua New Guinea
Kouvola, Finland


Ben Kennedy
 

On 26 Apr 2019, at 4:26 am, John Brownie <john_brownie@sil.org> wrote:

I have what seems to be a simple operation to perform, but it crashes. I have a custom view which includes an NSTextView called textView. In the mouseDragged(with:) function in my view subclass, I have the following code (leaving out the logic deciding whether it is appropriate as a drag source):

textView?.setSelectedRange(NSMakeRange(0, textView!.textStorage!.length))
let dragImage = textView?.dragImageForSelection(with: event, origin: nil)
I don't have any useful help for you, but a couple of things struck me as odd about this.

First of all, you're treating textView as an optional sometimes, and force-unwrapping it elsewhere. Prefacing the whole block with a `guard` or `if let` would clean this up.

More on topic, I looked up the documentation for `setSelectedRange()`, and it is strange:

https://developer.apple.com/documentation/appkit/nstextview/1449256-setselectedrange?language=objc

charRange: A non-nil, non-empty array of objects responding to the NSValue rangeValue method. The ranges in the ranges array must begin and end on glyph boundaries and not split base glyphs and their nonspacing marks.
The method clearly takes an NSRange argument, but the documentation instead talks about an array of NSValue objects. WTH? I guess this is a documentation error? (Somebody file a radar... crickets)

If you call `selectedRange` immediately after `setSelectionRange(…)`, does it reflect the range you tried to set?

b


Gary L. Wade
 

The bug in documentation has been reported.

Try forcing layout on the text view after setting the selection; there might be some kind of delay from setting it till it’s renderable, and that might get it refreshed.
--
Gary L. Wade
http://www.garywade.com/

On Apr 26, 2019, at 11:51 AM, Ben Kennedy <ben-groups@zygoat.ca> wrote:


On 26 Apr 2019, at 4:26 am, John Brownie <john_brownie@sil.org> wrote:

I have what seems to be a simple operation to perform, but it crashes. I have a custom view which includes an NSTextView called textView. In the mouseDragged(with:) function in my view subclass, I have the following code (leaving out the logic deciding whether it is appropriate as a drag source):

textView?.setSelectedRange(NSMakeRange(0, textView!.textStorage!.length))
let dragImage = textView?.dragImageForSelection(with: event, origin: nil)
I don't have any useful help for you, but a couple of things struck me as odd about this.

First of all, you're treating textView as an optional sometimes, and force-unwrapping it elsewhere. Prefacing the whole block with a `guard` or `if let` would clean this up.

More on topic, I looked up the documentation for `setSelectedRange()`, and it is strange:

https://developer.apple.com/documentation/appkit/nstextview/1449256-setselectedrange?language=objc

charRange: A non-nil, non-empty array of objects responding to the NSValue rangeValue method. The ranges in the ranges array must begin and end on glyph boundaries and not split base glyphs and their nonspacing marks.
The method clearly takes an NSRange argument, but the documentation instead talks about an array of NSValue objects. WTH? I guess this is a documentation error? (Somebody file a radar... crickets)

If you call `selectedRange` immediately after `setSelectionRange(…)`, does it reflect the range you tried to set?

b


John Brownie
 

Gary L. Wade wrote on 27/4/19 02:07:
The bug in documentation has been reported.

Try forcing layout on the text view after setting the selection; there might be some kind of delay from setting it till it’s renderable, and that might get it refreshed.
Doesn't seem to help.
On Apr 26, 2019, at 11:51 AM, Ben Kennedy <ben-groups@zygoat.ca> wrote:


First of all, you're treating textView as an optional sometimes, and force-unwrapping it elsewhere. Prefacing the whole block with a `guard` or `if let` would clean this up.
Yes, it's a result of various ways of setting the range correctly. I've fixed it so it looks nicer.
More on topic, I looked up the documentation for `setSelectedRange()`, and it is strange:

https://developer.apple.com/documentation/appkit/nstextview/1449256-setselectedrange?language=objc

charRange: A non-nil, non-empty array of objects responding to the NSValue rangeValue method. The ranges in the ranges array must begin and end on glyph boundaries and not split base glyphs and their nonspacing marks.
The method clearly takes an NSRange argument, but the documentation instead talks about an array of NSValue objects. WTH? I guess this is a documentation error? (Somebody file a radar... crickets)

If you call `selectedRange` immediately after `setSelectionRange(…)`, does it reflect the range you tried to set?
Yes, the range is correct after calling setSelectedRange.

I wonder if the answer is to draw my own drag image...

John
--
John Brownie
Mussau-Emira language, New Ireland Province, Papua New Guinea
Kouvola, Finland


John Brownie
 

Further experimentation reveals that the problem is (as far as I can tell) due to the code that handles a click updating the window. I seem to get a race condition: if I click, hold and drag, the drag image is created (though weirdly distorted to match the rect I supply), but a click and drag in one motion causes the crash.

Updating the window may cause the text view to get a new string supplied, so I'm guessing that it is somehow hitting the text view when the string is being replaced, causing the problem. How that happens, I'm not sure, since everything is on the main thread (since it's UI stuff), which shouldn't cause threading issues like I'm imagining.

Could I be on the right track here, or what else could the problem be?

John
--
John Brownie
Mussau-Emira language, New Ireland Province, Papua New Guinea
Kouvola, Finland