All artboards property on root node

Property that contains an array of all artboards excluding pasteboard items.

The root.children object includes pasteboard items. This property would exclude the paste board items.

Use Case
Hank is working on a plugin that when he presses a button adds his companies logo to each artboard. He’s encountered a few times that the logo is added to pasteboard items. He uses the new root.artboards array to skip pasteboard items.

It’s so easy to check that a given child is an artboard that this doesn’t really seem necessary.

1 Like

@cpryland It is easy :wink: But it’s not about easy in this case. It’s about having a well rounded API and avoiding bugs.

I submit to you all of the Array functions that weren’t created because they were easy to work around but always stumble up newbies.

“Arrays are interesting. How do you check if something is in an array?”
“Get the position of the array and then check if it’s the not found value of -1.”
“Uh what?”
"Use array.indexOf(value)!=-1"
“Wouldn’t it be easier to have an array.contains(value) method?”

Also, if you work with the HTML Dom or E4X you can remember the frustration on having one method that had to do the work of 2 or 3 methods.

"How do you insert an element before another element?"
"Use element.insertBefore()"
“How do you insert an element after another element?”
“Also use insertBefore(). …Get the parent of the element and insert the new node after the element’s next sibling.”
“Um. But wha… how?”
"Use element.parentNode.insertBefore(newElement, element.nextSibling)"
“Couldn’t you… couldn’t you just create an insertAfter()?”

Yes, but at what point is the new feature just clutter?

E.g., do we need rootNode.pasteboardChildren as well?

It’s always a balancing act.

1 Like

In my opinion there has never been a case of too much API. Same as there has never been a case of too much code comments.

Maybe in the past it was an issue but with code intelligence and code completion the API is presented as you go.

And since we come from different backgrounds and skill levels it may be easy to an experienced developer but not for someone new.

It matters who the target audience is too. In this case some are designers coming to the plugin platform to add their own extensions. They may or may not have a CS background.

JavaScript did get an Array.includes() method about 20 years(?) later.

@Velara

You know that I won’t stay out of the discussion when you write something like this, right? :wink:

First of all, by extension of your argument (to keep everything consistent and therefore relatively easy to use, no matter the experience of the user), we’d also need GroupNode.textNodes, GroupNode.ellipseNodes etc. Where do we draw the line?

Furthermore, regarding your statement about there being no option of having “too much API”: Take a look at Java or PHP. Both of them have (due to their “nature”, Java with its “Standard Library” and PHP, well, because it has :slightly_smiling_face: ) tons of APIs. Don’t get me wrong here: I’ve worked (and am working) with both languages in my job and/or my studies and appreciate many of these things. The one target audience who least appreciates this, however, are especially newer users not having much experience with programming (and therefore navigating and understanding technical documentation), to whom the fact that there are basically 100 ways of doing something (e.g., in PHP, should you, in the days when PHP 5 was the latest, use mysql_connect(), mysqli_connect(), new mysqli, the PDO APIs or something different). The answer is: There was (and, aside from mysql being deprecated now, still is) no correct way for the PHP example. This means that if someone asks what they should use, they won’t get a real answer, and eventually get frustrated. On the other hand, doing something like filtering a list is “easy enough” and should be doable by the user, even if they’re less experienced (especially then, learning this is incredibly valuable as it teaches dealing with tree-like structures etc.). After all, there’s even a section in the guides for that: https://adobexdplatform.com/plugin-docs/tutorials/how-to-work-with-scenenodelist/#4-create-the-handler-function-for-filterandcolor.

I have to agree with @cpryland here. Things like that would clutter up the APIs, having children in many “places” feels counter-intuitive and does, in my opinion, not make it easier to deal with (by, in my opinion, even obfuscating the APIs. A node having children makes sense. Having the same child in children, artboardChildren, and by extension of that for GroupNodes in children, graphicNodes and reactangleNodes does not make the APIs clearer).

Last but not least, forgive me for getting a bit technical here, but this also obfuscates performance. “Getting” a member always feels like an operation running in O(1). Instead, it will still have to filter nodes and therefore run in O(n), which becomes “invisible” without reading the documentation for every getter. When you loop and filter the nodes manually, you “get” that it is a loop and performance will be O(n) for it (and it’s easy to see that running 7 nested loops of that is probably not such a good idea). However, by obfuscating this performance, creating plugins that are performing well (which, according to https://adobexdplatform.com/plugin-docs/devbestpractices/1-performance.html?h=performance, is basically a must) will get much harder, especially for people without a CS background :wink:

All in all, while I get where you’re coming with this request, I believe this would do more bad than good, obfuscating without any real benefit. I feel like it’s a great thing that the APIs as straight-forward as they are (especially for ease of use), and believe adding such things for a bit of comfort for people familiar with all these APIs (you’d have to know a lot of functions, if you wanted to use such functions if they were to be included in the APIs, there are a lot of things that could be easier for developers :wink:) wouldn’t do the APIs any good, especially considering newer users with perhaps less of a technical background.

If you didn’t I would be worried. :grin:

I would say that if it performs a different function it has a place. And if you use it more than once it’s a candidate to become a function or method.

Take the HTMLElement.childNodes and HTMLElement.children properties:

Node.childNodes includes all child nodes, including non-element nodes like text and comment nodes. To get a collection of only elements, use ParentNode.children instead. - MDN

Sure you can easily iterate through and filter out text nodes and comments. But if you are doing that throughout a project or across multiple projects and project types then maybe it’s an API candidate.

As far as cluttering it up… I would say keep adding APIs until someone says stop.

Code completion will filter first by type and then by member and then by letter.

I ran tests a while ago using reflection to generate code completion for an editor it was negligible. This was on an 2011 laptop. IIRC it was a fraction of a millisecond or less. But if you design it right it should be a fraction of the CPU budget.

But one other reason I forgot to mention is if the method or property is in the official API it should have better performance if it’s native. If the API is compiled down then it’s no competition to JIT.

As far as PHP goes, I’ll give you that. That’s one reason why PHP class wrapper sites were so popular. “Need an database class? Here’s 100 of them.”

1 Like

Agreed, but that doesn’t mean it has to be a part of the core APIs. What I’m willing to “consider to accept” would be something like SceneNodeList::filterByType(types[]): SceneNodeList. This would make sense as it is a method filtering it (instead of a “magical” second member that just includes some of the children), provides, as you say, a common function, and wouldn’t have the problem of changing the philosophy of the APIs, meaning my “by extension we’d also have to do xy” comments would become obselete. This would then also not hide the performance, as it would be a real “filter” function where people familiar enough with performance know that it has to run with a runtime complexity of at least O(n).

Which, apperently, some already do with this request :wink:

The thing isn’t performance with completion or things like that, but about ease of use and a not too steep learning curve. If we consider designers not too familiar with programming developing their own, small plugins to help with a workflow, they’ll have enough to learn with how module.exports and other of the JS concepts work (async etc.). If we integrate thousands of helpful functions into the core APIs, learning about all of those is just another, bigger step of getting ready to create a plugin, meaning it achieves the opposite.

As with another longer discussion about a similar thing we had, it’s not this specific request I have a problem with. It is the fact that it “enables” a philosophy where APIs have to get added (to keep it consistent and therefore somewhat usable) not because they are necessary, but because they are more comfortable, which in the long run adds incredible complexity and an inability to keep APIs more or less up-to-date with the quick release cycle of XD (which, in my opinion, should be a bigger priority)…

All the more, I think this could be a point where frameworks for XD plugins (with integrated functionality like that) could be a good thing. Since the level of abstraction is an extremely opinionated question (as is obvious by the length of our comments), “deciding” about it in the core APIs isn’t (in my opinion) a good idea. Instead, there could be a framework that has this philosophy (lots of helper functions), and another that is easier to learn, but leaves more up to the individual developer. As you’ve mentioned:

This popularity, in one way or another (and with different, opinionated approaches) added a level of consistency to the APIs used by the developer (not coming from the core APIs anymore). Why not go a “cleaner” way and do it the other way around, keeping the APIs clean and easy to use (in the sense of least complexity when learning them) and allowing opinionated decisions to be done by boilerplates and frameworks…

1 Like

Good point. You want a balance but in the case of the Array class in JS… 30 years later finally add include() method. It only took 100 million developers determining it’s less code to write array.includes() than array.indexOf()!=-1. Actually, it’d be interesting to find out what the reason it was finally added in.

But… take the BitmapData methods like bitmapDraw() found in various graphics frameworks. I worked with many custom classes that would draw a bitmap or add filters or convert to ArrayBuffers. Developer would make their own custom (that were interpreted or JIT compiled). The results were something like 200ms but when the development teams added those calls into the API the result was 2-5ms and I believe it was because it was compiled natively (AOT compilation). A native compiled C class is about 10-20 times more performant than interpreted JS (cite needed).

@kerrishotts is this accurate?

Getting a list of artboards won’t make much difference but in general this is one reason for adding the APIs natively across the board.

Another reason is that if I have a getAllArtboards() method I am writing that based on the knowledge I’ve gained from the documentation and forums and examples.

In my case the results would contain a bug because the method was unaware of pasteboard items. And I ran into this bug. If that call is native then those familiar with the inner workings of the project would have taken that into account. Bug avoided.

There was a neurological study a few years back and it was said that humans have more of a functional programming system for tasks instead of object oriented. In that, if you performed a task repetitively it was like a specific function call using it’s own set of neurons. If you did a similar but slightly different task it used it’s own separate neurons. It was it’s own specific function. And my point is… nevermind.

I don’t see any complexity to having a well rounded API. My point is if it was a database, an application or an OOP design there would be the complexities you mentioned. But an API is different. It’s not the same in my opinion.

1 Like

OK So I found this with some help:

1 Like

@Velara

I can’t believe I’m writing this, but I think I agree with everything you’ve written (in the previous two comments) :wink:.

The thing I’m not quite sure is:

Is this something you’d “agree” to too, then (as it serves the same functionality with all the advantages you’ve mentioned without the drawbacks). If that’s the case, I think we, for what to my knowledge is the first time here in the forums, would part ways in a (longer) discussion on the same “side” :slightly_smiling_face:

1 Like

:face_with_raised_eyebrow: Who are you and what have you done with @pklaschka? lol :smile:

Yes. That’s a good point. If resources are limited then the criteria is make a core set of API then add as needed usually by usage, across projects, etc.

For example, if someone said I need scenegraph.artboards I don’t think I would suggest adding scenegraph.rectangles and scenegraph.ellipsis.

But if it was my job to make an API I might be looking at what else I could add. And yeah, that’s where you’d want to not overdo it.

1 Like

Wow, there’s been a lot of discussion here – sorry I’ve not been able to participate (lots of work for MAX!)

So as far as APIs on SceneNodeList, we can certainly bring it further in line with Array, but it’s difficult to justify when there’s a lot of other complex work going on (as in making plugins work with co-editing).

What I always do, however, is just move the SceneNodeList to an Array, so all the methods I’m used to are available:

const nodes = node.children.map(i => i);
const artboards = nodes.filter(node => node instanceof Artboard);

I’d love to see SceneNodeList to expose the Iterable interface so that you could just use Array.from(node.children)map doesn’t declare the intended semantics quite as nicely.

But as long as we’re catching the APIs up to matching XD feature parity, changes to the SceneNodeList API are lower priority. What would be great, though, is if you want to contribute to the Plugin Toolkit API, that might serve as a jumping-off point until bandwidth does open up.

@kerrishotts Great to see you back! We missed you.

1 Like

What’s the Plugin Toolkit, an OSS library?