For several days now, I've been working on a new drawing approach. The 
preliminary results of this work can be seen in the new branch tree-refresh 
<https://github.com/leo-editor/leo-editor/tree/tree-refresh>. There are 
still some things missing (like hoisting and chapters) but they won't be 
hard to add if Edward accepts this idea. 

I had an Aha moment: The outline structure can be represented as a set of 
links, where each link is just a tuple (parent_id, index, child_id). In 
other words, all the information about the structure (or shape) of the Leo 
outline including the information about clones, can be represented by the 
set of elements (parent.gnx, index, child.gnx). And what is more important 
is that obtaining this set from the Leo is not too expensive operation.

import timeit
def makeset():
    def it(v):
        for i, ch in enumerate(v.children):
            yield v.fileIndex, i, ch.fileIndex
            yield from it(ch)
    return set(it(c.hiddenRootNode))
g.es(timeit.timeit(makeset, number=100)/100*1000, 'ms')

On my fastest computer this gives 6.14ms for LeoPy.leo. On older computers 
it might be about 12ms.

The idea is to keep track of this set each time we redraw the tree. On the 
next redraw we can simply use the set arithmetic to find what is changed in 
the tree. We calculate new set (let's call it a current_links) and then use 
(current_links - last_links) to get all the inserts and (last_links - 
current_links) to get all deletes. 
Both of these operations are very fast and then we just need to add several 
new links or delete some of the previously existstent links to draw new 
version of tree.

This techinque can eliminate a lot of unnecessary redraws. For example if 
the node is expanded nothing is changed and the only thing necessary is to 
set certain tree item to expanded state.

It isn't only about the speed. There are more benefits of using this 
approach to draw tree. After redrawing the tree, for every position in the 
Leo there exists a single item. This fact alone can make a lot of other 
parts of Leo much simpler. A lot of checks that position exists and is 
still valid may be skipped if the position comes from the tree. Keeping 
track of expanded/collapsed state for each position can be delegated to the 
tree items, because for each valid position there exists a single item. 

The code in tree-refresh branch is a prototype that shows this approach as 
possible. It doesn't do hoisting and chapters yet, and the code is not 
cleaned. A lot of qt_tree code is not used and can be deleted. There is one 
more status field in the status line which shows how much time took the 
last redraw. All of the unit tests pass.

There is a room for improvements of diff-calcualtion. In the current 
version bare set difference is used which is not producing the smallest 
possible diff. For example if a parent node has 10 children and we delete 
first one, the difference will contain not just one delete operation as it 
would ideally but 10 deletes followed by 9 inserts, because every child has 
now the different index. Perhaps we can process these differences a bit 
before applying them. But for now it works just fine. Deleting 9 items and 
creating new 9 items is still much less work than deleting the whole 
outline and recreating it from scratch.

Vitalije


-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/leo-editor/945d9bd3-7653-47ba-9617-8359587ae8b5%40googlegroups.com.

Reply via email to