Отправка события в NSControl под другим NSControl, если он не обрабатывает его - PullRequest
0 голосов
/ 08 октября 2009

У меня есть несколько наложенных элементов управления, которые могут обрабатывать щелчок мыши при определенных условиях. Что я хочу сделать, это:

  1. Верхний элемент управления получает mouseDown: событие.
  2. Верхний элемент управления решает, обрабатывать ли он событие mouseDown:.
  3. Если это так, сделайте что-нибудь и запретите другим элементам управления получать событие mouseDown:.
  4. Если это не так, отправьте событие в элемент управления, находящийся под ним.
  5. Этот элемент управления решает, обрабатывает ли он событие.
  6. и т.д.

По сути, я пытаюсь отправить событие элементу управления, чей "Z-Order" находится чуть ниже верхнего элемента управления, при этом верхнему элементу управления не нужно знать о других элементах управления или нужны какие-то специальные настройка при создании экземпляра.

Первое, что пришло мне в голову, это отправить событие на [topControl nextResponder], но, похоже, nextResponder для всех элементов управления в окне - это само окно, а не цепочка элементов управления, основанная на их Z-порядке как Я раньше думал.

Есть ли способ сделать это, не прибегая к настройке следующего респондента вручную? Цель состоит в том, чтобы получить элемент управления, независимый от других элементов управления, который можно просто перетащить в окно и работать, как ожидается.

Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 09 октября 2009

Все, что вам нужно сделать, это позвонить [super mouseDown:event]. Поскольку Mac OS X 10.5 (раньше не работала так же, как раньше), NSView знает, как обрабатывать перекрывающиеся представления, и позаботится об обработке событий за вас.


Если вам нужно ориентироваться на релизы до 10.5: Это действительно плохая идея . Не только механизм обработки событий не знает, как обращаться с перекрывающимися подпредставлениями, но и система рисования, и вы можете увидеть некоторые очень странные артефакты. Тем не менее, если вы полны решимости:

Переопределить -[NSView hitTest:] в вашем пользовательском элементе управления / представлении. AppKit использует этот метод, чтобы определить, в какое представление в иерархии доставлять события мыши. Если вы вернете nil, то эта точка в вашем пользовательском представлении игнорируется, и событие должно быть доставлено в следующий вид, охватывающий эту точку.

Имейте в виду, что это все еще плохая идея по причинам, которые я изложил выше. В то время это не было чем-то официально поддерживаемым AppKit. Более общепринятый обходной путь в 10.4 и более ранних версиях - использовать дочернее окно.

1 голос
/ 09 октября 2009

Трудно точно определить лучший подход, потому что я не знаю, что делает ваше приложение, но вот мысль. Похоже, вы хотите передать сообщения через иерархию представлений ... каким-то образом.

Независимо от того, представление сделало бы одну из двух вещей:

  • обработать сообщение
  • передать его в «следующее представление» (как вы определяете «следующее представление» зависит от вашего приложения)

Итак. Как бы вы это сделали? Поведение по умолчанию для представления должно состоять в том, чтобы передать сообщение следующему представлению. Хороший способ реализации подобных вещей - через неофициальный протокол.

@interface NSView (MessagePassing)

- (void)handleMouseDown:(NSEvent *)event;
- (NSView *)nextViewForEvent:(NSEvent *)event;

@end

@implementation NSView (MessagePassing)

- (void)handleMouseDown:(NSEvent *)event {
    [[self nextView] handleMouseDown:event];
}

- (NSView *)nextViewForEvent:(NSEvent *)event {
    // Implementation dependent, but here's a simple one:
    return [self superview];
}

@end

Теперь в представлениях, которые должны иметь такое поведение, вы должны сделать следующее:

- (void)mouseDown:(NSEvent *)event {
    [self handleMouseDown:event];
}

- (void)handleMouseDown:(NSEvent *)event {
    if (/* Do I not want to handle this event? */) {
        // Let superclass decide what to do.
        // If no superclass handles the event, it will be punted to the next view
        [super handleMouseDown:event];
        return;
    }

    // Handle the event
}

Возможно, вы захотите создать подкласс NSView для переопределения mouseDown:, на котором вы затем будете основывать свои другие пользовательские классы представления.

Если вы хотите определить «следующее представление» на основе фактического z-порядка, имейте в виду, что z-порядок определяется порядком в коллекции subviews, причем более поздние представления появляются первыми. Итак, вы можете сделать что-то вроде этого:

- (void)nextViewForEvent:(NSEvent *)event {
    NSPoint pointInSuperview = [[self superview] convertPoint:[event locationInWindow] fromView:nil];
    NSInteger locationInSubviews = [[[self superview] subviews] indexOfObject:self];
    for (NSInteger index = locationInSubviews - 1; index >= 0; index--) {
        NSView *subview = [[[self superview] subviews] objectAtIndex:index];
        if (NSPointInRect(pointInSuperview, [subview frame]))
            return subview;
    }
    return [self superview];
}

Возможно, это намного больше, чем вы хотели, но я надеюсь, что это поможет.

...