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.