Essentially I would base my own solution on recursion. I would extend the container class with the following:
cursor_position
- Property that stores the index of the highlighted element (or the element that contains the element that contains the highlighted element, or any level of recursion beyond that).
repr_with_cursor
- This method should return a printable version of the container's content, already highlighting the item currently selected.
mov_right
- This method should be invoked when the cursor move right. Returns the new index of the cursor within the element or None
if the cursor falls "outside" the current container (if you move past hte last element in the container.
mov_left
- Idem, but towards the left.
The way the recursion should work, is that for each method, depending of the type of the highlighted method you should have two different behaviours:
- if the cursor is on a container it should invoke the method of the "pointed" container.
- if the cursor is on a non-container it should perform the 'real thing'.
EDIT
I had a spare half an hour so I threw together an example class that implements my idea. It' not feature complete (for example it doesn't handle well when it reaches the either end of the largest container, and requires each instance of the class to be used only once in the largest sequence) but it works enough to demonstrate the concept. I shall repeat before people comments on that: this is proof-of-concept code, it's not in any way ready to be used!
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class C(list):
def __init__(self, *args):
self.cursor_position = None
super(C, self).__init__(*args)
def _pointed(self):
'''Return currently pointed item'''
if self.cursor_position == None:
return None
return self[self.cursor_position]
def _recursable(self):
'''Return True if pointed item is a container [C class]'''
return (type(self._pointed()) == C)
def init_pointer(self, end):
'''
Recursively set the pointers of containers in a way to point to the
first non-container item of the nested hierarchy.
'''
assert end in ('left', 'right')
val = 0 if end == 'left' else len(self)-1
self.cursor_position = val
if self._recursable():
self.pointed._init_pointer(end)
def repr_with_cursor(self):
'''
Return a representation of the container with highlighted item.
'''
composite = '['
for i, elem in enumerate(self):
if type(elem) == C:
composite += elem.repr_with_cursor()
else:
if i != self.cursor_position:
composite += str(elem)
else:
composite += '**' + str(elem) + '**'
if i != len(self)-1:
composite += ', '
composite += ']'
return composite
def mov_right(self):
'''
Move pointer to the right.
'''
if self._recursable():
if self._pointed().mov_right() == -1:
if self.cursor_position != len(self)-1:
self.cursor_position += 1
else:
if self.cursor_position != len(self)-1:
self.cursor_position += 1
if self._recursable():
self._pointed().init_pointer('left')
else:
self.cursor_position = None
return -1
def mov_left(self):
'''
Move pointer to the left.
'''
if self._recursable():
if self._pointed().mov_left() == -1:
if self.cursor_position != 0:
self.cursor_position -= 1
else:
if self.cursor_position != 0:
self.cursor_position -= 1
if self._recursable():
self._pointed().init_pointer('right')
else:
self.cursor_position = None
return -1
A simple test script:
# Create the nested structure
LevelOne = C(('I say',))
LevelTwo = C(('Hello', 'Bye', 'Ciao'))
LevelOne.append(LevelTwo)
LevelOne.append('!')
LevelOne.init_pointer('left')
# The container's content can be seen as both a regualar list or a
# special container.
print(LevelOne)
print(LevelOne.repr_with_cursor())
print('---')
# Showcase the effect of moving the cursor to right
for i in range(5):
print(LevelOne.repr_with_cursor())
LevelOne.mov_right()
print('---')
# Showcase the effect of moving the cursor to left
LevelOne.init_pointer('right')
for i in range(5):
print(LevelOne.repr_with_cursor())
LevelOne.mov_left()
It outputs:
['I say', ['Hello', 'Bye', 'Ciao'], '!']
[**I say**, [Hello, Bye, Ciao], !]
---
[**I say**, [Hello, Bye, Ciao], !]
[I say, [**Hello**, Bye, Ciao], !]
[I say, [Hello, **Bye**, Ciao], !]
[I say, [Hello, Bye, **Ciao**], !]
[I say, [Hello, Bye, Ciao], **!**]
---
[I say, [Hello, Bye, Ciao], **!**]
[I say, [Hello, Bye, **Ciao**], !]
[I say, [Hello, **Bye**, Ciao], !]
[I say, [**Hello**, Bye, Ciao], !]
[**I say**, [Hello, Bye, Ciao], !]
Fun problem! My favourite OS question of the day! :)