Added Combobox.

This commit is contained in:
Pietro Gagliardi 2014-02-14 12:16:27 -05:00
parent 9403224eb0
commit 39442cefeb
6 changed files with 235 additions and 76 deletions

63
combobox.go Normal file
View File

@ -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)
}

View File

@ -82,6 +82,7 @@ const (
_BST_CHECKED = 0x0001
_BST_INDETERMINATE = 0x0002
)
/*
var (
checkDlgButton = user32.NewProc("CheckDlgButton")
@ -119,84 +120,87 @@ func IsDlgButtonChecked(hDlg HWND, nIDButton int) (state uint32, err error) {
uintptr(nIDButton))
return uint32(r1), nil
}
*/
// Combobox styles.
const (
// from winuser.h
CBS_SIMPLE = 0x0001
CBS_DROPDOWN = 0x0002
CBS_DROPDOWNLIST = 0x0003
CBS_OWNERDRAWFIXED = 0x0010
CBS_OWNERDRAWVARIABLE = 0x0020
CBS_AUTOHSCROLL = 0x0040
CBS_OEMCONVERT = 0x0080
CBS_SORT = 0x0100
CBS_HASSTRINGS = 0x0200
CBS_NOINTEGRALHEIGHT = 0x0400
CBS_DISABLENOSCROLL = 0x0800
CBS_UPPERCASE = 0x2000
CBS_LOWERCASE = 0x4000
_CBS_SIMPLE = 0x0001
_CBS_DROPDOWN = 0x0002
_CBS_DROPDOWNLIST = 0x0003
_CBS_OWNERDRAWFIXED = 0x0010
_CBS_OWNERDRAWVARIABLE = 0x0020
_CBS_AUTOHSCROLL = 0x0040
_CBS_OEMCONVERT = 0x0080
_CBS_SORT = 0x0100
_CBS_HASSTRINGS = 0x0200
_CBS_NOINTEGRALHEIGHT = 0x0400
_CBS_DISABLENOSCROLL = 0x0800
_CBS_UPPERCASE = 0x2000
_CBS_LOWERCASE = 0x4000
)
// Combobox messages.
// TODO filter out messages not provided in windows 2000
const (
// from winuser.h
CB_GETEDITSEL = 0x0140
CB_LIMITTEXT = 0x0141
CB_SETEDITSEL = 0x0142
CB_ADDSTRING = 0x0143
CB_DELETESTRING = 0x0144
CB_DIR = 0x0145
CB_GETCOUNT = 0x0146
CB_GETCURSEL = 0x0147
CB_GETLBTEXT = 0x0148
CB_GETLBTEXTLEN = 0x0149
CB_INSERTSTRING = 0x014A
CB_RESETCONTENT = 0x014B
CB_FINDSTRING = 0x014C
CB_SELECTSTRING = 0x014D
CB_SETCURSEL = 0x014E
CB_SHOWDROPDOWN = 0x014F
CB_GETITEMDATA = 0x0150
CB_SETITEMDATA = 0x0151
CB_GETDROPPEDCONTROLRECT = 0x0152
CB_SETITEMHEIGHT = 0x0153
CB_GETITEMHEIGHT = 0x0154
CB_SETEXTENDEDUI = 0x0155
CB_GETEXTENDEDUI = 0x0156
CB_GETDROPPEDSTATE = 0x0157
CB_FINDSTRINGEXACT = 0x0158
CB_SETLOCALE = 0x0159
CB_GETLOCALE = 0x015A
CB_GETTOPINDEX = 0x015B
CB_SETTOPINDEX = 0x015C
CB_GETHORIZONTALEXTENT = 0x015D
CB_SETHORIZONTALEXTENT = 0x015E
CB_GETDROPPEDWIDTH = 0x015F
CB_SETDROPPEDWIDTH = 0x0160
CB_INITSTORAGE = 0x0161
CB_MULTIPLEADDSTRING = 0x0163
CB_GETCOMBOBOXINFO = 0x0164
_CB_GETEDITSEL = 0x0140
_CB_LIMITTEXT = 0x0141
_CB_SETEDITSEL = 0x0142
_CB_ADDSTRING = 0x0143
_CB_DELETESTRING = 0x0144
_CB_DIR = 0x0145
_CB_GETCOUNT = 0x0146
_CB_GETCURSEL = 0x0147
_CB_GETLBTEXT = 0x0148
_CB_GETLBTEXTLEN = 0x0149
_CB_INSERTSTRING = 0x014A
_CB_RESETCONTENT = 0x014B
_CB_FINDSTRING = 0x014C
_CB_SELECTSTRING = 0x014D
_CB_SETCURSEL = 0x014E
_CB_SHOWDROPDOWN = 0x014F
_CB_GETITEMDATA = 0x0150
_CB_SETITEMDATA = 0x0151
_CB_GETDROPPEDCONTROLRECT = 0x0152
_CB_SETITEMHEIGHT = 0x0153
_CB_GETITEMHEIGHT = 0x0154
_CB_SETEXTENDEDUI = 0x0155
_CB_GETEXTENDEDUI = 0x0156
_CB_GETDROPPEDSTATE = 0x0157
_CB_FINDSTRINGEXACT = 0x0158
_CB_SETLOCALE = 0x0159
_CB_GETLOCALE = 0x015A
_CB_GETTOPINDEX = 0x015B
_CB_SETTOPINDEX = 0x015C
_CB_GETHORIZONTALEXTENT = 0x015D
_CB_SETHORIZONTALEXTENT = 0x015E
_CB_GETDROPPEDWIDTH = 0x015F
_CB_SETDROPPEDWIDTH = 0x0160
_CB_INITSTORAGE = 0x0161
_CB_MULTIPLEADDSTRING = 0x0163
_CB_GETCOMBOBOXINFO = 0x0164
)
// Combobox WM_COMMAND notificaitons.
// TODO filter out notifications not provided in windows 2000
const (
// from winuser.h
CBN_ERRSPACE = (-1) // TODO this will blow up the Go compiler if it's used
CBN_SELCHANGE = 1
CBN_DBLCLK = 2
CBN_SETFOCUS = 3
CBN_KILLFOCUS = 4
CBN_EDITCHANGE = 5
CBN_EDITUPDATE = 6
CBN_DROPDOWN = 7
CBN_CLOSEUP = 8
CBN_SELENDOK = 9
CBN_SELENDCANCEL = 10
// TODO get _CB_ERR out
_CBN_ERRSPACE = (-1) // TODO this will blow up the Go compiler if it's used
_CBN_SELCHANGE = 1
_CBN_DBLCLK = 2
_CBN_SETFOCUS = 3
_CBN_KILLFOCUS = 4
_CBN_EDITCHANGE = 5
_CBN_EDITUPDATE = 6
_CBN_DROPDOWN = 7
_CBN_CLOSEUP = 8
_CBN_SELENDOK = 9
_CBN_SELENDCANCEL = 10
)
/*
// Edit control styles.
const (
// from winuser.h

14
main.go
View File

@ -10,7 +10,9 @@ func main() {
w.Closing = make(chan struct{})
b := NewButton("Click 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)
if err != nil {
panic(err)
@ -22,7 +24,15 @@ mainloop:
case <-w.Closing:
break mainloop
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 {
panic(err)
}

View File

@ -10,6 +10,7 @@ type cSysData struct {
ctype int
event chan struct{}
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 {
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) {
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 (
c_window = iota
c_button
c_checkbox
c_combobox
nctypes
)

View File

@ -19,29 +19,42 @@ type sysData struct {
}
type classData struct {
name string
style uint32
xstyle uint32
mkid bool
name string
style uint32
xstyle uint32
mkid bool
editStyle uint32
appendMsg uintptr
insertAfterMsg uintptr
deleteMsg uintptr
}
const controlstyle = _WS_CHILD | _WS_VISIBLE | _WS_TABSTOP
const controlxstyle = 0
var classTypes = [nctypes]*classData{
c_window: &classData{
style: _WS_OVERLAPPEDWINDOW,
xstyle: 0,
c_window: &classData{
style: _WS_OVERLAPPEDWINDOW,
xstyle: 0,
},
c_button: &classData{
name: "BUTTON",
style: _BS_PUSHBUTTON | controlstyle,
xstyle: 0 | controlxstyle,
name: "BUTTON",
style: _BS_PUSHBUTTON | controlstyle,
xstyle: 0 | controlxstyle,
},
c_checkbox: &classData{
name: "BUTTON",
style: _BS_AUTOCHECKBOX | controlstyle,
xstyle: 0 | controlxstyle,
name: "BUTTON",
style: _BS_AUTOCHECKBOX | controlstyle,
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
}
style := uintptr(ct.style)
if s.editable {
style = uintptr(ct.editStyle)
}
uitask <- &uimsg{
call: _createWindowEx,
p: []uintptr{
uintptr(ct.xstyle),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(classname))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(initText))),
uintptr(ct.style),
style,
uintptr(_CW_USEDEFAULT), // TODO
uintptr(_CW_USEDEFAULT),
uintptr(initWidth),
@ -219,3 +236,58 @@ func (s *sysData) isChecked() (bool, error) {
r := <-ret
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
}

View File

@ -3,6 +3,7 @@ so I don't forget:
- Control.Show()/Control.Hide()
- Control.SetText()
- Groupbox
- determine if a selection in a non-editable combobox has been made
super ultra important things:
- the windows build appears to be unstable: