NSButtonCell в пользовательском NSCell

в моем приложении какао мне нужен пользовательский NSCell для NSTableView. Этот подкласс NSCell содержит пользовательский NSButtonCell для обработки щелчка (и два или три NSTextFieldCells для текстового содержания). Вы найдете ниже упрощенный пример моего кода.

@implementation TheCustomCell

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
   // various NSTextFieldCells
   NSTextFieldCell *titleCell = [[NSTextFieldCell alloc] init];
   ....
   // my custom NSButtonCell
   MyButtonCell *warningCell = [[MyButtonCell alloc] init];
   [warningCell setTarget:self];
   [warningCell setAction:@selector(testButton:)];
   [warningCell drawWithFrame:buttonRect inView:controlView];
}

Проблема, с которой я застреваю: что является лучшим/правильным способом получить ту Кнопку (более точно: NSButtonCell) в этом NSCell для работы правильно? "работа" означает: инициируйте присвоенное сообщение действия и покажите дополнительное изображение при нажатии. Из поля кнопка ничего не делает при нажатии.

Информацию / чтения по этой теме трудно найти. Единственные сообщения, которые я нашел в сети, указали на меня на реализацию

- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp; 

Действительно ли это - корректный способ сделать это??? Реализация trackMouse: в моем содержании NSCell? И затем передайте событие к NSButtonCell? Я ожидал бы, что сам NSButtonCell будет знать, что сделать, когда он нажимается (и я видел trackMouse: методы больше в cunjunction с реальным отслеживанием движений мыши - не как учебное колесо для 'стандартного' поведения щелчка). Но кажется, что это не делает этого при включении в саму ячейку... Кажется, что я не схватил большое изображение на пользовательских ячейках, еще ;-)

Я радовался бы, мог ли кто-то ответить на это (или указать на меня на некоторое учебное руководство и т.п.) из его собственного опыта - и скажите мне, если я на правильном пути.

Заранее спасибо, Tobi

9
задан Tobidobi 19 February 2010 в 17:38
поделиться

2 ответа

Минимальные требования:

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

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

Чтобы отправить сообщение о действии, вам нужно знать, когда мышь будет отпущена, а затем использовать -[NSApplication sendAction:to:from:] для отправки сообщения.

Для того чтобы иметь возможность отправлять эти сообщения, вам необходимо подключиться к методам отслеживания событий, предоставляемым NSCell. Обратите внимание, что все эти методы отслеживания, кроме заключительного, -stopTracking:... , возвращают булево значение, отвечающее на вопрос: "Хотите ли вы продолжать получать сообщения об отслеживании?"

Последний поворот заключается в том, что для того, чтобы вообще получать сообщения об отслеживании, вам необходимо реализовать -hitTestForEvent:inRect:ofView: и вернуть соответствующую битовую маску из NSCellHit... значений. В частности, если в возвращаемом значении нет значения NSCellHitTrackableArea, вы не получите никаких сообщений об отслеживании!

Итак, на высоком уровне ваша реализация будет выглядеть примерно так:

- (NSUInteger)hitTestForEvent:(NSEvent *)event
                       inRect:(NSRect)cellFrame
                       ofView:(NSView *)controlView {
    NSUInteger hitType = [super hitTestForEvent:event inRect:cellFrame ofView:controlView];

    NSPoint location = [event locationInWindow];
    location = [controlView convertPointFromBase:location];
    // get the button cell's |buttonRect|, then
    if (NSMouseInRect(location, buttonRect, [controlView isFlipped])) {
        // We are only sent tracking messages for trackable areas.
        hitType |= NSCellHitTrackableArea;
    }
    return hitType;
}

+ (BOOL)prefersTrackingUntilMouseUp {
   // you want a single, long tracking "session" from mouse down till up
   return YES;
}

- (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView {
   // use NSMouseInRect and [controlView isFlipped] to test whether |startPoint| is on the button
   // if so, highlight the button
   return YES;  // keep tracking
}

- (BOOL)continueTracking:(NSPoint)lastPoint at:(NSPoint)currentPoint inView:(NSView *)controlView {
   // if |currentPoint| is in the button, highlight it
   // otherwise, unhighlight it
   return YES;  // keep on tracking
}

- (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag {
   // if |flag| and mouse in button's rect, then
   [[NSApplication sharedApplication] sendAction:self.action to:self.target from:controlView];
   // and, finally,
   [buttonCell setHighlighted:NO];
}
8
ответ дан 4 December 2019 в 15:17
поделиться

Смысл подклассов NSCell заключается в том, чтобы отделить ответственность за рендеринг и обработку общих элементов пользовательского интерфейса (элементов управления) от визуальной и иерархии событий. ответственности классов NSView. Такое разделение позволяет каждому из них обеспечить большую специализацию и вариативность, не перегружая другой. Посмотрите на большое количество экземпляров NSButton, которые можно создать в Cocoa. Представьте себе количество подклассов NSButton, которые существовали бы, если бы это разделение функциональности отсутствовало!

Используя язык паттернов проектирования для описания ролей: NSControl действует как фасад, скрывая детали своего состава от клиентов и передавая события и сообщения рендеринга своему экземпляру NSCell, который действует как делегат.

Поскольку ваш подкласс NSCell включает другие экземпляры подкласса NSCell в свою композицию, они больше не получают напрямую сообщения о событиях от экземпляра NSControl, который находится в иерархии представления. Таким образом, чтобы эти экземпляры ячеек могли получать сообщения о событиях из цепочки ответчиков событий (иерархии представления), ваш экземпляр ячейки должен передать эти соответствующие события. Вы воссоздаете работу иерархии NSView.

Это не обязательно плохо. Повторяя поведение NSControl (и его NSView суперкласса), но в форме NSCell, вы можете фильтровать события, передаваемые вашим вложенным ячейкам, по местоположению, типу события или другим критериям. Недостатком является повторение работы NSView/NSControl при построении механизма фильтрации и управления.

Поэтому при проектировании интерфейса вам нужно подумать, лучше ли NSButtonCellNSTextFieldCellы) разместить в NSControlах в обычной иерархии представления или как вложенные ячейки в вашем подклассе NSCell. Лучше использовать функциональность, которая уже существует в кодовой базе, чем изобретать ее заново (и продолжать поддерживать ее в дальнейшем) без необходимости.

5
ответ дан 4 December 2019 в 15:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: