Mailing list archives : pcb-rnd

Date:Fri, 17 Jul 2020 16:46:50 +0200 (CEST)
Subject:[pcb-rnd] menu patching - part II
Hi all,
this mail is about the solution that got implemented. If you decided you 
didn't need to read part I, you most probably can safely skip this mail 
This is a feature announcement. And it's a big one for some of our users 
as it finally solves the problem of maintaining local menu changes.
Part II - the new, run-time menu patching system
1. merging
We already have a similar mechanism for the config system where you can 
inject small config files that overwrite, append or prepend to existing 
config nodes. This way you can have a config modifications from the 
command line, from the board file, from the project file, or from an user 
config. Priorities allow you to tell which should override which.
I figured we need something similar for the menu. There are a few subtle 
differences, tho. 
With the conf system it's somewhat easier because the structure, the conf 
tree is decided by the code. Your config files can not create or remove 
nodes of the conf tree, they merely can change the value of existing 
config nodes. With menu files it's much harder because you need to be able 
to modify the tree structure itself, not only values.
This gets even more complicated when you need to undo things. For example 
you created a menu for some reason (e.g. loaded a script) but that reason 
is now gone (e.g. script is unloaded) - and you need to undo all the menu 
modifications the script did. It's not similar to our normal undo system, 
because there the order of do and undo are strictly reversed. But with 
menus, you can load an unload plugins/scripts in whatever order so we 
can't simple sequentially remember what we did and sequentially undo it. 
So my conslusion is this system:
- we keep a list of menu files and menu patches (see below), each having a 
priority; this guarantees we have a simple, data driven way to describe 
modifications to the menu file without having to actually edit the 
upstream menu file
- when this list changes, we merge them in order of priority; the merge 
process is well defined and deterministic; you can take out any file or 
patch from the middle of the list and we still will be able to perform a 
merge and tell what exactly it will end up in; this part guarantees we can 
"undo" things when we unload scripts/plugins - we just need to remove the 
menu file/patch that thing injected
- when the merge is finsished, we have a final menu image - it's basically 
a menu tree in the memory, in the same format as the menu file format; so 
the code just looks a the GUI and compares it with this menu image and 
makes the smallest number of modifications to the GUI to make it match the 
image. Or in short, the GUI menus always reflect the latest merged 
in-memory menu image
2. how this is done in practice
Prio 0 is our original pcb-menu-default.lht menu file. This is the base. 
We start the in-memory merging by copying this file. 
Then plugins, scripts and user actions can create new menus - but they 
never directly manipulate the GUI! Instead they are creating new, 
in-memory menu files/patches with a specific priority, then the whole 
thing is merged again. This way it's very easy to tell who contributed to 
the final result in what way, and extermely easy to undo the contribution. 
We even have a gui for this now, in the preferences window!
And of course, user menus! The user can maintain a set of menu 
files/patches that need to contain only his modifications, not the whole 
1000-line-long stock menu file. These menu files/patches are listed by 
file path in the new list typed conf node rc/menu_patches. Menu 
patches can have explicit priority specified in the file so the user even 
has control over the order of merging!
Bonus: since the extra menu file/patch paths are stored in a conf node, 
you have all the usual trickery to maintain it: you can get your board 
file to append an item, or you can have a project specific menu file added 
from the project file. Yes, that means project or even board level custom 
But of course the most common thing will be that the user config in 
~/.pcb-rnd/ will add a menu file/patch you always want to have when 
youstart pcb-rnd.
3. menu file vs. menu patch
For backward compatibility and because it was already a good format, we 
kept the original menu file format. The only little change is that we now 
have an explicit name for the root node: rnd-menu-v1. This is called the 
menu file format.
A menu file format is good for defining how exactly a subtree of the menu 
looks. Thus it is very limited in what kind of modifications it can 
do; it can append new submenu (trees) or overwrite submenu (trees). That 
means it's a convenient way to:
- describe the base menu layout (prio 0)
- describe a new menu or menu subtree (typical from a script, from a 
plugin but if you have a favorite sequence of actions you often use you 
can easily make it a menu this way)
- fully replace a submenu or submenu tree with a new one
However, you can not use a menu file to make a small modification on an 
existing menu (e.g. change the hotkey) without having to fully redefine 
that menu (re-creating the upstream collision problem in small!). And you 
have very limited control over where exactly your menu item ends up within 
the parent submenu.
On the other hand, a menu patch is an ordered collection of instructions 
on how pcb-rnd should modify the menu image. It has instructions for 
removing menus, inserting menus or making small modifications to existing 
menus. It's especially easy to modify the properties, e.g. hotkey, of an 
existing menu. Here's a small menu patch (full file) that does 
exactly that:
ha:rnd-menu-patch-v1 {
 li:patch {
  ha:overwrite_menu_props {
   path = /main_menu/File/Reports/Report found padstacks
   ha:props { a={<key>i;ctrl<key>f}; }
When loaded, this will go and find the menu at the specified path and 
overwrite it's property called "a" with the new value. The syntax within 
props is the same as the syntax within the menu file. 
You can have any amount of instructions in a menu patch file.
4. further documentation
The coming days I am going to write more docs and update the custom menu 
maintenance pool node (with more examples)
5. limitations and plans
Our menus are identified by name (and menu paths which are made up of menu 
names). This is how it all stays simple, in menu files, in menu patches 
and in actions. However, this has a few consequences, including not being 
able to rename existing menu items. I might find a way to do this safely, 
but for now the name of a menu is the name it was created on, and it 
remains that until you remove it.
The scope of the current effort, supported by NGI0, is to make the menu 
system more dynamic. That is you can apply changes to the /main_menu tree
and the /plugins tree.
You can not yet use this system to edit mouse bindings (the /mouse tree) 
or implement new scripts in the /scripts tree. These are planned, some 
time in the future these will be possible too.
Thanks again to nlnet for supporting this long planned feature!
Best regards,

Reply subtree:
4259 [pcb-rnd] menu patching - part II from