Fullscreen app using NSTextView

Discussion in 'Mac Programming' started by SRossi, May 27, 2009.

  1. macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #1
    Hello all,

    I have searched the internet on a stub of code or so that may help me allow to switch between fullscreen and windowed mode. I have set up a menu item to allow me to change to fullscreen but now i am stuck.

    What i am looking for is a start or any help so that i can display the NSTextView in full screen and allow the user to continue editing text on it.

    Any help will be appreciated.

    Thanks in advance

    Stephen
     
  2. macrumors 6502

    Joined:
    Mar 6, 2008
    Location:
    Melbourne, Australia
    #2
    In your MainView's M file (the one that is direct to the Full Screen Window) include this:

    Code:
    - (BOOL) canBecomeKeyWindow
    {
        return YES;
    }
    That will allow everything to be activated and made editable again. Text Fields and Views tend to disable themselves for some odd reason. :D
    If there is anything else you need, just reply or PM.
     
  3. thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #3
    Thanks man.

    Can you point me in the direction I should go with the fullscreen coding, as I am beat in what to do. I have learned about using carbon to hide the menu and dock but as for making the text view appear I have no knowledge to where to start or that.

    Thanks

    Stephen
     
  4. Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #4
    Create a title-bar-less window the size of the screen, position correctly and add the NSTextView to it. Basically read the NSWindow documentation.
     
  5. thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #5
    Right I understand what you are saying but do I take the NSWindow of the MyDocument.xib and set it to a NSWindow variable in MyDocument.h?

    I have found a tutorial on cocoadevcentral but thats using a panel, would I need to create a panel and add a NSTextView or just use the existing MyDocument.xib window?

    Sorry for being a bit stupid im just totally lost.

    Stephen
     
  6. Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #6
    I wasn't suggesting you do anything in Interface Builder at all. I would suggest that you have different windows for full screen and windowed mode. Your full-screen window would be created entirely in code with the appropriate styleMask.
     
  7. thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #7
    Ahhhh I get you now so what I have to do is create a boarderless window, then add my textView into it. Thanks for your help :). If I have any other problems i'll post them up.

    Can I ask though how is it that I add a NSTextView to my new window?

    Thanks

    Stephen
     
  8. Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #8
    Get the contentView of the window then add your NSTextView instance as a subView.
     
  9. thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #9
    Right I have done some code, I dont wether I am correct yet as it isn't running
    my program at all. But can anyone see if there is anything wrong with my code.

    iWriteDelegate.h
    Code:
    #import <AppKit/AppKit.h>
    
    
    @interface iWriteDelegate : NSObject 
    {
    	BOOL fullScreen;
    	NSWindow *mainWindow;
    }
    - (IBAction)fullScreen:(id)sender;
    
    - (void)showFullScreen;
    - (void)exitFullScreen;
    
    - (BOOL)isFullScreen;
    - (void)setFullScreen:(BOOL)sender;
    @end
    iWriteDelegate.m
    Code:
    #import "iWriteDelegate.h"
    #import "MyDocument.h"
    
    @implementation iWriteDelegate
    
    - (IBAction)fullScreen:(id)sender
    {
    	BOOL inFullScreen = [self isFullScreen];
    	
    	if (inFullScreen = YES) {
    		[self exitFullScreen];
    	} else {
    		[self showFullScreen];
    	}
    }
    
    - (void)showFullScreen
    {
    	[self setFullScreen:YES];
    	
    	int WindowLevel;
    	NSRect screenRect;
    	
    	if (CGDisplayCapture(kCGDirectMainDisplay) != kCGErrorSuccess) {
    		NSLog(@"Couldnt capture the main display!");
    	}
    	
    	WindowLevel = CGShieldingWindowLevel();
    	
    	screenRect = [[NSScreen mainScreen] frame];
    	
    	mainWindow = [[NSWindow alloc] initWithContentRect:screenRect
    											 styleMask:NSBorderlessWindowMask
    											   backing:NSBackingStoreBuffered
    												 defer:NO screen:[NSScreen mainScreen]];
    	
    	[mainWindow setLevel:WindowLevel];
    	
    	[mainWindow setBackgroundColor:[NSColor blackColor]];
    	
    	NSRect cFrame = [[mainWindow contentView] frame];
    	
    	NSTextView *theTextView = [[NSTextView alloc] initWithFrame:cFrame];
    	[mainWindow setContentView:theTextView];
    	[mainWindow makeKeyAndOrderFront:nil];
    	[mainWindow makeFirstResponder:theTextView];
    	
    	[theTextView insertText:mString];
    	
    }
    
    - (void)exitFullScreen
    {
    	[mainWindow orderOut:self];
    	[self setFullScreen:NO];
    	
    	if (CGDisplayRelease(kCGDirectMainDisplay) != kCGErrorSuccess) {
    		NSLog(@"Couldnt release the displays");
    	}
    }
    
    - (BOOL)isFullScreen
    {
    	return fullScreen;
    }
    
    - (void)setFullScreen:(BOOL)sender
    {
    	fullScreen = sender;
    }
    
    @end
    It is not posting any errors it just wont open my application think it is trying to open it in fullscreen to start with.

    Thanks in advance.

    Stephen
     
  10. macrumors 6502

    Joined:
    Nov 30, 2005
    #10
    An alternative, which is more or less exactly what I'm doing is to set up everything you want for your ordinary window in Interface Builder, then to do something like:

    Code:
    NSView *content = [normalWindow contentView];
    [content retain];
    [normalWindow setContentView:nil];
    [fullScreenWindow setContentView:content];
    [content release];
    
    Which should transfer the content you drew in Interface Builder from the NIB window to the one you just created, applying all the sizing criteria you set there. It's a slightly longer-than-one-line switch because an NSView can belong to only one window at a time and setContentView: will release the previous NSView.

    EDIT: sorry, we appear to have posted near simultaneously. Do you set the initial value of fullScreen anywhere?
     
  11. thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #11

    Right i have set it to NO in an initialize function in iWriteDelegate.m but it still is not opening my application.

    Thanks for your help

    Stephen
     
  12. macrumors 6502

    Joined:
    Nov 30, 2005
    #12
    Well, I don't seem to be able to make the full screen window able to become first responder (which is also a problem I'm having in my own code), but by going the Interface Builder route and otherwise copying most of your code, the attached and should happily switch some window contents between windowed mode and fullscreen (on command+enter, since command+f is already taken by one of the standard Edit menu entries).

    Application delegate code (also in the archive) is:

    Code:
    #import "IWriteAppDelegate.h"
    
    @interface IWriteWindow: NSWindow
    {
    }
    @end
    
    @implementation IWriteWindow
    
    - (BOOL)acceptsFirstResponder
    {
    	NSLog(@"accepts\n");
    	return YES;
    }
    
    @end
    
    
    @implementation IWriteAppDelegate
    - (BOOL)isFullScreen
    {
    	return fullScreen;
    }
    
    - (void)setFullScreen:(BOOL)sender
    {
    	fullScreen = sender;
    }
    
    - (void)exitFullScreen
    {
    	[self setFullScreen:NO];
    	
    	NSView *content = [fullScreenWindow contentView];
    	[content retain];
    	[fullScreenWindow setContentView:nil];
    	[desktopWindow setContentView:content];
    	[content release];
    
    	[fullScreenWindow orderOut:self];
    	[fullScreenWindow release];
    	if(CGDisplayRelease(kCGDirectMainDisplay) != kCGErrorSuccess)
    	{
    		NSLog(@"Couldn't release the display");
    	}
    }
    
    - (void)enterFullScreen
    {
    	[self setFullScreen:YES];
    	
    	int windowLevel;
    	NSRect screenRect;
    	
    	if(CGDisplayCapture(kCGDirectMainDisplay) != kCGErrorSuccess)
    	{
    		NSLog(@"Couldn't capture the main display!");
    	}
    	
    	windowLevel = CGShieldingWindowLevel();
    	screenRect = [[NSScreen mainScreen] frame];
    	
    	NSView *content = [desktopWindow contentView];
    	[content retain];
    	[desktopWindow setContentView:nil];
    
    	fullScreenWindow = [[IWriteWindow alloc] initWithContentRect:screenRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO screen:[NSScreen mainScreen]];
    	[fullScreenWindow setContentView:content];
    	[content release];
    	
    	[fullScreenWindow makeKeyAndOrderFront:nil];
    	[fullScreenWindow makeFirstResponder:content];
    	[fullScreenWindow setLevel:windowLevel];
    }
    
    - (IBAction)toggleFullScreen:(id)sender {
        
    	if([self isFullScreen])
    		[self exitFullScreen];
    	else
    		[self enterFullScreen];
    }
    
    - (void)applicationDidFinishLaunching:(NSNotification *) note
    {
    	fullScreen = NO;
    }
    
    @end
     

    Attached Files:

  13. thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #13
    Words cannot express how much that has helped me :D

    Thanks soo much, if there is anything you need help with please ask :D

    Thanks sooo much

    Stephen
     
  14. macrumors 6502

    Joined:
    Nov 30, 2005
    #14
    If you do figure out how to get the borderless window to become key (it's the reason I've subclassed NSWindow, albeit not successful with respect to the goal), I'd be really grateful if you could post it! Sadly I have to return to work for the rest of the afternoon...
     
  15. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #15
    If you only need 10.5+ compatibility, just use the new API:

    Code:
    NSView *view = [[self window] contentView];
    if ([view isInFullScreenMode])
        [view exitFullScreenModeWithOptions:nil];
    else
        [view enterFullScreenMode:[NSScreen mainScreen] withOptions:nil];
    
     
  16. Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #16
    As described here I think you need to override the canBecomeKeyWindow method in your NSWindow subclass to return YES.
     
  17. macrumors 6502

    Joined:
    Nov 30, 2005
    #17
    I thought I'd tried that immediately before acceptsFirstResponder, I guess I typed something incorrectly and was insufficiently thorough in my investigation of that. Thanks!

    kainjow> does that give the Quicktime-style full screen transition animation? I guess you could use [view respondsToSelector:mad:selector(enterFullScreenMode:withOptions:)] to determine whether the 10.5 API is available at runtime?

    EDIT: no animation, but presumably the following is smart?
    Code:
    #import "IWriteAppDelegate.h"
    
    @interface IWriteWindow: NSWindow
    {
    }
    @end
    
    @implementation IWriteWindow
    
    - (BOOL)canBecomeKeyWindow
    {
    	return YES;
    }
    
    @end
    
    
    @implementation IWriteAppDelegate
    - (BOOL)isFullScreen
    {
    	return fullScreen;
    }
    
    - (void)setFullScreen:(BOOL)sender
    {
    	fullScreen = sender;
    }
    
    - (void)exitFullScreen
    {
    	[self setFullScreen:NO];
    
    	if(fullScreenWindow)
    	{
    		NSView *content = [fullScreenWindow contentView];
    		[content retain];
    		[fullScreenWindow setContentView:nil];
    		[desktopWindow setContentView:content];
    		[content release];
    
    		[fullScreenWindow orderOut:self];
    		[fullScreenWindow release]; fullScreenWindow = nil;
    		if(CGDisplayRelease(kCGDirectMainDisplay) != kCGErrorSuccess)
    		{
    			NSLog(@"Couldn't release the display");
    		}
    	}
    	else
    	{
    		[[desktopWindow contentView] exitFullScreenModeWithOptions:nil];
    	}
    }
    
    - (void)enterFullScreen
    {
    	[self setFullScreen:YES];
    
    	NSView *content = [desktopWindow contentView];
    	if([content respondsToSelector:@selector(enterFullScreenMode:withOptions:)])
    	{
    		[content enterFullScreenMode:[NSScreen mainScreen] withOptions:nil];
    	}
    	else
    	{
    		int windowLevel;
    		NSRect screenRect;
    
    		if(CGDisplayCapture(kCGDirectMainDisplay) != kCGErrorSuccess)
    		{
    			NSLog(@"Couldn't capture the main display!");
    		}
    
    		windowLevel = CGShieldingWindowLevel();
    		screenRect = [[NSScreen mainScreen] frame];
    
    		[content retain];
    		[desktopWindow setContentView:nil];
    
    		fullScreenWindow = [[IWriteWindow alloc] initWithContentRect:screenRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO screen:[NSScreen mainScreen]];
    		[fullScreenWindow setContentView:content];
    		[content release];
    
    		[fullScreenWindow makeKeyAndOrderFront:nil];
    		[fullScreenWindow makeFirstResponder:content];
    		[fullScreenWindow setLevel:windowLevel];
    	}
    }
    
    - (IBAction)toggleFullScreen:(id)sender {
        
    	if([self isFullScreen])
    		[self exitFullScreen];
    	else
    		[self enterFullScreen];
    }
    
    - (void)applicationDidFinishLaunching:(NSNotification *) note
    {
    	fullScreen = NO;
    	fullScreenWindow = nil;
    }
    
    @end
    
     
  18. thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #18
    My lecturer was only got 10.4 unfortunately as in why i never went down using the new API.

    Although thanks.

    Stephen
     
  19. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #19
    It doesn't, and although there are options you can pass I didn't see anything related to that effect. Probably would have to use the CGDisplay functions for that I imagine.

    respondsToSelector could definitely be used if you need to build for 10.4 but want to use the new API for 10.5+ users, but if you have code that works on 10.4 I would just use that instead.
     
  20. Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #20
    If you want animation in the 10.4 compatible code set the full screen/borderless window to the size/position of the initial window and then call the setFrame:display:animate: method. If your contents are set to auto-size it should look pretty sweet :)
     
  21. thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #21
    Could that code be implemented into a Cocoa Document based application? If so where would I implement it?

    Thanks

    Stephen
     
  22. macrumors 6502

    Joined:
    Nov 30, 2005
    #22
    Oh, yep, got that in the code I have back at home from a few days ago that made me so willing and able to help out here so quickly. It's not identical to Quicktime since in that the viewer frame resizes with the content but fades away as the desktop goes with it, whereas with a CGDisplayCapture the background is just blanked out instantly, but it looks pretty good. And the animation is blocking, for want of a better word, so you don't even have to jump through hoops to figure out when to switch the content view back to the normal window when exiting full screen.

    Something else I preferred to the code posted here was to use [[[[desktopWindow screen] deviceDescription] @"NSScreenNumber"] unsignedIntValue] to get the value for CGDisplayCapture rather than always capturing the main screen; it should cause a window to become full screen on whichever display that the majority of it is on. NSScreenNumber still doesn't seem to be in the official docs, but per various resources located by Google has been present since at least 2003. Probably best to include a fallback though.

    EDIT: my home code is actually implemented in a multiple document application (as per my thread earlier in the week); I put it straight into my NSDocument derivative, having connected the full screen toggle from the menu bar to the first responder in that .xib, which leads to it being passed all the way down to the frontmost document.
     
  23. thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #23
    Aww cause I was connecting my toggle full screen menu item to a controller i created with new document files. Which is what arron hillegass done int his book.

    But if i connect it to the first responder of mainmenu.xib i will be able to acces the main window?

    Thanks

    Stephen
     
  24. macrumors 6502

    Joined:
    Nov 30, 2005
    #24
    I'm really no expert; I've recently returned to Interface Builder/Xcode after several years of absence and am still a tiny bit lost with the 3.0 rejig. If what I say is contrary to almost any other source, it's probably smarter to trust the other source.

    That said, my code does work fine. I'll try to remember to bring it to work (where I have the internet) tomorrow in case this thread is still ongoing.
     
  25. thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #25
    To be honest Thomas anything that you say I would believe because youve gave nothing but good advice all day :)

    If you could do that id be grateful, sorry if im keeping you from your work.

    Stephen
     

Share This Page