No items found.

When I started writing scripts this was one of the things that got me so confused, and for the right reason. There is not enough information about it on the web, at least not relative to the demand. Most scripts you used before in After-Effects have them, so why does it seem so complicated to achieve?

Let’s break it down to what makes a dockable ScriptUI Panel, well, dockable.

For the final code + TLDR scroll down.

The secret to a Dockable ScriptUI Panel

When I started writing scripts this was one of the things that got me so confused, and for the right reason. There is not enough information about it on the web, at least not relative to the demand. Most scripts you used before in After-Effects have them, so why does it seem so complicated to achieve?

Let’s break it down to what makes a dockable ScriptUI Panel, well, dockable.

For the final code + TLDR scroll down.

Let’s break it down to what makes a dockable ScriptUI Panel, well, dockable.

What’s a Rich Text element?

What’s a Rich Text element?

What’s a Rich Text element?

What’s a Rich Text element?

The rich text element allows you to create and format headings, paragraphs, blockquotes, images, and video all in one place instead of having to add and format them individually. Just double-click and easily create content.

Static and dynamic content editing

A rich text element can be used with static or dynamic content. For static content, just drop it into any page and begin editing. For dynamic content, add a rich text field to any collection and then connect a rich text element to that field in the settings panel. Voila!

How to customize formatting for each rich text

Headings, paragraphs, blockquotes, figures, images, and figure captions can all be styled after a class is added to the rich text element using the "When inside of" nested selector system.

Starting with a Non-Dockable Window

The simplest way to declare a window is the following:



var dialog = new Window("dialog");
dialog.show();


In the first line you create the dialog window, then in the second line you show it. If you run this script an empty dialog would appear. Good!


What kind of sorcery is this? 🧙

Follow the Good Boy Ninja on Twitter for After-Effects tips and tricks
Follow

Making it dockable

Let’s completely ignore the code above for a second.

In order to make a dockable panel, all we need is one simple line:



var dialog = this;


That’s it? Well, yes, theoretically. Here’s a short explanation:

The word “this” refers to the document itself (yes, the document is an object). In fact, the document is a different kind of object based on whether the script is launched from the scriptUI Panels folder or not. We’ll get to why in a moment.
All you need to remember is that when you launch your script from the ScriptUI Panels folder, the document object is the Panel. We refer to the document object by using the word “this”.

Congratulations! You made a dockable panel. Now please let me ruin the party and tell you all the reasons why it’s actually not as simple as one line of code. Bear with me, this is some good stuff!


Panels ≠  Windows

Our instinct when starting to learn about this is that windows and panels are the same, the only difference is that panels can be docked.
Windows and Panels are actually completely different, and this is where things get complex.
Remember in the first example, when we declared a dialog window, we specifically showed it?


dialog.show();


“.show()” is a method that only exists for windows, not panels.

Notice that we don’t need to call “.show()” for the panel?
When creating a dockable panel you don’t need to specifically show it, but when you are creating a window you do (if you want to see it anyways).
In fact, if you try to call “.show()” on a panel your code will break. “.show()” is simply not part of the things panels can do.


More tools for Adobe After-Effects
Wiggle
Colors
Noodle
shpr
Paths from Nulls

You want to work with a window and end up with a Panel

So here’s the thing. When working on a script, you want to be able to quickly launch it from the code editor. Unfortunately, If we declare a panel it would only work when you launch it from the ScriptUI Panels folder. The same script would simply not run from the code editor.

To solve this issue we want the code to basically perform a quick check:
“Is “this” a panel? If it is, use it, otherwise declare a window”.

Here’s how we can do it:


var isPanel = this instanceof Panel;
if (isPanel) {
	var dialog = this;
} else {
	var dialog = new Window("dialog");
  dialog.show();
}

Now let me make this straight, this code is bad and we will get to why in a second.
The first variable is either True or False based on whether the script is launched from the scriptUI Panels Folder or not.
We can then use that variable to create a condition. If it does, declare a panel (using this, as we did earlier). Otherwise, declare a window and show it using “.show()”
This code is easy to read and follow.

