pcb-rnd knowledge pool


Run-time menu patching system

menu_patch_new by Tibor 'Igor2' Palinkas on 2020-07-18

Tags: announcement, menu, patching, patch

node source



Abstract: pcb-rnd offers a more flexible menu description from version 2.2.4, based on run-time menu patching. This article describes the basic mechanism implemented.

  Imported from the mailing list archives.

The problems this system is trying to solve are described in this menu patching announcement.

There is a howto about how to use the system for maintaining your own menu modifications.

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 conclusion is this system:

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 extremely 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 menus!

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 you start 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:

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. 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.