summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--futureplans.md1
-rw-r--r--listbox.go3
-rw-r--r--prefsize_windows.go121
-rw-r--r--sysdata_windows.go8
-rw-r--r--unmigrated/hscrolllistbox.go178
5 files changed, 183 insertions, 128 deletions
diff --git a/futureplans.md b/futureplans.md
index a75fe02..baab440 100644
--- a/futureplans.md
+++ b/futureplans.md
@@ -102,6 +102,7 @@ big dumb things:
- and raymond suggests GWL_USERDATA here: http://blogs.msdn.com/b/oldnewthing/archive/2005/03/03/384285.aspx
- listboxes should have horizontal scrollbars on all platforms; this is way too hard on OS X and doesn't work; my code is in experiments/
- also moved the Windows code there for the sake of efficiency
+ - GTK+ works just fine though
specifics:
diff --git a/listbox.go b/listbox.go
index 4e26d3d..0601c23 100644
--- a/listbox.go
+++ b/listbox.go
@@ -9,7 +9,8 @@ import (
// A Listbox is a vertical list of items, of which either at most one or any number of items can be selected at any given time.
// On creation, no item is selected.
-// Listboxes have both horizontal and vertical scrollbars that are hidden when not needed.
+// Listboxes have vertical scrollbars that are hidden when not needed.
+// The presence of horizontal scrollbars is currently undefined.
type Listbox struct {
// TODO Select event
diff --git a/prefsize_windows.go b/prefsize_windows.go
index 07a633f..850fdb5 100644
--- a/prefsize_windows.go
+++ b/prefsize_windows.go
@@ -150,127 +150,6 @@ func muldiv(ma int, mb int, div int) int {
return int(int32(r1))
}
-// List Boxes do not dynamically handle horizontal scrollbars.
-// We have to manually handle this ourselves.
-// TODO make this run on the main thread when we switch to uitask taking function literals
-// TODO this is inefficient; some caching would be nice
-func recalcListboxWidth(hwnd _HWND) {
- var size _SIZE
-
- ret := make(chan uiret)
- defer close(ret)
- uitask <- &uimsg{
- call: _sendMessage,
- p: []uintptr{
- uintptr(hwnd),
- uintptr(_LB_GETCOUNT),
- uintptr(0),
- uintptr(0),
- },
- ret: ret,
- }
- r := <-ret
- if r.ret == uintptr(_LB_ERR) { // failure
- panic(fmt.Errorf("error getting number of items for Listbox width calculations: %v", r.err))
- }
- n := int(r.ret)
- uitask <- &uimsg{
- call: _getWindowDC,
- p: []uintptr{uintptr(hwnd)},
- ret: ret,
- }
- r = <-ret
- if r.ret == 0 { // failure
- panic(fmt.Errorf("error getting DC for Listbox width calculations: %v", r.err))
- }
- dc := _HANDLE(r.ret)
- uitask <- &uimsg{
- call: _selectObject,
- p: []uintptr{
- uintptr(dc),
- uintptr(controlFont),
- },
- ret: ret,
- }
- r = <-ret
- if r.ret == 0 { // failure
- panic(fmt.Errorf("error loading control font into device context for Listbox width calculation: %v", r.err))
- }
- hextent := uintptr(0)
- for i := 0; i < n; i++ {
- uitask <- &uimsg{
- call: _sendMessage,
- p: []uintptr{
- uintptr(hwnd),
- uintptr(_LB_GETTEXTLEN),
- uintptr(_WPARAM(i)),
- uintptr(0),
- },
- ret: ret,
- }
- r := <-ret
- if r.ret == uintptr(_LB_ERR) {
- panic("UI library internal error: LB_ERR from LB_GETTEXTLEN in what we know is a valid listbox index (came from LB_GETSELITEMS)")
- }
- str := make([]uint16, r.ret)
- uitask <- &uimsg{
- call: _sendMessage,
- p: []uintptr{
- uintptr(hwnd),
- uintptr(_LB_GETTEXT),
- uintptr(_WPARAM(i)),
- uintptr(_LPARAM(unsafe.Pointer(&str[0]))),
- },
- ret: ret,
- }
- r = <-ret
- if r.ret == uintptr(_LB_ERR) {
- panic("UI library internal error: LB_ERR from LB_GETTEXT in what we know is a valid listbox index (came from LB_GETSELITEMS)")
- }
- // r.ret is still the length of the string; this time without the null terminator
- uitask <- &uimsg{
- call: _getTextExtentPoint32,
- p: []uintptr{
- uintptr(dc),
- uintptr(unsafe.Pointer(&str[0])),
- r.ret,
- uintptr(unsafe.Pointer(&size)),
- },
- ret: ret,
- }
- r = <-ret
- if r.ret == 0 { // failure
- panic(fmt.Errorf("error getting width of item %d text for Listbox width calculation: %v", i, r.err))
- }
- if hextent < uintptr(size.cx) {
- hextent = uintptr(size.cx)
- }
- }
- uitask <- &uimsg{
- call: _releaseDC,
- p: []uintptr{
- uintptr(hwnd),
- uintptr(dc),
- },
- ret: ret,
- }
- r = <-ret
- if r.ret == 0 { // failure
- panic(fmt.Errorf("error releasing DC for Listbox width calculations: %v", r.err))
- }
- uitask <- &uimsg{
- call: _sendMessage,
- p: []uintptr{
- uintptr(hwnd),
- uintptr(_LB_SETHORIZONTALEXTENT),
- hextent,
- uintptr(0),
- },
- ret: ret,
- }
- <-ret
-}
-
type _SIZE struct {
cx int32 // originally LONG
cy int32
diff --git a/sysdata_windows.go b/sysdata_windows.go
index 5fcd742..57f7d58 100644
--- a/sysdata_windows.go
+++ b/sysdata_windows.go
@@ -87,13 +87,12 @@ var classTypes = [nctypes]*classData{
},
c_listbox: &classData{
name: "LISTBOX",
- // TODO also _WS_HSCROLL?
// we don't use _LBS_STANDARD because it sorts (and has WS_BORDER; see above)
// _LBS_NOINTEGRALHEIGHT gives us exactly the size we want
// TODO say why we don't use LBS_MULTISEL (listbox docs and http://msdn.microsoft.com/en-us/library/windows/desktop/aa511485.aspx)
- style: _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_HSCROLL | _WS_VSCROLL | controlstyle,
+ style: _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle,
xstyle: _WS_EX_CLIENTEDGE | controlxstyle,
- altStyle: _LBS_EXTENDEDSEL | _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_HSCROLL | _WS_VSCROLL | controlstyle,
+ altStyle: _LBS_EXTENDEDSEL | _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle,
appendMsg: _LB_ADDSTRING,
insertBeforeMsg: _LB_INSERTSTRING,
deleteMsg: _LB_DELETESTRING,
@@ -355,7 +354,6 @@ func (s *sysData) append(what string) {
} else if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) {
panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", r.err))
}
- recalcListboxWidth(s.hwnd)
}
func (s *sysData) insertBefore(what string, index int) {
@@ -377,7 +375,6 @@ func (s *sysData) insertBefore(what string, index int) {
} else if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) {
panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", r.err))
}
- recalcListboxWidth(s.hwnd)
}
func (s *sysData) selectedIndex() int {
@@ -528,7 +525,6 @@ func (s *sysData) delete(index int) {
if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) {
panic(fmt.Errorf("failed to delete item from combobox/listbox (last error: %v)", r.err))
}
- recalcListboxWidth(s.hwnd)
}
func (s *sysData) setIndeterminate() {
diff --git a/unmigrated/hscrolllistbox.go b/unmigrated/hscrolllistbox.go
new file mode 100644
index 0000000..3975a56
--- /dev/null
+++ b/unmigrated/hscrolllistbox.go
@@ -0,0 +1,178 @@
+// WINDOWS (works)
+
+ style: _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_HSCROLL | _WS_VSCROLL | controlstyle,
+ xstyle: _WS_EX_CLIENTEDGE | controlxstyle,
+ altStyle: _LBS_EXTENDEDSEL | _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_HSCROLL | _WS_VSCROLL | controlstyle,
+
+(call recalcListboxWidth() from sysData.append(), sysData.insertBefore(), and sysData.delete())
+
+// List Boxes do not dynamically handle horizontal scrollbars.
+// We have to manually handle this ourselves.
+// TODO make this run on the main thread when we switch to uitask taking function literals
+// TODO this is inefficient; some caching would be nice
+func recalcListboxWidth(hwnd _HWND) {
+ var size _SIZE
+
+ ret := make(chan uiret)
+ defer close(ret)
+ uitask <- &uimsg{
+ call: _sendMessage,
+ p: []uintptr{
+ uintptr(hwnd),
+ uintptr(_LB_GETCOUNT),
+ uintptr(0),
+ uintptr(0),
+ },
+ ret: ret,
+ }
+ r := <-ret
+ if r.ret == uintptr(_LB_ERR) { // failure
+ panic(fmt.Errorf("error getting number of items for Listbox width calculations: %v", r.err))
+ }
+ n := int(r.ret)
+ uitask <- &uimsg{
+ call: _getWindowDC,
+ p: []uintptr{uintptr(hwnd)},
+ ret: ret,
+ }
+ r = <-ret
+ if r.ret == 0 { // failure
+ panic(fmt.Errorf("error getting DC for Listbox width calculations: %v", r.err))
+ }
+ dc := _HANDLE(r.ret)
+ uitask <- &uimsg{
+ call: _selectObject,
+ p: []uintptr{
+ uintptr(dc),
+ uintptr(controlFont),
+ },
+ ret: ret,
+ }
+ r = <-ret
+ if r.ret == 0 { // failure
+ panic(fmt.Errorf("error loading control font into device context for Listbox width calculation: %v", r.err))
+ }
+ hextent := uintptr(0)
+ for i := 0; i < n; i++ {
+ uitask <- &uimsg{
+ call: _sendMessage,
+ p: []uintptr{
+ uintptr(hwnd),
+ uintptr(_LB_GETTEXTLEN),
+ uintptr(_WPARAM(i)),
+ uintptr(0),
+ },
+ ret: ret,
+ }
+ r := <-ret
+ if r.ret == uintptr(_LB_ERR) {
+ panic("UI library internal error: LB_ERR from LB_GETTEXTLEN in what we know is a valid listbox index (came from LB_GETSELITEMS)")
+ }
+ str := make([]uint16, r.ret)
+ uitask <- &uimsg{
+ call: _sendMessage,
+ p: []uintptr{
+ uintptr(hwnd),
+ uintptr(_LB_GETTEXT),
+ uintptr(_WPARAM(i)),
+ uintptr(_LPARAM(unsafe.Pointer(&str[0]))),
+ },
+ ret: ret,
+ }
+ r = <-ret
+ if r.ret == uintptr(_LB_ERR) {
+ panic("UI library internal error: LB_ERR from LB_GETTEXT in what we know is a valid listbox index (came from LB_GETSELITEMS)")
+ }
+ // r.ret is still the length of the string; this time without the null terminator
+ uitask <- &uimsg{
+ call: _getTextExtentPoint32,
+ p: []uintptr{
+ uintptr(dc),
+ uintptr(unsafe.Pointer(&str[0])),
+ r.ret,
+ uintptr(unsafe.Pointer(&size)),
+ },
+ ret: ret,
+ }
+ r = <-ret
+ if r.ret == 0 { // failure
+ panic(fmt.Errorf("error getting width of item %d text for Listbox width calculation: %v", i, r.err))
+ }
+ if hextent < uintptr(size.cx) {
+ hextent = uintptr(size.cx)
+ }
+ }
+ uitask <- &uimsg{
+ call: _releaseDC,
+ p: []uintptr{
+ uintptr(hwnd),
+ uintptr(dc),
+ },
+ ret: ret,
+ }
+ r = <-ret
+ if r.ret == 0 { // failure
+ panic(fmt.Errorf("error releasing DC for Listbox width calculations: %v", r.err))
+ }
+ uitask <- &uimsg{
+ call: _sendMessage,
+ p: []uintptr{
+ uintptr(hwnd),
+ uintptr(_LB_SETHORIZONTALEXTENT),
+ hextent,
+ uintptr(0),
+ },
+ ret: ret,
+ }
+ <-ret
+}
+
+// DARWIN (does not work)
+
+// NSTableView is actually in a NSScrollView so we have to get it out first
+// NSTableView and NSTableColumn both provide sizeToFit methods, but they don't do what we want (NSTableView's sizes to fit the parent; NSTableColumn's sizes to fit the column header)
+// We have to get the width manually; see also http://stackoverflow.com/questions/4674163/nstablecolumn-size-to-fit-contents
+// We can use the NSTableView sizeToFit to get the height, though.
+// TODO this is inefficient!
+// TODO move this to listbox_darwin.go
+var (
+ _dataCellForRow = sel_getUid("dataCellForRow:")
+ _cellSize = sel_getUid("cellSize")
+ _setMinWidth = sel_getUid("setMinWidth:")
+ _setWidth = sel_getUid("setWidth:")
+)
+
+func listboxPrefSize(control C.id) (width int, height int) {
+ var maxwidth C.int64_t
+
+ listbox := listboxInScrollView(control)
+ _, height = controlPrefSize(listbox)
+ column := listboxTableColumn(listbox)
+ n := C.objc_msgSend_intret_noargs(listbox, _numberOfRows)
+ for i := C.intptr_t(0); i < n; i++ {
+ cell := C.objc_msgSend_int(column, _dataCellForRow, i)
+ csize := C.objc_msgSend_stret_size_noargs(cell, _cellSize)
+ if maxwidth < csize.width {
+ maxwidth = csize.width
+ }
+ }
+ // and in order for horizontal scrolling to work, we need to set the column width to this
+ C.objc_msgSend_cgfloat(column, _setMinWidth, C.double(maxwidth))
+ C.objc_msgSend_cgfloat(column, _setWidth, C.double(maxwidth))
+ return int(maxwidth), height
+}
+
+func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error {
+ // winheight - y because (0,0) is the bottom-left corner of the window and not the top-left corner
+ // (winheight - y) - height because (x, y) is the bottom-left corner of the control and not the top-left
+ C.objc_msgSend_rect(s.id, _setFrame,
+ C.int64_t(x), C.int64_t((winheight - y) - height), C.int64_t(width), C.int64_t(height))
+ // TODO having this here is a hack; split it into a separate function in listbox_darwin.go
+ // the NSTableView:NSTableColumn ratio is what determines horizontal scrolling; see http://stackoverflow.com/questions/7050497/enable-scrolling-for-nstableview
+ if s.ctype == c_listbox {
+ listbox := listboxInScrollView(s.id)
+ C.objc_msgSend_rect(listbox, _setFrame,
+ 0, 0, C.int64_t(width), C.int64_t(height))
+ }
+ return nil
+}