From affc65a5a402534527c4d4fbc5b381b5b1283445 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 Jun 2014 16:37:55 -0400 Subject: [PATCH] Fixed the build. New API works on Windows! Also removed TODO on dialogs in Windows since I can't reproduce the weird behavior anymore; I guess the new code fixes it. --- button.go | 1 - dialog.go | 6 +----- dialog_windows.go | 4 ++-- init.go | 15 +++++++++++--- sysdata_windows.go | 1 + test/kbtest.go | 2 +- test/main.go | 40 +++++++++++++++++++------------------ test/spacing.go | 2 +- todo.md | 9 ++++++--- uitask_windows.go | 15 +++++++------- window.go | 20 ++++++++----------- zconstants_windows_386.go | 1 + zconstants_windows_amd64.go | 1 + 13 files changed, 63 insertions(+), 54 deletions(-) diff --git a/button.go b/button.go index 25d0600..c4143aa 100644 --- a/button.go +++ b/button.go @@ -14,7 +14,6 @@ func NewButton(text string) (b *Button) { return &Button{ sysData: mksysdata(c_button), initText: text, - Clicked: newEvent(), } } diff --git a/dialog.go b/dialog.go index e676127..6c62bfd 100644 --- a/dialog.go +++ b/dialog.go @@ -12,11 +12,7 @@ const ( // sentinel (not nil so programmer errors don't go undetected) // this window is invalid and cannot be used directly // notice the support it uses -var dialogWindow = &Window{ - sysData: &sysData{ - winhandler: dh, - } -} +var dialogWindow = new(Window) // 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. diff --git a/dialog_windows.go b/dialog_windows.go index 18de661..5095999 100644 --- a/dialog_windows.go +++ b/dialog_windows.go @@ -11,11 +11,11 @@ var ( _messageBox = user32.NewProc("MessageBoxW") ) -var dialogResponse = map[uintptr]Response{ +var dialogResponses = map[uintptr]Response{ _IDOK: OK, } -func _msgBox(parent *Window, primarytext string, secondarytext string, uType uint32) (result int) { +func _msgBox(parent *Window, primarytext string, secondarytext string, uType uint32) Response { // 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 != "" { diff --git a/init.go b/init.go index a6a4882..72f24ef 100644 --- a/init.go +++ b/init.go @@ -18,7 +18,7 @@ import ( // If you must, and if the toolkit also has environment variable equivalents to these flags (for instance, GTK+), use those instead. func Go() error { runtime.LockOSThread() - if err := uiinit(main); err != nil { + if err := uiinit(); err != nil { return err } Ready <- struct{}{} @@ -37,9 +37,18 @@ var Ready = make(chan struct{}) var Stop = make(chan struct{}) // 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. -// 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) +// TODO make sure this acts sanely if called from uitask itself func touitask(f func()) { + done := make(chan struct{}) + defer close(done) go func() { // to avoid locking uitask itself - uitask <- f + done2 := make(chan struct{}) // make the chain uitask <- f <- uitask to avoid deadlocks + defer close(done2) + uitask <- func() { + f() + done2 <- struct{}{} + } + done <- <-done2 }() + <-done } diff --git a/sysdata_windows.go b/sysdata_windows.go index 42b73c7..1d3541a 100644 --- a/sysdata_windows.go +++ b/sysdata_windows.go @@ -391,6 +391,7 @@ func (s *sysData) setWindowSize(width int, height int) error { if err != nil { panic(fmt.Errorf("error actually resizing window: %v", err)) } + return nil } func (s *sysData) delete(index int) { diff --git a/test/kbtest.go b/test/kbtest.go index 7a10262..4e97708 100644 --- a/test/kbtest.go +++ b/test/kbtest.go @@ -74,7 +74,7 @@ func (a *keyboardArea) Key(e KeyEvent) (repaint bool) { } type kbhandler struct{} -func (kbhandler) Handle(e Event, d interface{}) { +func (kbhandler) Event(e Event, d interface{}) { if e == Closing { *(d.(*bool)) = true } diff --git a/test/main.go b/test/main.go index 7d3ddd9..bdeb38b 100644 --- a/test/main.go +++ b/test/main.go @@ -47,10 +47,11 @@ func gridWindow() *Window { g.SetStretchy(1, 1) w.SetSpaced(*spacingTest) w.Open(g) - go func() {for {select { - case <-b12.Clicked: - c21.SetChecked(!c21.Checked()) - }}}() +//TODO +// go func() {for {select { +// case <-b12.Clicked: +// c21.SetChecked(!c21.Checked()) +// }}}() return w } @@ -236,7 +237,7 @@ func areaTest() { a: a, timedisp: timedisp, widthbox: widthbox, - heightbox: heighbox, + heightbox: heightbox, resize: resize, modaltest: modaltest, repainttest: repainttest, @@ -252,7 +253,7 @@ func areaTest() { } type areatestwinhandler struct { - areahandler *areahandler + areahandler *areaHandler a *Area timedisp *Label widthbox *LineEdit @@ -264,18 +265,19 @@ type areatestwinhandler struct { func (a *areatestwinhandler) Event(e Event, d interface{}) { switch e { case Closing: - *(data.(*bool)) = true + *(d.(*bool)) = true + Stop <- struct{}{} case Clicked: switch d { case a.resize: width, err := strconv.Atoi(a.widthbox.Text()) - if err != nil { println(err); continue } + if err != nil { println(err); return } height, err := strconv.Atoi(a.heightbox.Text()) - if err != nil { println(err); continue } + if err != nil { println(err); return } a.a.SetSize(width, height) - case modaltest: + case a.modaltest: MsgBox("Modal Test", "") - case repainttest: + case a.repainttest: a.areahandler.mutate() a.a.RepaintAll() } @@ -378,7 +380,7 @@ func runMainTest() { handler.resetl = func() { handler.l.SetText("This is a label") } - handler.b3 := NewButton("List Info") + handler.b3 = NewButton("List Info") s3 := NewHorizontalStack(handler.l, handler.b3) s3.SetStretchy(0) // s3.SetStretchy(1) @@ -390,15 +392,15 @@ func runMainTest() { handler.invalidButton = NewButton("Run Invalid Test") sincdec := NewHorizontalStack(handler.incButton, handler.decButton, handler.indetButton, handler.invalidButton) handler.password = NewPasswordEdit() - s0 := NewVerticalStack(handler.s2, handler.c, handler.cb1, handler.cb2, handler.e, handler.s3, handler.pbar, handler.sincdec, Space(), handler.password) + s0 := NewVerticalStack(s2, handler.c, handler.cb1, handler.cb2, handler.e, s3, handler.pbar, sincdec, Space(), handler.password) s0.SetStretchy(8) handler.lb1 = NewMultiSelListbox("Select One", "Or More", "To Continue") handler.lb2 = NewListbox("Select", "Only", "One", "Please") handler.i = 0 handler.doAdjustments = func() { handler.cb1.Append("append") - handler.cb2.InsertBefore(fmt.Sprintf("before %d", i), 1) - handler.lb1.InsertBefore(fmt.Sprintf("%d", i), 2) + handler.cb2.InsertBefore(fmt.Sprintf("before %d", handler.i), 1) + handler.lb1.InsertBefore(fmt.Sprintf("%d", handler.i), 2) handler.lb2.Append("Please") handler.i++ } @@ -414,8 +416,8 @@ func runMainTest() { if *invalidBefore { invalidTest(handler.cb1, handler.lb1, s, NewGrid(1, Space())) } - w.SetSpaced(*spacingTest) - w.Open(s) + handler.w.SetSpaced(*spacingTest) + handler.w.Open(s) if *gridtest { gridWindow() } @@ -481,7 +483,7 @@ func (handler *testwinhandler) Event(e Event, d interface{}) { switch e { case Closing: println("window closed event received") - *(d.(*bool)) = true + Stop <- struct{}{} case Clicked: switch d { case handler.b: @@ -555,7 +557,7 @@ type dialoghandler struct { } // == TODO == -func (handler *dialoghandler) Event(e Event, d Data) { +func (handler *dialoghandler) Event(e Event, d interface{}) { if e == Clicked { switch d { case handler.bMsgBox: diff --git a/test/spacing.go b/test/spacing.go index 16f1b83..610115f 100644 --- a/test/spacing.go +++ b/test/spacing.go @@ -41,7 +41,7 @@ func spaceTest() { a2 := NewArea(w, h, ah) a3 := NewArea(w, h, ah) a4 := NewArea(w, h, ah) - win := NewWindow("Stack", 250, 250, nullwindowhandler{}) + win := NewWindow("Stack", 250, 250, nullwinhandler{}) win.SetSpaced(true) win.Open(f(a1, a2)) win = NewWindow("Grid", 250, 250, nullwinhandler{}) diff --git a/todo.md b/todo.md index c6164db..a7351f7 100644 --- a/todo.md +++ b/todo.md @@ -3,6 +3,12 @@ ALL: - gtk+: currently requires labels to be filling for this to work: grids don't do this by default, for instance - won't cause any issues, just an inconvenience that should be addressed - make sure tab stop behavior for Areas makes sense, or provide a handler function +- the following seems weird and will not allow clean removal of the last window; think of something better? +``` + case ui.Closing: + *(d.(*bool)) = true + ui.Stop <- struct{}{} +``` MAC OS X: - NSComboBox scans the entered text to see if it matches one of the items and returns the index of that item if it does; find out how to suppress this so that it returns -1 unless the item was chosen from the list (like the other platforms) @@ -13,9 +19,6 @@ MAC OS X: - probably use fittingSize instead of sizeToFit WINDOWS: -- there seems to be a caching issue: with the test program and `-dialog`, click one of the dialog buttons, then quickly tap one of the buttons in the main window. The dialog will pop up twice, and after both are closed the program aborts with a send on closed channel - - appears to be a bug in my dialog code - - appears to have *always* been a bug in dialog code... - windows: windows key handling is just wrong; figure out how to avoid (especially since Windows intercepts that key by default) - control sizing is a MAJOR pain - http://stackoverflow.com/questions/24130548/is-there-a-proper-way-to-get-the-preferred-size-of-windows-controls-there-are-s diff --git a/uitask_windows.go b/uitask_windows.go index 1eb44f4..ef1c213 100644 --- a/uitask_windows.go +++ b/uitask_windows.go @@ -4,7 +4,6 @@ package ui import ( "fmt" - "runtime" "syscall" "unsafe" ) @@ -46,13 +45,15 @@ var ( _postMessage = user32.NewProc("PostMessageW") ) +var msghwnd _HWND + func uiinit() error { err := doWindowsInit() if err != nil { return fmt.Errorf("error doing general Windows initialization: %v", err) } - hwnd, err := makeMessageHandler() + msghwnd, err = makeMessageHandler() if err != nil { return fmt.Errorf("error making invisible window for handling events: %v", err) } @@ -68,7 +69,7 @@ func ui() { select { case m := <-uitask: r1, _, err := _postMessage.Call( - uintptr(hwnd), + uintptr(msghwnd), msgRequested, uintptr(0), uintptr(unsafe.Pointer(&m))) @@ -77,10 +78,10 @@ func ui() { } case <-Stop: r1, _, err := _postMessage.Call( - uintptr(hwnd), - msgQuit, - uintptr(0), - uintptr(0)) + uintptr(msghwnd), + msgQuit, + uintptr(0), + uintptr(0)) if r1 == 0 { // failure panic("error sending quit message to message loop: " + err.Error()) } diff --git a/window.go b/window.go index b348ae8..57283f2 100644 --- a/window.go +++ b/window.go @@ -88,20 +88,15 @@ func (w *Window) SetSpaced(spaced bool) { // Open creates the Window with Create and then shows the Window with Show. As with Create, you cannot call Open more than once per window. func (w *Window) Open(control Control) { - done := make(chan struct{}) - defer close(done) - touitask(func() { - w.Create(control) - w.Show() - done <- struct{}{} - }) - <-done + w.create(control, true) } // Create creates the Window, setting its control to the given control. It does not show the window. This can only be called once per window, and finalizes all initialization of the control. func (w *Window) Create(control Control) { - done := make(chan struct{}) - defer close(done) + w.create(control, false) +} + +func (w *Window) create(control Control, show bool) { touitask(func() { if w.created { panic("window already open") @@ -128,9 +123,10 @@ func (w *Window) Create(control Control) { } w.sysData.setText(w.initTitle) w.created = true - done <- struct{}{} + if show { + w.Show() + } }) - <-done } // Show shows the window. diff --git a/zconstants_windows_386.go b/zconstants_windows_386.go index f19d39a..cdf0b4e 100644 --- a/zconstants_windows_386.go +++ b/zconstants_windows_386.go @@ -35,6 +35,7 @@ const _FALSE = 0 const _GWLP_USERDATA = -21 const _GWL_STYLE = -16 const _ICC_PROGRESS_CLASS = 32 +const _IDOK = 1 const _LBS_EXTENDEDSEL = 2048 const _LBS_NOINTEGRALHEIGHT = 256 const _LBS_NOTIFY = 1 diff --git a/zconstants_windows_amd64.go b/zconstants_windows_amd64.go index ec55813..256de01 100644 --- a/zconstants_windows_amd64.go +++ b/zconstants_windows_amd64.go @@ -35,6 +35,7 @@ const _FALSE = 0 const _GWLP_USERDATA = -21 const _GWL_STYLE = -16 const _ICC_PROGRESS_CLASS = 32 +const _IDOK = 1 const _LBS_EXTENDEDSEL = 2048 const _LBS_NOINTEGRALHEIGHT = 256 const _LBS_NOTIFY = 1