7

I have a bunch of NSTextViews that I would like to share a single selection. I basically want this to behave like selecting text on a web page, where there are multiple text views but you can drag to sequentially select text among them.

I found this document which states that it is possible to have multiple NSTextContainer objects sharing a single NSLayoutManager and thus share the selection. This is halfway to what I want, except for the fact that one NSLayoutManager can only have a single NSTextStorage object. I want each text view to have its own NSTextStorage so that each text view can have its own text, but I still want to be able to select text in multiple text views with one drag. Is this possible?

indragie
  • 18,002
  • 16
  • 95
  • 164

2 Answers2

4

There's no easy way to solve this problem (as I tried to find by asking this question). It involves all the mouse event handling and text selection calculations you'd expect, so I wrote the code and have open sourced it as INDSequentialTextSelectionManager.

indragie
  • 18,002
  • 16
  • 95
  • 164
0

To make this separate text containers work, you would calculate the drawn size of each part of the string and limit the NSTextView to that size:

    NSLayoutManager * layout = [[NSLayoutManager alloc] init];
    NSString * storedString = @"A\nquick\nBrown\nFox";
    NSTextStorage * storage = [[NSTextStorage alloc] initWithString:storedString];
    [storage addLayoutManager:layout];

    //I assume you have a parent view to add the text views
    NSView * view;

    //Assuming you want to split up into separate view by line break
    NSArray * paragraphs = [storedString componentsSeparatedByString:@"\n"];
    for (NSString * paragraph in paragraphs)
    {
        NSSize paragraphSize = [paragraph sizeWithAttributes:@{}];
        //Create a text container only big enough for the string to be displayed by the text view
        NSTextContainer * paragraphContainer = [[NSTextContainer alloc] initWithContainerSize:paragraphSize];
        [layout addTextContainer:paragraphContainer];

        //Use autolayout or calculate size/placement as you go along
        NSRect lazyRectWithoutSizeOrPlacement = NSMakeRect(0, 0, 0, 0);
        NSTextView * textView = [[NSTextView alloc] initWithFrame:lazyRectWithoutSizeOrPlacement
                                                    textContainer:paragraphContainer];
        [view addSubview:textView];
    }

You can add a delegate to the NSLayoutManager to watch your text container usage:

- (void)layoutManager:(NSLayoutManager *)aLayoutManager
didCompleteLayoutForTextContainer:(NSTextContainer *)aTextContainer
                    atEnd:(BOOL)flag
{
    if (aTextContainer == nil)
    {
        //All text was unable to be displayed in existing containers. A new NSTextContainer is needed.
    }
}
Fruity Geek
  • 7,351
  • 1
  • 32
  • 41
  • Apologies if the original post wasn't clear. I'm not looking to sync the same selection range across multiple text views, but to sequentially select text _between_ text views. I'm looking for something like this: http://d.pr/i/kmnB. In that case it's using a WebView and gets this functionality without any extra work, but I'm looking to replicate the same effect with just text views. – indragie Mar 19 '13 at 23:37
  • Ahh.. yes I see what you mean. Editing answer. – Fruity Geek Mar 20 '13 at 19:13
  • 1
    Thanks for the amendments. I actually already have multiple text views working and displaying my text. My problem is specifically **selection**. How do I intercept a mouse drag on _one_ of the text views and have the selection rectangle spread to the selection ranges of text views above/below it when the mouse is dragged over them? – indragie Mar 24 '13 at 20:14