solemnwarning / rehex

Reverse Engineers' Hex Editor
https://rehex.solemnwarning.net/
GNU General Public License v2.0
2.31k stars 116 forks source link

Menu extension API #95

Closed solemnwarning closed 3 years ago

solemnwarning commented 4 years ago

Plugins should have a way to extend the menus created by MainWindow and Tab, specifically:

Existing menu items should be grouped or given well-known IDs so plugins can add items in appropriate places (e.g. a plugin adding an extra "Search" command).

Should plugins be able to replace menu items? Not sure if there are any legitimate use cases for this, and it adds complexity with item validity/lifespan.

How about plugins adding items to the same submenus within a menu? Can't think of any use cases for this either.

Part of the work for #4.

solemnwarning commented 4 years ago

Have added a mechanism to call hook functions during MainWindow's constructor to allow injecting menus and menu entries on the menu-plugins branch.

Example use:

static REHex::MainWindow::SetupHookRegistration hello(
    REHex::MainWindow::SetupPhase::HELP_MENU_TOP,
    [](REHex::MainWindow *window)
    {
        wxMenuItem *itm = window->get_help_menu()->Append(wxID_ANY, "Hello world");

        window->Bind(wxEVT_MENU, [window](wxCommandEvent &event)
        {
            wxMessageBox("Hello world", "Hello", wxOK | wxCENTRE, window);
        }, itm->GetId(), itm->GetId());
    });

I was originally planning on having plugins define the menus/items as data, and then MainWindow's constructor handling creating the items and setting up event bindings, but handling all variations of submenus/etc made the data structure too unwieldy to (easily) construct at compile time and it'd probably wind up being a horrible mess of macros, so went for the cruder approach with the setup hooks.

learn-more commented 4 years ago

This is what I did for plugins:

There is a reserved range for plugin menu entries: https://github.com/learn-more/rehex/commit/e781e4da24e3f2088027aab510bfc315cbc9ae14#diff-c72b037f3ac7abbd3d0ee519b6f456013a9bbf5a59df16308939320c04970447R154

Have a 'Plugins' submenu that is only added to the menu bar if there are actual plugins: https://github.com/learn-more/rehex/commit/e781e4da24e3f2088027aab510bfc315cbc9ae14#diff-c72b037f3ac7abbd3d0ee519b6f456013a9bbf5a59df16308939320c04970447R294-R312

Each .lua file will add one menu entry: https://github.com/learn-more/rehex/commit/e781e4da24e3f2088027aab510bfc315cbc9ae14#diff-7d3dffcabf3dfcf4305ca1d1711e46933c0d8b91d32d49bd810e8cc6509d090fR27-R48

The list of lua files (id + index) is kept in a vector, and when a menu item is clicked, the id is translated back to a name: https://github.com/learn-more/rehex/commit/e781e4da24e3f2088027aab510bfc315cbc9ae14#diff-c72b037f3ac7abbd3d0ee519b6f456013a9bbf5a59df16308939320c04970447R911-R924 and https://github.com/learn-more/rehex/commit/e781e4da24e3f2088027aab510bfc315cbc9ae14#diff-7d3dffcabf3dfcf4305ca1d1711e46933c0d8b91d32d49bd810e8cc6509d090fR55-R72

solemnwarning commented 4 years ago

Similar in scope to the "Hello" example then :)

Ideally I want to expose most of rehex's public APIs to the Lua scripts, as well as the wxWidgets API, so the Lua plugins could hypothetically implement custom data types/regions and whatever other extensions get added in the future.

learn-more commented 4 years ago

Similar in scope to the "Hello" example then :)

Ideally I want to expose most of rehex's public APIs to the Lua scripts, as well as the wxWidgets API, so the Lua plugins could hypothetically implement custom data types/regions and whatever other extensions get added in the future.

more or less, but leaving the actual binding / dispatching a responsibility of the mainwindow

solemnwarning commented 3 years ago

Merged to master in 700deca2a364c3725aa8f6219f67db5d0b786e51.

In addition to the above, Lua plugins have a simplified mechanism for adding commands to the "Tools" menu:

rehex.AddToToolsMenu("Analyse PE EXE/DLL", function(mainwindow)
    local doc = mainwindow:active_document()
    process_document(doc)
end);