NSOutlineView показать маркер отступа - PullRequest
0 голосов
/ 16 июня 2020

Как создать маркер отступа для NSOutlineView?

Маркеры NSOutlineview

Я не уверен, что это встроенная функция, потому что она присутствует в других приложениях, таких как Instruments

Обновление

Я попытался решить проблему, выполнив итерацию всех дочерних элементов элемента, который представляет строка, и отобразил маркер во всех дочерних строках в зависимости от уровня отступа, но я столкнулся с несколькими проблемами

  1. Как справиться со случаем, когда у элемента есть тысячи дочерних элементов. Просто невозможно нарисовать маркер для каждой строки, поскольку NSOutlineView будет рисовать строки по мере их отображения

  2. Когда я прокручиваю NSOutlineView, мышь перемещается из указанной строки, но mouseExited не вызывается. Таким образом, пользователь должен вручную переместить мышь, чтобы перезагрузить выделение.

Я решил эту проблему, но мое решение выглядит хакерским, поэтому я хотел знать, есть ли лучшее решение. Отсюда и вопрос

1 Ответ

2 голосов
/ 19 июня 2020

Сначала получает события mouseEntered: и mouseExited: , вам необходимо настроить прямоугольник отслеживания, используя NSTrackingArea .

Я бы начал с подкласс NSTableRowView , который перезаписывает setFrame: , обеспечивая обновление прямоугольника отслеживания при изменении размера представления:

@interface TableRowView : NSTableRowView {
    NSBox *_box;
    NSTrackingArea *_trackingArea;
}
@property (weak) id owner;
@property (copy) NSDictionary<id, id> *userInfo;
@property (nonatomic) CGFloat indentation;
@property (nonatomic) BOOL indentationMarkerHidden;
@end

@implementation TableRowView

- (void)setFrame:(NSRect)frame
{
    [super setFrame:frame];
    if (_trackingArea) {
        [self removeTrackingArea:_trackingArea];
    }
    _trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInKeyWindow owner:[self owner] userInfo:[self userInfo]];
    [self addTrackingArea:_trackingArea];
}

@end

Чтобы использовать NSTableRowView подкласс, реализуйте сообщения NSOutlineViewDelegate следующим образом:

 - (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
{
    TableRowView *view = [[TableRowView alloc] init];
    view.owner = self;
    view.userInfo = item;
    return view;
}

Теперь вы готовы получать mouseEntered: и mouseExited: событий. Используйте NSOutlineView levelForItem: вместе с indentationPerLevel для вычисления позиции маркера NSBox .:

- (void)mouseEntered:(NSEvent *)event
{
    id item = [event userData];
    CGFloat indentation = [_outlineView levelForItem:item] * [_outlineView indentationPerLevel];
    [self setIndentationMarker:indentation hidden:NO item:item];
}

- (void)mouseExited:(NSEvent *)event
{
    id item = [event userData];
    [self setIndentationMarker:0.0 hidden:YES item:item];
}

Теперь вы получаете подкласс NSTableRowView от rowViewAtRow: makeIfNeeded: и рекурсивно делаете то же самое для всех дочерних элементов в ваших данных:

- (void)setIndentationMarker:(CGFloat)indentation hidden:(BOOL)hidden item:(NSDictionary *)item
{
    TableRowView *view = [_outlineView rowViewAtRow:[_outlineView rowForItem:item] makeIfNecessary:NO];
    view.indentationMarkerHidden = hidden;
    view.indentation = indentation;
    for (NSMutableDictionary *child in [item objectForKey:@"children"]) {
        [self setIndentationMarker:indentation hidden:hidden item:child];
    }
}

Теперь разметьте NSBox подкласс NSTableRowView :

@implementation TableRowView

- (instancetype)init
{
    self = [super init];
    if (self) {
        _indentationMarkerHidden = YES;
        
        _box = [[NSBox alloc] init];
        _box.boxType = NSBoxCustom;
        _box.borderWidth = 0.0;
        _box.fillColor = [NSColor tertiaryLabelColor];
        _box.hidden = _indentationMarkerHidden;
        
        [self addSubview:_box];
    }
    return self;
}

- (void)layout
{
    [super layout];
    
    NSRect rect = [self bounds];
    rect.origin.x = _indentation + 7;
    rect.size.width = 10;
    _box.frame = rect;
}

- (void)setIndentation:(CGFloat)indentation
{
    _indentation = indentation;
    [self setNeedsLayout:YES];
}

- (void)setIndentationMarkerHidden:(BOOL)indentationMarkerHidden
{
    if (_indentationMarkerHidden != indentationMarkerHidden) {
        _indentationMarkerHidden = indentationMarkerHidden;
        _box.hidden = indentationMarkerHidden;
    }
}

@end

Этого достаточно, чтобы создать базовую c версию, как здесь:

Демонстрация маркера отступа NSOutlineView

...