I'm new to the whole Cocoa/Objective-C malarky (but have years of C/C++ on *nix).
I'm trying to create a basic Cocoa application that is filled with an NSOpenGLView. I have:
chink.mm
#import <OpenGL/gl.h>
#import "chink.h"
#implementation chinkView
#synthesize window;
- (void) applicationDidFinishLaunching : (NSNotification *)aNotification {
NSLog(#"Danger, Will Robinson! Danger!");
}
- (void) dealloc
{
[window release];
[super dealloc];
}
#end
and chink.h
#interface chinkView : NSOpenGLView {
NSWindow *window;
}
#property (assign) NSWindow *window;
#end
I have a single .xib set up like
https://skitch.com/cront/ri625/chinkxib
where 'chink View' is set as delegate for Window and File Owner.
I'm getting an 'invalid drawable' error (even before the application finished launching), which to my understanding means that it is trying to instantiate the opengl view before the window (although I'm probably wrong).
What I want is the solution to this problem of creating the window and setting the opengl view inside it. Once this is done I have absolutely no problem writing the OGL (like I said, I know C++) but all this NS calls gunk is alien to me.
I'm not looking for 'why don't you do it in SDL/GLUT' answers please. Just the code to make it create the window properly.
Okay so I sacked off this approach.
Went the long route and changed to this:
chink.mm
#import <Cocoa/Cocoa.h>
#import <OpenGL/OpenGL.h>
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>
#import "chink.h"
#implementation chinkNS
- (id)initWithFrame:(NSRect)frameRect
{
NSOpenGLPixelFormat * pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:(NSOpenGLPixelFormatAttribute[]) {
NSOpenGLPFAWindow,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFADepthSize, 32,
nil
}];
if(pixelFormat == NULL)
NSLog(#"pixelFormat == NULL");
[pixelFormat autorelease];
self = [super initWithFrame:frameRect pixelFormat:pixelFormat];
if(self == NULL) {
NSLog(#"Could not initialise self");
return NULL;
}
[[self openGLContext] makeCurrentContext];
[self initGL];
return self;
}
- (void)awakeFromNib
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification object:[self window]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:[self window]];
animationTimer = [NSTimer scheduledTimerWithTimeInterval:1/60.0 target:self selector:#selector(timerFired) userInfo:NULL repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSEventTrackingRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSModalPanelRunLoopMode];
lastTime = [[NSDate date] timeIntervalSince1970];
}
- (void)drawRect:(NSRect)frame
{
NSLog(#"Chink is up and running. Let's do this thing!");
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
glFlush();
[self drawGL];
[[self openGLContext] flushBuffer];
}
#pragma mark OpenGL
- (void)initGL { }
- (void)reshapeGL { }
- (void)drawGL { }
- (void)animate:(float)dt { }
#pragma mark Event Handling
- (void)timerFired
{
NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970];
[self animate:currentTime-lastTime];
lastTime = currentTime;
}
#pragma mark Loose ends
- (void)setFrame:(NSRect)frame
{
[super setFrame:frame];
[self reshapeGL];
}
// To get key events
- (BOOL)acceptsFirstResponder
{
return YES;
}
#end
and chink.h
#interface chinkNS : NSOpenGLView
{
NSTimer * animationTimer;
NSTimeInterval lastTime;
}
- (void)initGL;
- (void)reshapeGL;
- (void)drawGL;
- (void)animate:(float)dt;
#end
The same settings in the .xib except that it is a standard NSView that subclasses my opengl view.
I was hoping for a simpler, more elegant set up as above but you can't win 'em all.
Closed.
Related
In building a custom NSButton, I've run into a problem handling highlight behavior. After clicking down on the button, holding, and dragging the cursor outside the button's bounds, mouseExited: and mouseEntered: events are not delivered. I understand the reason why, because in mouseDown: calling [super mouseDown:event]; will block until the click is released.
In researching this I came across this Stack Overflow post which describes the same problem. The solution noted is to add NSTrackingEnabledDuringMouseDrag to the NSTrackingArea options, which I have done, yet I continue to see this problem. I tried the other proposed solution with handling the next events in a loop, but this resulted in odd behavior. The button text color turns black on mouse down instead of highlighting the dimmed color, and it doesn't unhighlight upon releasing the mouse, it remains black.
I am using Xcode 9.3, running on macOS 10.13.4.
Here is my NSButton subclass:
#interface BorderlessButton : NSButton {
NSColor *_tempColor;
}
#property (strong, nonatomic) NSColor *color;
#end
#interface BorderlessButton ()
#property (nonatomic) BOOL pressed;
#end
#implementation BorderlessButton
- (id)init {
if (self = [super init]) {
[self setUp];
}
return self;
}
- (id)initWithFrame:(NSRect)frameRect {
if (self = [super initWithFrame:frameRect]) {
[self setUp];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self setUp];
}
return self;
}
- (void)setUp {
_color = [NSColor redColor];
[self setTitle:self.title];
[self setButtonType:NSButtonTypeMomentaryChange];
[self setBordered:NO];
NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect:self.bounds
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingEnabledDuringMouseDrag
owner:self
userInfo:nil];
[self addTrackingArea:area];
}
- (void)setTitle:(NSString *)title {
[super setTitle:title];
NSMutableAttributedString *colorTitle = [[NSMutableAttributedString alloc] initWithAttributedString:[self attributedTitle]];
[colorTitle addAttributes:#{NSFontAttributeName: self.font, NSForegroundColorAttributeName: self.color} range:NSMakeRange(0, [colorTitle length])];
[self setAttributedTitle:colorTitle];
}
- (void)setColor:(NSColor *)color {
_color = color;
[self setTitle:self.title];
}
- (void)mouseDown:(NSEvent *)event {
self.pressed = YES;
[self highlight:YES];
[super mouseDown:event]; // this blocks until released
[self mouseUp:event];
}
- (void)mouseUp:(NSEvent *)event {
self.pressed = NO;
[self highlight:NO];
[super mouseUp:event];
}
//FIXME: Not called after mouse press down and hold then exit
- (void)mouseExited:(NSEvent *)event {
if (self.pressed) {
[self highlight:NO];
}
[super mouseExited:event];
}
- (void)mouseEntered:(NSEvent *)event {
if (self.pressed) {
[self highlight:YES];
}
[super mouseEntered:event];
}
- (void)highlight:(BOOL)flag {
if (flag) {
if (self.isEnabled) {
NSColor *dimmedColor = [self dimmedColor];
_tempColor = _color;
self.color = dimmedColor;
[self setTitle:self.title];
}
} else {
if (self.isEnabled) {
self.color = _tempColor;
[self setTitle:self.title];
}
}
}
- (NSColor *)dimmedColor {
return [self.color colorWithAlphaComponent:0.5];
}
#end
So I got a Window:
Window.xib
I got a WindowController too:
WindowController.h reads:
#import <Cocoa/Cocoa.h>
#interface MainWindowController : NSWindowController
{
IBOutlet NSView *firstView;
IBOutlet NSView *secondView;
IBOutlet NSView *thirdView;
int currentViewTag;
}
-(IBAction)switchView:(id)sender;
#end
And the WindowController.m reads:
#import "MainWindowController.h"
#interface MainWindowController ()
#end
#implementation MainWindowController
-(id)init
{
self = [super initWithWindowNibName:#"MainWindow"];
if (self){
// Initialization code here
}
return self;
}
//- (void)windowDidLoad {
// [super windowDidLoad];
//
// // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
//}
#pragma mark - Custom view drawing
-(NSRect)newFrameForNewContentView:(NSView *)view
{
NSWindow *window = [self window];
NSRect newFrameRect = [window frameRectForContentRect:[view frame]];
NSRect oldFrameRect = [window frame];
NSSize newSize = newFrameRect.size;
NSSize oldSize = oldFrameRect.size;
NSRect frame = [window frame];
frame.size = newSize;
frame.origin.y -= (newSize.height - oldSize.height);
return frame;
}
-(NSView *)viewForTag:(int)tag{
NSView *view = nil;
if (tag == 0) {
view = firstView;
} else if (tag == 1) {
view = secondView;
} else {
view = thirdView;
}
return view;
}
-(BOOL) validateToolbarItem:(NSToolbarItem *)item
{
if ([item tag] == currentViewTag) return NO;
else return YES;
}
-(void)awakeFromNib
{
[[self window] setContentSize:[firstView frame].size];
[[[self window] contentView]addSubview:firstView];
[[[self window] contentView]setWantsLayer:YES];
}
-(IBAction)switchView:(id)sender
{
int tag = [sender tag];
NSView *view = [self viewForTag:tag];
NSView *previousView = [self viewForTag:currentViewTag];
currentViewTag = tag;
NSRect newFrame = [self newFrameForNewContentView:view];
[NSAnimationContext beginGrouping];
if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask)
[[NSAnimationContext currentContext] setDuration:1.0];
[[[[self window]contentView]animator]replaceSubview:previousView with:view];
[[[self window]animator]setFrame:newFrame display:YES];
[NSAnimationContext endGrouping];
}
#end
The problem I have is that when i switch tabs in my app, the custom view (and there are three of different sizes) draw differently each time. Look at the screenshots, all of the numbers should be centre aligned but they are sometimes and others not. Can anyone see what my error is please?
I will also add that all of the actions have been correctly configured + the code works perfectly if the custom view size is the same all the time.
The view that works
The view that almost works
Again pointing out that in my .xib all of the numbers are aligned to 0x and 0y axis.
Appdelegate.h
#import <Cocoa/Cocoa.h>
#class MainWindowController;
#interface AppDelegate : NSObject <NSApplicationDelegate> {
MainWindowController *mainWindowController;
}
#end
Appdelegate.m
#interface AppDelegate ()
#property (nonatomic) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
-(void)awakeFromNib
{
if(!mainWindowController){
mainWindowController = [[MainWindowController alloc]init];
}
[mainWindowController showWindow:nil];
}
#end
In Interface Builder, make sure to disable the "autoresizes subviews" checkbox of the default view of your window
I am back again with my pigs (means cochon in French)! :)
I would like to created a at simple animated image with an NSTimer and I can't find example of it not with the iphone SDK.
after 3 second, the debugger displays : Program received signal: “EXC_BAD_ACCESS”
here is the code, if you have some time to check it and say what you think of it..
#import <Cocoa/Cocoa.h>
#interface maatView : NSView {
NSArray *mesImagesCochon;
NSImageView * monCochon;
int counter;
}
-(void)tick;
#end
here is the .m file:
#import "maatView.h"
#implementation maatView
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)awakeFromNib
{
counter=0;
mesImagesCochon = [NSArray arrayWithObjects:
[NSImage imageNamed:#"cochon.png"],
[NSImage imageNamed:#"cochon2.png"],
[NSImage imageNamed:#"cochon3.png"],
[NSImage imageNamed:#"cochon4.png"],
nil];
[NSTimer scheduledTimerWithTimeInterval:3
target:self
selector:#selector(tick)
userInfo:nil
repeats:NO];
monCochon = [[NSImageView alloc]init];
[monCochon setFrame:NSMakeRect(0, 0, 100, 100)];
}
- (void)drawRect:(NSRect)dirtyRect {
// Drawing code here.
}
-(void)tick
{
NSLog(#"method appele %d",mesImagesCochon.count);
counter++;
if (counter > mesImagesCochon.count)
{
counter = 0;
}
[monCochon setImage:[mesImagesCochon objectAtIndex:counter]];
[self addSubview:monCochon];
}
#end
I am trying to programmatically create a Cocoa window with an OpenGL context for an OS X application. I have been unable to find an example online which does not use the Interface Builder for creating a window and OpenGL view.
All I want is for glClear to make my window magenta (0xFF00FF). However, the window remains white.
Here is my project:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
NSOpenGLContext *openGLContext;
}
#property (assign) NSWindow *window;
#property (assign) NSOpenGLContext *openGLContext;
- (void)draw;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window;
#synthesize openGLContext;
static NSOpenGLPixelFormatAttribute glAttributes[] = {
0
};
- (void)draw {
NSLog(#"Drawing...");
[self.openGLContext makeCurrentContext];
glClearColor(1, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
[self.openGLContext flushBuffer];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSRect frame = NSMakeRect(0, 0, 200, 200);
self.window = [[[NSWindow alloc]
initWithContentRect:frame
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO] autorelease];
[self.window makeKeyAndOrderFront:nil];
NSOpenGLPixelFormat *pixelFormat
= [[NSOpenGLPixelFormat alloc] initWithAttributes:glAttributes];
self.openGLContext = [[NSOpenGLContext alloc]
initWithFormat:pixelFormat shareContext:nil];
[self.openGLContext setView:[self.window contentView]];
[NSTimer
scheduledTimerWithTimeInterval:.1
target:self
selector:#selector(draw)
userInfo:nil
repeats:YES];
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)_app {
return YES;
}
#end
main.m
#import "AppDelegate.h"
#import <Cocoa/Cocoa.h>
int main(int argc, char **argv) {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
return NSApplicationMain(argc, (const char **) argv);
}
The documentation for -[NSOpenGLContext flushBuffer] says:
Discussion
If the receiver is not a double-buffered context, this call does nothing.
You can cause your context to be double-buffered by including NSOpenGLPFADoubleBuffer in your pixel format attributes. Alternatively, you can call glFlush() instead of -[NSOpenGLContext flushBuffer] and leave your context single-buffered.
Use this code for the pixel format attributes instead.
NSOpenGLPixelFormatAttribute glAttributes[] =
{
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
0
};
I've tested it and you get the magenta screen you were looking for.
I have a class called ServerImage (sub of NSView) which I am calling from my AppController but for some reason it won't draw to the screen. I have other views which I am able to draw and add images to but for some reason not this one. I'm sure I am missing something in my code but am just not seeing it. Here's the relevant code from AppController:
//loop through masterServerDict and get server status
NSMutableString* key;
for(key in masterServerDict) {
ServerImage* newImage = [[ServerImage alloc] initWithFrame:NSMakeRect(200.0, 0.0, 48.0, 48.0)];
[newImage setServerName:key];
[[[NSApp mainWindow] contentView] addSubview:newImage];
[newImage setNeedsDisplay:YES];
}
masterServerDict is a mutable dictionary, the key is the name of a server, the object is an array, it holds the smb and afp paths to the server and whether it is mounted or not.
Here is ServerImage.h
#import <Cocoa/Cocoa.h>
#interface ServerImage : NSView {
NSString * serverName;
}
- (void) setServerName : (NSString* ) s;
#end
and ServerImage.m
#import "ServerImage.h"
#implementation ServerImage
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
NSLog(#"%f", self.frame.origin.x);
}
return self;
}
- (void)drawRect:(NSRect)rect {
NSLog(#"drawrect");
[[NSColor redColor] set];
NSRectFill(rect);
}
- (void) setServerName : (NSString* ) s {
NSLog(#"method");
serverName = s;
}
I can get the init and setServerName methods to log but not drawRect...
Check if [[NSApp mainWindow] contentView] is non-nil. Also check if it refers to the right window and that the coordinate is good (visible).