diff --git a/dialog.go b/dialog.go index 9a5ec5e..2e96b22 100644 --- a/dialog.go +++ b/dialog.go @@ -19,7 +19,7 @@ var dialogWindow *Window // Whether or not resizing Windows will still be allowed is implementation-defined; if the implementation does allow it, resizes will still work properly. // Whether or not the message box stays above all other W+indows in the program is also implementation-defined. func MsgBox(primaryText string, secondaryText string) { - dialogWindow.msgBox(parent, primaryText, secondaryText) + <-dialogWindow.msgBox(primaryText, secondaryText) } // MsgBox behaves like the package-scope MsgBox function, except the message box is modal to w only. @@ -28,24 +28,26 @@ func MsgBox(primaryText string, secondaryText string) { // Whether w can be resized while the message box is displayed is implementation-defined, but will work properly if allowed. // If w has not yet been created, MsgBox() panics. // If w has not been shown yet or is currently hidden, what MsgBox does is implementation-defined. -func (w *Window) MsgBox(primaryText string, secondaryText string) { +// +// On return, done will be a channel that is pulsed when the message box is dismissed. +func (w *Window) MsgBox(primaryText string, secondaryText string) (done chan struct{}) { if !w.created { panic("parent window passed to Window.MsgBox() before it was created") } - w.msgBox(primaryText, secondaryText) + return w.msgBox(primaryText, secondaryText) } // MsgBoxError displays a message box to the user with just an OK button and an icon indicating an error. // Otherwise, it behaves like MsgBox. func MsgBoxError(primaryText string, secondaryText string) { - dialogWindow.msgBoxError(parent, primaryText, secondaryText) + <-dialogWindow.msgBoxError(primaryText, secondaryText) } // MsgBoxError displays a message box to the user with just an OK button and an icon indicating an error. // Otherwise, it behaves like Window.MsgBox. -func (w *Window) MsgBoxError(primaryText string, secondaryText string) { +func (w *Window) MsgBoxError(primaryText string, secondaryText string) (done chan struct{}) { if !w.created { panic("parent window passed to MsgBoxError() before it was created") } - w.msgBoxError(primaryText, secondaryText) + return w.msgBoxError(primaryText, secondaryText) } diff --git a/dialog_windows.go b/dialog_windows.go index 131319d..d863f13 100644 --- a/dialog_windows.go +++ b/dialog_windows.go @@ -11,7 +11,7 @@ var ( _messageBox = user32.NewProc("MessageBoxW") ) -func _msgBox(parent *Window, primarytext string, secondarytext string, uType uint32) (result int) { +func _msgBox(parent *Window, primarytext string, secondarytext string, uType uint32) (result chan 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 != "" { @@ -19,36 +19,50 @@ func _msgBox(parent *Window, primarytext string, secondarytext string, uType uin } ptext := toUTF16(text) ptitle := toUTF16(os.Args[0]) - ret := make(chan uiret) - defer close(ret) parenthwnd := _HWND(_NULL) - if parent != nil { + if parent != dialogWindow { parenthwnd = parent.sysData.hwnd uType |= _MB_APPLMODAL // only for this window } else { uType |= _MB_TASKMODAL // make modal to every window in the program (they're all windows of the uitask, which is a single thread) } - 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)) - } - return int(r.ret) + 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 } -func msgBox(parent *Window, primarytext string, secondarytext string) { - _msgBox(parent, primarytext, secondarytext, _MB_OK) +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 msgBoxError(parent *Window, primarytext string, secondarytext string) { - _msgBox(parent, primarytext, secondarytext, _MB_OK | _MB_ICONERROR) +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 } diff --git a/test/main.go b/test/main.go index 31e66c1..59a5791 100644 --- a/test/main.go +++ b/test/main.go @@ -49,7 +49,7 @@ var macCrashTest = flag.Bool("maccrash", false, "attempt crash on Mac OS X on de func invalidTest(c *Combobox, l *Listbox, s *Stack, g *Grid) { x := func(what string ) { if j := recover(); j == nil { - MsgBoxError(nil, "test", fmt.Sprintf("%s: no panic", what)) + MsgBoxError("test", fmt.Sprintf("%s: no panic", what)) panic("invalid test fail") } else { println("got", j.(error).Error()) @@ -143,7 +143,7 @@ func invalidTest(c *Combobox, l *Listbox, s *Stack, g *Grid) { defer x("Area.SetSize() " + q.msg); a.SetSize(q.x, q.y); panic(nil) }() } - MsgBox(nil, "test", "all working as intended") + MsgBox("test", "all working as intended") } var invalidBefore = flag.Bool("invalid", false, "run invalid test before opening window") @@ -226,7 +226,7 @@ func areaTest() { if err != nil { println(err); continue } a.SetSize(width, height) case <-modaltest.Clicked: - MsgBox(nil, "Modal Test", "") + MsgBox("Modal Test", "") } } } @@ -260,6 +260,8 @@ func areaboundsTest() { <-w.Closing } +var dialogTest = flag.Bool("dialog", false, "add Window.MsgBox() channel test window") + func myMain() { if *doArea { areaTest() @@ -284,6 +286,9 @@ func myMain() { cb2 := NewCombobox("You can't edit me!", "No you can't!", "No you won't!") e := NewLineEdit("Enter text here too") l := NewLabel("This is a label") + resetl := func() { + l.SetText("This is a label") + } b3 := NewButton("List Info") s3 := NewHorizontalStack(l, b3) s3.SetStretchy(0) @@ -330,6 +335,17 @@ func myMain() { ticker := time.Tick(time.Second) + dialog_bMsgBox := NewButton("MsgBox()") + dialog_bMsgBoxError := NewButton("MsgBoxError()") + dialog_win := NewWindow("Dialogs", 200, 200) + if *dialogTest { + dialog_win.Open(NewVerticalStack( + dialog_bMsgBox, + dialog_bMsgBoxError)) + } + + var dialog_sret chan struct{} = nil + mainloop: for { select { @@ -368,7 +384,7 @@ _=curtime if c.Checked() { f = MsgBoxError } - f(nil, "List Info", + f("List Info", fmt.Sprintf("cb1: %d %q (len %d)\ncb2: %d %q (len %d)\nlb1: %d %q (len %d)\nlb2: %d %q (len %d)", cb1.SelectedIndex(), cb1.Selection(), cb1.Len(), cb2.SelectedIndex(), cb2.Selection(), cb2.Len(), @@ -393,8 +409,18 @@ _=curtime case <-invalidButton.Clicked: invalidTest(cb1, lb1, nil, nil) case <-bmsg.Clicked: - MsgBox(nil, "Title Only, no parent", "") - MsgBox(w, "Title and Text", "parent") + MsgBox("Title Only, no parent", "") + MsgBox("Title and Text", "parent") + // dialogs + case <-dialog_bMsgBox.Clicked: + dialog_sret = dialog_win.MsgBox("Message Box", "Dismiss") + l.SetText("DIALOG") + case <-dialog_bMsgBoxError.Clicked: + dialog_sret = dialog_win.MsgBoxError("Message Box", "Dismiss") + l.SetText("DIALOG") + case <-dialog_sret: + dialog_sret = nil + resetl() } } w.Hide()