summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dialog.go50
-rw-r--r--dialog_windows.go61
-rw-r--r--init.go15
-rw-r--r--window.go1
4 files changed, 72 insertions, 55 deletions
diff --git a/dialog.go b/dialog.go
index 9c2ad2c..a275a64 100644
--- a/dialog.go
+++ b/dialog.go
@@ -2,9 +2,40 @@
package ui
+// Dialog is an interface adopted by all dialogs.
+type Dialog struct {
+ Response() Response // response code from the dialog
+ Selection() interface{} // currently nil
+}
+
+// Response denotes a response from the user to a Dialog.
+type Response uint
+const (
+ NotDone Response = iota
+ OK
+)
+
+// basic return
+type dialogret struct {
+ res Response
+ sel interface{}
+}
+func (d *dialogret) Response() Response { return d.res }
+func (d *dialogret) Selection() interface{} { return d.sel }
+
// sentinel (not nil so programmer errors don't go undetected)
// this window is invalid and cannot be used directly
-var dialogWindow = new(Window)
+// notice the support it uses
+var dialogWindow = &Window{
+ sysData: &sysData{
+ winhandler: dh,
+ }
+}
+type dhandler chan *dialogret
+func (d dhandler) Event(e Event, dat interface{}) {
+ d <- dat.(*dialogret)
+}
+var dh = make(dhandler)
// MsgBox displays an informational message box to the user with just an OK button.
// primaryText should be a short string describing the message, and will be displayed with additional emphasis on platforms that support it.
@@ -13,13 +44,14 @@ var dialogWindow = new(Window)
// On platforms that allow for the message box window to have a title, os.Args[0] is used.
//
// See "On Dialogs" in the package overview for behavioral information.
-func MsgBox(primaryText string, secondaryText string) {
- <-dialogWindow.msgBox(primaryText, secondaryText)
+func MsgBox(primaryText string, secondaryText string) Response {
+ dialogWindow.msgBox(primaryText, secondaryText)
+ return (<-dh).res
}
// MsgBox is the Window method version of the package-scope function MsgBox.
// See that function's documentation and "On Dialogs" in the package overview for more information.
-func (w *Window) MsgBox(primaryText string, secondaryText string) (done chan struct{}) {
+func (w *Window) MsgBox(primaryText string, secondaryText string) Dialog {
if !w.created {
panic("parent window passed to Window.MsgBox() before it was created")
}
@@ -30,15 +62,13 @@ func (w *Window) MsgBox(primaryText string, secondaryText string) (done chan str
// Otherwise, it behaves like MsgBox.
//
// See "On Dialogs" in the package overview for more information.
-func MsgBoxError(primaryText string, secondaryText string) {
- <-dialogWindow.msgBoxError(primaryText, secondaryText)
+func MsgBoxError(primaryText string, secondaryText string) Dialog {
+ dialogWindow.msgBoxError(primaryText, secondaryText)
+ return (<-dh).res
}
// MsgBoxError is the Window method version of the package-scope function MsgBoxError.
// See that function's documentation and "On Dialogs" in the package overview for more information.
-func (w *Window) MsgBoxError(primaryText string, secondaryText string) (done chan struct{}) {
- if !w.created {
- panic("parent window passed to MsgBoxError() before it was created")
- }
+func (w *Window) MsgBoxError(primaryText string, secondaryText string) Dialog {
return w.msgBoxError(primaryText, secondaryText)
}
diff --git a/dialog_windows.go b/dialog_windows.go
index c6da94b..5422f1c 100644
--- a/dialog_windows.go
+++ b/dialog_windows.go
@@ -11,7 +11,11 @@ var (
_messageBox = user32.NewProc("MessageBoxW")
)
-func _msgBox(parent *Window, primarytext string, secondarytext string, uType uint32) (result chan int) {
+var dialogResponse = map[uintptr]Response{
+ _IDOK: OK,
+}
+
+func _msgBox(parent *Window, primarytext string, secondarytext string, uType uint32) (result int) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa511267.aspx says "Use task dialogs whenever appropriate to achieve a consistent look and layout. Task dialogs require Windows Vista® or later, so they aren't suitable for earlier versions of Windows. If you must use a message box, separate the main instruction from the supplemental instruction with two line breaks."
text := primarytext
if secondarytext != "" {
@@ -26,43 +30,28 @@ func _msgBox(parent *Window, primarytext string, secondarytext string, uType uin
} else {
uType |= _MB_TASKMODAL // make modal to every window in the program (they're all windows of the uitask, which is a single thread)
}
- retchan := make(chan int)
- go func() {
- ret := make(chan uiret)
- defer close(ret)
- uitask <- &uimsg{
- call: _messageBox,
- p: []uintptr{
- uintptr(parenthwnd),
- utf16ToArg(ptext),
- utf16ToArg(ptitle),
- uintptr(uType),
- },
- ret: ret,
- }
- r := <-ret
- if r.ret == 0 { // failure
- panic(fmt.Sprintf("error displaying message box to user: %v\nstyle: 0x%08X\ntitle: %q\ntext:\n%s", r.err, uType, os.Args[0], text))
- }
- retchan <- int(r.ret)
- }()
- return retchan
+ r1, _, err := _messageBox.Call(
+ uintptr(parenthwnd),
+ utf16ToArg(ptext),
+ utf16ToArg(ptitle),
+ uintptr(uType))
+ if r1 == 0 { // failure
+ panic(fmt.Sprintf("error displaying message box to user: %v\nstyle: 0x%08X\ntitle: %q\ntext:\n%s", err, uType, os.Args[0], text))
+ }
+ w.sysData.winhandler.Event(Dismissed, &dialogret{
+ res: dialogResponses[r1],
+ })
}
-func (w *Window) msgBox(primarytext string, secondarytext string) (done chan struct{}) {
- done = make(chan struct{})
- go func() {
- <-_msgBox(w, primarytext, secondarytext, _MB_OK)
- done <- struct{}{}
- }()
- return done
+func (w *Window) msgBox(primarytext string, secondarytext string) {
+ // send to uitask so the function can return immediately
+ touitask(func() {
+ _msgBox(w, primarytext, secondarytext, _MB_OK)
+ })
}
-func (w *Window) msgBoxError(primarytext string, secondarytext string) (done chan struct{}) {
- done = make(chan struct{})
- go func() {
- <-_msgBox(w, primarytext, secondarytext, _MB_OK|_MB_ICONERROR)
- done <- struct{}{}
- }()
- return done
+func (w *Window) msgBoxError(primarytext string, secondarytext string) {
+ touitask(func() {
+ _msgBox(w, primarytext, secondarytext, _MB_OK|_MB_ICONERROR)
+ })
}
diff --git a/init.go b/init.go
index 2e59301..0474612 100644
--- a/init.go
+++ b/init.go
@@ -17,13 +17,10 @@ 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; regardless of whether or not you respond to this signal, the application will not quit.
-// 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()
+// This function is a simple helper functionn that basically pushes the effect of a function call for later. This allows the selected safe Window methods to be safe.
+// It's also currently used by the various dialog box functions on Windows to allow them to return instantly, rather than wait for the dialog box to finish (which both GTK+ and Mac OS X let you do). I consider this a race condition bug. TODO (also TODO document the /intended/ behavior)
+func touitask(f func()) {
+ go func() { // to avoid locking uitask itself
+ uitask <- f
+ }()
}
diff --git a/window.go b/window.go
index 9b58937..3cf52b0 100644
--- a/window.go
+++ b/window.go
@@ -37,6 +37,7 @@ type Event int
const (
Closing Event = iota // Window close
Clicked // Button click
+ Dismissed // Dialog closed
CustomEvent = 5000 // very high number; higher than the package would ever need, anyway
)