Why Doesn’t Vision Have a Native Modal Popup Option Like Perspective?

Hi everyone,

I have a question regarding Vision popup windows.

In Vision, when we open a popup window, the background windows can still be interacted with unless we use additional scripting or other workarounds. However, in Perspective, we have the option to configure a popup as a modal, which prevents interaction with the background.

Why doesn't Vision provide a built-in option similar to Perspective's modal popup functionality? Is there any technical limitation that prevents the Ignition team from providing a native "modal" or "blocking" popup option in Vision?

I'm not looking for a workaround or solution. I'm mainly interested in understanding the technical reason behind this difference between Vision and Perspective.

Thanks!

True modal dialogs in Java Swing, the underlying execution engine used in both Vision and the Designer, block the event dispatch thread. For convenience and safety, all user scripting also happens on the EDT. Thus, if we gave you free-form modal popups where you got to control the layout of the components we'd have to go through some significant hurdles to make it work at all, and significantly more to make it work safely. For instance, if we spun up a second EDT thread to run your modal popup, and then you wrote a script that used system.vision functions to attempt to interact with a "background" UI element...you've now hopelessly deadlocked your GUI.

What Vision does have, is fixed-form modal dialogs, as in:

These are basically lifted from the core Swing API of JOptionPane. You didn't ask for workarounds, but it's also very possible in Vision to directly use underlying Swing constructs like JDialog directly. Whether that's a good idea for future maintainability or not is up to you and your environment.

Yuck. Consider constructing a glass pane (programmatically) and assigning it to the desktop JFrame's root pane. This lets everything continue running but only the glass pane components can receive UI events.

There is also a way to use the paintable canvas as a glass pane. @justinedwards.jle detials his approach here:

Be aware that Justin's method is not a true java glass pane. You really need a proper glass pane on the JFrame to make it effectively modal. The modal approach would not pass any UI events to underlying components, and would simply be removed from the JFrame to restore normal behavior. In a multi-desktop environment, they can be independently modal with glass panes.

Just out of curiosity, how do y'all do it in the script console? That things not running in the EDT, but I've never seen it deadlock when interacting with the underlying swing components.

If you're calling out to swing, you're (technically) rolling the dice every time. We're not doing anything special. Mostly. A few of our functions do internally marshal onto the EDT before executing, even when called from a background thread.

It's unlikely to be a problem in practice because, generally, while you're interacting with the script console you're not interacting with anything else. But it's still technically not 'correct' to read/write Swing properties from the script console.

Thank you everyone for taking the time to explain the technical details behind this. I really appreciate the responses from @paul-griffith, @pturmel , @justinedwards.jle , @lrose and everyone else who contributed. The discussion helped me better understand the Swing EDT limitations and why Vision doesn't provide a native modal popup option similar to Perspective.

For my current requirement, I ended up using a simple approach that gives users a modal-like experience. I placed a transparent rectangle component that covers the entire main window and bound its visibility to a Boolean tag. The rectangle's mouse-click event script simply contains pass, so when it is visible it absorbs all mouse clicks without performing any action.

When a user clicks a button to open a popup, I first set the Boolean tag to True, which makes the transparent rectangle visible, and then I open the popup window. The popup remains fully interactive, but any clicks on the background window are intercepted by the transparent rectangle, so the user cannot interact with the underlying components. The background screens continue running normally, but only the popup can receive user interaction.

Whenever the popup is closed, or when the operator completes the action and closes the popup, I set the Boolean tag back to False. This makes the transparent rectangle invisible again and immediately restores normal interaction with the underlying window and components.

This approach has worked well for my use case and provides a modal-like experience for operators while allowing the rest of the application to continue running normally.

@pturmel , would you consider this approach conceptually similar to the glass-pane approach you described, or is it fundamentally different from what you mean by a proper Java Glass Pane attached to the JFrame?

Thanks again to everyone for the detailed explanations. I learned a lot from this discussion, especially regarding Swing, the EDT, modal dialogs, and the glass-pane concept.

This is a client tag?

No, I didn't use a client tag. I used a standard Boolean Memory Tag.

The transparent rectangle component's visibility is bound to that Memory Tag. When opening the popup, I write True to the tag, which makes the rectangle visible and prevents interaction with the background components. When the popup closes, I write False to the same tag, making the rectangle invisible and restoring normal interaction.

I used a Memory Tag because my use case only required a simple way to enable and disable the overlay behavior.

Is there the possibility of more than one client displaying this window at the same time?

If so this approach will result in all users being locked out of interaction with the underlying window.

At the very least you need to use something which is only in the scope of a client.

It is a fair approximation of a glass pane but you should use a custom property on the root container, not a tag.

By using a tag you are dependent on an external scope, which makes this approach fragile. What if another designer decides to copy your approach and uses the same tag? What if a tag write fails (unlikely with a memory tag but not impossible)?

In my particular application, only one Vision client will be connected to the Gateway at a time, so I don't have to worry about multiple clients interacting with the same Memory Tag. Because of that limitation, the approach works for my use case. However, I understand your point that if multiple clients were connected simultaneously, using a shared Memory Tag could affect all clients and a client-scoped solution such as a root container property would be a safer design.

Yes. But the real glass pane also allows blocking the entire application window, including its menu.

If your client crashes, the gateway will never reset that tag, and later clients will start up with the "glass pane" already loaded. Using a memory tag for this is a serious mistake, even if you only use one client.

That's a good point. In my implementation, I also considered that scenario. I set the Boolean Memory Tag to False during both Client Startup and Client Shutdown events to ensure the overlay is cleared whenever a client starts or closes. This helps prevent the transparent rectangle from remaining visible if the popup was left open unexpectedly.

That said, I understand your concern that a Memory Tag is still a Gateway-scoped resource and that a root container custom property would be a more robust and client-scoped solution. For my particular application, there will only be a single Vision client connected to the Gateway, which is why the Memory Tag approach has been sufficient so far.

That's why I brought this up when you said you were using tags instead of a local property. Controlling local behaviors with gateway tags can become a gotcha in quite a few different ways.

The other problem you'll eventually run into with this approach is maintaining z-order. If anybody adds anything to the window later, those components will invariably end up on top of the label and will consequently be able to receive mouse events.

Whenever I need to enforce a specific z-order, I use this script to position components in order from bottom to top.

def setTopPosition(component):
	'''
	Arguments:
		component: The component to position at the top of the z-order
	
	Returns:
		None
	
	Overview:
		This function removes the component and adds it back to give it
		top-level positioning.
	'''
	# Assign the parent to a variable,
	# ...since the component can't call its parent after it has been removed from it
	parent = component.parent
			
	# Remove the component from the parent container and add it back,
	# ...so it moves to the top of the z-order and won't inadvertently be underneath anything
	parent.remove(component)
	parent.addComponent(component)
	
	# Repaint the component in its new position
	parent.repaint()

To ensure label is always on top at runtime, simply put the setTopPosition function in the script library, and call it from the label's property change event handler like this:

# Triggers at runtime in the client environment
if event.propertyName == 'componentRunning' and event.newValue:
	
	# Move this component to the top of the z-order
	pathToScript.setTopPosition(event.source)

That's a very good observation. I hadn't considered the impact of the component Z-order when the screen is modified later. In my current application, the window design is fairly static, so I haven't run into that issue yet, but I can definitely see how adding new components in the future could result in them being placed above the transparent rectangle and bypassing the overlay behavior.

Thank you @justinedwards.jle , for pointing that out and for sharing the setTopPosition approach. I'll keep that in mind if the screens evolve further. It also helps me better understand the difference between my overlay-based solution and a true glass pane implementation, where the event blocking mechanism is independent of the component Z-order.