Search code examples
godotgdscript

Godot: Display EditorInspector on another panel with plugins?


I'm attempting to make a plugin to make loading a dictionary or resources easier. There are many aspects I'm not even sure will work, and I can't seem to find an answer to elsewhere, so I'll ask here.

  1. Is it possible to, when clicking on a custom resource/custom property, to open a specific tab on the bottom panel (where output, debugger, etc. are)? In this case, my custom Dictionary Loader tab.

  2. Since EditorInspector inherits from ScrollContainer, is it possible to make my own node of it, and place it within a custom ui? I understand I can't access it from the editor, but how about gdscript? I tried EditorInspector.new() and adding it as a child, but it doesn't display in the scene hierarchy or visually so I can't tell if it works. Can I/should I use EditorInspectorPlugin?

  3. If 2 is possible, can I also assign it the node/resource I want to see the properties of? Nothing in the class's documentation shows how it populates its ui with the EditorProperties.

My custom editor panel The 2nd column is an itemlist of dictionary keys. Clicking one would bring up an EditorInspector equivalent in the 3rd column area for its resource value. Before I found the EditorInspector class in the documentation, I was considering using _get_property_list() and populating the 3rd column with custom ui for each property. But if a class already exists to do that, why can't I use a copy of it? I just don't know how to set it up, if possible.


Solution

  • You can create a inspector plugin that adds a control that will show a panel...


    Start by creating an plugin from the editor (Project -> Project settings -> Plugins -> Create -> Fill the form and click Create). It will generate the plugin in the addons folder of your project (there should be a script that extends EditorPlugin).


    Now add a script that extends EditorInspectorPlugin. From your plugin script you can load it, instance it, and add it with add_inspector_plugin and remove it with remove_inspector_plugin. You want to add it on _enter_tree and remove it on _exit_tree.


    In the script that extends EditorInspectorPlugin, override the can_handle function. Godot will call it when an object is shown in the inspector, and your script must return true or false depending if it can handle said object. For example, I want to handle Camera, I would write this:

    func can_handle(object:Object) -> bool:
        return object is Camera
    

    You probably want to simply return true since your inspector plugin is not based on the type of object, but the properties it has.


    Then there are some other methods you can override. In particular, I believe you want parse_property, which looks like this:

    func parse_property(object: Object, type: int, path: String, hint: int, hint_text: String, usage: int) -> bool:
        return false
    

    The code you want to add there must do these things:

    • Check if it is a property you can handle.

      Since you care about the type, you want to read it, which you can do calling get on the object and passing the path. And then check if what you got is a Dictionary:

      if object.get(path) is Dictionary:
          pass
      
    • If it is a property you can handle, add a Control using add_custom_control.

      You can create a Control scene, instance it, and pass it to add_custom_control.

    • Return true if you did all that. Otherwise return false.


    So, you probably will add a button that must open a panel. So you need a panel... We will figure out how to connect them soon…

    Presumably you already have the scene you want to show in the panel. You can add it from your plugin script with add_control_to_bottom_panel, and remove it with remove_control_from_bottom_panel.

    You also want to be able to call a method on that scene that takes either a reference to the object being edited and the property path, or a reference to the dictionary itself directly. Whichever is more convenient for you. You will also need to be able to clear said reference.

    On a similar note, you need to handle visibility of said panel. So it hides when there is nothing to edit.


    Alright, on the script that extends EditorInspectorPlugin you can add a signal to notify about what to show on the panel. And on the plugin script you can handle it, and have it call the method on the panel scene.

    Furthermore, you can override parse_begin... It is intended to add controls at the start of the inspector, but you can use it to emit the signal to notify to show nothing on the panel because the user is now inspecting a different object.

    So when the user clicks the button (or interacts with whatever control) you added to the inspector, you want to emit the same signal too. So you connect a signal from that button (or whatever control) to a method that emits your signal.

    And so when the user interacts, it emits a signals that the script that extends EditorInspectorPlugin handles by emitting a signal that the plugin script handles by telling the panel what to show.

    Yes, you can cut some steps here: have the plugin script give a reference to the panel to the script that extends EditorInspectorPlugin so it can call it directly. I'll leave it to you if you want to couple them like that.

    You may also use a parse_begin and parse_end, plus keeping track of how many controls you added in parse_property (in parse_begin you reset the count, in parse_property you increment the count, in parse_end you check). To find out if an object has no properties you handle, or just for debugging.