Yesterday I proved to my satisfaction that a *major* simplification of
Leo's drawing code will work. The actual code in in the key-handling
branch.
**Important**: the new code base is **precisely** equivalent to the
old code base when g.newDrawing is False. So the present (new) code
base *is* safe to use.
The general idea is that code that presently actually redraws,
recolors and otherwise updates the screen is converted to code that
merely *requests* a redraw, recoloring or refocusing later. The
actual redrawing is done in a new method, c.outerUpdate. We must
ensure that all commands and all events eventually call
c.outerUpdate. In the rare cases where drawing must happen
immediately, the drawing can be forced by calling c.outerUpdate.
The Aha that made this work possible was this: I must be able to test
the new scheme without changing the code base in any dangerous way.
At present, the code looks *exactly* the same as the old, except for
the following changes:
1. leoGlobals.py defines "compile-time" constant, g.newDrawing.
2. A few new calls to the new c.outerUpdate appear throughout the
code.
3. The k.masterX methods have been changed to wrapper methods that
call k.masterKWrapper. For example:
def masterKeyHandler (self,event,stroke=None):
c = self.c
val = self.masterKeyHandlerHelper(event,stroke)
# Careful: the stroke may destroy c.
if c.exists: self.c.outerUpdate()
return val
However, as we shall see later, we can eliminate these wrappers.
4. leoCommands.py defines new versions of all the drawing utilities
when g.newDrawing is True, and uses the legacy versions of those
methods when g.newDrawing is False. Crucially, c.outerUpdate does
nothing when g.newDrawing is False.
I said earlier that to make this scheme work we must ensure that all
commands and all events eventually call c.outerUpdate. It's trivial to
do this for commands: I just added a call to c.outerUpdate in
c.doCommand.
Yesterday I did this by brute force for several crucial event
handlers. For example, I created the wrappers for the k.masterX
methods. That was enough to prove that the scheme would work.
This morning I saw that the easy way to ensure all event handlers
eventually call c.outerUpdate is to change all calls to w.bind(a,b)
into c.bind(w,a,b), where c.bind is defined as follows:
def bind (self,w,pattern,func):
c = self
def bindCallback(event,c=c,func=func):
val = func(event)
# Careful: func may destroy c.
if c.exists: c.outerUpdate()
return val
w.bind(pattern,bindCallback)
This is relatively safe change, but it needs to be made in Leo's core
and all plugins. It can be done with a regex search/replace. I'll
experiment with this global change today. I'll be responsible for
changing all bindings in all plugins.
This is another huge step forward for Leo. It's important for several
reasons, in order of increasing importance:
1. Eventually, all calls to c.beginUpdate and c.endUpdate will be
replaced by c.requestRedraw. This will eliminate some ugly code.
2. In the new scheme, **the order of drawing code no longer matters**,
a huge improvement in reliability. For example, the old code must
carefully consider whether calls to c.selectPosition should be done
insider or outside of the begin/endUpdate pair. In the new scheme,
the order of actual drawing is completely determined by the order of
code in c.outerUpdate--the order of *requests* is completely
irrelevant.
3. The new code is an immense improvement in Leo's focus-handling
code. Compare the trivial new code with the almost impossible-to-
understand old code. This alone is enough to prefer the new scheme.
4. The new scheme guarantees that focus is set exactly once per
command or event, and that redrawing and recoloring are each done at
most once per command event. This is a major performance guarantee
accomplished through simplified code.
The new scheme accomplishes something that I have wanted to do for at
least five years. I'll leave the calls to begin/endUpdate in place at
least for several more weeks. There is no hurry to remove them.
Edward
P.S. Here are my original design notes, unmodified except for typos:
QQQ
- c.outerUpdate actually updates everything, using c.requestedRedraw,
c.requestedWidget, c.requestedMinibufferPrompt. c.outerUpdate may be
called
anytime to force an immediate update. The master command handler
calls
outerUpdate, as do all event handlers that make any requests.
- g.newDrawing selects between old and new code. When g.newDrawing is
False,
c.outerUpdate does nothing. When g.newDrawing is True, many of Leo's
drawing
methods simply make requests:
- c.beginUpdate does nothing.
- c.endUpdate sets c.requestRedrawFlag to request a redraw.
- c.xWantsFocus set c.requestedFocusWidget.
- c.k.showStateAndMode sets c.requestedMinibufferPrompt.
- c.selectPosition sets c.requestedPosition.
- c.recolor() sets c.frame.requestRecolorFlag.
This design might substantially simplify some very complex code. My
intention is
to make *no* changes to the code base except for calls to
c.outerUpdate. Thus,
it will be easy to see what has changed. The experiment can be done in
the
key-handling branch.
This is a highly speculative project. Previous attempts have
failed...
QQQ
The actual code is even simpler than this: experimentation showed that
the calls to c.selectPosition and c.k.showStateAndMode can be done
immediately: there is no need to queue them.
I also realized after writing these notes that calling c.outerUpdate
multiple times in wrappers does not matter. The call in the innermost
wrapper will actually do something--the other calls will do nothing,
and do it very quickly.
EKR
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"leo-editor" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/leo-editor?hl=en
-~----------~----~----~----~------~----~------~--~---