how to get window with semi-transparent blurred background - macos

I'd like to get a window that has a semi-transparent blurred background, just like what the Terminal can do. See this video, about 30 sec in, to see what I mean: http://www.youtube.com/watch?v=zo8KPRY6-Mk
See an image here: http://osxdaily.com/wp-content/uploads/2011/04/mac-os-x-lion-terminal.jpg
I've been googling for an hour, and can't get anything to work. I believe I need to somehow create a core animation layer and add a background filter, but I've been unsuccessful so far... I just see the gray background of my window. Here's the code I've got so far:
Code:
// Get the content view -- everything but the titlebar.
NSView *theView = [[self window] contentView];
[theView setAlphaValue:0.5];
// Create core animation layer, with filter
CALayer *backgroundLayer = [CALayer layer];
[theView setWantsLayer:YES];
[theView setLayer:backgroundLayer];
CIFilter *blurFilter = [CIFilter filterWithName:#"CIGaussianBlur"];
[blurFilter setDefaults];
[theView layer].backgroundFilters = [NSArray arrayWithObject:blurFilter];
[[theView layer] setBackgroundFilters:[NSArray arrayWithObject:blurFilter]];
Any tips or examples to do what I'm trying to do?
Thanks!

no need for layers and filters, NSWindow can do it itself
[mywindow setOpaque:NO];
[mywindow setBackgroundColor: [NSColor colorWithCalibratedHue:0.0 saturation:0.0 brightness:0.2 alpha:0.5]];
please do not use this, as it will alpha your title bar also (post it here just in case others need)
[mywindow setOpaque:NO];
[mywindow setBackgroundColor: [NSColor blackColor]];
[mywindow setAlphaValue:0.5];

For the transparency use Jiulong Zhao's suggestion.
For a blurred background use this
The call on a NSWindow :
[self enableBlurForWindow:self];
The function :
-(void)enableBlurForWindow:(NSWindow *)window
{
//!!!! Uses private API - copied from http://blog.steventroughtonsmith.com/2008/03/using-core-image-filters-onunder.html
CGSConnection thisConnection;
uint32_t compositingFilter;
int compositingType = 1; // Under the window
/* Make a new connection to CoreGraphics */
CGSNewConnection(NULL, &thisConnection);
/* Create a CoreImage filter and set it up */
CGSNewCIFilterByName(thisConnection, (CFStringRef)#"CIGaussianBlur", &compositingFilter);
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:2.0] forKey:#"inputRadius"];
CGSSetCIFilterValuesFromDictionary(thisConnection, compositingFilter, (__bridge CFDictionaryRef)options);
/* Now apply the filter to the window */
CGSAddWindowFilter(thisConnection, [window windowNumber], compositingFilter, compositingType);
}
NB: It uses a private API

For those reading this in 2017 and using Swift 4 and wanting to change your BG Alpha you can add the following to your custom NSWindow class:
self.backgroundColor = NSColor.black
self.backgroundColor = NSColor.init(calibratedHue: 0, saturation: 0, brightness: 0, alpha: 0.2)
p.s. I do not need the blur effect yet and when I do, I'll update the answer

Related

Capturing an offline NSView to an NSImage

I'm trying to make a custom animation for replacing an NSView with another.
For that reason I need to get an image of the NSView before it appears on the screen.
The view may contain layers and NSOpenGLView subviews, and therefore standard options like initWithFocusedViewRect and bitmapImageRepForCachingDisplayInRect do not work well in this case (they layers or OpenGL content well in my experiments).
I am looking for something like CGWindowListCreateImage, that is able to "capture" an offline NSWindow including layers and OpenGL content.
Any suggestions?
I created a category for this:
#implementation NSView (PecuniaAdditions)
/**
* Returns an offscreen view containing all visual elements of this view for printing,
* including CALayer content. Useful only for views that are layer-backed.
*/
- (NSView*)printViewForLayerBackedView;
{
NSRect bounds = self.bounds;
int bitmapBytesPerRow = 4 * bounds.size.width;
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
CGContextRef context = CGBitmapContextCreate (NULL,
bounds.size.width,
bounds.size.height,
8,
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
if (context == NULL)
{
NSLog(#"getPrintViewForLayerBackedView: Failed to create context.");
return nil;
}
[[self layer] renderInContext: context];
CGImageRef img = CGBitmapContextCreateImage(context);
NSImage* image = [[NSImage alloc] initWithCGImage: img size: bounds.size];
NSImageView* canvas = [[NSImageView alloc] initWithFrame: bounds];
[canvas setImage: image];
CFRelease(img);
CFRelease(context);
return canvas;
}
#end
This code is primarily for printing NSViews which contain layered child views. Might help you too.

Using layer backed NSView as NSDockTile contentView

Is there a way to use layer backed NSView as the contentView of a NSDockTile? Tried all sorts of tricks, but all I get is transparent area. Also tried going different route and get an image out of the CALayer and use that for [NSApp setApplicationIconImage:], but no luck either - I think the issue here is creating image representation for offscreen image.
As usual, I got my answer soon after posting the question :) I'll post it here for future reference: I solved it by creating NSImage out of the layer as described in Cocoa is my girlfriend blog post here http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/
The only missing piece is that in order to have anything rendered, a view must be added to a window, so using example code from the post, my solution is:
NSView *myView = ...
NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(-1000.0, -1000.0, 256.0, 256.0) styleMask:0 backing:NSBackingStoreNonretained defer:NO];
[window setContentView:myView];
NSUInteger pixelsHigh = myView.bounds.size.height;
NSUInteger pixelsWide = myView.bounds.size.width;
NSUInteger bitmapBytesPerRow = pixelsWide * 4;
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGContextRef context = CGBitmapContextCreate(NULL, pixelsWide, pixelsHigh, 8, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
[myView.layer.presentationLayer renderInContext:context];
CGImageRef image = CGBitmapContextCreateImage(context);
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:image];
CFRelease(image);
NSImage *img = [[NSImage alloc] initWithData:[bitmap TIFFRepresentation]];
[NSApp setApplicationIconImage:img];

iTunes or Xcode style information box at top of window

What are my options to recreate the box at the top of iTunes in Cocoa, or like Apple uses in XCode 4?
Is this just a plain image, with control on top? Or is it an NSBox with some custom style magic?
I had to code up something similar for one of my projects. For my solution, you will need the two categories available from the sample code in this tutorial. This code will draw the background gradient and the necessary shadows, it would be up to you to add additional content inside the control. Currently, the code will draw the Xcode style gradient as the background, but you could comment that out and uncomment the iTunes style one if that is what you need. Hope this helps.
#import "NSShadow+MCAdditions.h" // from the tutorial linked to above
#import "NSBezierPath+MCAdditions.h" // from the same tutorial
- (void)drawRect:(NSRect)dirtyRect {
static NSShadow *kDropShadow = nil;
static NSShadow *kInnerShadow = nil;
static NSGradient *kBackgroundGradient = nil;
static NSColor *kBorderColor = nil;
if (kDropShadow == nil) {
kDropShadow = [[NSShadow alloc] initWithColor:[NSColor colorWithCalibratedWhite:.863 alpha:.75] offset:NSMakeSize(0, -1.0) blurRadius:1.0];
kInnerShadow = [[NSShadow alloc] initWithColor:[NSColor colorWithCalibratedWhite:0.0 alpha:.52] offset:NSMakeSize(0.0, -1.0) blurRadius:4.0];
kBorderColor = [[NSColor colorWithCalibratedWhite:0.569 alpha:1.0] retain];
// iTunes style
/*
kBackgroundGradient = [[NSGradient alloc] initWithColorsAndLocations:[NSColor colorWithCalibratedRed:0.929 green:0.945 blue:0.882 alpha:1.0],0.0,[NSColor colorWithCalibratedRed:0.902 green:0.922 blue:0.835 alpha:1.0],0.5,[NSColor colorWithCalibratedRed:0.871 green:0.894 blue:0.78 alpha:1.0],0.5,[NSColor colorWithCalibratedRed:0.949 green:0.961 blue:0.878 alpha:1.0],1.0, nil];
*/
// Xcode style
kBackgroundGradient = [[NSGradient alloc] initWithColorsAndLocations:[NSColor colorWithCalibratedRed:0.957 green:0.976 blue:1.0 alpha:1.0],0.0,[NSColor colorWithCalibratedRed:0.871 green:0.894 blue:0.918 alpha:1.0],0.5,[NSColor colorWithCalibratedRed:0.831 green:0.851 blue:0.867 alpha:1.0],0.5,[NSColor colorWithCalibratedRed:0.82 green:0.847 blue:0.89 alpha:1.0],1.0, nil];
}
NSRect bounds = [self bounds];
bounds.size.height -= 1.0;
bounds.origin.y += 1.0;
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:bounds xRadius:3.5 yRadius:3.5];
[NSGraphicsContext saveGraphicsState];
[kDropShadow set];
[path fill];
[NSGraphicsContext restoreGraphicsState];
[kBackgroundGradient drawInBezierPath:path angle:-90.0];
[kBorderColor setStroke];
[path strokeInside];
[path fillWithInnerShadow:kInnerShadow];
}
I'd write a custom NSView. Draw the background and border yourself in drawRect:. You can use NSGradient to draw the filled background, or alternatively you could save it as an image and draw that. You could probably use a white NSShadow to get the embossed effect for the border.
Finally, to get your text on there, you could just add text fields as subviews of your new custom view.

