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.
This commit is contained in:
parent
02d6a03ba3
commit
affc65a5a4
|
@ -14,7 +14,6 @@ func NewButton(text string) (b *Button) {
|
||||||
return &Button{
|
return &Button{
|
||||||
sysData: mksysdata(c_button),
|
sysData: mksysdata(c_button),
|
||||||
initText: text,
|
initText: text,
|
||||||
Clicked: newEvent(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,7 @@ const (
|
||||||
// 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
|
||||||
// notice the support it uses
|
// notice the support it uses
|
||||||
var dialogWindow = &Window{
|
var dialogWindow = new(Window)
|
||||||
sysData: &sysData{
|
|
||||||
winhandler: dh,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
||||||
|
|
|
@ -11,11 +11,11 @@ var (
|
||||||
_messageBox = user32.NewProc("MessageBoxW")
|
_messageBox = user32.NewProc("MessageBoxW")
|
||||||
)
|
)
|
||||||
|
|
||||||
var dialogResponse = map[uintptr]Response{
|
var dialogResponses = map[uintptr]Response{
|
||||||
_IDOK: OK,
|
_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."
|
// 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 != "" {
|
||||||
|
|
17
init.go
17
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.
|
// If you must, and if the toolkit also has environment variable equivalents to these flags (for instance, GTK+), use those instead.
|
||||||
func Go() error {
|
func Go() error {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
if err := uiinit(main); err != nil {
|
if err := uiinit(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
Ready <- struct{}{}
|
Ready <- struct{}{}
|
||||||
|
@ -37,9 +37,18 @@ var Ready = make(chan struct{})
|
||||||
var Stop = 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.
|
// 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()) {
|
func touitask(f func()) {
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer close(done)
|
||||||
go func() { // to avoid locking uitask itself
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -391,6 +391,7 @@ func (s *sysData) setWindowSize(width int, height int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("error actually resizing window: %v", err))
|
panic(fmt.Errorf("error actually resizing window: %v", err))
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) delete(index int) {
|
func (s *sysData) delete(index int) {
|
||||||
|
|
|
@ -74,7 +74,7 @@ func (a *keyboardArea) Key(e KeyEvent) (repaint bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type kbhandler struct{}
|
type kbhandler struct{}
|
||||||
func (kbhandler) Handle(e Event, d interface{}) {
|
func (kbhandler) Event(e Event, d interface{}) {
|
||||||
if e == Closing {
|
if e == Closing {
|
||||||
*(d.(*bool)) = true
|
*(d.(*bool)) = true
|
||||||
}
|
}
|
||||||
|
|
40
test/main.go
40
test/main.go
|
@ -47,10 +47,11 @@ func gridWindow() *Window {
|
||||||
g.SetStretchy(1, 1)
|
g.SetStretchy(1, 1)
|
||||||
w.SetSpaced(*spacingTest)
|
w.SetSpaced(*spacingTest)
|
||||||
w.Open(g)
|
w.Open(g)
|
||||||
go func() {for {select {
|
//TODO
|
||||||
case <-b12.Clicked:
|
// go func() {for {select {
|
||||||
c21.SetChecked(!c21.Checked())
|
// case <-b12.Clicked:
|
||||||
}}}()
|
// c21.SetChecked(!c21.Checked())
|
||||||
|
// }}}()
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +237,7 @@ func areaTest() {
|
||||||
a: a,
|
a: a,
|
||||||
timedisp: timedisp,
|
timedisp: timedisp,
|
||||||
widthbox: widthbox,
|
widthbox: widthbox,
|
||||||
heightbox: heighbox,
|
heightbox: heightbox,
|
||||||
resize: resize,
|
resize: resize,
|
||||||
modaltest: modaltest,
|
modaltest: modaltest,
|
||||||
repainttest: repainttest,
|
repainttest: repainttest,
|
||||||
|
@ -252,7 +253,7 @@ func areaTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type areatestwinhandler struct {
|
type areatestwinhandler struct {
|
||||||
areahandler *areahandler
|
areahandler *areaHandler
|
||||||
a *Area
|
a *Area
|
||||||
timedisp *Label
|
timedisp *Label
|
||||||
widthbox *LineEdit
|
widthbox *LineEdit
|
||||||
|
@ -264,18 +265,19 @@ type areatestwinhandler struct {
|
||||||
func (a *areatestwinhandler) Event(e Event, d interface{}) {
|
func (a *areatestwinhandler) Event(e Event, d interface{}) {
|
||||||
switch e {
|
switch e {
|
||||||
case Closing:
|
case Closing:
|
||||||
*(data.(*bool)) = true
|
*(d.(*bool)) = true
|
||||||
|
Stop <- struct{}{}
|
||||||
case Clicked:
|
case Clicked:
|
||||||
switch d {
|
switch d {
|
||||||
case a.resize:
|
case a.resize:
|
||||||
width, err := strconv.Atoi(a.widthbox.Text())
|
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())
|
height, err := strconv.Atoi(a.heightbox.Text())
|
||||||
if err != nil { println(err); continue }
|
if err != nil { println(err); return }
|
||||||
a.a.SetSize(width, height)
|
a.a.SetSize(width, height)
|
||||||
case modaltest:
|
case a.modaltest:
|
||||||
MsgBox("Modal Test", "")
|
MsgBox("Modal Test", "")
|
||||||
case repainttest:
|
case a.repainttest:
|
||||||
a.areahandler.mutate()
|
a.areahandler.mutate()
|
||||||
a.a.RepaintAll()
|
a.a.RepaintAll()
|
||||||
}
|
}
|
||||||
|
@ -378,7 +380,7 @@ func runMainTest() {
|
||||||
handler.resetl = func() {
|
handler.resetl = func() {
|
||||||
handler.l.SetText("This is a label")
|
handler.l.SetText("This is a label")
|
||||||
}
|
}
|
||||||
handler.b3 := NewButton("List Info")
|
handler.b3 = NewButton("List Info")
|
||||||
s3 := NewHorizontalStack(handler.l, handler.b3)
|
s3 := NewHorizontalStack(handler.l, handler.b3)
|
||||||
s3.SetStretchy(0)
|
s3.SetStretchy(0)
|
||||||
// s3.SetStretchy(1)
|
// s3.SetStretchy(1)
|
||||||
|
@ -390,15 +392,15 @@ func runMainTest() {
|
||||||
handler.invalidButton = NewButton("Run Invalid Test")
|
handler.invalidButton = NewButton("Run Invalid Test")
|
||||||
sincdec := NewHorizontalStack(handler.incButton, handler.decButton, handler.indetButton, handler.invalidButton)
|
sincdec := NewHorizontalStack(handler.incButton, handler.decButton, handler.indetButton, handler.invalidButton)
|
||||||
handler.password = NewPasswordEdit()
|
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)
|
s0.SetStretchy(8)
|
||||||
handler.lb1 = NewMultiSelListbox("Select One", "Or More", "To Continue")
|
handler.lb1 = NewMultiSelListbox("Select One", "Or More", "To Continue")
|
||||||
handler.lb2 = NewListbox("Select", "Only", "One", "Please")
|
handler.lb2 = NewListbox("Select", "Only", "One", "Please")
|
||||||
handler.i = 0
|
handler.i = 0
|
||||||
handler.doAdjustments = func() {
|
handler.doAdjustments = func() {
|
||||||
handler.cb1.Append("append")
|
handler.cb1.Append("append")
|
||||||
handler.cb2.InsertBefore(fmt.Sprintf("before %d", i), 1)
|
handler.cb2.InsertBefore(fmt.Sprintf("before %d", handler.i), 1)
|
||||||
handler.lb1.InsertBefore(fmt.Sprintf("%d", i), 2)
|
handler.lb1.InsertBefore(fmt.Sprintf("%d", handler.i), 2)
|
||||||
handler.lb2.Append("Please")
|
handler.lb2.Append("Please")
|
||||||
handler.i++
|
handler.i++
|
||||||
}
|
}
|
||||||
|
@ -414,8 +416,8 @@ func runMainTest() {
|
||||||
if *invalidBefore {
|
if *invalidBefore {
|
||||||
invalidTest(handler.cb1, handler.lb1, s, NewGrid(1, Space()))
|
invalidTest(handler.cb1, handler.lb1, s, NewGrid(1, Space()))
|
||||||
}
|
}
|
||||||
w.SetSpaced(*spacingTest)
|
handler.w.SetSpaced(*spacingTest)
|
||||||
w.Open(s)
|
handler.w.Open(s)
|
||||||
if *gridtest {
|
if *gridtest {
|
||||||
gridWindow()
|
gridWindow()
|
||||||
}
|
}
|
||||||
|
@ -481,7 +483,7 @@ func (handler *testwinhandler) Event(e Event, d interface{}) {
|
||||||
switch e {
|
switch e {
|
||||||
case Closing:
|
case Closing:
|
||||||
println("window closed event received")
|
println("window closed event received")
|
||||||
*(d.(*bool)) = true
|
Stop <- struct{}{}
|
||||||
case Clicked:
|
case Clicked:
|
||||||
switch d {
|
switch d {
|
||||||
case handler.b:
|
case handler.b:
|
||||||
|
@ -555,7 +557,7 @@ type dialoghandler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// == TODO ==
|
// == TODO ==
|
||||||
func (handler *dialoghandler) Event(e Event, d Data) {
|
func (handler *dialoghandler) Event(e Event, d interface{}) {
|
||||||
if e == Clicked {
|
if e == Clicked {
|
||||||
switch d {
|
switch d {
|
||||||
case handler.bMsgBox:
|
case handler.bMsgBox:
|
||||||
|
|
|
@ -41,7 +41,7 @@ func spaceTest() {
|
||||||
a2 := NewArea(w, h, ah)
|
a2 := NewArea(w, h, ah)
|
||||||
a3 := NewArea(w, h, ah)
|
a3 := NewArea(w, h, ah)
|
||||||
a4 := 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.SetSpaced(true)
|
||||||
win.Open(f(a1, a2))
|
win.Open(f(a1, a2))
|
||||||
win = NewWindow("Grid", 250, 250, nullwinhandler{})
|
win = NewWindow("Grid", 250, 250, nullwinhandler{})
|
||||||
|
|
9
todo.md
9
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
|
- 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
|
- 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
|
- 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:
|
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)
|
- 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
|
- probably use fittingSize instead of sizeToFit
|
||||||
|
|
||||||
WINDOWS:
|
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)
|
- 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
|
- 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
|
- http://stackoverflow.com/questions/24130548/is-there-a-proper-way-to-get-the-preferred-size-of-windows-controls-there-are-s
|
||||||
|
|
|
@ -4,7 +4,6 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -46,13 +45,15 @@ var (
|
||||||
_postMessage = user32.NewProc("PostMessageW")
|
_postMessage = user32.NewProc("PostMessageW")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var msghwnd _HWND
|
||||||
|
|
||||||
func uiinit() error {
|
func uiinit() error {
|
||||||
err := doWindowsInit()
|
err := doWindowsInit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error doing general Windows initialization: %v", err)
|
return fmt.Errorf("error doing general Windows initialization: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hwnd, err := makeMessageHandler()
|
msghwnd, err = makeMessageHandler()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error making invisible window for handling events: %v", err)
|
return fmt.Errorf("error making invisible window for handling events: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +69,7 @@ func ui() {
|
||||||
select {
|
select {
|
||||||
case m := <-uitask:
|
case m := <-uitask:
|
||||||
r1, _, err := _postMessage.Call(
|
r1, _, err := _postMessage.Call(
|
||||||
uintptr(hwnd),
|
uintptr(msghwnd),
|
||||||
msgRequested,
|
msgRequested,
|
||||||
uintptr(0),
|
uintptr(0),
|
||||||
uintptr(unsafe.Pointer(&m)))
|
uintptr(unsafe.Pointer(&m)))
|
||||||
|
@ -77,7 +78,7 @@ func ui() {
|
||||||
}
|
}
|
||||||
case <-Stop:
|
case <-Stop:
|
||||||
r1, _, err := _postMessage.Call(
|
r1, _, err := _postMessage.Call(
|
||||||
uintptr(hwnd),
|
uintptr(msghwnd),
|
||||||
msgQuit,
|
msgQuit,
|
||||||
uintptr(0),
|
uintptr(0),
|
||||||
uintptr(0))
|
uintptr(0))
|
||||||
|
|
20
window.go
20
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.
|
// 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) {
|
func (w *Window) Open(control Control) {
|
||||||
done := make(chan struct{})
|
w.create(control, true)
|
||||||
defer close(done)
|
|
||||||
touitask(func() {
|
|
||||||
w.Create(control)
|
|
||||||
w.Show()
|
|
||||||
done <- struct{}{}
|
|
||||||
})
|
|
||||||
<-done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// 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) {
|
func (w *Window) Create(control Control) {
|
||||||
done := make(chan struct{})
|
w.create(control, false)
|
||||||
defer close(done)
|
}
|
||||||
|
|
||||||
|
func (w *Window) create(control Control, show bool) {
|
||||||
touitask(func() {
|
touitask(func() {
|
||||||
if w.created {
|
if w.created {
|
||||||
panic("window already open")
|
panic("window already open")
|
||||||
|
@ -128,9 +123,10 @@ func (w *Window) Create(control Control) {
|
||||||
}
|
}
|
||||||
w.sysData.setText(w.initTitle)
|
w.sysData.setText(w.initTitle)
|
||||||
w.created = true
|
w.created = true
|
||||||
done <- struct{}{}
|
if show {
|
||||||
|
w.Show()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
<-done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show shows the window.
|
// Show shows the window.
|
||||||
|
|
|
@ -35,6 +35,7 @@ const _FALSE = 0
|
||||||
const _GWLP_USERDATA = -21
|
const _GWLP_USERDATA = -21
|
||||||
const _GWL_STYLE = -16
|
const _GWL_STYLE = -16
|
||||||
const _ICC_PROGRESS_CLASS = 32
|
const _ICC_PROGRESS_CLASS = 32
|
||||||
|
const _IDOK = 1
|
||||||
const _LBS_EXTENDEDSEL = 2048
|
const _LBS_EXTENDEDSEL = 2048
|
||||||
const _LBS_NOINTEGRALHEIGHT = 256
|
const _LBS_NOINTEGRALHEIGHT = 256
|
||||||
const _LBS_NOTIFY = 1
|
const _LBS_NOTIFY = 1
|
||||||
|
|
|
@ -35,6 +35,7 @@ const _FALSE = 0
|
||||||
const _GWLP_USERDATA = -21
|
const _GWLP_USERDATA = -21
|
||||||
const _GWL_STYLE = -16
|
const _GWL_STYLE = -16
|
||||||
const _ICC_PROGRESS_CLASS = 32
|
const _ICC_PROGRESS_CLASS = 32
|
||||||
|
const _IDOK = 1
|
||||||
const _LBS_EXTENDEDSEL = 2048
|
const _LBS_EXTENDEDSEL = 2048
|
||||||
const _LBS_NOINTEGRALHEIGHT = 256
|
const _LBS_NOINTEGRALHEIGHT = 256
|
||||||
const _LBS_NOTIFY = 1
|
const _LBS_NOTIFY = 1
|
||||||
|
|
Loading…
Reference in New Issue