Float an NSView above a window being used by DVDPlayback framework - macos

I am trying to write a simple DVD player using Mac OS X's DVDPlayback.framework. I can get the DVD to play within a window, but ultimately I want this to run as a full-screen app.
I'm having difficulty adding a sub-view to display media controls whilst the DVD is playing (pause / play, progress slider to scroll through the movie, change chapter etc).
It seems that if I create a sub-view (NSView) within the window being used by the DVD framework, it always seems to fall behind the DVD content, even if I tell the NSView to be at the top-most level.
Here's the simplified code, which just tries to create a white sub-view within a region of the window:
(I've tried the code on 10.6 and 10.7 with the same results).
const BOOL PLAY_DVD = YES;
#interface ControlsView : NSView {
}
#end
#implementation ControlsView
- (void)drawRect:(NSRect)rect {
[[NSColor whiteColor] set];
NSRectFill(rect);
}
#end
#implementation AppDelegate
#synthesize window = _window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSView *view = self.window.contentView;
if (PLAY_DVD) {
NSLog(#"%# Playing DVD video", [self class]);
// Register error handler
OSStatus err;
err = DVDInitialize();
if (err != noErr) {
NSLog(#"DVDInitialise failed with error code %d", err);
[NSApp terminate:self];
}
// Set window to be the main window
err = DVDSetVideoWindowID([self.window windowNumber]);
// Set the display...
CGDirectDisplayID display = (CGDirectDisplayID) [[[[self.window screen] deviceDescription] valueForKey:#"NSScreenNumber"] intValue];
Boolean isSupported;
err = DVDSwitchToDisplay(display, &isSupported);
// Set video bounds
NSRect frame = [self.window frame];
CGRect rect = CGRectMake(0, 0, frame.size.width, frame.size.height);
err = DVDSetVideoCGBounds(&rect);
FSRef ref;
DVDOpenMediaFileWithURL([NSURL URLWithString:#"file:///Path/to/my/TestDVD/VIDEO_TS"]);
DVDOpenMediaFile(&ref);
DVDPlay();
}
// Attempt to add a subview to show the controls...
ControlsView *controls = [[ControlsView alloc] initWithFrame:NSMakeRect(20, 20, 100, 50)];
[view addSubview:controls positioned:NSWindowAbove relativeTo:nil];
}
#end
If PLAY_DVD is NO, the subview is correctly rendered (and I can create other sub-views and show that the ordering is correct).
If PLAY_DVD is YES, the media starts playing, but the sub-view is never visible because it always seems to fall behind the video.
The only examples of DVD playback I've been able to find have had the controls in a second window, but for a full-screen application I'd like the controls to be part of the full-screen view and to fade in/out when required.
Does anyone know how best to do this? Do my full-screen controls have to be in a separate window which floats above the full-screen window? I've not been able to find an example which has the controls and the DVD playback in the same window.
Thanks in advance for your help!

Related

How to add watermark to Screen (NSScreen) in macOS

I am making an application that will add a watermark to the live screen on Mac. Which API should I choose? I tried NSScreen but I didn't find any method that could add a view to NSScreen.
Such app like Sakura in Mac Appstore.Please check it out,i have no idea which API should i use.
The easiest solution is to define a custom, transparent, window.
When you create the window, you specify the special BorderlessWindowMask. This creates a window that is a simple rectangular area on the screen with no titlebar, edges, etc.
Then you set up a bunch of properties so that
the window floats above the other windows
it doesn't respond to events
it doesn't have a shadow
its background is transparent
And so on
#implementation WatermarkOverlayWindow
- (id)initWithContentRect:(NSRect)contentRect
{
self = [super initWithContentRect:contentRect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
if (self!=nil)
{
self.level = NSFloatingWindowLevel;
self.ignoresMouseEvents = YES;
self.releasedWhenClosed = NO;
self.movableByWindowBackground = NO;
self.alphaValue = 1.0f;
self.backgroundColor = NSColor.clearColor;
self.opaque = NO;
self.hasShadow = NO;
}
return self;
}
...
Now you can add semi-transparent views to this window and those views will appear to float on the screen. Alternatively, you can place opaque views in this window and then change the overall alphaValue of the window to something less than 1.0.

How on OS X to programmatically force initial adjustment the size of image in an ImageWell, as ImageWell changes size due to LayoutConstraints

NSWindow
NSImageWell - with constraint to all four side of the Window's containerView
NSImage - is put in Well via NSPageControl
Using the mouse, I resize the Window and the ImageWell expands or contracts with the Window as desire.
The image in the Well remains the same size regardless of Window size.
The image does resize correctly, based on the size change of Window, if I drag the image a bit with the mouse.
How and where do I send a message to get this size to change "automatically" ?
Do I need to monitor size changes in the Window using Notifications?
Sorry, I was not clear. I want the image in the image well to maintain its
aspect ratio and to resize itself each time the Window is resized. Currently, when the window is resized, the user sees no change in the size of the image. He can however, cause the image to resize correctly by click dragging on the image or Window content view. I want the user to see this
resizing take place immediately, without needing to use the mouse. For example of an image that has an aspect ratio of 4/3. if the window is 4" x 4" , then when the image is added, it appears as 4" by 3". If the window goes to 8" x 8", then the image should automatically go to 8" x 6".
Guilherme was good enough to send me his demo NSView app. If, like in his app, I set an image in IB, my app also successfully resizes not only the ImageView but also the Image itself.
The problem appears to be because I put the images into the ImageView via an NSPageController. The only way to get the image to resize is to do a two finger drag inside the window.contentView
After Resizing window:
New Image Appears
Then following a two-finger drag on image :
<https://www.dropbox.com/s/d2he6bdzxbeefok/Screen%20Shot%202015-09-12%20at%2013.26.50.png?dl=0>
Below is relevant code:
AppDelegate follows:
#import "AppDelegate.h"
#import "MyImageView.h"
#interface AppDelegate ()
#property (strong, nonatomic) IBOutlet NSWindow *window;
#property (strong, nonatomic) IBOutlet NSPageController *pageController;
#property (strong, nonatomic) IBOutlet MyImageView *imageView;
#property (weak) IBOutlet NSTextField *infoLabel;
#property NSArray *imageArray;
#end
#implementation AppDelegate
#pragma mark Window delegate
- (void)windowDidResize:(NSNotification *)notification{
//[_window layoutIfNeeded];
//[_imageView doSelfLayout];
}
- (void)awakeFromNib {
[_window setDelegate:self];
[_imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
_imageArray = #[ [NSImage imageNamed:#"eggplant.png"],
[NSImage imageNamed:#"sandwich.png"],
[NSImage imageNamed:#"yoghurt.png"]];
[_pageController setDelegate:self];
[_pageController setArrangedObjects:_imageArray];
[_pageController setTransitionStyle:NSPageControllerTransitionStyleStackBook];
// Set info label's text
NSString *info = [NSString stringWithFormat:#"Image %ld/%ld", ([_pageController selectedIndex]+1), [_imageArray count]];
[_infoLabel setStringValue:info];
}
#pragma mark Page Controller delegate methods:
- (void)pageController:(NSPageController *)pageController didTransitionToObject:(id)object {
/* When image is changed, update info label's text */
NSString *info = [NSString stringWithFormat:#"Image %ld/%ld", ([_pageController selectedIndex]+1), [_imageArray count]];
[_infoLabel setStringValue:info];
//[_window layoutIfNeeded];
}
- (NSString *)pageController:(NSPageController *)pageController identifierForObject:(id)object {
NSString *identifier = [[NSNumber numberWithInteger:[_imageArray indexOfObject:object]] stringValue];
return identifier;
}
- (NSViewController *)pageController:(NSPageController *)pageController viewControllerForIdentifier:(NSString *)identifier {
/* Create new view controller and image view */
NSViewController *vController = [NSViewController new];
NSImageView *iView = [[NSImageView alloc] initWithFrame:[_imageView frame]];
[iView setImage:(NSImage *)[_imageArray objectAtIndex:[identifier integerValue]]];
[iView setImageFrameStyle:NSImageFrameNone];
/* Add image view to view controller and return view controller */
[vController setView:iView];
return vController;
}
#end
What option have you selected for the scaling property in IB?
Here's a sample project, the image resizes automatically with Proportionally Up or Down selected.
I have made a demo Cocoa project zipfile below that has what I was looking for, namely an OS X app that :
- uses NSPageController in History mode to display an array of images (Portrait and Landscape) in a resizeable window
- imageView maintains imagefile aspect ratio as it automatically resizes
- images list can be traversed using a PreviousButton and a NextButton, or using a touch pad, with a two-finger swipe.
https://www.dropbox.com/s/7qgmg5dr1bxosqq/PageController3%203.zip?dl=0
It is pretty basic stuff, but I post it with the hope it can same someone else some time. Mark

NSTextField Drawing on Top of Sub View

I have created a secondary NSViewController to create a progress indicator "popup". The reason for this is that the software has to interact with some hardware and some of the functions take the device a few seconds to respond. So being thoughtful of the end user I have a NSViewController that has a NSView (that is black and semi-transparent) and then a message/progress bar on top. This is added to the window using addSubView.
Everything works great except when the screen has a NSTextField in it. The popup shows but the NSTextField is drawn on top. What is this?
The view code I used for drawing semi-transparent:
#implementation ConnectingView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
CGContextSetRGBFillColor(context, 0.227,0.251,0.337,0.8);
CGContextFillRect(context, NSRectToCGRect(dirtyRect));
}
#end
The code I use to show the progress view
-(void) showProgressWithMessage:(NSString *) message andIsIndet:(BOOL) indet
{
connectingView = [[ConnectingViewController alloc] init];
[self.view.window.contentView addSubview:connectingView.view];
connectingView.view.frame = ((NSView*)self.view.window.contentView).bounds;
[connectingView changeProgressLabel:message];
if (indet)
[connectingView makeProgressBar:NO];
}
Is there a better way to add the subview or to tell the NSTextFields I don't want them to be drawn on top?
Thanks!
So Setting [self setWantsLayer] to my custom NSViews sort of worked however there are a lot of redraw issues (white borders, and backgrounds). A NSPopover may be better in some instances however I was going for "locked down" approach where the interface is unreachable until it finishes (or times out).
What worked for me was to go to the instance of my NSView, select the window in Interface Builder, then go to layers (far right on properties view) and select my view under "Core Animation Layer".

Two Finger Drag with IKImageView and NSScrollView in Mountain Lion

I have a Mac App that's been in the app store for a year or so now. It was first published with target SDK 10.7, Lion. Upon the update to Mountain Lion it no longer works.
The application displays large images in an IKImageView which is embedded in an NSScrollView. The purpose of putting it into a scrollview was to get two finger dragging working, rather than the user having to click to drag. Using ScrollViewWorkaround by Nicholas Riley, I was able to use two finger scrolling to show the clipped content after the user had zoomed in. Just like you see in the Preview app.
Nicholas Riley's Solution:
IKImageView and scroll bars
Now in Mountain Lion this doesn't work. After zooming in, pinch or zoom button, the image is locked in the lower left portion of the image. It won't scroll.
So the question is, what's the appropriate way to display a large image in IKImageView and have two finger dragging of the zoomed image?
Thank you,
Stateful
Well, Nicholas Riley's Solution is an ugly hack in that it addresses the wrong class; the issue isn't with NSClipView (which he subclassed, but which works just fine as is), but with IKImageView.
The issue with IKImageView is actually quite simple (God knows why Apple hasn't fixed this in what? … 7 years ...): Its size does not adjust to the size of the image it displays. Now, when you embed an IKImageView in an NSScrollView, the scroll view obviously can only adjust its scroll bars relative to the size of the embedded IKImageView, not to the image it contains. And since the size of the IKImageView always stays the same, the scroll bars won't work as expected.
The following code subclasses IKImageView and fixes this behavior. Alas, it won't fix the fact that IKImageView is crash-prone in Mountain Lion as soon as you zoom …
///////////////////// HEADER FILE - FixedIKImageView.h
#import <Quartz/Quartz.h>
#interface FixedIKImageView : IKImageView
#end
///////////////////// IMPLEMENTATION FILE - FixedIKImageView.m
#import "FixedIKImageView.h"
#implementation FixedIKImageView
- (void)awakeFromNib
{
[self setTranslatesAutoresizingMaskIntoConstraints:NO]; // compatibility with Auto Layout; without this, there could be Auto Layout error messages when we are resized (delete this line if your app does not use Auto Layout)
}
// FixedIKImageView must *only* be used embedded within an NSScrollView. This means that setFrame: should never be called explicitly from outside the scroll view. Instead, this method is overwritten here to provide the correct behavior within a scroll view. The new implementation ignores the frameRect parameter.
- (void)setFrame:(NSRect)frameRect
{
NSSize imageSize = [self imageSize];
CGFloat zoomFactor = [self zoomFactor];
NSSize clipViewSize = [[self superview] frame].size;
// The content of our scroll view (which is ourselves) should stay at least as large as the scroll clip view, so we make ourselves as large as the clip view in case our (zoomed) image is smaller. However, if our image is larger than the clip view, we make ourselves as large as the image, to make the scrollbars appear and scale appropriately.
CGFloat newWidth = (imageSize.width * zoomFactor < clipViewSize.width)? clipViewSize.width : imageSize.width * zoomFactor;
CGFloat newHeight = (imageSize.height * zoomFactor < clipViewSize.height)? clipViewSize.height : imageSize.height * zoomFactor;
[super setFrame:NSMakeRect(0, 0, newWidth - 2, newHeight - 2)]; // actually, the clip view is 1 pixel larger than the content view on each side, so we must take that into account
}
//// We forward size affecting messages to our superclass, but add [self setFrame:NSZeroRect] to update the scroll bars. We also add [self setAutoresizes:NO]. Since IKImageView, instead of using [self setAutoresizes:NO], seems to set the autoresizes instance variable to NO directly, the scrollers would not be activated again without invoking [self setAutoresizes:NO] ourselves when these methods are invoked.
- (void)setZoomFactor:(CGFloat)zoomFactor
{
[super setZoomFactor:zoomFactor];
[self setFrame:NSZeroRect];
[self setAutoresizes:NO];
}
- (void)zoomImageToRect:(NSRect)rect
{
[super zoomImageToRect:rect];
[self setFrame:NSZeroRect];
[self setAutoresizes:NO];
}
- (void)zoomIn:(id)sender
{
[super zoomIn:self];
[self setFrame:NSZeroRect];
[self setAutoresizes:NO];
}
- (void)zoomOut:(id)sender
{
[super zoomOut:self];
[self setFrame:NSZeroRect];
[self setAutoresizes:NO];
}
- (void)zoomImageToActualSize:(id)sender
{
[super zoomImageToActualSize:sender];
[self setFrame:NSZeroRect];
[self setAutoresizes:NO];
}
- (void)zoomImageToFit:(id)sender
{
[self setAutoresizes:YES]; // instead of invoking super's zoomImageToFit: method, which has problems of its own, we invoke setAutoresizes:YES, which does the same thing, but also makes sure the image stays zoomed to fit even if the scroll view is resized, which is the most intuitive behavior, anyway. Since there are no scroll bars in autoresize mode, we need not add [self setFrame:NSZeroRect].
}
- (void)setAutoresizes:(BOOL)autoresizes // As long as we autoresize, make sure that no scrollers flicker up occasionally during live update.
{
[self setHasHorizontalScroller:!autoresizes];
[self setHasVerticalScroller:!autoresizes];
[super setAutoresizes:autoresizes];
}
#end

How to create an OSD-like window with Cocoa on Mac OSX?

Please keep in mind that I'm a really newbie Cocoa developer
Scenario: I've a search the when reaches the end of document restarts from begin, a so called "wrap around".
When I do the wrap I want to show a window that flashes on screen for some time (one second??) like and OSD (On Screen Display) control window, TextWrangler and XCode do that when text search restarts from the begin.
How can I implement a similar window?
Implementing a view to do this would be relatively simple. The following code in an NSView subclass would display a partially transparent rounded rect which ignores events when placed in a window.
- (void)drawRect:(NSRect)dirtyRect {
[[NSColor colorWithDeviceWhite:0 alpha:.7] set];
[[NSBezierPath bezierPathWithRoundedRect:self.bounds xRadius:10 yRadius:10] fill];
//Additional drawing
}
- (NSView *)hitTest:(NSPoint)aPoint {
return nil;
}
- (BOOL)acceptsFirstResponder {
return NO;
}
- (BOOL)isOpaque {
return NO;
}
If you do want to do this in a window, you will need to create a borderless, non-opaque window and set this as the content view. Also, you will need to have the view fill it's bounds with a clear color at the start of the drawRect: method.
//Create and display window
NSPanel *panel = [[NSPanel alloc] initWithFrame:NSMakeRect(0,0,300,200) styleMask:NSBorderlessWindowMask|NSNonactivatingPanelMask backing:NSBackingStoreBuffered defer:YES];
[panel setOpaque:NO];
MyViewSubview *view = [MyViewSubview new];
[panel setContentView:view];
[view release];
[p setLevel:NSScreenSaverWindowLevel];
[p orderFront:nil];
//Add these two lines to the beginning of the drawRect: method
[[NSColor clearColor set];
NSRectFill(self.bounds);
However, this window will intercept events and I have not been able to disable this using standard methods.
To fade the view, check out NSViewAnimation, or use an NSTimer object and do it manually.

Resources