Added rewrite of most of the dialog system for the new API. The Windows API is, however, getting in the way...
This commit is contained in:
parent
9134189f52
commit
3fbf746682
50
dialog.go
50
dialog.go
|
@ -2,9 +2,40 @@
|
||||||
|
|
||||||
package ui
|
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)
|
// sentinel (not nil so programmer errors don't go undetected)
|
||||||
// this window is invalid and cannot be used directly
|
// 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.
|
// 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.
|
// 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.
|
// 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.
|
// See "On Dialogs" in the package overview for behavioral information.
|
||||||
func MsgBox(primaryText string, secondaryText string) {
|
func MsgBox(primaryText string, secondaryText string) Response {
|
||||||
<-dialogWindow.msgBox(primaryText, secondaryText)
|
dialogWindow.msgBox(primaryText, secondaryText)
|
||||||
|
return (<-dh).res
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgBox is the Window method version of the package-scope function MsgBox.
|
// 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.
|
// 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 {
|
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")
|
||||||
}
|
}
|
||||||
|
@ -30,15 +62,13 @@ func (w *Window) MsgBox(primaryText string, secondaryText string) (done chan str
|
||||||
// Otherwise, it behaves like MsgBox.
|
// Otherwise, it behaves like MsgBox.
|
||||||
//
|
//
|
||||||
// See "On Dialogs" in the package overview for more information.
|
// See "On Dialogs" in the package overview for more information.
|
||||||
func MsgBoxError(primaryText string, secondaryText string) {
|
func MsgBoxError(primaryText string, secondaryText string) Dialog {
|
||||||
<-dialogWindow.msgBoxError(primaryText, secondaryText)
|
dialogWindow.msgBoxError(primaryText, secondaryText)
|
||||||
|
return (<-dh).res
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgBoxError is the Window method version of the package-scope function MsgBoxError.
|
// 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.
|
// 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{}) {
|
func (w *Window) MsgBoxError(primaryText string, secondaryText string) Dialog {
|
||||||
if !w.created {
|
|
||||||
panic("parent window passed to MsgBoxError() before it was created")
|
|
||||||
}
|
|
||||||
return w.msgBoxError(primaryText, secondaryText)
|
return w.msgBoxError(primaryText, secondaryText)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,11 @@ var (
|
||||||
_messageBox = user32.NewProc("MessageBoxW")
|
_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."
|
// 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 != "" {
|
||||||
|
@ -26,43 +30,28 @@ func _msgBox(parent *Window, primarytext string, secondarytext string, uType uin
|
||||||
} 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)
|
r1, _, err := _messageBox.Call(
|
||||||
go func() {
|
uintptr(parenthwnd),
|
||||||
ret := make(chan uiret)
|
utf16ToArg(ptext),
|
||||||
defer close(ret)
|
utf16ToArg(ptitle),
|
||||||
uitask <- &uimsg{
|
uintptr(uType))
|
||||||
call: _messageBox,
|
if r1 == 0 { // failure
|
||||||
p: []uintptr{
|
panic(fmt.Sprintf("error displaying message box to user: %v\nstyle: 0x%08X\ntitle: %q\ntext:\n%s", err, uType, os.Args[0], text))
|
||||||
uintptr(parenthwnd),
|
}
|
||||||
utf16ToArg(ptext),
|
w.sysData.winhandler.Event(Dismissed, &dialogret{
|
||||||
utf16ToArg(ptitle),
|
res: dialogResponses[r1],
|
||||||
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 (w *Window) msgBox(primarytext string, secondarytext string) (done chan struct{}) {
|
func (w *Window) msgBox(primarytext string, secondarytext string) {
|
||||||
done = make(chan struct{})
|
// send to uitask so the function can return immediately
|
||||||
go func() {
|
touitask(func() {
|
||||||
<-_msgBox(w, primarytext, secondarytext, _MB_OK)
|
_msgBox(w, primarytext, secondarytext, _MB_OK)
|
||||||
done <- struct{}{}
|
})
|
||||||
}()
|
|
||||||
return done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) msgBoxError(primarytext string, secondarytext string) (done chan struct{}) {
|
func (w *Window) msgBoxError(primarytext string, secondarytext string) {
|
||||||
done = make(chan struct{})
|
touitask(func() {
|
||||||
go func() {
|
_msgBox(w, primarytext, secondarytext, _MB_OK|_MB_ICONERROR)
|
||||||
<-_msgBox(w, primarytext, secondarytext, _MB_OK|_MB_ICONERROR)
|
})
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
return done
|
|
||||||
}
|
}
|
||||||
|
|
15
init.go
15
init.go
|
@ -17,13 +17,10 @@ func Go(main func()) error {
|
||||||
return ui(main)
|
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).
|
// 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.
|
||||||
// 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.
|
// 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)
|
||||||
// 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.
|
func touitask(f func()) {
|
||||||
// Do not merely check this channel alone; it is not guaranteed to be pulsed on all systems or in all conditions.
|
go func() { // to avoid locking uitask itself
|
||||||
var AppQuit chan struct{}
|
uitask <- f
|
||||||
|
}()
|
||||||
func init() {
|
|
||||||
// don't expose this in the documentation
|
|
||||||
AppQuit = newEvent()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ type Event int
|
||||||
const (
|
const (
|
||||||
Closing Event = iota // Window close
|
Closing Event = iota // Window close
|
||||||
Clicked // Button click
|
Clicked // Button click
|
||||||
|
Dismissed // Dialog closed
|
||||||
CustomEvent = 5000 // very high number; higher than the package would ever need, anyway
|
CustomEvent = 5000 // very high number; higher than the package would ever need, anyway
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue