zakdances
zakdances

Reputation: 23675

How can I get the selection direction of an NSTextView?

I'm trying to get the direction of the selected ranges in an NSTextView. In other words, whether the selected ranges change their location or length when you use shift+leftarrow and shift+rightarrow. My first though was that selectionAffinity represents the direction, but it only seems to apply to multi-line selections.

How can I get the direction of selected ranges?

Upvotes: 12

Views: 819

Answers (3)

Logan
Logan

Reputation: 53112

iOS

Step 1: Declare Property

@property (nonatomic) NSRange lastRange;

Step 2: In TextView Delegate, add this:

- (void) textViewDidChangeSelection:(UITextView *)textView

    NSRange currentRange = NSMakeRange(textView.selectedRange.location, textView.selectedRange.length);

    if (currentRange.length == 0) {
        NSLog(@"Nothing selected");
        _lastRange = currentRange;
    }
    else {
        if (currentRange.location < _lastRange.location) {
            NSLog(@"Selecting LEFT");
        }
        else {
            NSLog(@"Selecting RIGHT");
        }
    }
}

OSX

OSX with all the features requested involved a little bit more work, but here's what I came up with for a working, consistent solution. Renaming is optional, or rather ... encouraged ...

Step 1: Subclass a NSTextView

In your SubClass.h:

#import <Cocoa/Cocoa.h>

@interface TheSelectionizer : NSTextView

@property (nonatomic) NSRange lastAnchorPoint;

@end

In your SubClass.m

#import "TheSelectionizer.h"

- (void)setSelectedRange:(NSRange)charRange affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag {

    if (charRange.length == 0) {
        _lastAnchorPoint = charRange;
    }
    [super setSelectedRange:charRange affinity:affinity stillSelecting:stillSelectingFlag];
}

@end
Step 2: Implement NSTextViewDelegate
- (NSRange) textView:(NSTextView *)textView willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange toCharacterRange:(NSRange)newSelectedCharRange {

    if (newSelectedCharRange.length != 0) {

        TheSelectionizer * selectionizer = (TheSelectionizer *)textView;

        int anchorStart = (int)selectionizer.lastAnchorPoint.location;
        int selectionStart = (int)newSelectedCharRange.location;
        int selectionLength = (int)newSelectedCharRange.length;

        /*
         If mouse selects left, and then a user arrows right, or the opposite, anchor point flips.
         */
        int difference = anchorStart - selectionStart;
        if (difference > 0 && difference != selectionLength) {
            if (oldSelectedCharRange.location == newSelectedCharRange.location) {
                // We were selecting left via mouse, but now we are selecting to the right via arrows
                anchorStart = selectionStart;
            }
            else {
                // We were selecting right via mouse, but now we are selecting to the left via arrows
                anchorStart = selectionStart + selectionLength;
            }
            selectionizer.lastAnchorPoint = NSMakeRange(anchorStart, 0);
        }

        // Evaluate Selection Direction
        if (anchorStart == selectionStart) {
            if (oldSelectedCharRange.length < newSelectedCharRange.length) {
                // Bigger
                NSLog(@"Will select right in overall right selection");
            }
            else {
                // Smaller
                NSLog(@"Will select left in overall right selection");
            }
        }
        else {
            if (oldSelectedCharRange.length < newSelectedCharRange.length) {
                // Bigger
                NSLog(@"Will select left in overall left selection");
            }
            else {
                // Smaller
                NSLog(@"Will select right in overall left selection");
            }
        }
    }

    return newSelectedCharRange;
}

I posted a project just in case someone wants to try it or wants to see it.

Full "project" available HERE

Upvotes: 7

Rouvis
Rouvis

Reputation: 159

Assumed that NSRange yourPreviousRange is declared.

//Check when NSTextView changed its value
@interface delegateAppDelegate : NSObject <NSApplicationDelegate, NSTextViewDelegate> {
    NSWindow *window;
}

-(void)textDidChange:(NSNotification *)notification {
    NSRange yourRange = notification.object.selectedRange;
    if (yourRange.location == yourPreviousRange.location - 1 || yourRange.length == yourPreviousRange.length){
     //left direction  
    } else if (yourRange.location == yourPreviousRange.location || yourRange.length == yourPreviousRange.length + 1){
    //right direction
    }
    yourPreviousRange = yourRange;
}

Upvotes: 0

Emmanuel
Emmanuel

Reputation: 2917

Use NSTextViewDelegate methods : -textView:willChangeSelectionFromCharacterRange:toCharacterRange: or -textView:willChangeSelectionFromCharacterRanges:toCharacterRanges: if you want multi-selections support.

Note that if you only implements -textView:willChangeSelectionFromCharacterRange:toCharacterRange: the multi-selections will be disallowed.

For example without multi-selections :

- (NSRange)textView:(NSTextView *)aTextView willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange toCharacterRange:(NSRange)newSelectedCharRange
{
    if (newSelectedCharRange.length == 0 && oldSelectedCharRange.length == 0) 
    {
        /* move the insertion point */

        if (newSelectedCharRange.location < oldSelectedCharRange.location)
            NSLog(@"insertion point move left");
        else
            NSLog(@"insertion point move right");
    }
    else if (newSelectedCharRange.length == 0 && oldSelectedCharRange.length != 0)
    {
        /* end a selection and move insertion point */

        if (newSelectedCharRange.location == oldSelectedCharRange.location)
            NSLog(@"insertion point at start from previous selection");
        else if (newSelectedCharRange.location < oldSelectedCharRange.location)
            NSLog(@"insertion point move left");
        else
            NSLog(@"insertion point move right");
    }
    else if (oldSelectedCharRange.length == 0 && newSelectedCharRange.length != 0)
    {
        /* start a selection */

        if (newSelectedCharRange.location == oldSelectedCharRange.location)
            NSLog(@"start a selection at insertion point, move right");
        else if (newSelectedCharRange.location < oldSelectedCharRange.location)
            NSLog(@"start a selection, move left");
        else
            NSLog(@"start a selection, move right");
    }
    else
    {
        if (newSelectedCharRange.location < oldSelectedCharRange.location)
            NSLog(@"selection move left");
        else
            NSLog(@"selection move right");
    }

    return newSelectedCharRange;
}

Upvotes: 0

Related Questions