Added Combobox.
This commit is contained in:
parent
9403224eb0
commit
39442cefeb
|
@ -0,0 +1,63 @@
|
||||||
|
// 14 february 2014
|
||||||
|
//package ui
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Combobox is a drop-down list of items, of which only one can be selected at any given time. You may optionally make the combobox editable to allow custom items.
|
||||||
|
type Combobox struct {
|
||||||
|
// TODO Select event
|
||||||
|
|
||||||
|
lock sync.Mutex
|
||||||
|
created bool
|
||||||
|
sysData *sysData
|
||||||
|
initItems []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCombobox makes a new combobox with the given items. If editable is true, the combobox is editable.
|
||||||
|
func NewCombobox(editable bool, items ...string) (c *Combobox) {
|
||||||
|
c = &Combobox{
|
||||||
|
sysData: mksysdata(c_combobox),
|
||||||
|
initItems: items,
|
||||||
|
}
|
||||||
|
c.sysData.editable = editable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Append, InsertBefore, Delete
|
||||||
|
|
||||||
|
// Selection returns the current selection.
|
||||||
|
func (c *Combobox) Selection() (string, error) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
return c.sysData.text()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO SelectedIndex
|
||||||
|
|
||||||
|
func (c *Combobox) make(window *sysData) (err error) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
err = c.sysData.make("", 300, 300, window)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, s := range c.initItems {
|
||||||
|
err = c.sysData.append(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Combobox) setRect(x int, y int, width int, height int) error {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
return c.sysData.setRect(x, y, width, height)
|
||||||
|
}
|
|
@ -82,6 +82,7 @@ const (
|
||||||
_BST_CHECKED = 0x0001
|
_BST_CHECKED = 0x0001
|
||||||
_BST_INDETERMINATE = 0x0002
|
_BST_INDETERMINATE = 0x0002
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
var (
|
var (
|
||||||
checkDlgButton = user32.NewProc("CheckDlgButton")
|
checkDlgButton = user32.NewProc("CheckDlgButton")
|
||||||
|
@ -119,84 +120,87 @@ func IsDlgButtonChecked(hDlg HWND, nIDButton int) (state uint32, err error) {
|
||||||
uintptr(nIDButton))
|
uintptr(nIDButton))
|
||||||
return uint32(r1), nil
|
return uint32(r1), nil
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Combobox styles.
|
// Combobox styles.
|
||||||
const (
|
const (
|
||||||
// from winuser.h
|
// from winuser.h
|
||||||
CBS_SIMPLE = 0x0001
|
_CBS_SIMPLE = 0x0001
|
||||||
CBS_DROPDOWN = 0x0002
|
_CBS_DROPDOWN = 0x0002
|
||||||
CBS_DROPDOWNLIST = 0x0003
|
_CBS_DROPDOWNLIST = 0x0003
|
||||||
CBS_OWNERDRAWFIXED = 0x0010
|
_CBS_OWNERDRAWFIXED = 0x0010
|
||||||
CBS_OWNERDRAWVARIABLE = 0x0020
|
_CBS_OWNERDRAWVARIABLE = 0x0020
|
||||||
CBS_AUTOHSCROLL = 0x0040
|
_CBS_AUTOHSCROLL = 0x0040
|
||||||
CBS_OEMCONVERT = 0x0080
|
_CBS_OEMCONVERT = 0x0080
|
||||||
CBS_SORT = 0x0100
|
_CBS_SORT = 0x0100
|
||||||
CBS_HASSTRINGS = 0x0200
|
_CBS_HASSTRINGS = 0x0200
|
||||||
CBS_NOINTEGRALHEIGHT = 0x0400
|
_CBS_NOINTEGRALHEIGHT = 0x0400
|
||||||
CBS_DISABLENOSCROLL = 0x0800
|
_CBS_DISABLENOSCROLL = 0x0800
|
||||||
CBS_UPPERCASE = 0x2000
|
_CBS_UPPERCASE = 0x2000
|
||||||
CBS_LOWERCASE = 0x4000
|
_CBS_LOWERCASE = 0x4000
|
||||||
)
|
)
|
||||||
|
|
||||||
// Combobox messages.
|
// Combobox messages.
|
||||||
// TODO filter out messages not provided in windows 2000
|
// TODO filter out messages not provided in windows 2000
|
||||||
const (
|
const (
|
||||||
// from winuser.h
|
// from winuser.h
|
||||||
CB_GETEDITSEL = 0x0140
|
_CB_GETEDITSEL = 0x0140
|
||||||
CB_LIMITTEXT = 0x0141
|
_CB_LIMITTEXT = 0x0141
|
||||||
CB_SETEDITSEL = 0x0142
|
_CB_SETEDITSEL = 0x0142
|
||||||
CB_ADDSTRING = 0x0143
|
_CB_ADDSTRING = 0x0143
|
||||||
CB_DELETESTRING = 0x0144
|
_CB_DELETESTRING = 0x0144
|
||||||
CB_DIR = 0x0145
|
_CB_DIR = 0x0145
|
||||||
CB_GETCOUNT = 0x0146
|
_CB_GETCOUNT = 0x0146
|
||||||
CB_GETCURSEL = 0x0147
|
_CB_GETCURSEL = 0x0147
|
||||||
CB_GETLBTEXT = 0x0148
|
_CB_GETLBTEXT = 0x0148
|
||||||
CB_GETLBTEXTLEN = 0x0149
|
_CB_GETLBTEXTLEN = 0x0149
|
||||||
CB_INSERTSTRING = 0x014A
|
_CB_INSERTSTRING = 0x014A
|
||||||
CB_RESETCONTENT = 0x014B
|
_CB_RESETCONTENT = 0x014B
|
||||||
CB_FINDSTRING = 0x014C
|
_CB_FINDSTRING = 0x014C
|
||||||
CB_SELECTSTRING = 0x014D
|
_CB_SELECTSTRING = 0x014D
|
||||||
CB_SETCURSEL = 0x014E
|
_CB_SETCURSEL = 0x014E
|
||||||
CB_SHOWDROPDOWN = 0x014F
|
_CB_SHOWDROPDOWN = 0x014F
|
||||||
CB_GETITEMDATA = 0x0150
|
_CB_GETITEMDATA = 0x0150
|
||||||
CB_SETITEMDATA = 0x0151
|
_CB_SETITEMDATA = 0x0151
|
||||||
CB_GETDROPPEDCONTROLRECT = 0x0152
|
_CB_GETDROPPEDCONTROLRECT = 0x0152
|
||||||
CB_SETITEMHEIGHT = 0x0153
|
_CB_SETITEMHEIGHT = 0x0153
|
||||||
CB_GETITEMHEIGHT = 0x0154
|
_CB_GETITEMHEIGHT = 0x0154
|
||||||
CB_SETEXTENDEDUI = 0x0155
|
_CB_SETEXTENDEDUI = 0x0155
|
||||||
CB_GETEXTENDEDUI = 0x0156
|
_CB_GETEXTENDEDUI = 0x0156
|
||||||
CB_GETDROPPEDSTATE = 0x0157
|
_CB_GETDROPPEDSTATE = 0x0157
|
||||||
CB_FINDSTRINGEXACT = 0x0158
|
_CB_FINDSTRINGEXACT = 0x0158
|
||||||
CB_SETLOCALE = 0x0159
|
_CB_SETLOCALE = 0x0159
|
||||||
CB_GETLOCALE = 0x015A
|
_CB_GETLOCALE = 0x015A
|
||||||
CB_GETTOPINDEX = 0x015B
|
_CB_GETTOPINDEX = 0x015B
|
||||||
CB_SETTOPINDEX = 0x015C
|
_CB_SETTOPINDEX = 0x015C
|
||||||
CB_GETHORIZONTALEXTENT = 0x015D
|
_CB_GETHORIZONTALEXTENT = 0x015D
|
||||||
CB_SETHORIZONTALEXTENT = 0x015E
|
_CB_SETHORIZONTALEXTENT = 0x015E
|
||||||
CB_GETDROPPEDWIDTH = 0x015F
|
_CB_GETDROPPEDWIDTH = 0x015F
|
||||||
CB_SETDROPPEDWIDTH = 0x0160
|
_CB_SETDROPPEDWIDTH = 0x0160
|
||||||
CB_INITSTORAGE = 0x0161
|
_CB_INITSTORAGE = 0x0161
|
||||||
CB_MULTIPLEADDSTRING = 0x0163
|
_CB_MULTIPLEADDSTRING = 0x0163
|
||||||
CB_GETCOMBOBOXINFO = 0x0164
|
_CB_GETCOMBOBOXINFO = 0x0164
|
||||||
)
|
)
|
||||||
|
|
||||||
// Combobox WM_COMMAND notificaitons.
|
// Combobox WM_COMMAND notificaitons.
|
||||||
// TODO filter out notifications not provided in windows 2000
|
// TODO filter out notifications not provided in windows 2000
|
||||||
const (
|
const (
|
||||||
// from winuser.h
|
// from winuser.h
|
||||||
CBN_ERRSPACE = (-1) // TODO this will blow up the Go compiler if it's used
|
// TODO get _CB_ERR out
|
||||||
CBN_SELCHANGE = 1
|
_CBN_ERRSPACE = (-1) // TODO this will blow up the Go compiler if it's used
|
||||||
CBN_DBLCLK = 2
|
_CBN_SELCHANGE = 1
|
||||||
CBN_SETFOCUS = 3
|
_CBN_DBLCLK = 2
|
||||||
CBN_KILLFOCUS = 4
|
_CBN_SETFOCUS = 3
|
||||||
CBN_EDITCHANGE = 5
|
_CBN_KILLFOCUS = 4
|
||||||
CBN_EDITUPDATE = 6
|
_CBN_EDITCHANGE = 5
|
||||||
CBN_DROPDOWN = 7
|
_CBN_EDITUPDATE = 6
|
||||||
CBN_CLOSEUP = 8
|
_CBN_DROPDOWN = 7
|
||||||
CBN_SELENDOK = 9
|
_CBN_CLOSEUP = 8
|
||||||
CBN_SELENDCANCEL = 10
|
_CBN_SELENDOK = 9
|
||||||
|
_CBN_SELENDCANCEL = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
// Edit control styles.
|
// Edit control styles.
|
||||||
const (
|
const (
|
||||||
// from winuser.h
|
// from winuser.h
|
||||||
|
|
14
main.go
14
main.go
|
@ -10,7 +10,9 @@ func main() {
|
||||||
w.Closing = make(chan struct{})
|
w.Closing = make(chan struct{})
|
||||||
b := NewButton("Click Me")
|
b := NewButton("Click Me")
|
||||||
c := NewCheckbox("Check Me")
|
c := NewCheckbox("Check Me")
|
||||||
s := NewStack(Vertical, b, c)
|
cb1 := NewCombobox(true, "You can edit me!", "Yes you can!", "Yes you will!")
|
||||||
|
cb2 := NewCombobox(false, "You can't edit me!", "No you can't!", "No you won't!")
|
||||||
|
s := NewStack(Vertical, b, c, cb1, cb2)
|
||||||
err := w.Open(s)
|
err := w.Open(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -22,7 +24,15 @@ mainloop:
|
||||||
case <-w.Closing:
|
case <-w.Closing:
|
||||||
break mainloop
|
break mainloop
|
||||||
case <-b.Clicked:
|
case <-b.Clicked:
|
||||||
err := w.SetTitle(fmt.Sprintf("Check State: %v", c.Checked()))
|
cs1, err := cb1.Selection()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
cs2, err := cb2.Selection()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = w.SetTitle(fmt.Sprintf("%v | %s | %s", c.Checked(), cs1, cs2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ type cSysData struct {
|
||||||
ctype int
|
ctype int
|
||||||
event chan struct{}
|
event chan struct{}
|
||||||
resize func(x int, y int, width int, height int) error
|
resize func(x int, y int, width int, height int) error
|
||||||
|
editable bool // for Combobox
|
||||||
}
|
}
|
||||||
func (c *cSysData) make(initText string, initWidth int, initHeight int, window *sysData) error {
|
func (c *cSysData) make(initText string, initWidth int, initHeight int, window *sysData) error {
|
||||||
panic(runtime.GOOS + " sysData does not define make()")
|
panic(runtime.GOOS + " sysData does not define make()")
|
||||||
|
@ -29,11 +30,19 @@ func (c *cSysData) setRect(x int, y int, width int, height int) error {
|
||||||
func (c *cSysData) isChecked() (bool, error) {
|
func (c *cSysData) isChecked() (bool, error) {
|
||||||
panic(runtime.GOOS + " sysData does not define isChecked()")
|
panic(runtime.GOOS + " sysData does not define isChecked()")
|
||||||
}
|
}
|
||||||
|
func (c *cSysData) text() (string, error) {
|
||||||
|
panic(runtime.GOOS + " sysData does not define text()")
|
||||||
|
}
|
||||||
|
func (c *cSysData) append(string) error {
|
||||||
|
panic(runtime.GOOS + " sysData does not define append()")
|
||||||
|
}
|
||||||
|
// TODO insertAfter
|
||||||
|
|
||||||
const (
|
const (
|
||||||
c_window = iota
|
c_window = iota
|
||||||
c_button
|
c_button
|
||||||
c_checkbox
|
c_checkbox
|
||||||
|
c_combobox
|
||||||
nctypes
|
nctypes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -19,29 +19,42 @@ type sysData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type classData struct {
|
type classData struct {
|
||||||
name string
|
name string
|
||||||
style uint32
|
style uint32
|
||||||
xstyle uint32
|
xstyle uint32
|
||||||
mkid bool
|
mkid bool
|
||||||
|
editStyle uint32
|
||||||
|
appendMsg uintptr
|
||||||
|
insertAfterMsg uintptr
|
||||||
|
deleteMsg uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
const controlstyle = _WS_CHILD | _WS_VISIBLE | _WS_TABSTOP
|
const controlstyle = _WS_CHILD | _WS_VISIBLE | _WS_TABSTOP
|
||||||
const controlxstyle = 0
|
const controlxstyle = 0
|
||||||
|
|
||||||
var classTypes = [nctypes]*classData{
|
var classTypes = [nctypes]*classData{
|
||||||
c_window: &classData{
|
c_window: &classData{
|
||||||
style: _WS_OVERLAPPEDWINDOW,
|
style: _WS_OVERLAPPEDWINDOW,
|
||||||
xstyle: 0,
|
xstyle: 0,
|
||||||
},
|
},
|
||||||
c_button: &classData{
|
c_button: &classData{
|
||||||
name: "BUTTON",
|
name: "BUTTON",
|
||||||
style: _BS_PUSHBUTTON | controlstyle,
|
style: _BS_PUSHBUTTON | controlstyle,
|
||||||
xstyle: 0 | controlxstyle,
|
xstyle: 0 | controlxstyle,
|
||||||
},
|
},
|
||||||
c_checkbox: &classData{
|
c_checkbox: &classData{
|
||||||
name: "BUTTON",
|
name: "BUTTON",
|
||||||
style: _BS_AUTOCHECKBOX | controlstyle,
|
style: _BS_AUTOCHECKBOX | controlstyle,
|
||||||
xstyle: 0 | controlxstyle,
|
xstyle: 0 | controlxstyle,
|
||||||
|
},
|
||||||
|
c_combobox: &classData{
|
||||||
|
name: "COMBOBOX",
|
||||||
|
style: _CBS_DROPDOWNLIST | controlstyle,
|
||||||
|
xstyle: 0 | controlxstyle,
|
||||||
|
editStyle: _CBS_DROPDOWN | _CBS_AUTOHSCROLL | controlstyle,
|
||||||
|
appendMsg: _CB_ADDSTRING,
|
||||||
|
insertAfterMsg: _CB_INSERTSTRING,
|
||||||
|
deleteMsg: _CB_DELETESTRING,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,13 +93,17 @@ func (s *sysData) make(initText string, initWidth int, initHeight int, window *s
|
||||||
}
|
}
|
||||||
classname = n
|
classname = n
|
||||||
}
|
}
|
||||||
|
style := uintptr(ct.style)
|
||||||
|
if s.editable {
|
||||||
|
style = uintptr(ct.editStyle)
|
||||||
|
}
|
||||||
uitask <- &uimsg{
|
uitask <- &uimsg{
|
||||||
call: _createWindowEx,
|
call: _createWindowEx,
|
||||||
p: []uintptr{
|
p: []uintptr{
|
||||||
uintptr(ct.xstyle),
|
uintptr(ct.xstyle),
|
||||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(classname))),
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(classname))),
|
||||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(initText))),
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(initText))),
|
||||||
uintptr(ct.style),
|
style,
|
||||||
uintptr(_CW_USEDEFAULT), // TODO
|
uintptr(_CW_USEDEFAULT), // TODO
|
||||||
uintptr(_CW_USEDEFAULT),
|
uintptr(_CW_USEDEFAULT),
|
||||||
uintptr(initWidth),
|
uintptr(initWidth),
|
||||||
|
@ -219,3 +236,58 @@ func (s *sysData) isChecked() (bool, error) {
|
||||||
r := <-ret
|
r := <-ret
|
||||||
return r.ret == _BST_CHECKED, nil
|
return r.ret == _BST_CHECKED, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO adorn error messages with which part failed
|
||||||
|
func (s *sysData) text() (str string, err error) {
|
||||||
|
var tc []uint16
|
||||||
|
|
||||||
|
ret := make(chan uiret)
|
||||||
|
defer close(ret)
|
||||||
|
// TODO figure out how to handle errors
|
||||||
|
uitask <- &uimsg{
|
||||||
|
call: _sendMessage,
|
||||||
|
p: []uintptr{
|
||||||
|
uintptr(s.hwnd),
|
||||||
|
uintptr(_WM_GETTEXTLENGTH),
|
||||||
|
uintptr(0),
|
||||||
|
uintptr(0),
|
||||||
|
},
|
||||||
|
ret: ret,
|
||||||
|
}
|
||||||
|
r := <-ret
|
||||||
|
length := r.ret + 1 // terminating null
|
||||||
|
tc = make([]uint16, length)
|
||||||
|
// TODO figure out how to handle errors
|
||||||
|
uitask <- &uimsg{
|
||||||
|
call: _sendMessage,
|
||||||
|
p: []uintptr{
|
||||||
|
uintptr(s.hwnd),
|
||||||
|
uintptr(_WM_GETTEXT),
|
||||||
|
uintptr(_WPARAM(length)),
|
||||||
|
uintptr(_LPARAM(unsafe.Pointer(&tc[0]))),
|
||||||
|
},
|
||||||
|
ret: ret,
|
||||||
|
}
|
||||||
|
<-ret
|
||||||
|
// TODO check character count
|
||||||
|
return syscall.UTF16ToString(tc), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO figure out how to handle errors
|
||||||
|
func (s *sysData) append(what string) (err error) {
|
||||||
|
ret := make(chan uiret)
|
||||||
|
defer close(ret)
|
||||||
|
uitask <- &uimsg{
|
||||||
|
call: _sendMessage,
|
||||||
|
p: []uintptr{
|
||||||
|
uintptr(s.hwnd),
|
||||||
|
uintptr(classTypes[s.ctype].appendMsg),
|
||||||
|
uintptr(_WPARAM(0)),
|
||||||
|
uintptr(_LPARAM(unsafe.Pointer(syscall.StringToUTF16Ptr(what)))),
|
||||||
|
},
|
||||||
|
ret: ret,
|
||||||
|
}
|
||||||
|
<-ret
|
||||||
|
// TODO error handling
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
1
todo.md
1
todo.md
|
@ -3,6 +3,7 @@ so I don't forget:
|
||||||
- Control.Show()/Control.Hide()
|
- Control.Show()/Control.Hide()
|
||||||
- Control.SetText()
|
- Control.SetText()
|
||||||
- Groupbox
|
- Groupbox
|
||||||
|
- determine if a selection in a non-editable combobox has been made
|
||||||
|
|
||||||
super ultra important things:
|
super ultra important things:
|
||||||
- the windows build appears to be unstable:
|
- the windows build appears to be unstable:
|
||||||
|
|
Loading…
Reference in New Issue