From 2fb7056be4e26a5eecc365cd2608a8e8d552807f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 12 Mar 2014 12:14:24 -0400 Subject: [PATCH] Standardized message boxes so they appear similarly on all platforms. This shafts Windows because primary/secondary text message boxes were only added in Windows Vista, but at least MSDN provides discourse. --- dialog.go | 15 +++++++++------ dialog_darwin.go | 15 +++++++-------- dialog_unix.go | 20 ++++++++++---------- dialog_windows.go | 19 +++++++++++-------- test/main.go | 12 ++++++++---- todo.md | 3 --- 6 files changed, 45 insertions(+), 39 deletions(-) diff --git a/dialog.go b/dialog.go index 7b4fbeb..f24927b 100644 --- a/dialog.go +++ b/dialog.go @@ -2,16 +2,19 @@ package ui import ( - "fmt" + // ... ) - // MsgBox displays an informational message box to the user with just an OK button. -func MsgBox(title string, textfmt string, args ...interface{}) { - msgBox(title, fmt.Sprintf(textfmt, args...)) +// primaryText should be a short string describing the message, and will be displayed with additional emphasis on platforms that support it. +// secondaryText can be used to provide more information. +// On platforms that allow for the message box window to have a title, os.Args[0] is used. +func MsgBox(primaryText string, secondaryText string) { + msgBox(primaryText, secondaryText) } // MsgBoxError displays a message box to the user with just an OK button and an icon indicating an error. -func MsgBoxError(title string, textfmt string, args ...interface{}) { - msgBoxError(title, fmt.Sprintf(textfmt, args...)) +// Otherwise, it behaves like MsgBox. +func MsgBoxError(primaryText string, secondaryText string) { + msgBoxError(primaryText, secondaryText) } diff --git a/dialog_darwin.go b/dialog_darwin.go index ff7e166..7021a30 100644 --- a/dialog_darwin.go +++ b/dialog_darwin.go @@ -26,14 +26,13 @@ var ( _runModal = sel_getUid("runModal") ) -func _msgBox(title string, text string, style uintptr, button0 string) { +func _msgBox(primarytext string, secondarytext string, style uintptr, button0 string) { ret := make(chan struct{}) defer close(ret) uitask <- func() { box := objc_new(_NSAlert) - // TODO is this appropriate for title? - C.objc_msgSend_id(box, _setMessageText, toNSString(title)) - C.objc_msgSend_id(box, _setInformativeText, toNSString(text)) + C.objc_msgSend_id(box, _setMessageText, toNSString(primarytext)) + C.objc_msgSend_id(box, _setInformativeText, toNSString(secondarytext)) objc_msgSend_uint(box, _setAlertStyle, style) C.objc_msgSend_id(box, _addButtonWithTitle, toNSString(button0)) C.objc_msgSend_noargs(box, _runModal) @@ -42,11 +41,11 @@ func _msgBox(title string, text string, style uintptr, button0 string) { <-ret } -func msgBox(title string, text string) { +func msgBox(primarytext string, secondarytext string) { // TODO _NSInformationalAlertStyle? - _msgBox(title, text, _NSWarningAlertStyle, "OK") + _msgBox(primarytext, secondarytext, _NSWarningAlertStyle, "OK") } -func msgBoxError(title string, text string) { - _msgBox(title, text, _NSCriticalAlertStyle, "OK") +func msgBoxError(primarytext string, secondarytext string) { + _msgBox(primarytext, secondarytext, _NSCriticalAlertStyle, "OK") } diff --git a/dialog_unix.go b/dialog_unix.go index 4676cc4..2756f0d 100644 --- a/dialog_unix.go +++ b/dialog_unix.go @@ -16,15 +16,15 @@ import ( // GtkWidget *gtkNewMsgBox(GtkMessageType type, GtkButtonsType buttons, char *title, char *text) { GtkWidget *k = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, type, buttons, "%s", (gchar *) title); gtk_message_dialog_format_secondary_text((GtkMessageDialog *) k, "%s", (gchar *) text); return k; } import "C" -func _msgBox(text string, title string, msgtype C.GtkMessageType, buttons C.GtkButtonsType) (result C.gint) { +func _msgBox(primarytext string, secondarytext string, msgtype C.GtkMessageType, buttons C.GtkButtonsType) (result C.gint) { ret := make(chan C.gint) defer close(ret) uitask <- func() { - ctitle := C.CString(title) - defer C.free(unsafe.Pointer(ctitle)) - ctext := C.CString(text) - defer C.free(unsafe.Pointer(ctext)) - box := C.gtkNewMsgBox(msgtype, buttons, ctitle, ctext) + cprimarytext := C.CString(primarytext) + defer C.free(unsafe.Pointer(cprimarytext)) + csecondarytext := C.CString(secondarytext) + defer C.free(unsafe.Pointer(csecondarytext)) + box := C.gtkNewMsgBox(msgtype, buttons, cprimarytext, csecondarytext) response := C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(box))) C.gtk_widget_destroy(box) ret <- response @@ -32,11 +32,11 @@ func _msgBox(text string, title string, msgtype C.GtkMessageType, buttons C.GtkB return <-ret } -func msgBox(title string, text string) { +func msgBox(primarytext string, secondarytext string) { // TODO add an icon? - _msgBox(text, title, C.GtkMessageType(C.GTK_MESSAGE_OTHER), C.GtkButtonsType(C.GTK_BUTTONS_OK)) + _msgBox(primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_OTHER), C.GtkButtonsType(C.GTK_BUTTONS_OK)) } -func msgBoxError(title string, text string) { - _msgBox(text, title, C.GtkMessageType(C.GTK_MESSAGE_ERROR), C.GtkButtonsType(C.GTK_BUTTONS_OK)) +func msgBoxError(primarytext string, secondarytext string) { + _msgBox(primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_ERROR), C.GtkButtonsType(C.GTK_BUTTONS_OK)) } diff --git a/dialog_windows.go b/dialog_windows.go index e189082..546c5c5 100644 --- a/dialog_windows.go +++ b/dialog_windows.go @@ -3,6 +3,7 @@ package ui import ( "fmt" + "os" "syscall" "unsafe" ) @@ -73,23 +74,25 @@ var ( _messageBox = user32.NewProc("MessageBoxW") ) -func _msgBox(lpText string, lpCaption string, uType uint32) (result int) { +func _msgBox(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 + "\n\n" + secondarytext r1, _, err := _messageBox.Call( uintptr(_NULL), - uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpText))), - uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpCaption))), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(os.Args[0]))), 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, lpCaption, lpText)) + panic(fmt.Sprintf("error displaying message box to user: %v\nstyle: 0x%08X\ntitle: %q\ntext:\n%s", err, uType, os.Args[0], text)) } return int(r1) } -func msgBox(title string, text string) { +func msgBox(primarytext string, secondarytext string) { // TODO add an icon? - _msgBox(text, title, _MB_OK) + _msgBox(primarytext, secondarytext, _MB_OK) } -func msgBoxError(title string, text string) { - _msgBox(text, title, _MB_OK | _MB_ICONERROR) +func msgBoxError(primarytext string, secondarytext string) { + _msgBox(primarytext, secondarytext, _MB_OK | _MB_ICONERROR) } diff --git a/test/main.go b/test/main.go index 3dbae24..77adfae 100644 --- a/test/main.go +++ b/test/main.go @@ -40,7 +40,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("test", "%s: no panic", what) + MsgBoxError("test", fmt.Sprintf("%s: no panic", what)) panic("invalid test fail") } else { println("got", j.(error).Error()) @@ -200,12 +200,16 @@ mainloop: lb2.Delete(4) } case <-b3.Clicked: - MsgBox("List Info", - "cb1: %d %q (len %d)\ncb2: %d %q (len %d)\nlb1: %d %q (len %d)\nlb2: %d %q (len %d)", + f := MsgBox + if c.Checked() { + f = MsgBoxError + } + 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(), lb1.SelectedIndices(), lb1.Selection(), lb1.Len(), - lb2.SelectedIndices(), lb2.Selection(), lb2.Len()) + lb2.SelectedIndices(), lb2.Selection(), lb2.Len())) case <-incButton.Clicked: prog++ if prog > 100 { diff --git a/todo.md b/todo.md index 8bb383a..5f7a2bb 100644 --- a/todo.md +++ b/todo.md @@ -22,9 +22,6 @@ so I don't forget: - should message box text be selectable on all platforms or only on those that make it the default? - Listbox/Combobox.Index(n) - Index(n) is the name used by reflect.Value; use a different one? -- message boxes need cleanup: - - Windows: title: titlebar; text: message box text; no such thing as secondary text - - GTK+ AND Cocoa: title: message box text; text: secondary text; nothing: titlebar - change sysData.make() so it does not take the initial window text as an argument and instead have the respective Control/Window.make() call sysData.setText() expressly; this would allow me to remove the "no such concept of text" checks from the GTK+ and Mac OS X backends important things: