. .

smalltalk

The Joys of Selection Trackers

December 14, 2010 20:16:19.375

One of the small annoyances in VisualWorks is the way selection works with listboxes when you set them for:

  • Select on (mouse button) down (the normal way to do it)
  • Set up the listbox for multiple selections
  • Want the user to be able to select mutiple items, come back, and then drag the previously selected group

That last thing is what doesn't work out of the box - instead, when you first click the left button, the selected group is de-selected, and the one thing you were pointing at when you wanted to do a drag operation becomes the new selection.

As you might guess, given the way most end user tools now work, end users are not entirely pleased with this turn of events. So... It was off to controller land. A few minutes of investigation told me that multi-selection listboxes use the class EmulatedSequenceController, and I started trawling through the code.

Now, other than patching some of the more egregious flaws in the way Datasets work, I haven't done a lot of hacking on controllers. So it took me a bit of rathole diving before I realized that the problem was in EmulatedSequenceTracker, which works in conjunction with the controller. If you look at the #redButtonPressedEvent: method there (with a flashback to Xerox mice tossed in), you find that you end up calling down to #setUpDragDropFor:


setUpDragDropFor: aMouseButtonEvent 
	"If we are doing drag and drop, we don't have to do the preparation 
	work"

	self outside: false.
	(controller selectOnDownWithDrag and: [self view selectionIndex ~= self view targetIndex])
		ifTrue: 
			[self controller toggleTargetWithEvent: aMouseButtonEvent.
			selectionDone := true]

The problem resides in the first statement in the block - the controller is told to toggle the selection, and it dutifully does so. It's only later in the call chain that drag/drop is checked for, by which time the selection has been reset. So... I hacked up three methods in EmulatedSequenceController to make that happen on red button up instead of on red button down - note the extra checks for whether we want to drag or not; without that, you get some truly weird behavior:


setUpDragDropFor: aMouseButtonEvent 
	"If we are doing drag and drop, we don't have to do the preparation 
	work"

	"JR 12/13/10: For multi-select, don't toggle target on red down.  Instead, that should happen on red up"
	self outside: false.
	(controller selectOnDownWithDrag and: [self view selectionIndex ~= self view targetIndex])
		ifTrue: 
			["self controller toggleTargetWithEvent: aMouseButtonEvent."
			selectionDone := true]



redButtonReleasedEvent: aMouseButtonEvent
	self sensor ungrabMouseEventFor: self.
	self finishSelectionForEvent: aMouseButtonEvent.

	"JR 12/13/10 - moved out of the #redButtonPressedEvent: method.  
	For multi-select lists, if drag/drop is on, we want selections toggled on mouse up"

	self wantsToDrag
		ifTrue: [self controller toggleTargetWithEvent: aMouseButtonEvent.
				selectionDone := true]

This method below is mostly the same as the inherited version, with the extra ifFalse: clause at the end:


setUpFor: aMouseButtonEvent 
	"If we are doing drag and drop, we don't have to do the preparation 
	work"

	"JR 12/13/10 - When not dragging, actually make the selection"

	| index |
	selectionDone := false.
	startPoint := self cursorPointFor: aMouseButtonEvent.
	self outside: false.
	(index := self view numberOfElements) = 0 ifTrue: [^self].
	index := self controller findElementFor: (self cursorPointFor: aMouseButtonEvent).
	self view targetIndex: index.
	self wantsToDrag 
		ifTrue: [self setUpDragDropFor: aMouseButtonEvent]"This API is obsolete. Use cursorPointFor: instead."
		ifFalse: [self controller toggleTargetWithEvent: aMouseButtonEvent.
				selectionDone := true]

In the button release method, I copied over both lines from the block in #setUpDragDropFor:. After that, things worked the way I wanted them to - with multiple selections only deselecting on mouse button up. Kind of amusing that such a small fix took me most of the day to track down, but there you go :)

Technorati Tags: ,

posted by James Robertson

 Share Tweet This