Incorrect NSCursor originating from background NSView - cocoa

I wanted to change the cursor in my cocoa application. I've followed the example in this answer, with success.
I have an NSView, let's call it NSViewA, behind another NSView, NSViewB. NSViewA contains a subclassed NSButton where the cursor has been changed. NSViewA and NSViewB have the same superview. So something like this:
- NSWindow
- NSViewA
- NSButtonSubclass
- NSViewB
My problem is that when NSViewB is shown, and the cursor is ontop of NSViewB, but in the same x y coordinates of the NSButton behind NSViewB, the cursor changes to that specified in the NSButton subclass.
How do I stop this behaviour?
The reason for this layout is that I'm creating a 'lightbox' control. If you think something along the lines of the NSWindow greying out, and a centred box appearing showing an error message. That sort of thing.
I previously had a problem where you could still click buttons, etc, behind NSViewB. This was solved by suppressing mouseDown: and mouseUp:. I've tried doing something similar with other mouse-related events, such as mouseEntered: and mouseExited: with no luck.

Could you make the addition of your custom cursor rectangle contingent on the enabled status of your button? In other words, your resetCursorRects would look like this:
// MyButton.swift
override func resetCursorRects() {
if enabled {
addCursorRect(bounds, cursor: NSCursor.pointingHandCursor())
}
}
Whenever viewB is about to be shown, disable the button, and call for the rects belonging to your button to be invalidated. If you're using Swift, you could do this second bit in a property observer attached to the enabled property itself:
// MyButton.swift
override var enabled: Bool {
didSet {
window!.invalidateCursorRectsForView(self)
}
}
If you don't want your button to take on a disabled look, make the addCursorRect call contingent on some other flag.

Related

Receive trigger when active NSView focus changes

I'm surprised that I haven't been able to find an answer for this from searching. So if there is a page describing how to do this, let me know, but I've been unable to find it.
I have 3 sibling NSOutlineViews all inside an NSSplitView I have added observers for NSOutlineViewSelectionDidChange which, when triggered update an NSTextView editor. I even test for negative row values to indicate that the user has unselected the row and clear the text.
The NSOutlineViews are connected to custom datasource objects and the NSSplitView is created by my custom NSWindowController in turn created by my custom NSDocument and custom NSDocumentController (I'm not using .nibs)
However I cannot seem to receive triggers for the NSOutlineViews changing their active status, it works if the user selects a different row in a different NSOutlineView, as the selection has changed but if they click the selected row of a different view, I don't receive any event or notification that anything has changed. Visually I can see the change as the row selection highlight colour changes from coloured to grey, in the outline view that has lost focus and the selection row colour changes from grey to coloured in the newly activated view.
I've tried to catch mouseDown events, tried becoming first responder, tried observing changes in the NSSplitView I've been right through the NSObject hierarchy from NSOutlineView to NSResponder looking for the appropriate notification or method. I found deprecated documentation regarding a focus change notification. I've tried combinations of nsview, nsnotification, nsoutlineview and various actions in google but can't find the 'this is how you do it'
EDIT:
this is the code I've added to my NSOutlineView subclass (along with prototypes in the headers) to become first responder, but it is never triggered.
- (BOOL)acceptsFirstResponder { return YES; }
- (BOOL)becomeFirstResponder {
NSLog(#"becomefirstResponder %#",self);
return YES;
}

Editable NSTextField in NSTableView

I have a view based NSTableView with some labels in my customized and subclassed view. One of the label should be editable, so therefore I set this NSTextField to editable.
But now I have two problems, I can't solve:
1) If I move the mouse over the editable NSTextField, the cursor don't change to the IBeamCursor (the edit cursor).
2) I need to double click at the label, to be able to edit. I want to have a single click. I found some solutions for this problem here at stackoverflow, the best one is to override the acceptsFirstResponder to return always true, but then, clicking at the NSTextField selects the whole text instead of placing the cursor at the clicked position.
Sorry... this is a duplicate. I found this:
NSTableView - select row and respond to mouse events immediately
You have to subclass NSTableView. My swift code:
class TableViewEditing: NSTableView {
...
override func validateProposedFirstResponder(responder: NSResponder, forEvent event: NSEvent?) -> Bool {
return true
}
}
EDIT:
Just one disadvantage: Sometimes entering the edit mode, it seems that the text is just shortly selected and deselected. But you can see, that this is a cocoa problem, it's the same for example in Apple reminders app.

Maintaining focus or first responder state of NSOutlineView

I have a subclass of an NSOutline. When it loses focus, the disclosure triangle of a selected row changes from its inverted template form (white) back to black. I don't want this to happen: I want the appearance of a selected row to stay the same, as in XCode or Mail.
I've tried intercepting firstResponder-based messages in the NSOutlineView subclass, as well in my custom rowViews, but to no avail. Any ideas?
In my subclass of NSTableRowView, doing the following works:
- (BOOL)isEmphasized {
return YES;
}

Changing tooltips in a custom view

I've got a sample project at:
http://ericgorr.net/cocoadev/tooltip.zip
What I would like to do is define a single tooltip rect for an entire view but be able to change the tooltip as the cursor moves inside of the view.
Is there a way to do that? Is there a way to force it to hide the current tooltip and display a new one while calling view:stringForToolTip:point:userData:?
I could create my own window that simulates a real tooltip, but wanted to make sure there was nothing built-in that would support this.
Check the MAAttachedWindow sample project:
http://mattgemmell.com/source/
Great start for creating custom tooltips.
NSView has specific handlers for mouse events.
Simply change the NSView (tooltip) based on these events.
I added some snippets to get you started.
- (void)mouseMove:(NSEvent *)theEvent {
NSPoint mousePositionInWindow = [theEvent locationInWindow];
}
- (void)mouseDown:(NSEvent *)theEvent {
}
- (void)mouseDragged:(NSEvent *)theEvent {
}
- (void)mouseUp:(NSEvent *)theEvent {
}
Response to comment:
Once I struggled with exactly the same problem: One view with continuous tooltip updates showing the cursor position and some additional information. I got it never working with the native tooltips. Finally i came up with the solution above, which is easy to implement and made it even look better.
Instead of using the separate window, you can also draw the custom tooltip inside the NSView itself, in relation to the cursor position. You can also put an extra NSView on top of the existing NSView to show the custom tooltips.
I don't like the native tooltip behavior. Apparently they have build-in time-delays which cannot be changed, for example: The cursor has to be on one position for some time to show the tooltip for the first time. Once the first tooltip showed up, the next will show with much less delay, but it's still quite annoying.
Of cours, you can always show the info in a label located near the view, which is really easy to implement. But that is no real answer to your question :)

NSOutlineView with transparent field editor

I'm working with a NSOutlineView located on a HUD panel. I configured it so that it doesn't draw its background. Everything looks fine until I double click to edit a cell.
The field editor draws its background and focus ring which completely ruin the whole user experience.
This is what I'm doing in the subclass of NSTextFieldCell:
- (NSText *)setUpFieldEditorAttributes:(NSText *)textObj
{
NSText *text = [super setUpFieldEditorAttributes:textObj];
[text setDrawsBackground:YES];
[text setBackgroundColor:[NSColor darkGrayColor]];
return text;
}
If I use setDrawsBackground:NO it's completely ignored and I get a white background. My solution is far from being good because I can't touch the alpha component of the color (if I do that, again the field editor will use another color as a background), but at least I don't get a white background.
I'm wondering if there's an actual solution to this problem. Do I have to provide my own field editor? Is it worth it?
What I want is simply a field editor with no background and no focus ring, just the cursor blinking.
Thanks!
The problem is that the white background is drawn by NSTableView when it's sent -editColumn:row:withEvent:select:. It fills the cell's rect with +[NSColor textBackgroundColor].
If there's a public API for overriding the current setting for named colors from the developer colorspace, we could set it inside an override of -editColumn:row:withEvent:select: or the like. I do not recall such an API (pointers are appreciated). ALSO: I've only tested this code on Snow Leopard (even the Leopard SDK addendum below). Verify the code against the actual SDKs and runtime environments you intend to support.
NSTableView has a private accessor it uses for the fill color, but it's a read-only property. No setter, so we can't just change the value on a standard NSTableView. We must subclass it. (Since you want the same behavior in an outlineView and NSOutlineView is already a subclass of NSTableView, we're going to subclass NSOutlineView. But, aside from the superclass, the code is identical.)
#interface ASCOutlineView : NSOutlineView {
}
#end
#implementation ASCOutlineView
- _textBackgroundColor
{
return ([NSColor clearColor]);
}
#end
seems to be all one needs to prevent that glaring white block from ruining your HUD when editing table cells in Snow Leopard.
Apps compiled against the Leopard SDK need a little more support though. Leopard's tableViews may have hard-coded some rendering properties so we need to override a choice method.
NSTextFieldCells are actually wrappers for NSTextViews so they can be used inside controls. They normally share the same textView instance, which is managed by the window (or its subclass, panel, in this case). NSTableView alters the settings of the NSTextFieldCell to conform to system UI settings for editing data. Mostly. The NSTextFieldCell then propagates those settings to the NSTextView. At any point along this pipeline we can override a method or two to alter the values of those properties to match our own UI.
I use -[NSTextFieldCell setDrawsBackground:] because it requires little effort to get correct. It's also important to keep the internal state as consistent with the effect we're hoping to achieve in the event some other object might depend on that state.
#interface ASCTextFieldCell : NSTextFieldCell {
}
#end
#implementation ASCTextFieldCell
- (void)setDrawsBackground: (BOOL)flag
{
[super setDrawsBackground: NO];
}
#end
And preventing the focus ring from appearing while the cell's being edited is a simple matter of changing the setting of its focus ring type. Frustratingly, IB doesn't provide access to this property, so it must be done programmatically:
for(eachColumn in [hudOutlineView tableColumns])
{
columnCell = [[ASCTextFieldCell alloc] initTextCell: #""];
[eachColumn setDataCell: columnCell];
if([columnCell respondsToSelector: #selector(setFocusRingType:)] != NO)
[(NSTextFieldCell *)columnCell setFocusRingType: NSFocusRingTypeNone];
}
It looks like there is other background behind field editor, which is drawn as white.
Probably, NSCell, or background of row, whatever else.

Resources