summaryrefslogtreecommitdiff
path: root/experiments/hscrolllistbox.go
blob: 3975a56ba8b043d7dc0cd9cc2e50a8badbfddd92 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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
}