We successfully made it possible to launch a window when the script is running from the code editor and launch a dockable panel when we run it from the ScriptsUI Panels folder or not.

But this code needs refactoring. Notice how we are declaring “dialog” twice?
Also, where do you write the rest of your script? Inside the if block? Inside the else block?
This simply is not the way to do it. Instead, we’re going to use a similar logic but using a different structure:


var isPanel = this instanceof Panel; // true or false
var dialog = isPanel  ? this : new Window("dialog");

// rest of script goes here

if (!isPanel) {
	dialog .show();
} 

That is much better. We start with the same “isPanel” variable.
Then we use it in the dialog declaration using a tannery operator which is basically a compact if/else statement.
That way the “dialog” variable will be a panel if the document is a panel and a window if it isn’t. This is a neat way to get the best of both worlds.
This also leaves us space to write the rest of the script. You usually want to create the UI and other script logic stuff before you show the window.
Finally, we are using an if statement to say (“if it’s not a panel, use .show()”).
Remember how we said “.show()” simply does not work for panels? See how we tricked our way around this?

However, we can do better.

The Final Code

While the code above works, I want to also attach what is common to find online about the topic. Usually, people would recommend to write your script like this:


(function(thisObj){
    var isPanel = thisObj instanceof Panel; // true or false
    var dialog = isPanel  ? thisObj : new Window("dialog", "My Script Name");

    // rest of your script goes here

    if (!isPanel) {
      // if it's a window
      dialog.show();
    } else {
      // if it's a panel
      dialog.layout.layout(true);
      dialog.layout.resize();
      }
    
})(this);

Again, don’t panic. This is almost the same as what we wrote before.
The only difference is that the entire thing is written inside a function that launches itself (which has a funny syntax if you are new to JavaScript).

This is a good practice because “this” in javascript can change based on where it is called from. In order to make sure the “this” we want refers to the document, we pass it from outside the function so it’s inside nothing else but the document. We pass it from outside and assign it to a variable called “thisObj” (you can call it anything really, it’s just a variable). Then inside the function, we use “thisObj” to perform all the same checks we performed before.

Also, this code has some additional methods for when the thing is a dockable panel, like “layout” and “resize”. They basically arrange the content of the window when the script is launched.
So this is your final template. You can save this as a snippet so you won’t have to start from scratch.

TLDR

We learned that writing a dockable panel is actually easy, really easy, we’re talking one-line easy. The hard part is actually making it comfortable for us to work on the script from our text editor. We had to create a block of code that can determine whether the script is running from the ScriptUI Panels folder or not, and based on that whole premise decide whether to build a panel or a window.

That’s why I am writing this blog post. When I started scripting years ago this felt like hell. It feels very unintuitive at first and seems like a whole lot of unnecessary preparation for something that feels like it should be as simple as an On/Off toggle.

This whole thing stems from the fact that Panels and Windows are not of the same kind. We had to write code to adjust for both scenarios.

We also learned that “this”, in the global scope, refers to the document. If you are just starting out you have no way to know this and it’s conceptually weird. But it’s good to know that the type of the document changes based on whether the script is launched from the scriptUI Panels folder or not.

We then wrapped the entire thing in a function and passed “this” from outside, simply to make sure it refers to the document and not to anything else. That function invokes itself, therefore the weird syntax you might not be familiar with.

So in short, there’s a lot to wrap your head around here. Please save this snippet, even if you feel like it’s a little too advanced for where you are now. It’s a good starting point for your scripts and I promise you it gets much easier to read and understand the more you practice writing JavaScript / ExtendScript code.

You can do it!

Good Boy Ninja


Good Boy Ninja
Motion Designer since 2008.
Maker of tools for Adobe After-Effects
Quick jump:
August 22, 2021
 
Similar Posts
Congratulations sailor, you made it to March🎉
I'm keeping the 22.22.2022 discounts up for a few more days. Thanks for getting my tools!