Browse Source

Merge pull request #3306 from aaronfranke/main-screen

Simplify the "Making main screen plugins" article
Aaron Franke 5 years ago
parent
commit
285db9caf1
1 changed files with 74 additions and 186 deletions
  1. 74 186
      tutorials/plugins/editor/making_main_screen_plugins.rst

+ 74 - 186
tutorials/plugins/editor/making_main_screen_plugins.rst

@@ -7,43 +7,24 @@ What this tutorial covers
 -------------------------
 -------------------------
 
 
 As seen in the :ref:`doc_making_plugins` page, making a basic plugin that
 As seen in the :ref:`doc_making_plugins` page, making a basic plugin that
-extends the editor is fairly easy. This plugin mechanism also allows you to
-create new UIs in the central part of the editor, similarly to the basic 2D, 3D,
-Script and AssetLib views. Such editor plugins are referred as "Main screen
-plugins".
+extends the editor is fairly easy. Main screen plugins allow you to create
+new UIs in the central part of the editor, which appear next to the
+"2D", "3D", "Script", and "AssetLib" buttons. Such editor plugins are
+referred as "Main screen plugins".
 
 
-This tutorial leads you through the creation of a basic main screen plugin. With 
-this plugin example, we want to demonstrate:
-
-- Creating a main screen plugin
-- Linking the main screen to another plugin GUI element (such as a Tab panel,
-  similar to the Inspector tab)
-
-For the sake of simplicity, the two GUI elements of our main screen plugin will
-both consist in a Label and a Button. Pressing one element's button will display
-some text on the other's label node.
+This tutorial leads you through the creation of a basic main screen plugin.
+For the sake of simplicity, our main screen plugin will contain a single
+button that prints text to the console.
 
 
 Initializing the plugin
 Initializing the plugin
 -----------------------
 -----------------------
 
 
-The plugin itself is a Godot project. It is best to set its contents in an
-``addons/my_plugin_name/`` structure. The only files that lie in the root folder
-are the project.godot file, and the project icon.
-
-In the ``addons/my_plugin_name/`` folder, we create the ``plugin.cfg`` file as
-described in the :ref:`doc_making_plugins` page.
-
-::
-
-    [plugin]
-    name="Main screen plugin demo"
-    description="A plugin that adds a main screen panel and a side-panel which communicate with each other."
-    author="Your Name Here"
-    version="1.0.0"
-    script="main_screen_plugin.gd"
+First create a new plugin from the Plugins menu. For this tutorial, we'll put
+it in a folder called ``main_screen``, but you can use any name you'd like.
 
 
-We also initialize the file targeted by the ``script=`` property of the ``.cfg``
-file. In our example, ``main_screen_plugin.gd``.
+The plugin script will come with ``_enter_tree()`` and ``_exit_tree()``
+methods, but for a main screen plugin we need to add a few extra methods.
+Add five extra methods such that the script looks like this:
 
 
 ::
 ::
 
 
@@ -73,158 +54,66 @@ file. In our example, ``main_screen_plugin.gd``.
     func get_plugin_icon():
     func get_plugin_icon():
        return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons")
        return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons")
 
 
-The important part in this script is the ``has_main_screen()`` function, which is
-overloaded so it returns ``true``. This function is automatically called by the
-editor on plugin activation, to tell it that this plugin adds a new center view to
-the editor. For now, we'll leave this script as-is and we'll come back to it
-later.
-
-Scenes
-------
-
-The ``main_screen_plugin.gd`` file will be responsible for each of our plugin's
-UI element instantiation, and it will also manage the communication between them.
-
-As a matter of fact, we wish to design each UI element in their own scene.
-Different scenes are not aware of each other unless they are both children of a
-parent scene, yet they will then require ``get_node("../sibling")`` accessors.
-Such practice is more likely to produce errors at runtime, especially if these
-scenes do not share the same parent node. This is why, they should only be
-allowed to access their children.
-
-So, in order to communicate information to another scene, the best design is to
-define signals. If a user action in a UI scene #1 has to trigger something in
-another UI scene #2, then this user action has to emit a signal from scene #1,
-and scene #2 will be connected to that signal. Since all of our UI scenes will
-be instanced by ``main_screen_plugin.gd`` script, this one script will also
-connect each of them to the required signals.
-
-.. note:: If the ``main_screen_plugin.gd`` instantiates the UI scenes, won't
-          they be sibling nodes then?
-
-Not necessarily: this script may add all UI scenes as children of the same node
-of the editor's scene tree - but maybe it won't. And the ``main_screen_plugin.gd``
-script will *not* be the parent node of any instantiated scene because it is a
-script, not a node! This script will only hold references to instantiated
-scenes.
+The important part in this script is the ``has_main_screen()`` function,
+which is overloaded so it returns ``true``. This function is automatically
+called by the editor on plugin activation, to tell it that this plugin
+adds a new center view to the editor. For now, we'll leave this script
+as-is and we'll come back to it later.
 
 
 Main screen scene
 Main screen scene
 -----------------
 -----------------
 
 
