Перемещение NSPopover как прокрутка NSRulerView - PullRequest
1 голос
/ 08 апреля 2019

Фон

Реализация по умолчанию 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: &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
    
  • PopoverController:

    @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`.

...