Changing alpha of window background, not the whole window

so i have a quick question i have the method below which sets the alpha value of a window depending on the value from a slider, however the content of the window also becomes translucent and eventually disappears with the window.
Is there a way to just change the alpha value of the window and not the content view inside it?
- (IBAction)changeTransparency:(id)sender {
// Set the window's alpha value. This will cause the views in the window to redraw.
[self.window setAlphaValue:[sender floatValue]];}
Thanks, Sami.
Apple's docs gives a way to do this. The key is to set the window's backgroundColor's alpha to the desired value. You must also make sure to set the window's opaque property to NO (which is YES by default.)
e.x.
// At some point in your code...
[window setOpaque:NO];
// Then in your changeTransparency: method...
NSColor *backgroundColor = [window backgroundColor];
backgroundColor = [backgroundColor colorWithAlphaComponent:[sender floatValue]];
[window setBackgroundColor:backgroundColor];
Here is another way.
Suppose,
self.window <--- base view and this alpha will be changed (but exacatly fake).
subView1, subView2 <-- these views are contents of self.window. and theier alpha should not be changed.
self.window.backgroundColor = [UIColor clearColor];
UIView* anAlphaView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.window.frame.size.widht, self.window.frame.size.height)];
anAlphaView.backgroundColor = [UIColor blackColor]; // as you want
anAlphaView.alpha = 0.5f; // as you want.
[self.window addSubview:anAlphaView];
[anAlphaView release];
[self.window addSubview:subView1]; // you should add sub views to self.window
[self.window addSubview:subView2];
You can make a method using above code :)

