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_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
View File

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

View File

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

View File

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

View File

@ -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: