自定义 NSCell 内的 NSButtonCell
在我的可可应用程序中,我需要一个用于 NSTableView 的自定义 NSCell。这个 NSCell 子类包含一个自定义 NSButtonCell 用于处理点击(以及两个或三个用于文本内容的 NSTextFieldCell)。您将在下面找到我的代码的简化示例。
@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];
}
我遇到的问题是:让该 NSCell 内的 Button(更准确地说:NSButtonCell)正常工作的最佳/正确方法是什么?“工作”意味着:触发指定的操作消息并在单击时显示替代图像。开箱即用后,单击该按钮不会执行任何操作。
关于这个主题的信息/读物很难找到。我在网上找到的唯一帖子指出我要实施
- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp;
这是正确的方法吗??? 在我包含的NSCell中实现trackMouse:?然后将事件转发到NSButtonCell?我本来希望 NSButtonCell 本身知道在被单击时要做什么(并且我看到了 trackMouse: 方法更多地与真正跟踪鼠标移动的结合 - 而不是作为“标准”单击行为的训练轮)。但当它包含在单元格本身中时,它似乎不会这样做...... 看来我还没有掌握自定义单元格的大局;-)
如果有人能根据自己的经验回答这个问题(或向我指出一些教程等),我会很高兴 - 并告诉我如果我走在正确的轨道上。
提前致谢, 飞鸟
in my cocoa application, I need a custom NSCell for an NSTableView. This NSCell subclass contains a custom NSButtonCell for handling a click (and two or three NSTextFieldCells for textual contents). You'll find a simplified example of my code below.
@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];
}
The problem I'm stuck with is: what is the best/right way to get that Button (more precisely: the NSButtonCell) inside this NSCell to work properly? "work" means: trigger the assigned action message and show the alternate image when clicked. Out of the box, the button doesn't do anything when clicked.
Information / readings on this topic is hard to find. The only posts I found on the net pointed me to implementing
- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp;
Is this the correct way to do it??? Implement trackMouse: in my containing NSCell? And then forward the event to the NSButtonCell? I would have expected the NSButtonCell itself to know what to do when it's being clicked (and I saw the trackMouse: methods more in cunjunction with really tracking mouse movements - not as a training wheel for 'standard' click behaviour). But it seems like it doesn't do this when included in a cell itself...
It seems I haven't grasped the big picture on custom cells, yet ;-)
I'd be glad if someone could answer this (or point me to some tutorial or the like) out of his own experience - and tell me if I'm on the right track.
Thanks in advance,
Tobi
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
最低要求是:
要使按钮看起来被按下,您需要根据需要更新按钮单元格的
highlighted
属性。单独更改状态并不能实现此目的,但您想要的是当且仅当其状态为NSOnState
时按钮才会突出显示。要发送操作消息,您需要知道鼠标何时释放,然后使用
-[NSApplication sendAction:to:from:]
发送消息。为了能够发送这些消息,您需要挂钩 NSCell 提供的事件跟踪方法。请注意,除了最后一个
-stopTracking:...
方法之外,所有这些跟踪方法都会返回一个布尔值来回答问题“您想继续接收跟踪消息吗?”最后的转折点是,为了发送任何跟踪消息,您需要实现 -hitTestForEvent:inRect:ofView: 并返回适当的 NSCellHit 位掩码...代码>值。具体来说,如果返回的值中没有
NSCellHitTrackableArea
值,您将不会收到任何跟踪消息!因此,从较高的层面来看,您的实现将类似于:
The minimal requirements are:
To make the button look pressed, you need to update the button cell's
highlighted
property as appropriate. Changing the state alone will not accomplish this, but what you want is for the button to be highlighted if, and only if, its states isNSOnState
.To send the action message, you need to be aware of when the mouse is released, and then use
-[NSApplication sendAction:to:from:]
to send the message.In order to be in position to send these messages, you will need to hook into the event tracking methods provided by
NSCell
. Notice that all those tracking methods, except the final,-stopTracking:...
method, return a Boolean to answer the question, "Do you want to keep receiving tracking messages?"The final twist is that, in order to be sent any tracking messages at all, you need to implement
-hitTestForEvent:inRect:ofView:
and return an appropriate bitmask ofNSCellHit...
values. Specifically, if the value returned doesn't have theNSCellHitTrackableArea
value in it, you won't get any tracking messages!So, at a high level, your implementation will look something like:
NSCell
子类的要点是将渲染和处理常见 UI 元素(控件)的责任与视觉和事件层次结构分开NSView
类的职责。这种配对允许每一方提供更大的专业化和可变性,而不会给另一方带来负担。看看可以在 Cocoa 中创建的大量NSButton
实例。想象一下,如果不存在这种功能分割,将会存在多少NSButton
子类!使用设计模式语言来描述角色:
NSControl
充当外观,向客户端隐藏其组成的详细信息,并将事件和渲染消息传递到其充当角色的NSCell
实例作为代表。由于您的 NSCell 子类在其组合中包含其他 NSCell 子类实例,因此它们不再直接从位于查看层次结构。因此,为了使这些单元实例能够从(视图层次结构的)事件响应程序链接收事件消息,您的单元实例需要传递这些相关事件。您正在重新创建
NSView
层次结构的工作。这不一定是坏事。通过以
NSCell
形式复制NSControl
(及其NSView
超类)的行为,您可以过滤传递给您的子类的事件按位置、事件类型或其他条件划分的单元格。缺点是在构建过滤和显示方面重复了NSView/NSControl
的工作。管理机制。因此,在设计界面时,您需要考虑
NSButtonCell
(和NSTextFieldCell
)是否在普通视图层次结构中的NSControl
中更好,或者作为NSCell
子类中的子单元。最好利用代码库中已有的功能,而不是不必要地重新发明它(并在以后继续维护它)。The point of
NSCell
subclasses is to separate responsibility for rendering and handling common UI elements (the controls) from the visual- and event-hierarchyresponsibilities of the
NSView
classes. This pairing permits each one to provide greater specialization and variability without burdening the other. Look at the large number ofNSButton
instances one can create in Cocoa. Imagine the number ofNSButton
sub-classes that would exist if this split in functionality were absent!Using design pattern language to describe the roles: an
NSControl
acts as a façade, hiding details of its composition from its clients and passing events and rendering messages to itsNSCell
instance which acts as a delegate.Because your
NSCell
subclass includes otherNSCell
subclass instances within its composition, they no longer directly receive these event messages from theNSControl
instance which is in the view hierarchy. Thus, in order for these cell instances to receive event messages from the event responder chain (of the view hierarchy), your cell instance needs to pass along those relevant events. You are recreating the work of theNSView
hierarchy.This isn't necessarily a bad thing. By replicating the behavior of
NSControl
(and itsNSView
superclass) but in anNSCell
form, you can filter the events passed on to your sub-cells by location, event type, or other criteria. The drawback is replicating the work ofNSView/NSControl
in building the filtering & management mechanism.So in designing your interface, you need to consider whether the
NSButtonCell
(andNSTextFieldCell
s) are better off inNSControl
s in the normal view hierarchy, or as sub-cells in yourNSCell
subclass. It's better to leverage the functionality which already exists for you in a codebase than to re-invent it (and continue maintaining it later) unnecessarily.