summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPietro Gagliardi <[email protected]>2014-02-14 12:16:27 -0500
committerPietro Gagliardi <[email protected]>2014-02-14 12:16:27 -0500
commit39442cefebe21b33e7796ffcf9687a8af34c5a20 (patch)
tree2bd82d16545940a404192996136d2dc4f25108b5
parent9403224eb0842c680f60541190ab32d571a545da (diff)
Added Combobox.
-rw-r--r--combobox.go63
-rw-r--r--controls_windows.go124
-rw-r--r--main.go14
-rw-r--r--sysdata.go9
-rw-r--r--sysdata_windows.go100
-rw-r--r--todo.md1
6 files changed, 235 insertions, 76 deletions
diff --git a/combobox.go b/combobox.go
new file mode 100644
index 0000000..08d4605
--- /dev/null
+++ b/combobox.go
@@ -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)
+}
diff --git a/controls_windows.go b/controls_windows.go
index 3a6f472..8a3de3e 100644
--- a/controls_windows.go
+++ b/controls_windows.go
@@ -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
diff --git a/main.go b/main.go
index beb2f0e..81e48fa 100644
--- a/main.go
+++ b/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)
}
diff --git a/sysdata.go b/sysdata.go
index eda749e..1f46ac9 100644
--- a/sysdata.go
+++ b/sysdata.go
@@ -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
)
diff --git a/sysdata_windows.go b/sysdata_windows.go
index b62d90a..07a387d 100644
--- a/sysdata_windows.go
+++ b/sysdata_windows.go
@@ -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
+}
diff --git a/todo.md b/todo.md
index 6cc362d..f50e4d7 100644
--- a/todo.md
+++ b/todo.md
@@ -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: