Internally, Leadwerks Editor uses an EventHandler class for every interface in the program. The material editor is a class extended from the EventHandler. So is the little window that has all the controls to calculate normals. So is every viewport.
The event handler class has one important function:
Every EventHandler has access to events as they occur. This is how all program actions are handled in the editor.
The plugin system will work by hooking into the event system. Each plugin will have a Lua script that receive events before the rest of the program sees them:
function Script:ProcessEvent(event) return event end
If the plugin makes no changes to the event then it simply returns the original event. The returned event is then sent to other event handlers.
Here is an example of a plugin that would disable the close window button on the main window. Because the function returns nil the event is discarded before the main window ever evaluates it:
function Script:ProcessEvent(event) if event.id == EVENT_WINDOWCLOSE and event.source == editor.mainwindow then return nil else return event end end
Here is an example of a very mean plugin that would make it so that clicking the File > Open menu item in the main window quits the program:
function Script:ProcessEvent(event) if event.id == EVENT_MENUEVENT then if event.source == editor.mainwindow then if event.extra == MENU_FILEOPEN then event.id = EVENT_WINDOWCLOSE end end end return event end
Okay, now let's see if we can design a plugin for something people would actually want. Let's imagine we have a new visual material design system. The exact details of how it works are not important, it's just a system that overrides the default material editor. The design system would require materials to have a special file associated with them with the extension .DESIGN. If you open the material "brick.mat" we will look for a file in the same folder called "brick.design". If the design file is found we open the material in our special editor. If the design file is missing we will just fall back to the default material editor.
Now let's see how our system can handle this:
function Script:Start() --Create our interface self.window = CreateWindow("Material Designer",0,0,800,600,editor.mainwindow,WINDOW_CENTER + WINDOW_TITLEBAR + WINDOW_RESIZABLE) end function Script:ProcessEvent(event) if event.id == EVENT_FILEOPEN --Check for material files being opened if ExtractExt(event.extra)=="mat" --Look for design file local designfilename = StripExt(event.extra).".design" if FileType( designfilename ) == 1 then --Load the design file local stream = ReadFile(designfilename) if stream ~= nil then --Display our custom material editor self.window:Show() self.window:Activate() else Print("Error: Failed to load design file.") end --Discard the event return nil end end end return event end
As you can see, this approach is extremely powerful. The event IDs and design rarely change, if ever, so this allows a lot of flexibility and at the same time gives us the optimal compatibility as changes are made to the core editor. With this approach to plugins you can literally do anything you want in the editor.