adding delegate to responder chain stops scrolling from working - delegates

For a number of reasons, I have added my class that implements NSOutlineViewDelegate protocol to the responder chain:
[myOutlineView setNextResponder:self];
This stops my outline view from scrolling. Take the call out - scrolling works fine, put it back - scrolling stops. If I use the up and down arrows to move the selection through the view it scrolls to show the selected row OK, but gesture scrolling doesn't do anything.
The delegate contains quite a few methods for supporting drag and drop, and ibaction methods for supporting context menus, but I can't think what is in there that would interfere with scrolling (I am using a macbook air with gesture scrolling). Anyone got any ideas what is causing the interference? or any ideas how to diagnose?
I should add that I made the delegate class a subclass of NSResponder.

So the answer is, when adding a delegate into the responder chain, you must also add to the delegate the responder that used to be in its place - otherwise the chain gets broken and the events don't get handled, so it goes:
NSResponder *nextResponder = myOutlineView.nextResponder;
[myOutlineView setNextResponder:self];
[self setNextResponder:nextResponder];
With the responder chain restored, my outline view now scrolls again. Hooray

Related

validateMenuItem on NSViewController

Is it possible to have validateMenuItem work with NSViewControllers? The docs say NSViewController is a subclass of NSResponder so I would think it would participate in the responder chain, but I have never been able to get it to work. I can make it work with NSView, no problem.
The particular use case I have is a menu item in the MainMenu.xib, connected to First Responder. The selector from my view controller subclass shows up in the First Responder pop up list of selectors in MainMenu, so I would think it would work, but it doesn't. The validateMenuItem method of my subclass is never called. (The one in the app delegate is called, but not for the item which is connected to First Responder.)

When WebView is added to a specific view in NSWindow, :hover stops working. How can I fix this?

If I create a WebView and add it to NSThemeFrame like so:
Let window be the NSWindow instance I'm adding to:
NSView * themeFrame = window.contentView;
[themeFrame.superview addSubview:myWebView];
Then any :hover tags of elements in the page I load doesn't work anymore. However, the :hover does appear on mouseDown/mouseUp. So maybe this is a refresh issue. I tried calling [myWebView setNeedsDisplay:YES] in mouseMoved: but no luck.
Is there something special about contentView? I tried the same thing with an NSView and overrode its mouseMoved: method while calling setNeedsDisplay and the NSView handled mouseMove fine. Is there something special about :hover?
Any ideas or ramblings are welcome!
I might have the same problem as Cocoa WebView on os X not firing mouse hover events without having to click and hold left mouse key but there's no answer there.
I had a similar problem and after debugging and reading the source of WebKit, I found a solution. The crucial part seems to the WebHTMLView class, specifically the method -[WebHTMLView _updateMouseoverWithEvent:]. There's a check whether the view's window is a key window and sure enough, my window wasn't key even though I called makeKeyWindow on it.
My window was a child window of a document window and to solve the hover issue, I created a NSWindow subclass and overrode isKeyWindow like this:
- (BOOL)isKeyWindow
{
return [super isKeyWindow] || [[self parentWindow] isKeyWindow];
}
This made hover work in WebView in my document's child window.

Mouse Events Bleeding Through NSView

I have an NSView which covers its parent window's content view. This view has a click event handler which removes it from the content view. Inside this view, I have another view. When I drag the mouse in this inner view, the mouse events are applied not only to the view in the front, but also to the views behind. Additionally, the cursors from the views behind are showing up as well. This is the same problem occurring here: NSView overlay passes mouse events to underlying subviews? but the answer there won't work for my project because I can't open another window.
Without seeing your event-handling code it's difficult to know what's happening, but I suspect you might be calling super's implementation of the various event-handling methods in your implementations.
NSView is a subclass of NSResponder, so by default un-handled events are passed up the responder chain. The superview of a view is the next object in the responder chain, so if you call, for example, [super mouseDown:event] in your implementation of ‑mouseDown:, the event will be passed to the superview.
The fix is to ensure you don't call super's implementation in your event handlers.
This is incorrect:
- (void)mouseDown:(NSEvent*)anEvent
{
//do something
[super mouseDown:event];
}
This is correct:
- (void)mouseDown:(NSEvent*)anEvent
{
//do something
}
Rob's answer and Maz's comment on that answer solve this issue, but just to make it absolutely explicit. In order to prevent a NSView from bleeding it's mouse events to the parent, one must implement the empty methods.
// NSResponder =========================================
- (void) mouseDown:(NSEvent*)event {}
- (void) mouseDragged:(NSEvent*)event {}
- (void) mouseUp:(NSEvent*)event {}

When an NSWindow object has a delegate that is a NSWindow subclass, who is responsible to act on received events?

