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.
This commit is contained in:
parent
6eea59c30a
commit
2fb7056be4
15
dialog.go
15
dialog.go
|
@ -2,16 +2,19 @@
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
// ...
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// MsgBox displays an informational message box to the user with just an OK button.
|
// MsgBox displays an informational message box to the user with just an OK button.
|
||||||
func MsgBox(title string, textfmt string, args ...interface{}) {
|
// primaryText should be a short string describing the message, and will be displayed with additional emphasis on platforms that support it.
|
||||||
msgBox(title, fmt.Sprintf(textfmt, args...))
|
// 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.
|
// 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{}) {
|
// Otherwise, it behaves like MsgBox.
|
||||||
msgBoxError(title, fmt.Sprintf(textfmt, args...))
|
func MsgBoxError(primaryText string, secondaryText string) {
|
||||||
|
msgBoxError(primaryText, secondaryText)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,14 +26,13 @@ var (
|
||||||
_runModal = sel_getUid("runModal")
|
_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{})
|
ret := make(chan struct{})
|
||||||
defer close(ret)
|
defer close(ret)
|
||||||
uitask <- func() {
|
uitask <- func() {
|
||||||
box := objc_new(_NSAlert)
|
box := objc_new(_NSAlert)
|
||||||
// TODO is this appropriate for title?
|
C.objc_msgSend_id(box, _setMessageText, toNSString(primarytext))
|
||||||
C.objc_msgSend_id(box, _setMessageText, toNSString(title))
|
C.objc_msgSend_id(box, _setInformativeText, toNSString(secondarytext))
|
||||||
C.objc_msgSend_id(box, _setInformativeText, toNSString(text))
|
|
||||||
objc_msgSend_uint(box, _setAlertStyle, style)
|
objc_msgSend_uint(box, _setAlertStyle, style)
|
||||||
C.objc_msgSend_id(box, _addButtonWithTitle, toNSString(button0))
|
C.objc_msgSend_id(box, _addButtonWithTitle, toNSString(button0))
|
||||||
C.objc_msgSend_noargs(box, _runModal)
|
C.objc_msgSend_noargs(box, _runModal)
|
||||||
|
@ -42,11 +41,11 @@ func _msgBox(title string, text string, style uintptr, button0 string) {
|
||||||
<-ret
|
<-ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func msgBox(title string, text string) {
|
func msgBox(primarytext string, secondarytext string) {
|
||||||
// TODO _NSInformationalAlertStyle?
|
// TODO _NSInformationalAlertStyle?
|
||||||
_msgBox(title, text, _NSWarningAlertStyle, "OK")
|
_msgBox(primarytext, secondarytext, _NSWarningAlertStyle, "OK")
|
||||||
}
|
}
|
||||||
|
|
||||||
func msgBoxError(title string, text string) {
|
func msgBoxError(primarytext string, secondarytext string) {
|
||||||
_msgBox(title, text, _NSCriticalAlertStyle, "OK")
|
_msgBox(primarytext, secondarytext, _NSCriticalAlertStyle, "OK")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
// 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"
|
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)
|
ret := make(chan C.gint)
|
||||||
defer close(ret)
|
defer close(ret)
|
||||||
uitask <- func() {
|
uitask <- func() {
|
||||||
ctitle := C.CString(title)
|
cprimarytext := C.CString(primarytext)
|
||||||
defer C.free(unsafe.Pointer(ctitle))
|
defer C.free(unsafe.Pointer(cprimarytext))
|
||||||
ctext := C.CString(text)
|
csecondarytext := C.CString(secondarytext)
|
||||||
defer C.free(unsafe.Pointer(ctext))
|
defer C.free(unsafe.Pointer(csecondarytext))
|
||||||
box := C.gtkNewMsgBox(msgtype, buttons, ctitle, ctext)
|
box := C.gtkNewMsgBox(msgtype, buttons, cprimarytext, csecondarytext)
|
||||||
response := C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(box)))
|
response := C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(box)))
|
||||||
C.gtk_widget_destroy(box)
|
C.gtk_widget_destroy(box)
|
||||||
ret <- response
|
ret <- response
|
||||||
|
@ -32,11 +32,11 @@ func _msgBox(text string, title string, msgtype C.GtkMessageType, buttons C.GtkB
|
||||||
return <-ret
|
return <-ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func msgBox(title string, text string) {
|
func msgBox(primarytext string, secondarytext string) {
|
||||||
// TODO add an icon?
|
// 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) {
|
func msgBoxError(primarytext string, secondarytext string) {
|
||||||
_msgBox(text, title, C.GtkMessageType(C.GTK_MESSAGE_ERROR), C.GtkButtonsType(C.GTK_BUTTONS_OK))
|
_msgBox(primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_ERROR), C.GtkButtonsType(C.GTK_BUTTONS_OK))
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -73,23 +74,25 @@ var (
|
||||||
_messageBox = user32.NewProc("MessageBoxW")
|
_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(
|
r1, _, err := _messageBox.Call(
|
||||||
uintptr(_NULL),
|
uintptr(_NULL),
|
||||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpText))),
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
|
||||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpCaption))),
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(os.Args[0]))),
|
||||||
uintptr(uType))
|
uintptr(uType))
|
||||||
if r1 == 0 { // failure
|
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)
|
return int(r1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func msgBox(title string, text string) {
|
func msgBox(primarytext string, secondarytext string) {
|
||||||
// TODO add an icon?
|
// TODO add an icon?
|
||||||
_msgBox(text, title, _MB_OK)
|
_msgBox(primarytext, secondarytext, _MB_OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func msgBoxError(title string, text string) {
|
func msgBoxError(primarytext string, secondarytext string) {
|
||||||
_msgBox(text, title, _MB_OK | _MB_ICONERROR)
|
_msgBox(primarytext, secondarytext, _MB_OK | _MB_ICONERROR)
|
||||||
}
|
}
|
||||||
|
|
12
test/main.go
12
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) {
|
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("test", "%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())
|
||||||
|
@ -200,12 +200,16 @@ mainloop:
|
||||||
lb2.Delete(4)
|
lb2.Delete(4)
|
||||||
}
|
}
|
||||||
case <-b3.Clicked:
|
case <-b3.Clicked:
|
||||||
MsgBox("List Info",
|
f := MsgBox
|
||||||
"cb1: %d %q (len %d)\ncb2: %d %q (len %d)\nlb1: %d %q (len %d)\nlb2: %d %q (len %d)",
|
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(),
|
cb1.SelectedIndex(), cb1.Selection(), cb1.Len(),
|
||||||
cb2.SelectedIndex(), cb2.Selection(), cb2.Len(),
|
cb2.SelectedIndex(), cb2.Selection(), cb2.Len(),
|
||||||
lb1.SelectedIndices(), lb1.Selection(), lb1.Len(),
|
lb1.SelectedIndices(), lb1.Selection(), lb1.Len(),
|
||||||
lb2.SelectedIndices(), lb2.Selection(), lb2.Len())
|
lb2.SelectedIndices(), lb2.Selection(), lb2.Len()))
|
||||||
case <-incButton.Clicked:
|
case <-incButton.Clicked:
|
||||||
prog++
|
prog++
|
||||||
if prog > 100 {
|
if prog > 100 {
|
||||||
|
|
3
todo.md
3
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?
|
- should message box text be selectable on all platforms or only on those that make it the default?
|
||||||
- Listbox/Combobox.Index(n)
|
- Listbox/Combobox.Index(n)
|
||||||
- Index(n) is the name used by reflect.Value; use a different one?
|
- 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
|
- 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:
|
important things:
|
||||||
|
|
Loading…
Reference in New Issue