Files
supersonic/ui/util/listselectionmanager.go
T

155 lines
3.3 KiB
Go

package util
type ListSelectionManager struct {
lastSelectedRow int
numSelected int
selected BitSet
len func() int
}
func NewListSelectionManager(lenFn func() int) ListSelectionManager {
return ListSelectionManager{lastSelectedRow: -1, len: lenFn}
}
// If the given row is not selected, reset the selection
// and select only the given row.
func (l *ListSelectionManager) Select(row int) {
if row < 0 || l.selected.IsSet(uint(row)) {
return
}
l.UnselectAll()
l.selectAdd(row)
}
// If it is not selected, add the given row to the selection.
// If it is selected, unselet it.
func (l *ListSelectionManager) SelectAddOrRemove(row int) {
if row < 0 {
return
}
if l.selected.IsSet(uint(row)) {
// find new last selected row
if row == l.lastSelectedRow {
for i := row - 1; i >= -1; i++ {
if i == -1 {
l.lastSelectedRow = i
} else if l.selected.IsSet(uint(i)) {
l.lastSelectedRow = i
break
}
}
}
l.selected.Unset(uint(row))
l.numSelected -= 1
return
}
l.selectAdd(row)
}
func (l *ListSelectionManager) selectAdd(row int) {
l.numSelected += 1
l.selected.Set(uint(row))
if row > l.lastSelectedRow {
l.lastSelectedRow = row
}
}
// Select a range between the given row and the furthest-down
// row that is currently selected (which may be above the given row)
// Note: this is modeled after what, as far as I can tell, is Gmail's selection behavior
func (l *ListSelectionManager) SelectRange(row int) {
if row < 0 || l.selected.IsSet(uint(row)) {
return
}
if l.numSelected == 0 {
l.selectAdd(row)
return
}
m := maxInt(row, l.lastSelectedRow)
for i := minInt(l.lastSelectedRow, row); i <= m; i++ {
l.selectAdd(i)
}
}
func (l *ListSelectionManager) SelectAll() {
for i := 0; i < l.len(); i++ {
l.selectAdd(i)
}
}
func (l *ListSelectionManager) UnselectAll() {
l.selected = nil
l.numSelected = 0
l.lastSelectedRow = -1
}
func (l *ListSelectionManager) IsSelected(row int) bool {
return row >= 0 && l.selected.IsSet(uint(row))
}
func (l *ListSelectionManager) GetSelection() []int {
var sel []int
for i := 0; i < l.len(); i++ {
if l.selected.IsSet(uint(i)) {
sel = append(sel, i)
}
if len(sel) == l.numSelected {
break
}
}
return sel
}
func (l *ListSelectionManager) AreAllSelected() bool {
return l.numSelected == l.len()
}
func minInt(a, b int) int {
if a < b {
return a
}
return b
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
// BitSet implementation from
// https://stackoverflow.com/questions/2311373/how-to-implement-bitset-with-go
const uint64size = 64
// BitSet is a set of bits that can be set, cleared and queried.
type BitSet []uint64
// Set ensures that the given bit is set in the BitSet.
func (s *BitSet) Set(i uint) {
if len(*s) < int(i/uint64size+1) {
r := make([]uint64, i/uint64size+1)
copy(r, *s)
*s = r
}
(*s)[i/uint64size] |= 1 << (i % uint64size)
}
// Unset ensures that the given bit is cleared (not set) in the BitSet.
func (s *BitSet) Unset(i uint) {
if len(*s) >= int(i/uint64size+1) {
(*s)[i/uint64size] &^= 1 << (i % uint64size)
}
}
// IsSet returns true if the given bit is set, false if it is cleared.
func (s *BitSet) IsSet(i uint) bool {
idx := i / uint64size
if idx >= uint(len(*s)) {
return false
}
return (*s)[i/uint64size]&(1<<(i%uint64size)) != 0
}