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_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
14
main.go
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue