Unrecognised selector sent to instance when adding tracking area - objective-c

I have an NSImage in an NSPanel in which I'd like to get the mouse X, Y co-ords.
If I set the NSImage layer content withlayer.content =splitNSImage (where splitNSImage is an NSImage created from a URL)and then
NSRect layerBounds = [layer bounds];
NSTrackingAreaOptions options = (NSTrackingActiveAlways|NStrackingInVisibleRect|NSTrackingMouseEnter|
NSTrackingMouseExit|NSTrackingMouseMoved);
NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:layerBounds owner:self userinfo:nil];
[[layer contents] addTrackingArea:trackingArea];
I get a runtime error "Thread 1: "-[NSImage addTrackingArea:]: Unrecognised selector sent to instance 0x600003335400" on the last line.
If I use the use [NSPanel contentView] instead of [layer contents] it works OK but gives the mouse co-ords (in the MouseMoved event) in the panel rather than the imageView

Related

Cocoa resize window to fit screen

I have 2 monitors, and I'd like to get my videoWindow to be placed and scaled to the size of the second monitor. I'd like to do this programmatically, as the second monitor resolution may change. I'm able to get the window placed in the bottom-left of the second monitor, but I'm not able to scale it to fit.
The warning on this line:
[self.videoWindow setFrame: screenRect];
Is: 'NSWindow' may not respond to 'setFrame'
// inside my .h file
#property (assign) IBOutlet NSWindow *videoWindow;
// inside my .m file
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
[super windowControllerDidLoadNib:aController];
NSRect videoPreviewScreenRect;
NSArray *screenArray = [NSScreen screens];
//Using index of 1, to get secondary monitor
NSScreen *videoPreviewScreen = [screenArray objectAtIndex: 1];
NSRect screenRect = [videoPreviewScreen frame];
videoPreviewScreenRect = [videoPreviewScreen visibleFrame];
// Get and set the screen origin based on the second monitors origin
NSPoint videoScreenOrigin ;
videoScreenOrigin.x = videoPreviewScreenRect.origin.x;
videoScreenOrigin.y = videoPreviewScreenRect.origin.y;
[self.videoWindow setFrameOrigin: videoScreenOrigin];
// **** THIS LINE DOESN'T WORK ****
[self.videoWindow setFrame: screenRect];
[self.videoWindow setBackgroundColor: NSColor.blackColor];
[self.videoWindow display];
[self.videoWindow makeKeyAndOrderFront:nil];
}
I was able to figure out what the issue was.
[self.videoWindow setFrame: screenRect];
needed to be changed to this:
[[self videoWindow] setFrame:screenRect display:YES animate:NO];

Draw into a NSView via mouse event

i am new to programming, objective-c (and stackoverflow). I'm learning and moving forward very slowly ;) but then I ran into a problem, which google couldn't solve. I have a single window and a NSview and then added a mouse event that should draw the coordinates into my view, but it doesn't. The funny thing is: it is drawn when the mouse moves over the window buttons of my apps window...
- (void)drawRect:(NSRect)dirtyRect {
NSPoint imagePos = NSMakePoint(0, 0);
NSImage *aImage = [NSImage imageNamed:#"mw_bg01.png"];
[aImage dissolveToPoint:imagePos fraction:1.0];
}
- (void)mouseDown:(NSEvent*)theEvent;{
mouseLoc = [NSEvent mouseLocation];
mousePosX = mouseLoc.x;mousePosY = mouseLoc.y;
NSString* mouseString = [NSString stringWithFormat:#"%d", mousePosX];
NSPoint textPoint = NSMakePoint(5, 5);
NSMutableDictionary *textAttrib = [[NSMutableDictionary alloc] init];
[textAttrib setObject:[NSFont fontWithName:#"Helvetica Light" size:10]
forKey:NSFontAttributeName];
[textAttrib setObject:[NSColor grayColor] forKey:NSForegroundColorAttributeName];
[mouseString drawAtPoint:textPoint withAttributes:textAttrib];
}
I don't know how to go on, any suggestions? Thanks!
You shouldn't do drawing in the -mouseDown: method. Rather, you must do all your drawing in -drawRect: (or methods that you call from -drawRect:). Try something like this:
#interface MyView ()
#property NSPoint lastMousePoint;
#end
#implementation MyView
- (void)drawLastMousePoint
{
NSString *mouseString = NSStringFromPoint(self.lastMousePoint);
NSPoint textPoint = NSMakePoint(5, 5);
NSMutableDictionary *textAttrib = [[NSMutableDictionary alloc] init];
[textAttrib setObject:[NSFont fontWithName:#"Helvetica Light" size:10]
forKey:NSFontAttributeName];
[textAttrib setObject:[NSColor grayColor] forKey:NSForegroundColorAttributeName];
[mouseString drawAtPoint:textPoint withAttributes:textAttrib];
}
- (void)drawRect:(NSRect)dirtyRect
{
NSPoint imagePos = NSMakePoint(0, 0);
NSImage *aImage = [NSImage imageNamed:#"mw_bg01.png"];
[aImage dissolveToPoint:imagePos fraction:1.0];
[self drawLastMousePoint];
}
- (void)mouseDown:(NSEvent*)theEvent;
{
self.lastMousePoint = [theEvent locationInWindow];
[self setNeedsDisplay:YES];
}
#end
When you get a mouse down event, you simply store the location of the mouse down. The drawing is done in -drawLastMousePoint which you call in your -drawRect: method. Since you know you need to redraw anytime the mouse is clicked, you call -setNeedsDisplay: to inform the view that it needs to be redrawn. Note that the redraw doesn't happen immediately, rather, it will happen the next time through the run loop. In other words, you're saying "hey, something changed, and I need to draw my view's contents again. Please call -drawRect: again as soon as possible!"
One other note: +[NSEvent mouseLocation] is really designed for getting the current mouse location outside the event stream. Typically, in a -mouseDown: method, you call -locationInWindow on the NSEvent passed as the argument to the method. If you need to convert to local/view coordinates, you should call [self convertPoint:[theEvent locationInWindow] fromView:nil];.

Unrecognized Selector - [NSView borderRect]

I'm having trouble getting the NSRect of a view's border. I keep getting an unrecognized selector error.
Here is the code:
NSGradient *BorderGradient = [[NSGradient alloc] initWithStartingColor:[NSColor blackColor] endingColor:[NSColor whiteColor]];
[BorderGradient drawInRect:[self.window.contentView borderRect] angle:-90];
// Unrecognized Selector error here
I'm trying to access the border rect to add a color gradient to the border. The code also crashes when I try to access the borderRect by itself, like this:
NSRect rect = [self.window.contentView borderRect];
NSLog(#"origin.x = %f", rect.origin.x);
And if trying to convert the drawInRect: to CGRect. Like so:
NSGradient *BorderGradient = [[NSGradient alloc] initWithStartingColor:[NSColor blackColor] endingColor:[NSColor whiteColor]];
[BorderGradient drawInRect:NSRectToCGRect([self.window.contentView borderRect]) angle:-90];
// Unrecognized Selector error here
Any help would be greatly appreciated.
Thanks!
NSView doesn't have a method named -borderRect. Is it a custom method you've implemented? NSBox does have a -borderRect method. Are you expecting your window's content view to be an instance of NSBox? Have you checked to see that that's actually true.
Perhaps you really want -frame or -bounds?
NSView (what is returned from self.window.contentView) does not have a borderRect method, which is causing the Unrecognized Selector error.
You probably want frame or bounds instead.

Programmatically create NSWindow with specific size fails

I'm trying to programmatically create a borderless NSWindow close to the mouse cursor and with the size of the image it should display. However I always get a much larger window (each side is about three times the size it should be)! I double-checked that my imageObj has the correct size and that all the NSSize and NSRect structures are created with correct values. The commented-out line doesn't work either (however which would be correct?)
I already searched through this site but I couldn't find any similar issue... What am I doing wrong?
Here is my code:
NSString* imageName = [[NSBundle mainBundle] pathForResource:#"os_unknown" ofType:#"icns"];
NSImage* imageObj = [[NSImage alloc] initWithContentsOfFile:imageName];
[view setImage:imageObj];
NSSize s = [imageObj size];
NSPoint p = [NSEvent mouseLocation];
NSRect r = [NSWindow frameRectForContentRect:NSMakeRect(p.x, p.y, s.width, s.height)
styleMask:NSBorderlessWindowMask];
//NSRect r = {p, s};
if (win) [win release];
win = [[NSWindow alloc] initWithContentRect:r
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:false];
[win setLevel:kCGUtilityWindowLevel];
[view setBounds:NSMakeRect(0, 0, s.width, s.height)];
[[win contentView] addSubview:view];
[win orderFront:sender];
Here *win is an NSWindow and *view is an NSImageView. Both are declared on top of my source file.
I'm running Snow Leopard so any strictly >10.6.8 code won't work (already tried something).
Thank you in advance for any help.
Solved! The NSImageView *view was created in Interface Builder. The size I got was the size set in there, no matter what code I used to resize it. By creating the NSImageView programmatically everything works great!
Special thanks to rdelmar who made me think and get the solution!

Is it possible to draw in the label area of NSToolbar?

I have a NSToolbarItem that uses a view similar to the Xcode status view. It currently has no label, but I can't figure out a way to draw into the area where the item label would normally be drawn. I would like the view to extend into that area just as the Xcode status view does. I know the very bottom portion of pixels of NSToolbar is out of bounds, but I have seen other applications draw into the label area. Any ideas?
Edit: For clarification, this is the status view I'm referring to in Xcode:
I want the bounds of my view to extend past the label area of the toolbar just as the view in Xcode does.
The Xcode status view is not an NSToolbarItem is a custom NSView inserted in the NSToolbar.
If you log
NSLog(#" %#", [[self.window.contentView superview] subviews]);
You will get
NSToolbarView doesn't autoresize it's subviews so you have issues with centering it.
And [self.window.contentView superview] doesn't contain the toolbarview when full screen.
You can add the view you want in the center of the toolbar to the [self.window.contentView superview] when not in fullscreen and position it properly. It will autoresize and stay centered.
When switching to full screen remove it from [self.window.contentView superview] and add it to NSToolbarView in the center that way it stays in the toolbar and it also moves down with the toolbar when you reveal the status bar.
You can get the toolbar view by iterating thru the subviews or with a private method
[[self.window toolbar] performSelector:#selector(_toolbarView)];
Update:
I did a little more digging with the debugger and I found out that this is what Xcode does. At least while not in full screen.
thealch3m1st$ sudo lldb
(lldb) process attach -p 11478
Process 11478 stopped
Executable module set to "/Applications/Xcode.app/Contents/MacOS/Xcode".
Architecture set to: x86_64.
(lldb) po [NSApplication sharedApplication]
(id) $0 = 0x000000040013f5e0 <IDEApplication: 0x40013f5e0>
(lldb) po [$0 mainWindow]
(id) $1 = 0x0000000000000000 <nil>
(lldb) po [$0 windows]
(id) $2 = 0x0000000408278460 <__NSArrayM 0x408278460>(
<IDEWelcomeWindow: 0x40141c1e0>,
<IDEWorkspaceWindow: 0x401ef2780>,
<NSComboBoxWindow: 0x402019be0>,
<NSWindow: 0x4022adc60>,
<IDEOrganizerWindow: 0x402951b20>
)
(lldb) po [$0 windows]
(id) $3 = 0x0000000408820300 <__NSArrayM 0x408820300>(
<IDEWelcomeWindow: 0x40141c1e0>,
<IDEWorkspaceWindow: 0x401ef2780>,
<NSComboBoxWindow: 0x402019be0>,
<NSWindow: 0x4022adc60>,
<IDEOrganizerWindow: 0x402951b20>
)
(lldb) [$3 objectAtIndex:1]
error: '[$3' is not a valid command.
(lldb) po [$3 objectAtIndex:1]
(id) $4 = 0x0000000401ef2780 <IDEWorkspaceWindow: 0x401ef2780>
(lldb) po [$4 contentView]
(id) $5 = 0x0000000401ef0920 <NSView: 0x401ef0920>
(lldb) po [$5 superview]
(id) $6 = 0x0000000401ef2e20 <NSThemeFrame: 0x401ef2e20>
(lldb) po [$6 subviews]
(id) $7 = 0x0000000401ef3800 <__NSArrayM 0x401ef3800>(
<_NSThemeCloseWidget: 0x401ef3120>,
<_NSThemeWidget: 0x401ef3b80>,
<_NSThemeWidget: 0x401ef40e0>,
<NSView: 0x401ef0920>,
<IDEActivityView: 0x4020cd700>,
<_NSThemeFullScreenButton: 0x402017b20>,
(<NSToolbarView: 0x4020192e0>: Xcode.IDEKit.ToolbarDefinition.Workspace),
<DVTDualProxyWindowTitleView: 0x40225e0a0>,
<NSThemeDocumentButton: 0x402698020>
)
(lldb) po [$7 objectAtIndex:4]
(id) $8 = 0x00000004020cd700 <IDEActivityView: 0x4020cd700>
(lldb) [$8 setHidden:YES]
error: '[$8' is not a valid command.
(lldb) po [$8 setHidden:YES]
(id) $9 = 0x0000000000000000 <nil>
(lldb) continue
Process 11478 resuming
(lldb)
And the activity view is gone :)
While in full screen however. It doesn't add it to NSToolbarView it adds it to NSNextStepFrame which is NSToolbarView's superview. The toolbar is not contained in the window's contentview superview when in full screen. I think it has something to do with full screen behavior and spaces.
You have to subclass NSToolbarItem:
- (id)initWithItemIdentifier:(NSString *)itemIdentifier {
self = [super initWithItemIdentifier:itemIdentifier];
if (self) {
self.hideLabel = NO;
}
return self;
}
- (NSView *)view {
NSView *view = [super view];
if (self.hideLabel) {
CGRect frame = view.frame;
frame.size.height = 45.0f;
frame.origin.y = 8.0f;
view.frame = frame;
}
return view;
}
- (NSString *)label {
return self.hideLabel ? #"" : [super label];
}
Create a toolbar:
NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:#"Toolbar"];
toolbar.delegate = self;
self.window.toolbar = toolbar;
Use NSToolbarDelegate to fill your toolbar with items:
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar {
return [NSArray arrayWithObjects:#"Button", #"LCD", NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, nil];
}
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar {
return [NSArray arrayWithObjects:#"Button", NSToolbarFlexibleSpaceItemIdentifier, #"LCD", NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, nil];
}
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag {
MyToolbarItem *item = [[MyToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
if ([itemIdentifier isEqualToString:#"LCD"]) {
item.view = self.lcdView;
item.hideLabel = YES;
} else if ([itemIdentifier isEqualToString:#"Button"]) {
item.label = NSLocalizedString(#"Button", nil);
item.image = [NSImage imageNamed:#"Button"];
item.hideLabel = NO;
}
return item;
}
The lcd view should be (in this case) 32 points high before you hand it over to the toolbar item. If it's bigger, the toolbar will be too high.
The Xcode status view is actually a separate window floating over the toolbar. (This is easily tested: press ⇧⌘4 and press space to take a screen shot of a window, and hover the mouse over it.)
This code installs a window floating on top of the toolbar.
-(void)applicationWillFinishLaunching:(NSNotification *)aNotification {
NSRect winframe = [self.window frame];
NSRect viewrect = NSMakeRect(0, 0, 400, 50);
NSRect winrect = viewrect;
winrect.origin.x = NSMidX(winframe) - NSMidX(winrect);
winrect.origin.y = NSHeight(winframe) - NSHeight(winrect) - 18;
NSWindow* win = [[[NSWindow alloc] initWithContentRect:winrect styleMask: NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: NO] autorelease];
[win setBackgroundColor:[NSColor clearColor]];
[win setOpaque:NO];
[win setIgnoresMouseEvents:YES];
MyStatusView* v = [[[MyStatusView alloc] initWithFrame:viewrect] autorelease];
[win setContentView: v];
[self.window addChildWindow:win ordered:NSWindowAbove];
}
The iTunes-XCode-LCD that extends in the label area is not a NSToolbarItem. Since NSToolbar isn't a NSView, you cannot add a subview to a NSToolbar instance.
But you can add a custom view directly in the window frame, that can be accessed through the contentView.superview property path of the NSWindow instance!
I.e. make your own subclass of NSWindowController and put some code like this in the 'windowDidLoad' method:
- (void)windowDidLoad
{
[super windowDidLoad];
NSImage *image = [NSImage imageNamed:#"lcd"];
NSRect lcdFrameRect = NSMakeRect(self.window.frame.size.width / 2 - image.size.width/2, self.window.frame.size.height - image.size.height - 20,
image.size.width, image.size.height);
NSImageView *lcdView = [[NSImageView alloc] initWithFrame: lcdFrameRect];
[lcdView setImage: image];
lcdView.autoresizingMask = NSViewMinYMargin | NSViewMinXMargin | NSViewMaxXMargin;
NSView * contentView = self.window.contentView;
[contentView.superview addSubview: lcdView];
}
This code will not work in Lion's full-screen mode, since the frame window isn't drawn when in fullscreen. To fix this, the view can be moved in a floating window, child of the main one (just check the NSWindow addChildWindow:ordered: method).