-Create a new scene with a ``Panel`` root node. Select this root node,
-and in the viewport, click the ``Layout`` menu and select ``Full Rect``.
-You also need to enable the ``Expand`` vertical size flag in the inspector.
-The panel now uses all the space available in the viewport.
-Now, let's add a new script on the root node. Name it ``main_panel.gd``.
-
-We then add 2 children to this Panel node: first a ``Button`` node. Place it
-anywhere on the Panel. Then add a ``Label`` node.
-
-Now we need to define a behaviour when this button is pressed. This is covered
-by the :ref:`Handling a signal <doc_scripting_handling_a_signal>` page, so this
-part will not be described in details in this tutorial.
-Select the Button node and click the ``Node`` side dock.
-Select the ``pressed()`` signal and click the ``Connect`` button (you can also
-double-click the ``pressed()`` signal instead). In the window that opened,
-select the Panel node (we will centralize all behaviors in its attached
-script). Keep the default function name, make sure that the ``Make function``
-toggle is ON and hit ``Connect``. This creates an ``_on_Button_pressed()``
-function in the ``main_panel.gd`` script, that will be called every time the
-button is pressed.
-
-As the button gets pressed, we want the side-panel's ``Label`` node to show a
-specific text. As explained above, we cannot directly access the target scene,
-so we'll emit a signal instead. The ``main_screen_plugin.gd`` script will then
-connect this signal to the target scene. Let's continue in the ``main_panel.gd``
-script:
-
-::
-
-    tool
-    extends Panel
-
-    signal main_button_pressed(value)
-
-    func _on_Button_pressed():
-       emit_signal("main_button_pressed", "Hello from main screen!")
-
-In the same way, this main scene's Label node has to show a value when it
-receives a specific signal. Let's create a new
-``_on_side_button_pressed(text_to_show)`` function for this purpose:
-
-::
-
-    func _on_side_button_pressed(text_to_show):
-       $Label.text = text_to_show
-
-We are done for the main screen panel. Save the scene as ``main_panel.tscn``.
+Create a new scene with a root node derived from ``Control`` (for this
+example plugin, we'll make the root node a ``CenterContainer``).
+Select this root node, and in the viewport, click the ``Layout`` menu
+and select ``Full Rect``. You also need to enable the ``Expand``
+vertical size flag in the inspector.
+The panel now uses all the space available in the main viewport.
 
 
-Tabbed panel scene
-------------------
-
-The tabbed panel scene is almost identical to the main panel scene. You can
-either duplicate the ``main_panel.tscn`` file and name the new file
-``side_panel.tscn``, or re-create it from a new scene by following the previous
-section again. However, you will have to create a new script and attach it to
-the Panel root node. Save it as ``side_panel.gd``. Its content is slightly
-different, as the signal emitted and the target function have different names.
-Here is the script's full content:
+Next, let's add a button to our example main screen plugin.
+Add a ``Button`` node, and set the text to "Print Hello" or similar.
+Add a script to the button like this:
 
 
 ::
 ::
 
 
     tool
     tool
-    extends Panel
-
-    signal side_button_pressed(value)
+    extends Button
 
 
-    func _on_Button_pressed():
-       emit_signal("side_button_pressed", "Hello from side panel!")
+    func _on_PrintHello_pressed():
+       print("Hello from the main screen plugin!")
 
 
+Then connect the "pressed" signal to itself. If you need help with signals,
+see the :ref:`Signals <doc_getting_started_step_by_step_signals>` article.
 
 
-    func _on_main_button_pressed(text_to_show):
-       $Label.text = text_to_show
+We are done with the main screen panel. Save the scene as ``main_panel.tscn``.
 
 
-Connecting the two scenes in the plugin script
-----------------------------------------------
+Update the plugin script
+------------------------
 
 
-We now need to update the ``main_screen_plugin.gd`` script so the plugin
-instances our 2 GUI scenes and places them at the right places in the editor.
-Here is the full ``main.gd``:
+We need to update the ``main_screen_plugin.gd`` script so the plugin
+instances our main panel scene and places it where it needs to be.
+Here is the full plugin script:
 
 
 ::
 ::
 
 
     tool
     tool
     extends EditorPlugin
     extends EditorPlugin
 
 
-    const MainPanel = preload("res://addons/my_plugin_name/main_panel.tscn")
-    const SidePanel = preload("res://addons/my_plugin_name/side_panel.tscn")
+    const MainPanel = preload("res://addons/main_screen/main_panel.tscn")
 
 
     var main_panel_instance
     var main_panel_instance
-    var side_panel_instance
 
 
     func _enter_tree():
     func _enter_tree():
        main_panel_instance = MainPanel.instance()
        main_panel_instance = MainPanel.instance()
-       side_panel_instance = SidePanel.instance()
-
        # Add the main panel to the editor's main viewport.
        # Add the main panel to the editor's main viewport.
        get_editor_interface().get_editor_viewport().add_child(main_panel_instance)
        get_editor_interface().get_editor_viewport().add_child(main_panel_instance)
-
-       # Add the side panel to the Upper Left (UL) dock slot of the left part of the editor.
-       # The editor has 4 dock slots (UL, UR, BL, BR) on each side (left/right) of the main screen.
-       add_control_to_dock(DOCK_SLOT_LEFT_UL, side_panel_instance)
-
-       # Hide the main panel
+       # Hide the main panel. Very much required.
        make_visible(false)
        make_visible(false)
 
 
 
 
     func _exit_tree():
     func _exit_tree():
-       main_panel_instance.queue_free()
-       side_panel_instance.queue_free()
-
-
-    func _ready():
-       main_panel_instance.connect("main_button_pressed", side_panel_instance, "_on_main_button_pressed")
-       side_panel_instance.connect("side_button_pressed", main_panel_instance, "_on_side_button_pressed")
+       if main_panel_instance:
+          main_panel_instance.queue_free()
 
 
 
 
     func has_main_screen():
     func has_main_screen():
@@ -232,10 +121,8 @@ Here is the full ``main.gd``:
 
 
 
 
     func make_visible(visible):
     func make_visible(visible):
-       if visible:
-          main_panel_instance.show()
-       else:
-          main_panel_instance.hide()
+       if main_panel_instance:
+          main_panel_instance.visible = visible
 
 
 
 
     func get_plugin_name():
     func get_plugin_name():
@@ -246,41 +133,42 @@ Here is the full ``main.gd``:
        # Must return some kind of Texture for the icon.
        # Must return some kind of Texture for the icon.
        return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons")
        return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons")
 
 
-A couple of specific lines were added. First, we defined the constants that
-contain our 2 GUI packed scenes (``MainPanel`` and ``SidePanel``). We will use
-these resources to instance both scenes.
-
-The ``_enter_tree()`` function is called before ``_ready()``. This is where we
-actually instance the 2 GUI scenes, and add them as children of specific parts
-of the editor. The side panel case is similar to the example shown in
-:ref:`doc_making_plugins` page: we add the scene in an editor dock. We specified
-it will be placed in the left-side dock, upper-left tab.
-
-``EditorPlugin`` class does not provide any function to add an element in the
-main viewport. We thus have to use the
-``get_editor_interface().get_editor_viewport()`` to obtain this viewport and add
-our main panel instance as a child to it. We call the ``make_visible(false)``
-function to hide the main panel so it is not directly shown when first
-activating the plugin.
-
-The ``_exit_tree()`` is pretty straightforward. It is automatically called when
-the plugin is deactivated. It is then important to ``queue_free()`` the elements
-previously instanced to preserve memory. If you don't, the elements will
-effectively be invisible in the editor, but they will remain present in the
-memory. Multiple de-activations/re-activations will then increase memory usage
-without any way to free it, which is not good.
-
-Finally the ``make_visible()`` function is overridden to hide or show the main
+A couple of specific lines were added. ``MainPanel`` is a constant that holds
+a reference to the scene, and we instance it into `main_panel_instance`.
+
+The ``_enter_tree()`` function is called before ``_ready()``. This is where
+we instance the main panel scene, and add them as children of specific parts
+of the editor. We use ``get_editor_interface().get_editor_viewport()`` to
+obtain the viewport and add our main panel instance as a child to it.
+We call the ``make_visible(false)`` function to hide the main panel so
+it doesn't compete for space when first activating the plugin.
+
+The ``_exit_tree()`` function is called when the plugin is deactivated.
+If the main screen still exists, we call ``queue_free()`` to free the
+instance and remove it from memory.
+
+The ``make_visible()`` function is overridden to hide or show the main
 panel as needed. This function is automatically called by the editor when the
 panel as needed. This function is automatically called by the editor when the
-user clicks on another main viewport button such as 2D, 3D or Script.
+user clicks on the main viewport buttons at the top of the editor.
+
+The ``get_plugin_name()`` and ``get_plugin_icon()`` functions control
+the displayed name and icon for the plugin's main viewport button.
+
+Another function you can add is the ``handles()`` function, which
+allows you to handle a node type, automatically focusing the main
+screen when the type is selected. This is similar to how clicking
+on a 3D node will automatically switch to the 3D viewport.
 
 
 Try the plugin
 Try the plugin
 --------------
 --------------
 
 
-Activate the plugin in the Project Settings. You'll observe a new button next to
-2D, 3D, Script above the main viewport. You'll also notice a new tab in the left
-dock. Try to click the buttons in both side and main panels: events are emitted
-and caught by the corresponding target scene to change the Label caption inside it.
+Activate the plugin in the Project Settings. You'll observe a new button next
+to 2D, 3D, Script above the main viewport. Clicking it will take you to your
+new main screen plugin, and the button in the middle will print text.
+
+If you would like to try a finished version of this plugin,
+check out the plugin demos here:
+https://github.com/godotengine/godot-demo-projects/tree/master/plugins
 
 
 If you would like to see a more complete example of what main screen plugins
 If you would like to see a more complete example of what main screen plugins
 are capable of, check out the 2.5D demo projects here:
 are capable of, check out the 2.5D demo projects here: