NSWindow: place part of image outside window - objective-c

I've a Cocoa application with a NSWindow with the style NSBorderlessWindowMask (without titlebar). I would like to place a image in the window but a part of the image should be places outside of the window.
How can this be done?

Here are two articles I found related to what you want to do.
Cocoa With Love Example
parmanoir.com example
The gist is to subclass NSWindow to make it a borderless transparent window, then make a sub view that draws your custom shape and make it the windows content view.
From the look of the sample the shadow should still apply.
From Cocoa With Love:
The shadow behind the window is drawn automatically for whatever shape we draw. Any part of the window that is left completely clear will not receive mouse clicks (they will fall through the window).
to draw the border do something like this in your view class display method:
NSBezierPath* border = [NSBezierPath bezierPathWithRect:self.frame];
[border setLineWidth: 1.0];
[[NSColor windowFrameColor] set];
[border stroke];
If you don't have a custom view class then do [view lockFocus]; before doing that path and replace self with your view instance. after drawing be sure to do [view unlockFocus];
An important message from the docs concerning lockFocus:
Hiding or miniaturizing a one-shot window causes the backing store for that window to be released. If you don’t use the standard display mechanism to draw, you should use lockFocusIfCanDraw rather than lockFocus if there is a chance of drawing while the window is either miniaturized or hidden.
Another way to do this that for sure would keep the shadow would be to use two windows and make one a child to the other.
You will not ever be able to draw outside of a window for various reasons, not the least of which being your process needs to own or have permission for what it draws to (many other reasons too).

Related

Identifying correct window frame size for filling background color

I am developing in Cocoa, and I am currently having problems with filling the background of a NSWindowController.
I understand that subclassing is the way forward if you want to customise your cocoa app. So I created a custom NSView named whiteView and added this view as a subview to my windowController's contentView; however, there are some issues with completely filling the background of the window. Can anyone explain how I can have the color cover the complete surface area of the window's frame pls. Thank you
These are the results that I have so far.
1) This is the window when I leave it as it is, notice the white color only having covered half of the window.
2)Here is the same window again when I adjust the window far to the right and bottom. The white screen seems to stretch enough so that it covers the elements.
This is how I create the custom view
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
[[NSColor whiteColor] set];
NSRectFill([self bounds]);
}
And this how I achieve plaster the view onto my window.
WhiteView *whiteBackgroundView = [[WhiteView alloc] initWithFrame:self.window.frame];
[self.window.contentView addSubview:whiteBackgroundView positioned:NSWindowBelow relativeTo:self.window.contentView];
What do I need to do to correctly allow for my window's background to be fully covered in white?
First, the simple solution is to use -[NSWindow setBackgroundColor:] to just set the window's background color. No need for a view.
If you're still interested in how to fix the view-based approach, probably what's wrong is that you haven't set the autoresizing mask of the view to make it follow the changes in the window size. For example, you could do [whiteBackgroundView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable].
However, you could also set the whiteBackgroundView as the window's contentView rather than as a subview of it. The window's content view is always kept at the size necessary to fill the window's content rect. All of the other views of your window would be subviews of the white background view. In my opinion, this is better than making it a sibling that just happens to be at the back. Using relative ordering among siblings views to achieve a particular rendering order is a hack.
Finally, there's no reason to invoke super's implementation in your -drawRect: if the superclass is NSView itself. NSView doesn't do any drawing in its -drawRect:. Also, your subclass takes over full responsibility for the entire drawn contents of its bounds, so you'd overdraw whatever super had drawn, anyway. (Also, you need only fill dirtyRect rather than [self bounds].)
While you're at it, since your class fills its bounds, you should override -isOpaque to return YES for optimization.
Update: regarding the frame of the view: if it's not going to be the window's content view, then you want to set its frame to be its prospective superview's bounds. So, you should have used self.window.contentView.bounds if you wanted whiteBackgroundView to fill the content view.
More generally, if you want the content rect of a window, you would do [window contentRectForFrameRect:window.frame]. But if a view is going to be a window's content view, there's no need to set its frame to anything in particular. It will be resized automatically.
Update 2:
To transfer the view hierarchy from the original content view to the new content view (when you're making the white background view the content view):
NSArray* subviews = [self.window.contentView.subviews copy];
[subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
[whiteBackgroundView setSubviews:subviews];
[subviews release];
(Written for manual retain-release. If using ARC, just drop the -release invocation.)
Regarding the frame to use, as mentioned in the first update: keep in mind that the view's frame should be expressed in the coordinate system of its superview. So, as I said, self.window.contentView.bounds would work if you're putting the new view into the content view. The window's frame and content rect are in screen coordinates. They would be completely incorrect for positioning a view.

Create dragable view for selecting screen area for screen recording in mac

can anyone give idea me how can i create this type of view. In which i can drag a part of mac screen for screen recording(the image shown below is the only example)
For this kind of thing you need what is called a cover window.
It's a type of borderless window that happens to take up the full screen.
Within that you need a draggable and resizable view.
Those are two separate things that can be easily implemented but you will benefit most by doing the rest of the footwork yourself to find out how to code these.
Some keywords that might help.
NSWindow
NSBorderlessWindowMask
NSView
NSViewController
NSTrackingArea
NSBezierPath
NSRect
CGRect
NSEvent
NSPoint
CGPoint

NSWindow: alternative to -setOpaque:NO

I want to have a window which is like QuickTime X window. An all opaque window with rounded corners.
I've obtained it implementing a custom borderless NSWindow with:
[window setOpaque:NO];
[window setBackgroundColor: [NSColor clearColor]];
and a custom NSView with:
- (void)drawRect:(NSRect)rect
{
NSBezierPath* thePath = [NSBezierPath bezierPath];
[thePath appendBezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
[thePath fill];
}
It works as expected but the window becomes noticeably slow when it gets resized fast.
I've identified that this slowdown is given by -setOpaque:NO; if I remove that line, the window can be resized fast again but corners are obviously no more rounded.
Is there a way to avoid using -setOpaque:NO and still be able to have rounded corners? Maybe one can have a window which is all opaque except for the corners?
The view is a NSOpenGLView so I can leverage on OpenGL if it may helps.
See this Apple developer example: https://developer.apple.com/library/mac/#samplecode/RoundTransparentWindow/Introduction/Intro.html
In Quartz/Core Graphics, opaque rects are faster to composite than non-opaque rects.
But an opaque window means you need to draw a rectangle and anything you didn't fill would be black.
If you want a custom window that is not a sharp-edged rectangle, you must set the window to be non-opaque.
Anyway, this is almost certainly, not your main bottleneck.
First thing is to stop doing so much stuff in drawRect:
Make that NSBezierPath a property.
Only replace it when the rect is actually changing size.
Only do that in viewWillDraw: or some earlier point.
Maybe one of the view or window resize NSNotifications or a delegate method.
In drawRect: you should do only drawing really, as much as possible.
You should only redraw what you need to, as much as possible.
This is still small, and probably not your bottleneck.
You'll need to examine the drawing in ALL of the views in your window.
Your window is really the root CGContext (drawing context) that you get access to on OS X. Everything in your context is all of your subviews.
Every subview is a potential drawing bottleneck.
An OpenGL view drawing during live resize of the window sounds like a prime candidate.
You might throttle frame rates or something during live resize of the view or the window.
That should get it a little snappier.
Beyond that, you'll notice from NSWindow and NSView classes that not drawing during live resize IS a performance win.
See the NSView Class documentation and specifically the methods noted under the section Managing Live Resize
inLiveResize
preservesContentDuringLiveResize
getRectsExposedDuringLiveResize:count:
rectPreservedDuringLiveResize
viewWillStartLiveResize
viewDidEndLiveResize
Those last two look like a great place to sandwich some reduction in drawing,
Try using the setAlphaValue method of NSWindow.

How to implement a custom Focus Ring in drawRect for NSTextField or NSTextVew

I want to draw a custom focus ring for my NSTextView subclass (which doesn't have a focus ring by default). I managed to implement it by overriding the parent NSScrollView drawRect and adding this code:
- (void)drawRect:(NSRect)dirtyRect {
if (focused) {
NSSetFocusRingStyle(NSFocusRingOnly);
NSRectFill(dirtyRect);
}
[super drawRect:dirtyRect];
}
However, I want to draw my own, custom focus ring. I have searched and searched for examples of this, and tried messing around and writing it myself, to no avail. The biggest issue I have is the fact that it will get cropped to the NSScrollView/NSTextView frame, no matter how I do it.
Thanks.
Updating this answer for 10.7+:
Now you should override drawFocusRingMask to render (simply drawing a shape; the system will take care of color/style), and override focusRingMaskBounds to hint at its boundaries. Also, call noteFocusRingMaskChanged if you change the shape in some way that the system could not figure out on its own.
(Below is the previous answer, requiring older APIs:)
In the Carbon framework there are HIThemeBeginFocus() and HIThemeEndFocus(), which allow you to cause any series of drawings (such as a rectangle or shape) to have an automatic "focused" appearance. Requires Mac OS X 10.5 or later.
This uses Core Graphics directly. To find the CG context from a drawRect: method in Cocoa, you'd do something like:
NSGraphicsContext* contextMgr = [NSGraphicsContext currentContext];
CGContextRef drawingContext = (CGContextRef)[contextMgr graphicsPort];
As far as avoiding clipping, one option is to use a parent view (such as an NSBox that has no border) to give extra padding. Perform the custom drawing at an inset location in the parent view that won't be clipped; in other words, give the illusion that the view is a bit smaller than its actual rectangle.

Objective-C can someone explain how to programmatically display a view

I'm just not getting this, I want to display a view in my app when a user clicks a button.
I've gotten this far:
NSView* unitMarker = [[NSView alloc] initWithFrame: NSMakeRect( 20.0, 20.0, 80.0, 50.0 ) ];
How would I display this view with a red background?
Thanks
#Aaron: he says he wants to do this programmatically. Also he's using NSView, not UIView, so an iPhone tutorial would be almost irrelevant.
#Mike: Your description of what you want to do is a bit vague. We have a bit of code creating, a view, but no context to tell us exactly what you are trying to do. Do you want the view to come up in a new window or the same? Do you want to replace a view that's already there? We don't even really know your skill level.
I suggest you go check out the documentation for NSView: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/Reference/NSView.html
I've gotten this far:
NSView* unitMarker = [[NSView alloc] initWithFrame: NSMakeRect( 20.0, 20.0, 80.0, 50.0 ) ];
How would I display this view with a red background?
You wouldn't, because a plain NSView doesn't draw anything.
First, you need to subclass NSView, implement drawRect: in that subclass to fill its bounds with red, and instantiate that subclass instead of NSView directly.
You should read the View Programming Guide.
How would I display this view …
You wouldn't. The view displays itself when it is appropriate to do so.
It won't ever be appropriate for it to draw itself until you add it to a view hierarchy. Every window has one, rooted at its content view. You need to add this view either to a content view or to some descendant view (subview, subview of a subview, etc.) of a content view.
You normally should not tell a view to display from your controller. That's the window's job. When you do change a property or properties of the view that affect what it draws, set the view as needing display, and let the window tell the view to display when it's appropriate to do that.
Say this out loud 10 times repeatedly:
Interface Builder is my friend.
I was able to put together a sample project doing exactly what you want to do in about 5 minutes by leveraging Interface Builder as part of the process. http://www.markdouma.com/developer/ShowWindowWithRedView.zip.
You can't show a view without placing it in a window first; by far the easiest way to do this kind of a thing is to drag out a second window in Interface Builder, set it to not be visible on launch, drag a generic NSView custom view onto the Window, set its class to be SRRedView (your red view subclass). As d11wtq posted, you override NSView's primitive drawing method like he shows (though personally I prefer NSBezierPath :-P).
In your controller class, you define IBOutlets, and then hook those up in Interface Builder. These provide you with a way to reference the important parts of your interface so that you can manipulate them programmatically.
I added one IBAction method, which the button in the main window is hooked up to call. That method simply tells the second window to show itself.
- (IBAction)showWindowWithRedView:(id)sender {
[windowWithRedView makeKeyAndOrderFront:nil];
}
In the 8 years or more that I've been doing Cocoa programming, I don't think I've ever needed to resort to manually creating windows and views. It has always been much faster to simply load another nib file that contains the windows or views I need to display. Using Interface Builder along with Xcode to create your app is quite a bit different than how other IDEs work. (Specifically, when you arrange stuff in Interface Builder you're not generating code as much as you are creating instances of UI objects and then "freeze-drying" them in their current arrangement into a .nib archive file. When you launch the app, they are brought back to life. Or at least that's the way I think about it).
You need to subclass NSView and implement drawRect:.
In this case you're just going to fill the entire rect with red.
#interface MyView : NSView {
}
#end
#implementation MyView
-(void)drawRect:(NSRect)dirtyRect {
[[NSColor redColor] set];
NSRectFill(dirtyRect);
}
#end
That's an extremely basic example that does what you want, but if you want to do more complex things (adding borders (strokes) or drawing curves (paths) etc), then you need to learn all the drawing classes available to you.
Scott Stevenson has written some easy to follow tutorials on this:
http://cocoadevcentral.com/d/intro_to_quartz/ and;
http://cocoadevcentral.com/d/intro_to_quartz_two/