События мыши входа / выхода на частично скрытых NSViews - PullRequest
5 голосов
/ 03 апреля 2012

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

У меня естьстек NSViews (на уровне слоя, если это как-то помогает, предоставляет какое-то лучшее решение), как показано ниже:

The view stack/layout

Дело в том, что это по сути меню, нопарить чувствительны к регистру.Если пользователь наводит курсор на одну из открытых частей представлений нижнего уровня, мне нужно выполнить действие в зависимости от того, что это за представление.Это динамическая система, поэтому количество таких пунктов меню может меняться, что усложняет статические вычисления.Как вы можете видеть, они в основном все являются копиями (по форме) первого элемента, но затем немного поворачиваются по мере продвижения вниз по стеку с помощью простого преобразования преобразования.

Мой вопрос к SO-сообществуЧто вы думаете, что лучший подход к получению mouseEntered: и mouseExited: события только для буквально видимых частей этих представлений?

Что я пытался сделать, это использовать NSTrackingArea на части visibleRect этих представленийЭто звучит гораздо удобнее, чем на самом деле в этой ситуации.В действительности visibleRect кажется «видимым» для них всех, все время.Ничто явно не блокируется и не скрывается ничем иным, как частично перекрывающимся NSView.Все, что происходит, это то, что я получаю спам-консоль от всех видов, кричащих сразу, что мышь вошла в их прямоугольник.

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

У кого-нибудь есть идея получше?Возможно один из опыта?

Спасибо!

Ответы [ 4 ]

3 голосов
/ 03 апреля 2012

Я знаю, что у вас уже есть решение, но я подумал, что попробую другой подход, который не требует получения множества событий mouseMoved.Я создал 3 пользовательских вида в коде, добавил для них ректы отслеживания и отправил все сообщения mouseEntered и mouseExited тому же методу, который выполняет hitTest, чтобы определить, какой вид является самым верхним.Это код для представления содержимого окна.

@implementation MainView
@synthesize oldView;

-(void)awakeFromNib {
    oldView = nil;
    Card *card1 = [[Card alloc]initWithFrame:NSMakeRect(150, 150, 200, 150) color:[NSColor redColor] name:@"Red Box"];
    NSTrackingArea *area1 = [[NSTrackingArea alloc]initWithRect:card1.frame options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp owner:self userInfo:nil];
    [self addTrackingArea:area1];
    [self addSubview:card1];

    Card *card2 = [[Card alloc]initWithFrame:NSMakeRect(180, 120, 200, 150) color:[NSColor yellowColor] name:@"Yellow Box"];
    NSTrackingArea *area2 = [[NSTrackingArea alloc]initWithRect:card2.frame options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp owner:self userInfo:nil];
    [self addTrackingArea:area2];
    [self addSubview:card2];

    Card *card3 = [[Card alloc]initWithFrame:NSMakeRect(210, 90, 200, 150) color:[NSColor greenColor] name:@"Green Box"];
    NSTrackingArea *area3 = [[NSTrackingArea alloc]initWithRect:card3.frame options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp owner:self userInfo:nil];
    [self addTrackingArea:area3];
    [self addSubview:card3];
}

-(void)mouseEntered:(NSEvent *)theEvent {
    [self reportTopView:theEvent];
}

-(void)mouseExited:(NSEvent *)theEvent {
    [self reportTopView:theEvent];
}

-(void)reportTopView:(NSEvent *)theEvent {
    id topView = [self hitTest:[theEvent locationInWindow]];
    if (![topView isEqual:oldView]) {
        oldView = topView;
        ([topView isKindOfClass:[Card class]])? NSLog(@"%@",[(Card *)topView name]):NULL;
    }
}

Это код для того, что я назвал карточками (цветные прямоугольники):

@implementation Card
@synthesize name,fillColor;

- (id)initWithFrame:(NSRect)frame color:(NSColor *)color name:(NSString *)aName{
    self = [super initWithFrame:frame];
    if (self) {
        self.fillColor = color;
        self.name = aName;
    }
    return self;
}

- (void)drawRect:(NSRect)rect {
    [self.fillColor drawSwatchInRect:rect];

}
1 голос
/ 15 декабря 2015

Перекрывающиеся области отслеживания:

Все, что вам нужно сделать, - это нажать Хиттест на виде, в котором вы находитесь. Если это правда:

 window.view.hitTest(window.mousePos) === self/*sudo code*/

Этот код делает то, что он возвращает представление под позицией мыши. Теперь все, что вам нужно сделать, это настроить несколько предложений «if» и «else», чтобы убедиться, что ваша мышь выключена или находится в представлении.

Пример полного кода:
https://gist.github.com/eonist/537ae53b86d5fc332fd3

Полное описание концепции здесь: (постоянная ссылка)
http://stylekit.org/blog/2015/12/20/Overlapping-tracking-areas/

Example

VS поведение входа и выхода по умолчанию:

enter image description here

1 голос
/ 04 апреля 2012

Я наконец пришел к решению в Твиттере через Стивена Тротона-Смита. Вот как это работает:

В каждом пункте меню я игнорирую все, что связано с NSTrackingArea или прямой интерпретацией положения мыши. Вместо этого представление родительского контроллера обрабатывает все события отслеживания и получения движения мыши.

Каждый элемент меню имеет переопределенный метод hitTest:, который выполняет преобразование точки и возвращает информацию о том, находится ли проверяемая точка в фоновом изображении (там есть тени и прочее, что делает его более сложным, чем ванильная реализация).

Затем я настраиваю своего рода обратный вызов «элемент меню при наведении» в контроллере, чтобы я мог обрабатывать изменения меню при наведении.

Это было довольно простое решение. Очень рад, что решил остановиться и спросить, а не взламывать что-то вместе с моей предыдущей идеей.

Спасибо, Стивен!

0 голосов
/ 11 февраля 2016

Example

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

Ответ долго запутан, но он работает:

http://stylekit.org/blog/2016/01/28/Hit-testing-sub-views/

включает использование метода, предоставленного яблоком: CGPathContainsPoint (путь, преобразование, точка)

Если вы перейдете по ссылке на этот пост в блоге, а затем оттуда, проверьте репозиторий styleKit на github. Вы найдете код, необходимый для получения приведенного выше примера анимации GIF. Я предоставляю это как указатель на ответ, так как это может занять у вас значительно меньше времени, чем попытка исследовать это самостоятельно. Я использую эту технику во всех своих элементах пользовательского интерфейса, и она работает безупречно.

Example

...