So I'm building a program that features the use of the IKImageBrowserView component as a subview in an NSWindow. As a side note, I have a controller object called ImageBrowserController which subclasses NSWindow and is set as the delegate of the NSWindow object of my app.
I have sent IKImageBrowserView the message setCanControlQuickLookPanel:YES to enable it to automatically use the QuickLook functionality to preview image files when the IKImageBrowserView is a first responder to receive key events. Then it took me a while to figure out how to make the IKImageBrowserView a first responder which I finally got working by overriding acceptsFirstResponder inside my ImageBrowserController.
Now I understand that as the delegate to the NSWindow, ImageBrowserController has a place in the responder chain after the event gets triggered on NSWindow. And I understand that as a subview of NSWindow, IKImageBrowserView is in line to be passed events for event handling. What I don't get is where the connection is between the ImageBrowserController being a first responder and the event somehow making it to the IKImageBrowserView. I didn't set NSWindow or IKImageBrowserView as first responders explicitly. So why isn't it necessary for me to implement event handling inside my ImageBrowserController?
EDIT: So after reading the accepted answer and going back to my code I tried removing the acceptsFirstResponder override in my ImageBrowserController and the QuickLook functionality still triggered just like the accepted answer said it would. Commenting out the setCanControlQuickLookPanel:YES made the app beep at me when I tried to invoke QuickLook functionality via the spacebar. I'm getting the feeling that my troubles were caused by user error of XCode in hitting the RUN button instead of the BUILD button after making changes to my code (sigh).
Some of what you are saying regarding the interactions between your objects does not make sense, and it is hard to address your stated question without some background.
As you say, your window delegate has a place at the end of the responder chain, after the window itself. The key point I think you are missing is that GUI elements, such as your IKImageBrowserView, will be at the beginning of the chain, and any one of them in a given window could be the current firstResponder.
When your application gets an event, it passes it off to the key window (which is just the window which currently accepts "key" (i.e., "keystroke") events). That window begins by asking its firstResponder to handle the event. If that object refuses, it passes the event to its own nextResponder, usually its superview, which either handles it or passes it on, until the event has either been handled or passed all the way up to the window object itself. Only then will the window (if it does not handle the event itself) ask its delegate to handle the event.
This means that the connection between the window delegate and the IKImageBrowserView is only through the Responder Chain, and its nature is simply that if the view declines to handle any given event, the delegate may eventually be asked to handle it, if no other object in between them handles it first.
Your window delegate does not need to be a firstResponder. Nor does overriding acceptsFirstResponder on the window delegate have any effect on one of the window's subviews.*
Your window delegate also does not need to (and, indeed should not) be a subclass of NSWindow. All it needs is to be a subclass of NSObject which implements whatever methods from the NSWindowDelegate Protocol you are interested in, and methods to handle any events you might want to catch if they are not handled by other objects.
So, the answer to your explicit question at the end is (and I do not mean this sarcastically): you only need to implement event handling in your window delegate if you want it to handle events itself.
*: IKImageBrowserView already responds YES to acceptsFirstResponder. If there are no other subviews in your window, it will automatically be the firstResponder when your application starts. You can set this initialFirstResponder explicitly in Interface Builder by connecting that outlet on the window to whatever object you want.

NSTextView overlay causes oddities with first responder status

I have an NSTextView in an NSScrollView, and I am programmatically inserting an NSView subclass as a subview of the NSTextView. This NSView acts as an overlay, superimposing graphical information about the text beneath it.
I thought it was working quite well until I noticed that the text view does not respond to right clicks. Other operations (editing, selection) seem to work just fine.
Also, if the first responder is changed to a sibling of the scroll view (an outline view, for example) the text view does not regain first responder status from clicking on it. The selection will change in response to clicking, but the selection highlight is gray instead of blue (indicating that the text view is not the first responder).
If I offset the frame of the overlay subview, the text view behaves 100% normally in the area not overlapped by the overlay, but the overlapped area behaves incorrectly, as outlined above.
Steps To Replicate This Behavior on Mac OS X 10.6.4:
Create a plain old non-document-based Cocoa app.
Add an `NSTextView' IBOutlet to the app delegate .h.
Add an NSTextView to the window in MainMenu.xib. Connect the textView outlet.
Type in a bit of code:
In applicationDidFinishLaunching:
NSView *overlay = [[NSView alloc] initWithFrame:textView.bounds];
[textView addSubview:overlay];
[overlay release];
Run the app, observe that right click in the text area does not work as it should, yet you can still otherwise interact with the text view.
Next, add an NSOutlineView to the window in the xib. Observe that once focus leaves the text area (if you click on the outline view) with the overlay in place, you cannot set the focus back to the text view (it will not become first responder again).
Is there some way I can enable the NSTextView to receive all of its events, even though my NSView overlay does not accept first responder or mouse events? I suspect this might be related to the field editor – perhaps it is ignoring events it thinks are destined to the overlay view?
You probably need to make your overlay an instance of a custom view class that forwards all events and accessibility messages to the text view. You may also need to convert any view-relative coordinates to the text view's coordinate system.
I don't have a lot of experience with it, but another possibility would be to use a Core Animation layer as an overlay.
A clean way to handle this is by making your overlay view a custom subclass of NSView, and then overriding the hitTest: method to always return nil. This will prevent the overlay view from participating in the responder chain. Instead, events will get sent automatically to it's superview or views higher up the view hierarchy. You might also want to override acceptsFirstResponder to return NO to be safe (in case it's accidentally set programatically).

Resources