This Engineering Notebook discusses a script-based approach to changing and 
restoring the arrangement of Leo's panes, including the body editor, the 
nav pane, the Viewrendered and Viewrendered3 plugins' panes, and 
potentially others.  In a recent discussion thread here in Leo's Google 
Groups site (About layouts and their settings 
<https://groups.google.com/g/leo-editor/c/R4TeA5qqr8Y>), Edward asked if I 
thought I could extend my experimental layout scripts in a way that we 
could avoid having to reload an outline or restart Leo just to change 
layouts.

I've been able to do this. In this ENB I want to explain how the system 
works and the key idea that makes it feasible to restore a default layout.

It's not hard to write a script to change to a different layout.  I've got 
several of I've written, and Jacob recently posted some examples of his 
own. These scripts can be easily put into @command nodes and into custom 
menu items.  The trick is to undo them and restore a default layout.  The 
hard part is dealing with user widgets, like the VR pane, or additional 
splitters or other Qt elements that some layout script might install. It 
can be hard to identify them and to know how to remove them.

In the case of user widgets, there is no general way to destroy them, and 
if it's not done right, Leo's event system will stay hooked up to the 
remains of a dead widget. This could spell trouble. If the widgets are 
stored somewhere so they don't get garbage-collected then they have to be 
found the next time the user wants to see them again. If they are stored 
outside of Leo's GUI structures, then they won't be found by the new method 
g.app.gui.find_widget_by_name. There is no other consistent way to find 
them, either. It's these problems that I have solved.

The solution is to add a new, hidden QWidget to the main splitter. It acts 
as a place to store hidden and unused widgets. This cache widget will never 
become visible, and it will never get lost because there must always be a 
main splitter. A widget can be removed from its location simply by changing 
its parent to the cache widget.  Qt will handle all the bookkeeping when we 
move a widget into and out of the cache so we don't have to write *any* 
caching code ourselves.  The widgets will remain hidden but whole and 
active, ready to be relocated from the cache to a new layout at any time.

With this idea, it becomes feasible to find and cache all user objects (I 
mean ones that are not PyQt widgets) whether we know anything about them or 
not.  They just have to have an objectName, so if they have been cached 
they can be found and put into a layout.

To make this whole system work, there needs to be code to create the cache 
widget and initialize some data structures, which are stored in 
*c.user_dict*. This code must be called by the command that restores the 
default layout.  Where should this command be?

It could be added to Leo core code.  But it doesn't have to be.  It can be 
put into LeoSettings.leo or myLeoSettings.leo, or for testing purposes, 
into the settings tree of an outline.

During the implementation of this plan, I did all the work right in my 
workbook.leo outline. Now I've moved it over to myLeoSettings.leo.  I think 
that at some point, LeoSettings.leo would be a good home for the key code..

The command to restore the default layout runs the initialization command 
using c.doCommandByName(), and if the initialization has already happened 
then it will be skipped.

My prototype commands are working well for me.  I'm sure some edge cases 
and bugs will surface as others try them out. In a following post I will 
include the scripts. There is the matter of how Leo can first apply a 
default layout when an outline is opened. The simplest way is to stick with 
what Leo already does in the current devel branch.  However, my command to 
restore the "default layout" might restore to a different one than the Leo 
setting.  We can work that out.

Another way would be to have Leo execute my restore command after the 
outline's key widgets have been created. There could be different versions 
of my initialization script for different default layouts.

A third way would be to use parametrized layout values. The initialization 
code is structured to make that feasible, I think. The restore code already 
uses structure parameters it gets from the initialization code and I don't 
think it would need to be changed.

This post has gotten long but I want to say something about how I developed 
the restore code and how Leo made it readable and quick to do.  I first 
worked out the algorithm in pseudo-code.  Then I used named sections for 
the main steps so that the top-level node reads almost exactly like the top 
level of the algorithm's pseudo-code. Each named section is short and so 
easy to write and debug.

What, you didn't know you could use named sections in a minibuffer command? 
Sure you can, and @others too. These commands are complete Python programs, 
and there is nothing that says they have to be short or confined to a 
single node. Here's the top-level restore node:

c.doCommandByName('layout-initialize')

def find_widget(name):
    return g.app.gui.find_widget_by_name(c, name)

ms = find_widget('main_splitter')
ss = find_widget('secondary_splitter')

initialized = True
<< initialize data structures >>

if initialized:
    << rehome body editor >>
    << cache known added widgets >>
    << set default orientations >>
    << handle bodyTabWidget >>
    << rehome essential widgets >>
    << clean up splitters >>
    << resize splitters >>
    editor.show()

Summary:
1. A script-based approach to applying and restoring layouts is feasible 
and practical.
2. The key new idea is to use a long-lived QWidget as a cache to hold 
widgets that get removed from a layout.
3. The prototype implementation is working well.
4. The approach is compatible with the layout methods currently used in the 
devel branch in the sense that it can undo the those layout changes and 
return to a clean default state.
5. There are several potential ways to integrate these scripts with Leo 
core code.

-- 
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 leo-editor+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/leo-editor/4b3d9b7e-92f0-49cd-b272-703a75c58222n%40googlegroups.com.

Reply via email to