implement additional movement options

This commit is contained in:
Jakob Ketterl 2024-01-15 19:05:06 +01:00
parent dcc0f404b7
commit 1fb3ed4066
2 changed files with 128 additions and 1 deletions

View file

@ -39,6 +39,12 @@ class ActiveListIndexDeleted(ActiveListChange):
self.oldValue = oldValue
class ActiveListIndexMoved(ActiveListChange):
def __init__(self, old_index: int, new_index: int):
self.old_index = old_index
self.new_index = new_index
class ActiveListListener(ABC):
@abstractmethod
def onListChange(self, source: "ActiveList", changes: list[ActiveListChange]):
@ -105,6 +111,8 @@ class ActiveListTransformationListener(ActiveListListener):
elif isinstance(change, ActiveListIndexDeleted):
del self.target[change.index]
self.transformation.unmonitor(change.oldValue)
elif isinstance(change, ActiveListIndexMoved):
self.target.move(change.old_index, change.new_index)
def _onMonitor(self, value):
idx = self.source.index(value)
@ -152,6 +160,9 @@ class ActiveListFilterListener(ActiveListListener):
del self.keyMap[idx]
for i in range(idx, len(self.keyMap)):
self.keyMap[i] -= 1
elif isinstance(change, ActiveListIndexMoved):
idx = self.keyMap.index(change.old_index)
#TODO update keymap, fire change event
def _onMonitor(self, value):
idx = self.source.index(value)
@ -199,6 +210,12 @@ class ActiveListFlattenListener(ActiveListListener):
change.oldValue.removeListener(self)
idx = self.getOffsetForIndex(change.index)
del self.target[idx, idx + len(change.oldValue)]
elif isinstance(change, ActiveListIndexMoved):
old_index = self.getOffsetForIndex(change.old_index)
new_index = self.getOffsetForIndex(change.new_index)
moved_list = self.source[change.new_index]
for (idx, element) in enumerate(moved_list):
self.target.move(old_index + idx, new_index + idx)
else:
if isinstance(change, ActiveListIndexAdded):
self.target.insert(self.getOffsetFor(source) + change.index, change.newValue)
@ -206,6 +223,9 @@ class ActiveListFlattenListener(ActiveListListener):
self.target[self.getOffsetFor(source) + change.index] = change.newValue
elif isinstance(change, ActiveListIndexDeleted):
del self.target[self.getOffsetFor(source) + change.index]
elif isinstance(change, ActiveListIndexMoved):
offset = self.getOffsetFor(source)
self.target.move(offset + change.old_index, offset + change.new_index)
class ActiveList:
@ -241,6 +261,10 @@ class ActiveList:
self.delegate.insert(index, value)
self.__fireChanges([ActiveListIndexInserted(index, value)])
def move(self, old_index, new_index):
self.delegate.insert(new_index, self.delegate.pop(old_index))
self.__fireChanges([ActiveListIndexMoved(old_index, new_index)])
def index(self, value):
return self.delegate.index(value)

View file

