From f7817f6987f83d84893466ee4f8a99af98ee643f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 6 Apr 2014 23:33:27 -0400 Subject: [PATCH] Added (untested; VM issues) code to handle Mac OS X Quit Dock menu items and other related stuff that may happen in the future. Will drop the TODO after I can test it. --- bleh_darwin.m | 20 +++++++++++++++++++- delegate_darwin.go | 12 ++++++++++++ doc.go | 3 ++- init.go | 11 +++++++++++ objc_darwin.go | 18 ++++++++++++++---- objc_darwin.h | 4 ++++ test/main.go | 3 +++ 7 files changed, 65 insertions(+), 6 deletions(-) diff --git a/bleh_darwin.m b/bleh_darwin.m index 8f77b84..6094eeb 100644 --- a/bleh_darwin.m +++ b/bleh_darwin.m @@ -20,6 +20,7 @@ though this is not always the case. #include #include #include +#include /* used by listbox_darwin.go; requires NSString */ id *_NSObservedObjectKey = (id *) (&NSObservedObjectKey); @@ -241,7 +242,8 @@ static void __areaView_drawRect(id self, SEL sel, NSRect r) void *_areaView_drawRect = (void *) __areaView_drawRect; -/* the only objective-c feature you'll see here +/* +this and one below it are the only objective-c feature you'll see here unfortunately NSRect both varies across architectures and is passed as just a structure, so its encoding has to be computed at compile time because @encode() is NOT A LITERAL, we're going to just stick it all the way back in objc_darwin.go @@ -327,3 +329,19 @@ void objc_setFont(id what, unsigned int csize) objc_msgSend(what, s_setFont, objc_msgSend(c_NSFont, s_systemFontOfSize, size)); } + +/* +-[NSApplicationDelegate applicationShouldTerminate] used to return a BOOL, but now returns a NSApplicationTerminateReply, which is a NSUInteger; hence, here. +*/ + +extern void appDelegate_applicationShouldTerminate(); + +static NSApplicationTerminateReply __appDelegate_applicationShouldTerminate(id self, SEL sel) +{ + appDelegate_applicationShouldTerminate(); + return NSTerminateCancel; // don't quit +} + +void *_appDelegate_applicationShouldTerminate = (void *) __appDelegate_applicationShouldTerminate; + +char *encodedTerminateReply = @encode(NSApplicationTerminateReply); diff --git a/delegate_darwin.go b/delegate_darwin.go index 00bdf6e..a8140e2 100644 --- a/delegate_darwin.go +++ b/delegate_darwin.go @@ -12,6 +12,7 @@ This creates a class goAppDelegate that will be used as the delegate for /everyt - handles window close events (windowShouldClose:) - handles window resize events (windowDidResize:) - handles button click events (buttonClicked:) + - handles the application-global Quit event (such as from the Dock) (applicationShouldTerminate) */ // #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit @@ -45,6 +46,8 @@ var appDelegateSels = []selector{ "handling window resize events"}, selector{"buttonClicked:", uintptr(C.appDelegate_buttonClicked), sel_bool_id, "handling button clicks"}, + selector{"applicationShouldTerminate", uintptr(C._appDelegate_applicationShouldTerminate), sel_terminatereply, + "handling Quit menu items (such as from the Dock)/the AppQuit channel"}, } func mkAppDelegate() error { @@ -94,3 +97,12 @@ func appDelegate_buttonClicked(self C.id, sel C.SEL, button C.id) { sysData := getSysData(button) sysData.signal() } + +//export appDelegate_applicationShouldTerminate +func appDelegate_applicationShouldTerminate() { + // asynchronous so as to return control to the event loop + go func() { + AppQuit <- struct{}{} + }() + // xxx in bleh_darwin.m tells Cocoa not to quit +} diff --git a/doc.go b/doc.go index a0a1b01..8a545fd 100644 --- a/doc.go +++ b/doc.go @@ -20,6 +20,7 @@ Here is a simple, complete program that asks the user for their name and greets func myMain() { w := ui.NewWindow("Hello", 400, 100) + ui.AppQuit = w.Closing // treat quitting the application like closing the main window nameField := ui.NewLineEdit("Enter Your Name Here") button := ui.NewButton("Click Here For a Greeting") err := w.Open(ui.NewVerticalStack(nameField, button)) @@ -29,7 +30,7 @@ Here is a simple, complete program that asks the user for their name and greets for { select { - case <-w.Closing: // user tries to close the window + case <-w.Closing: // user tries to close the window or quit the program return case <-button.Clicked: // user clicked the button ui.MsgBox("Hello, " + nameField.Text() + "!", "") diff --git a/init.go b/init.go index b4214ac..9349713 100644 --- a/init.go +++ b/init.go @@ -14,3 +14,14 @@ package ui func Go(main func()) error { return ui(main) } + +// AppQuit is pulsed when the user decides to quit the program if their operating system provides a facility for quitting an entire application, rather than merely close all windows (for instance, Mac OS X via the Dock icon). +// You should assign one of your Windows's Closing to this variable so the user choosing to quit the application is treated the same as closing that window. +// If you do not respond to this signal, nothing will happen. +// Do not merely check this channel alone; it is not guaranteed to be pulsed on all systems or in all conditions. +var AppQuit chan struct{} + +func init() { + // don't expose this in the documentation + AppQuit = newEvent() +} diff --git a/objc_darwin.go b/objc_darwin.go index d7e8f7a..b4b44e8 100644 --- a/objc_darwin.go +++ b/objc_darwin.go @@ -71,14 +71,16 @@ const ( sel_bool_id sel_bool sel_void_rect + sel_terminatereply nitypes ) var itypes = [nitypes][]C.char{ - sel_void_id: []C.char{'v', '@', ':', '@', 0}, - sel_bool_id: []C.char{'c', '@', ':', '@', 0}, - sel_bool: []C.char{'c', '@', ':', 0}, - sel_void_rect: nil, // see init() below + sel_void_id: []C.char{'v', '@', ':', '@', 0}, + sel_bool_id: []C.char{'c', '@', ':', '@', 0}, + sel_bool: []C.char{'c', '@', ':', 0}, + sel_void_rect: nil, // see init() below + sel_terminatereply: nil, } func init() { @@ -91,6 +93,14 @@ func init() { } x = append(x, 0) itypes[sel_void_rect] = x + + x = make([]C.char, 0, 256) // more than enough + y = C.GoString(C.encodedTerminateReply) + for _, b := range y { + x = append(x, C.char(b)) + } + x = append(x, '@', ':', 0) + itypes[sel_terminatereply] = x } func makeClass(name string, super C.id, sels []selector, desc string) (id C.id, err error) { diff --git a/objc_darwin.h b/objc_darwin.h index 84f4e70..fbe169c 100644 --- a/objc_darwin.h +++ b/objc_darwin.h @@ -117,8 +117,12 @@ extern struct xpoint getTranslatedEventPoint(id, id); /* for objc_darwin.go */ extern char *encodedNSRect; +extern char *encodedTerminateReply; /* for sysdata_darwin.go */ extern void objc_setFont(id, unsigned int); +/* for delegate_darwin.go */ +extern void *_appDelegate_applicationShouldTerminate; + #endif diff --git a/test/main.go b/test/main.go index 1c8f948..9df9c13 100644 --- a/test/main.go +++ b/test/main.go @@ -282,6 +282,9 @@ _=curtime case <-w.Closing: println("window closed event received") break mainloop + case <-AppQuit: + println("application quit event received") + break mainloop case <-b.Clicked: w.SetTitle(fmt.Sprintf("%v | %s | %s | %s | %s", c.Checked(),