Canvas.layout() is asynchronous?

Dear all,

To work around the lack of line labels, I added the id of the label as userData to the line. I then call canvas.layout(), and finally walk through all of the Lines, fetch the label and move to the center of the line’s geometry.

And … it doesn’t work. The geometry of the lines after calling layout has not updated by the time layout() returns to the execution of the next part of the script. Meaning it tries to reposition the labels where they were to begin with, and then layout moves them to where it thinks they should be.

Is there any way to catch events (e.g. that layout() has finished) or to force layout to complete before moving on with the script? Or other suggestions?

Many thanks!

I don’t think it is possible to make a line label with scripting right now even with a workaround. At best, I’d expect to end up with a label on top of (but not attached to) the line. Sorry for the inconvenience.

One workaround I might try is making a stencil object of a line with a line label in the desired location with unique placeholder text inside, place that stencil on canvas and then search for the placeholder line label string and replace it with the desired string. This should work because you don’t actually need to covert anything into a line label in the script. Similarly you could use a template that has a few line labels in it, then attach them to objects in your script. Granted, these are not ideal solutions, but could be possible workarounds.


Hi Lanette,

Sorry for the confusion… I understand the limitation about line labels. This issue is about the way in which canvas.layout() works – it does not complete before returning control to the script. So you can’t continue to make changes to object geometries after calling layout(), as they’re over-written. A plugin could have two functions, do everything up to layout() and then require the user to manually call the function that does the post-layout-processing, but having a way to wait for layout() to complete would be much better :)

If there isn’t an existing solution for this, I’m happy to write it up as a feature request via email to support.

canvas.layout() applies automatic layout to everything in the document. I’d expect that means you call that only when you want the document to lay out. Canvas.layout() isn’t for getting objects. I think that’s the issue here, and thus my confusion.

Let me share an example of getting graphics on a canvas without laying them out, which it sounds like is what you are trying to do? Is that correct? I think I misunderstood what you were trying to do.


To be more abstract about what I would like to do… my workflow is as follows:

  1. Generate a bunch of inter-connected objects.
  2. Call layout() to do a good-enough layout
  3. Do additional hand-coded tweaks to the layout to improve it for the specific domain

In this workflow, the additional changes in the third step get over-written by calling layout() in the second step, and there doesn’t appear to be a way to delay the third step until after the layout has finished.

(The line issue is one example of a situation when this is valuable … as I can easily calculate the correct position for the label by looking at the geometry of the line … but only after layout() has finished)

I see! When you call canvas.layout() it is as if you selected Layout Now in your Diagram Style and Layout inspector manually. By that I mean each time you call it, it lays out that canvas on a per canvas basis following the include scope and the spacing options set. There is no “wait for layout to complete” functionality.

A few solutions to try:

  1. Call canvas.layout() on the lines on each canvas at the start of the script before you get the location. Iterate over each canvas to get the lines again, now that their geometry has changed. Then add and position the labels at precise coordinates making sure during that portion of the script you don’t call canvas.layout(). Basically, you need some way to separate the broader layout from the precise placement. If that doesn’t solve the issue, add a document save, close and reopen between the 2 phases of the script.

  2. Split your script into 2 actions in the same plug-in. One that uses canvas.layout() to run first, then the more delicate positioning afterwards so that your manual positioning doesn’t get overwritten. Doesn’t sound ideal at all, but may be a reasonable workaround for now.

Thank you for your patience in explaining. Please email a feature request to make sure canvas.layout() in OmniJS has completed all changes before moving on to the next steps.


You can use a timer to delay the script and then perform follow-on actions:

var cnvs =[0].selection.canvas
	// perform actions

Here is the webpage about timers:

1 Like