@ -1,4 +1,4 @@
from owrx.active.list import ActiveList, ActiveListIndexUpdated, ActiveListIndexAppended, ActiveListIndexDeleted, ActiveListIndexInserted
from owrx.active.list import ActiveList, ActiveListIndexUpdated, ActiveListIndexAppended, ActiveListIndexDeleted, ActiveListIndexInserted, ActiveListIndexMoved
from unittest import TestCase
from unittest.mock import Mock
@ -333,3 +333,106 @@ class ActiveListTest(TestCase):
self.assertEqual(len(filteredList), 2)
# update should propagate
self.assertEqual(filteredList[0], 42)
def testListMove(self):
list = ActiveList([1, 2, 3, 4, 5])
list.move(1, 4)
self.assertEqual(len(list), 5)
self.assertEqual(list[1], 3)
self.assertEqual(list[4], 2)
list = ActiveList([1, 2, 3, 4, 5])
list.move(4, 1)
self.assertEqual(len(list), 5)
self.assertEqual(list[4], 4)
self.assertEqual(list[1], 5)
def testListMoveNotification(self):
list = ActiveList([1, 2, 3, 4, 5])
listenerMock = Mock()
list.addListener(listenerMock)
list.move(1, 4)
listenerMock.onListChange.assert_called_once()
source, changes = listenerMock.onListChange.call_args.args
self.assertIs(source, list)
self.assertEqual(len(changes), 1)
self.assertIsInstance(changes[0], ActiveListIndexMoved)
self.assertEqual(changes[0].old_index, 1)
self.assertEqual(changes[0].new_index, 4)
def testActiveTransformationMove(self):
list = ActiveList([1, 2, 3, 4, 5])
transformedList = list.map(lambda x: x + 10)
list.move(1, 4)
self.assertEqual(len(transformedList), 5)
self.assertEqual(transformedList[1], 13)
self.assertEqual(transformedList[4], 12)
def testActiveTransformationMoveNotification(self):
list = ActiveList([1, 2, 3, 4, 5])
transformedList = list.map(lambda x: x + 10)
listenerMock = Mock()
transformedList.addListener(listenerMock)
list.move(1, 4)
listenerMock.onListChange.assert_called_once()
source, changes = listenerMock.onListChange.call_args.args
self.assertIs(source, transformedList)
self.assertEqual(len(changes), 1)
self.assertIsInstance(changes[0], ActiveListIndexMoved)
self.assertEqual(changes[0].old_index, 1)
self.assertEqual(changes[0].new_index, 4)
#def testActiveFilterMove(self):
# list = ActiveList([1, 2, 3, 4, 5])
# filteredList = list.filter(lambda x: x != 3)
# list.move(1, 4)
# self.assertEqual(len(filteredList), 4)
# self.assertEqual(filteredList[1], 4)
# self.assertEqual(filteredList[3], 2)
def testActiveListFlattenMove(self):
firstMember = ActiveList([1, 2, 3, 4, 5])
list = ActiveList([firstMember, ActiveList([6, 7, 8, 9, 10])])
flattenedList = list.flatten()
firstMember.move(1, 4)
self.assertEqual(len(flattenedList), 10)
self.assertEqual(flattenedList[1], 3)
self.assertEqual(flattenedList[4], 2)
def testActiveListFlattenMoveNotification(self):
firstMember = ActiveList([1, 2, 3, 4, 5])
secondMember = ActiveList([6, 7, 8, 9, 10])
list = ActiveList([firstMember, secondMember])
flattenedList = list.flatten()
listenerMock = Mock()
flattenedList.addListener(listenerMock)
secondMember.move(1, 4)
listenerMock.onListChange.assert_called_once()
source, changes = listenerMock.onListChange.call_args.args
self.assertIs(source, flattenedList)
self.assertEqual(len(changes), 1)
self.assertIsInstance(changes[0], ActiveListIndexMoved)
self.assertEqual(changes[0].old_index, 6)
self.assertEqual(changes[0].new_index, 9)
def testActiveListFlattenListMove(self):
list = ActiveList([ActiveList([1]), ActiveList([2]), ActiveList([3]), ActiveList([4]), ActiveList([5])])
flattenedList = list.flatten()
list.move(1, 4)
self.assertEqual(len(flattenedList), 5)
self.assertEqual(flattenedList[1], 3)
self.assertEqual(flattenedList[4], 2)
def testActiveListFlattenListMoveNotification(self):
list = ActiveList([ActiveList([1, 2, 3, 4, 5]), ActiveList([6, 7, 8, 9, 10])])
flattenedList = list.flatten()
listenerMock = Mock()
flattenedList.addListener(listenerMock)
list.move(0, 1)
self.assertEqual(listenerMock.onListChange.call_count, 5)
for i in range(0, 5):
source, changes = listenerMock.onListChange.call_args_list[i].args
self.assertIs(source, flattenedList)
self.assertEqual(len(changes), 1)
self.assertEqual(changes[0].old_index, i)
self.assertEqual(changes[0].new_index, i + 5)