Apply a CIFilter background filter when host window is transparent

I want to replicate the background of dock Stacks in grid and list mode. The background is translucent black with a blur effect:
Example of dock stack in grid mode http://www.thecustommac.com/wp-content/uploads/2009/09/stack-highlight.jpg
The problem is that [CALayer backgroundFilters] only applies to content in the window, the filters are not applied to content in other windows. Here's my code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
//make window transparent
self.window.backgroundColor = [NSColor clearColor];
[self.window setOpaque:NO];
[self.window setHasShadow:NO];
[self.window setStyleMask:NSBorderlessWindowMask];
//make the content view layer hosting
CALayer *rootLayer = [CALayer layer];
[[self.window contentView] setLayer:rootLayer];
[[self.window contentView] setWantsLayer:YES];
//blur the background contents - NOT WORKING!
[rootLayer setBackgroundColor:CGColorCreateGenericGray(0.0, .716)];
CIFilter *blurFilter = [CIFilter filterWithName:#"CIGaussianBlur"];
[blurFilter setDefaults];
[rootLayer setBackgroundFilters:[NSArray arrayWithObject: blurFilter]];
}
I can't think of how else to achieve this effect. (I've taken a look at the Display Services to see if there are any useful functions but I can't see any.)
Any ideas?
There is private API available this. Here is sample code by Rob Keniger:
In 10.5 you can add any core image filter to a window using the
private function 'CGSAddWindowFilter'.
typedef void * CGSConnectionID;
extern OSStatus CGSNewConnection(const void **attr, CGSConnectionID *id);
- (void)enableBlurForWindow:(NSWindow *)window
{
CGSConnectionID _myConnection;
uint32_t __compositingFilter;
int __compositingType = 1; // Apply filter to contents underneath the window, then draw window normally on top
/* Make a new connection to CoreGraphics, alternatively you could use the main connection*/
CGSNewConnection(NULL , &_myConnection);
/* The following creates a new CoreImage filter, then sets its options with a dictionary of values*/
CGSNewCIFilterByName (_myConnection, (CFStringRef)#"CIGaussianBlur", &__compositingFilter);
NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:3.0] forKey:#"inputRadius"];
CGSSetCIFilterValuesFromDictionary(_myConnection, __compositingFilter, (CFDictionaryRef)optionsDict);
/* Now just switch on the filter for the window */
CGSAddWindowFilter(_myConnection, [window windowNumber], __compositingFilter, __compositingType );
}
The filters are not being used by the root layer:
From the docs:
/* An array of filters that are applied to the background of the layer.
* The root layer ignores this property. Animatable. */
#property(copy) NSArray *backgroundFilters;
Your code has some error, here is the code that just works:
typedef void * CGSConnection;
extern OSStatus CGSNewConnection(const void **attributes, CGSConnection * id);
-(void)enableBlurForWindow:(NSWindow *)window {
CGSConnection thisConnection;
NSUInteger compositingFilter;
NSInteger compositingType = 1 << 0;
// Make a new connection to Core Graphics
CGSNewConnection(NULL, &thisConnection);
// Create a Core Image filter and set it up
CGSNewCIFilterByName(thisConnection, (CFStringRef)#"CIGaussianBlur", &compositingFilter);
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:2] forKey:#"inputRadius"];
CGSSetCIFilterValuesFromDictionary(thisConnection, compositingFilter, (__bridge CFDictionaryRef)options);
// Apply the filter to the window
CGSAddWindowFilter(thisConnection, [window windowNumber], compositingFilter, compositingType);
}
Then, use it:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[_window setOpaque:NO];
[_window setBackgroundColor: [NSColor colorWithCalibratedHue:0.568 saturation:0.388 brightness:0.941 alpha:0.6]];
[self enableBlurForWindow:_window];
}
https://github.com/dsns/GaussianBlur_NSWindow_os_x_mavericks
its looks like crutch code for mavericks app
but it working

Resources