Перемещение NSPopover как прокрутки NSRulerView

#objective-c #cocoa #scroll #nspopover

#objective-c #cocoa #прокрутка #nspopover

Вопрос:

Предыстория

Реализация по умолчанию NSPopover перемещает всплывающее окно, когда оно отображается поверх прокрутки (и имеет специальную обработку для случаев, когда NSRect оно прикреплено к прокруткам вне поля зрения). NSPopover Может отображаться поверх NSRulerView , но NSPopover не перемещается при прокрутке вида документа (по-видимому, потому, что NSRulerView не обрабатывает прокрутку, как это делают другие виды, используя смещение чертежа вместо движущегося окна просмотра).

Вопрос

Как можно воссоздать поведение перемещения по умолчанию для объекта, NSPopover который прикреплен к NSRulerView ?

Пример кода

  • ViewController:

     @interface ViewController : NSViewController
    @property (weak) IBOutlet NSScrollView *scrollView;
    @property (strong) LineNumberView *ruler;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.ruler = [[LineNumberView alloc] initWithScrollView:self.scrollView orientation:NSVerticalRuler];
        self.scrollView.verticalRulerView = self.ruler;
        self.scrollView.hasVerticalRuler = YES;
        self.scrollView.rulersVisible = YES;
    }
    //...
    @end
      
  • LineNumberView Просмотр строки:

     @implementation LineNumberView
    -(void)mouseDown:(NSEvent *)event {
        [self popOver:[self convertPoint:event.locationInWindow fromView:nil]];
    }
    
    -(void)popOver:(NSPoint)point {
        NSRect anchor = [self rectForPoint:point];
        PopoverController *pop = [[PopoverController alloc] init];
        [pop showOver:anchor ofView:self];
    }
    
    -(NSRect)rectForPoint:(NSPoint)point {
        NSTextView* textView = (NSTextView*)self.scrollView.documentView;
    
        NSUInteger rectCount;
        NSRectArray frameRects = [textView.layoutManager
            rectArrayForCharacterRange: NSMakeRange(NSNotFound, 0)
            withinSelectedCharacterRange: kNullRange
            inTextContainer: textView.textContainer
            rectCount: amp;rectCount
        ];
    
        const CGFloat kWidth = NSWidth(self.bounds);
        const CGFloat kHeight = NSHeight(frameRects[0]);
        const CGFloat x = 0;
        CGFloat y = point.y - fmod(point.y, kHeight);
        //y  = NSMinY(self.scrollView.contentView.bounds);
    
        return NSMakeRect(x, y, kWidth, kHeight);
    }
    
    @end
      
  • ПоповерКонтроллер:

     @interface PopoverController : NSViewController<NSPopoverDelegate>
    @property (strong) NSPopover *popover;
    @end
    
    @implementation PopoverController
    -(instancetype)init {
        if ((self = [super init])) {        
            NSTextField *label = [NSTextField labelWithString:@"Pop!"];
            [label setFrameOrigin:NSMakePoint(kPadding, kPadding)];
            self.view = [[NSView alloc]
                initWithFrame:NSMakeRect(
                    0, 0,
                    2 * kPadding   label.frame.size.width,
                    2 * kPadding   label.frame.size.height)];
            [self.view addSubview:label];
    
            self.popover = [[NSPopover alloc] init];
            self.popover.contentViewController = self;
            self.popover.behavior = NSPopoverBehaviorTransient;
        }
        return self;
    }
    
    -(void)showOver:(NSRect)anchor ofView:(NSView*)view {
        [self.popover showRelativeToRect:anchor ofView:view preferredEdge:NSMaxYEdge];
    }
      
  • В основной раскадровке, на выходе ViewController's scene has an NSScrollView as a descendent view, connected to the ViewController 's ScrollView.