Replaced the horribly memory-inefficient NSIndexSet operation to get the list of selected indices in the Mac OS X Listbox with a far simpler option that avoids deferring some of the work (apart from objc_msgSend() calls) to cgo and the use of reflect.SliceHeader at all! It just grabs the indices from the NSIndexSet one at a time using the previous index as an anchor.
This commit is contained in:
parent
4813a9892e
commit
496ad98216
|
@ -30,7 +30,6 @@ id *_NSObservedObjectKey = (id *) (&NSObservedObjectKey);
|
||||||
These are all the selectors and class IDs used by the functions below.
|
These are all the selectors and class IDs used by the functions below.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static SEL s_getIndexes; /* NSIndexSetEntries() */
|
|
||||||
static id c_NSEvent; /* makeDummyEvent() */
|
static id c_NSEvent; /* makeDummyEvent() */
|
||||||
static SEL s_newEvent;
|
static SEL s_newEvent;
|
||||||
static id c_NSBitmapImageRep; /* drawImage() */
|
static id c_NSBitmapImageRep; /* drawImage() */
|
||||||
|
@ -50,7 +49,6 @@ static SEL s_initTrackingArea;
|
||||||
|
|
||||||
void initBleh()
|
void initBleh()
|
||||||
{
|
{
|
||||||
s_getIndexes = sel_getUid("getIndexes:maxCount:inIndexRange:");
|
|
||||||
c_NSEvent = objc_getClass("NSEvent");
|
c_NSEvent = objc_getClass("NSEvent");
|
||||||
s_newEvent = sel_getUid("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:");
|
s_newEvent = sel_getUid("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:");
|
||||||
c_NSBitmapImageRep = objc_getClass("NSBitmapImageRep");
|
c_NSBitmapImageRep = objc_getClass("NSBitmapImageRep");
|
||||||
|
@ -78,6 +76,11 @@ uintptr_t objc_msgSend_uintret_noargs(id obj, SEL sel)
|
||||||
return (uintptr_t) ((NSUInteger) objc_msgSend(obj, sel));
|
return (uintptr_t) ((NSUInteger) objc_msgSend(obj, sel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uintptr_t objc_msgSend_uintret_uint(id obj, SEL sel, uintptr_t a)
|
||||||
|
{
|
||||||
|
return (uintptr_t) ((NSUInteger) objc_msgSend(obj, sel, (NSUInteger) a));
|
||||||
|
}
|
||||||
|
|
||||||
id objc_msgSend_uint(id obj, SEL sel, uintptr_t a)
|
id objc_msgSend_uint(id obj, SEL sel, uintptr_t a)
|
||||||
{
|
{
|
||||||
return objc_msgSend(obj, sel, (NSUInteger) a);
|
return objc_msgSend(obj, sel, (NSUInteger) a);
|
||||||
|
@ -193,30 +196,6 @@ id objc_msgSend_point(id obj, SEL sel, int64_t x, int64_t y)
|
||||||
return objc_msgSend(obj, sel, NSMakePoint((CGFloat) x, (CGFloat) y));
|
return objc_msgSend(obj, sel, NSMakePoint((CGFloat) x, (CGFloat) y));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
This is a doozy: it deals with a NSUInteger array needed for this one selector, and converts them all into a uintptr_t array so we can use it from Go. The two arrays are created at runtime with malloc(); only the NSUInteger one is freed here, while Go frees the returned one. It's not optimal.
|
|
||||||
*/
|
|
||||||
|
|
||||||
uintptr_t *NSIndexSetEntries(id indexset, uintptr_t count)
|
|
||||||
{
|
|
||||||
NSUInteger *nsuints;
|
|
||||||
uintptr_t *ret;
|
|
||||||
uintptr_t i;
|
|
||||||
size_t countsize;
|
|
||||||
|
|
||||||
countsize = (size_t) count;
|
|
||||||
nsuints = (NSUInteger *) malloc(countsize * sizeof (NSUInteger));
|
|
||||||
/* TODO check return value */
|
|
||||||
objc_msgSend(indexset, s_getIndexes,
|
|
||||||
nsuints, (NSUInteger) count, nil);
|
|
||||||
ret = (uintptr_t *) malloc(countsize * sizeof (uintptr_t));
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
ret[i] = (uintptr_t) nsuints[i];
|
|
||||||
}
|
|
||||||
free(nsuints);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
See uitask_darwin.go: we need to synthesize a NSEvent so -[NSApplication stop:] will work. We cannot simply init the default NSEvent though (it throws an exception) so we must do it "the right way". This involves a very convoluted initializer; we'll just do it here to keep things clean on the Go side (this will only be run once anyway, on program exit).
|
See uitask_darwin.go: we need to synthesize a NSEvent so -[NSApplication stop:] will work. We cannot simply init the default NSEvent though (it throws an exception) so we must do it "the right way". This involves a very convoluted initializer; we'll just do it here to keep things clean on the Go side (this will only be run once anyway, on program exit).
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
// ...
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -230,6 +229,8 @@ var (
|
||||||
_setHeaderView = sel_getUid("setHeaderView:")
|
_setHeaderView = sel_getUid("setHeaderView:")
|
||||||
_selectedRowIndexes = sel_getUid("selectedRowIndexes")
|
_selectedRowIndexes = sel_getUid("selectedRowIndexes")
|
||||||
_count = sel_getUid("count")
|
_count = sel_getUid("count")
|
||||||
|
_firstIndex = sel_getUid("firstIndex")
|
||||||
|
_indexGreaterThanIndex = sel_getUid("indexGreaterThanIndex:")
|
||||||
_numberOfRows = sel_getUid("numberOfRows")
|
_numberOfRows = sel_getUid("numberOfRows")
|
||||||
_deselectAll = sel_getUid("deselectAll:")
|
_deselectAll = sel_getUid("deselectAll:")
|
||||||
)
|
)
|
||||||
|
@ -261,24 +262,19 @@ func insertListboxBefore(listbox C.id, what string, before int, alternate bool)
|
||||||
insertListboxArrayBefore(array, what, before)
|
insertListboxArrayBefore(array, what, before)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO change to use [indices firstIndex] and [indices indexGreaterThanIndex:], as suggested http://stackoverflow.com/questions/3773180/how-to-get-indexes-from-nsindexset-into-an-nsarray-in-cocoa
|
// technique from http://stackoverflow.com/questions/3773180/how-to-get-indexes-from-nsindexset-into-an-nsarray-in-cocoa
|
||||||
|
// we don't care that the indices were originally NSUInteger since by this point we have a problem anyway; Go programs generally use int indices anyway
|
||||||
|
// we also don't care about NSNotFound because we check the count first AND because NSIndexSet is always sorted (and NSNotFound can be a valid index if the list is large enough... since it's NSIntegerMax, not NSUIntegerMax)
|
||||||
func selectedListboxIndices(listbox C.id) (list []int) {
|
func selectedListboxIndices(listbox C.id) (list []int) {
|
||||||
var cindices []C.uintptr_t
|
|
||||||
|
|
||||||
indices := C.objc_msgSend_noargs(listboxInScrollView(listbox), _selectedRowIndexes)
|
indices := C.objc_msgSend_noargs(listboxInScrollView(listbox), _selectedRowIndexes)
|
||||||
count := int(C.objc_msgSend_uintret_noargs(indices, _count))
|
count := int(C.objc_msgSend_uintret_noargs(indices, _count))
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
list = make([]int, count)
|
list = make([]int, count)
|
||||||
cidx := C.NSIndexSetEntries(indices, C.uintptr_t(count))
|
list[0] = int(C.objc_msgSend_uintret_noargs(indices, _firstIndex))
|
||||||
defer C.free(unsafe.Pointer(cidx))
|
for i := 1; i < count; i++ {
|
||||||
pcindices := (*reflect.SliceHeader)(unsafe.Pointer(&cindices))
|
list[i] = int(C.objc_msgSend_uintret_uint(indices, _indexGreaterThanIndex, C.uintptr_t(list[i - 1])))
|
||||||
pcindices.Cap = count
|
|
||||||
pcindices.Len = count
|
|
||||||
pcindices.Data = uintptr(unsafe.Pointer(cidx))
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
list[i] = int(cindices[i])
|
|
||||||
}
|
}
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,8 @@ struct xpoint {
|
||||||
int64_t y;
|
int64_t y;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern uintptr_t objc_msgSend_uintret_noargs(id objc, SEL sel);
|
extern uintptr_t objc_msgSend_uintret_noargs(id obj, SEL sel);
|
||||||
|
extern uintptr_t objc_msgSend_uintret_uint(id obj, SEL sel, uintptr_t a);
|
||||||
|
|
||||||
extern intptr_t objc_msgSend_intret_noargs(id obj, SEL sel);
|
extern intptr_t objc_msgSend_intret_noargs(id obj, SEL sel);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue