Rolled back the Windows horizontally scrolling Listbox changes because I can't seem to get it to work on Mac OS X (and keeping that code there is inefficient); removed the guarantee of horizontal scrollbars in Listbox and made that a future plan. Both the Windows and the non-working Mac OS X code is in unmigrated/hscrolllistbox.go (and the unmigrated/ folder will be renamed shortly).
This commit is contained in:
parent
417bdb8949
commit
6c265dd54d
|
@ -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
|
- 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/
|
- 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
|
- also moved the Windows code there for the sake of efficiency
|
||||||
|
- GTK+ works just fine though
|
||||||
|
|
||||||
specifics:
|
specifics:
|
||||||
|
|
||||||
|
|
|
@ -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.
|
// 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.
|
// 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 {
|
type Listbox struct {
|
||||||
// TODO Select event
|
// TODO Select event
|
||||||
|
|
||||||
|
|
|
@ -150,127 +150,6 @@ func muldiv(ma int, mb int, div int) int {
|
||||||
return int(int32(r1))
|
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 {
|
type _SIZE struct {
|
||||||
cx int32 // originally LONG
|
cx int32 // originally LONG
|
||||||
cy int32
|
cy int32
|
||||||
|
|
|
@ -87,13 +87,12 @@ var classTypes = [nctypes]*classData{
|
||||||
},
|
},
|
||||||
c_listbox: &classData{
|
c_listbox: &classData{
|
||||||
name: "LISTBOX",
|
name: "LISTBOX",
|
||||||
// TODO also _WS_HSCROLL?
|
|
||||||
// we don't use _LBS_STANDARD because it sorts (and has WS_BORDER; see above)
|
// we don't use _LBS_STANDARD because it sorts (and has WS_BORDER; see above)
|
||||||
// _LBS_NOINTEGRALHEIGHT gives us exactly the size we want
|
// _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)
|
// 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,
|
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,
|
appendMsg: _LB_ADDSTRING,
|
||||||
insertBeforeMsg: _LB_INSERTSTRING,
|
insertBeforeMsg: _LB_INSERTSTRING,
|
||||||
deleteMsg: _LB_DELETESTRING,
|
deleteMsg: _LB_DELETESTRING,
|
||||||
|
@ -355,7 +354,6 @@ func (s *sysData) append(what string) {
|
||||||
} else if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
} else if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
||||||
panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", r.err))
|
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) {
|
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) {
|
} else if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
||||||
panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", r.err))
|
panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", r.err))
|
||||||
}
|
}
|
||||||
recalcListboxWidth(s.hwnd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) selectedIndex() int {
|
func (s *sysData) selectedIndex() int {
|
||||||
|
@ -528,7 +525,6 @@ func (s *sysData) delete(index int) {
|
||||||
if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
||||||
panic(fmt.Errorf("failed to delete item from combobox/listbox (last error: %v)", r.err))
|
panic(fmt.Errorf("failed to delete item from combobox/listbox (last error: %v)", r.err))
|
||||||
}
|
}
|
||||||
recalcListboxWidth(s.hwnd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setIndeterminate() {
|
func (s *sysData) setIndeterminate() {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue