Implemented the new MsgBox()/MsgBoxError() behavior on Windows.

This commit is contained in:
Pietro Gagliardi 2014-06-08 08:50:52 -04:00
parent 7cb8fc1e32
commit 641d11c6fe
3 changed files with 77 additions and 35 deletions

View File

@ -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 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. // 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) { 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. // 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. // 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 yet been created, MsgBox() panics.
// If w has not been shown yet or is currently hidden, what MsgBox does is implementation-defined. // 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 { if !w.created {
panic("parent window passed to Window.MsgBox() before it was 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. // MsgBoxError displays a message box to the user with just an OK button and an icon indicating an error.
// Otherwise, it behaves like MsgBox. // Otherwise, it behaves like MsgBox.
func MsgBoxError(primaryText string, secondaryText string) { 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. // 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. // 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 { if !w.created {
panic("parent window passed to MsgBoxError() before it was created") panic("parent window passed to MsgBoxError() before it was created")
} }
w.msgBoxError(primaryText, secondaryText) return w.msgBoxError(primaryText, secondaryText)
} }

View File

@ -11,7 +11,7 @@ var (
_messageBox = user32.NewProc("MessageBoxW") _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." // 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 text := primarytext
if secondarytext != "" { if secondarytext != "" {
@ -19,15 +19,17 @@ func _msgBox(parent *Window, primarytext string, secondarytext string, uType uin
} }
ptext := toUTF16(text) ptext := toUTF16(text)
ptitle := toUTF16(os.Args[0]) ptitle := toUTF16(os.Args[0])
ret := make(chan uiret)
defer close(ret)
parenthwnd := _HWND(_NULL) parenthwnd := _HWND(_NULL)
if parent != nil { if parent != dialogWindow {
parenthwnd = parent.sysData.hwnd parenthwnd = parent.sysData.hwnd
uType |= _MB_APPLMODAL // only for this window uType |= _MB_APPLMODAL // only for this window
} else { } else {
uType |= _MB_TASKMODAL // make modal to every window in the program (they're all windows of the uitask, which is a single thread) 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{ uitask <- &uimsg{
call: _messageBox, call: _messageBox,
p: []uintptr{ p: []uintptr{
@ -42,13 +44,25 @@ func _msgBox(parent *Window, primarytext string, secondarytext string, uType uin
if r.ret == 0 { // failure 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)) 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 <- int(r.ret)
}()
return retchan
} }
func msgBox(parent *Window, primarytext string, secondarytext string) { func (w *Window) msgBox(primarytext string, secondarytext string) (done chan struct{}) {
_msgBox(parent, primarytext, secondarytext, _MB_OK) done = make(chan struct{})
go func() {
<-_msgBox(w, primarytext, secondarytext, _MB_OK)
done <- struct{}{}
}()
return done
} }
func msgBoxError(parent *Window, primarytext string, secondarytext string) { func (w *Window) msgBoxError(primarytext string, secondarytext string) (done chan struct{}) {
_msgBox(parent, primarytext, secondarytext, _MB_OK | _MB_ICONERROR) done = make(chan struct{})
go func() {
<-_msgBox(w, primarytext, secondarytext, _MB_OK | _MB_ICONERROR)
done <- struct{}{}
}()
return done
} }

View File

@ -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) { func invalidTest(c *Combobox, l *Listbox, s *Stack, g *Grid) {
x := func(what string ) { x := func(what string ) {
if j := recover(); j == nil { 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") panic("invalid test fail")
} else { } else {
println("got", j.(error).Error()) 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) 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") var invalidBefore = flag.Bool("invalid", false, "run invalid test before opening window")
@ -226,7 +226,7 @@ func areaTest() {
if err != nil { println(err); continue } if err != nil { println(err); continue }
a.SetSize(width, height) a.SetSize(width, height)
case <-modaltest.Clicked: case <-modaltest.Clicked:
MsgBox(nil, "Modal Test", "") MsgBox("Modal Test", "")
} }
} }
} }
@ -260,6 +260,8 @@ func areaboundsTest() {
<-w.Closing <-w.Closing
} }
var dialogTest = flag.Bool("dialog", false, "add Window.MsgBox() channel test window")
func myMain() { func myMain() {
if *doArea { if *doArea {
areaTest() areaTest()
@ -284,6 +286,9 @@ func myMain() {
cb2 := NewCombobox("You can't edit me!", "No you can't!", "No you won't!") cb2 := NewCombobox("You can't edit me!", "No you can't!", "No you won't!")
e := NewLineEdit("Enter text here too") e := NewLineEdit("Enter text here too")
l := NewLabel("This is a label") l := NewLabel("This is a label")
resetl := func() {
l.SetText("This is a label")
}
b3 := NewButton("List Info") b3 := NewButton("List Info")
s3 := NewHorizontalStack(l, b3) s3 := NewHorizontalStack(l, b3)
s3.SetStretchy(0) s3.SetStretchy(0)
@ -330,6 +335,17 @@ func myMain() {
ticker := time.Tick(time.Second) 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: mainloop:
for { for {
select { select {
@ -368,7 +384,7 @@ _=curtime
if c.Checked() { if c.Checked() {
f = MsgBoxError 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)", 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(), cb1.SelectedIndex(), cb1.Selection(), cb1.Len(),
cb2.SelectedIndex(), cb2.Selection(), cb2.Len(), cb2.SelectedIndex(), cb2.Selection(), cb2.Len(),
@ -393,8 +409,18 @@ _=curtime
case <-invalidButton.Clicked: case <-invalidButton.Clicked:
invalidTest(cb1, lb1, nil, nil) invalidTest(cb1, lb1, nil, nil)
case <-bmsg.Clicked: case <-bmsg.Clicked:
MsgBox(nil, "Title Only, no parent", "") MsgBox("Title Only, no parent", "")
MsgBox(w, "Title and Text", "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() w.Hide()