Mouseover Text Behavior

I have many components in this overview with mouseover text defined. I’ve placed a transparent rectangle over an area of the window that the user can click on to get more details for that sub-area and it directs to another window.

My question is, is there any way to get the mouseover text for the components below the rectangle to show up? The rectangle on top has no mouseover text defined so I would’ve expected it to “pass through” to components below.

This rectangle also has a hover animation that changes the border via script. Is that consuming the mouseover event? Is there any way to have both?

Yes, that will consume the event.

I would expect any unconsumed event to pass to the parent, not necessarily a component below the rectangle.

You can deliberately add listeners to that rectangle that repackage the event for the underlying container to dispatch. (Look for "glass pane" forum posts.)

Wow… that seems like alot of work for something so simple.

My “glass pane” buttons over the top are a single template. What’s the best way to implement this so I don’t have to go touch every instance of the template?

I haven't had to do much of this myself. I'll let others dig deeper with you.

Possibly 'EventDelegateDispatcher' is what you want:

1 Like

I may need some more direction. What exactly do I do with that? Haven’t done much with listeners

On your transparent rectangle (in the actual window) you'd create delegating listeners once (propertyChange, event componentRunning with a value of True). They would 'clone' the events that the rectangle is consuming and "forward" them down to the lower component. The snippet I have in the linked thread isn't going to be exactly what you want, you'll have to play with the parameters you pass in to ensure things flow in the 'right' direction, but that's the basic idea - allow the rectangle to grab the mouse events, and "in the background" also forward them along to the lower component.

1 Like

So I have to create a delegating listener on every instance of my transparent button to pass the events down? I can’t just do it once on the template?

I’ve added the script to my library. How would I call it? Like this?

if event.propertyName == 'componentRunning':
	Navigation.addForwardingEventListener(event)

That doesn’t seem to have done anything when adding it to the propertyChange script for one of the template instances.

Yes, because your transparent button is the one that's consuming the events.

You could, in theory, go the opposite direction - but that way lies madness. Imagine a script where the template instance looks at where it's embedded and walks the component hierarchy upwards, looking for something (what?) that identifies "this is a transparent button that overlaps me" and injects a listener into it (when?) that does the correct delegation.

You could parameterize the transparent button into a template. Put a custom property on it giving the component path to the template instance to forward events to. Lots of ways to skin this cat - but from a "direction of control flow" standpoint, the correct thing to do is set this up on the component that is consuming the events and dispatch them downward.

Ya, I was not thinking that “up” would be better than the “down” that was proposed - I was just unsure if the listener could live inside the template instead of on each template instance.

Anyway,

In your project library:

from com.inductiveautomation.factorypmi.application.components.util import EventDelegateDispatcher

KEY_EVENT_MASK = EventDelegateDispatcher.KEY_EVENT_MASK
MOUSE_EVENT_MASK = EventDelegateDispatcher.MOUSE_EVENT_MASK
FOCUS_EVENT_MASK = EventDelegateDispatcher.FOCUS_EVENT_MASK
ALL_EVENTS_MASK = EventDelegateDispatcher.ALL_EVENTS_MASK

def delegateEvents(parent, child, mask = MOUSE_EVENT_MASK):
	EventDelegateDispatcher.initializeDispatchers(child, parent, mask)

And then in your actual transparent button:

if event.propertyName == "componentRunning" and event.newValue:
	myTemplateInstance = event.source... # path to the actual template **instance** component
	addForwardingEventListener(event.source, myTemplateInstance)

Be aware that you won't get the componentRunning event in the designer if you open the containing window in design mode. Event scripts only run in preview mode.

1 Like

True. For simple overlays, I don't go that far, and I only use the parts that are needed to achieve my end. In your case, it sounds like you only need the tool tip part.

To understand why I do it the way it's outlined in the tutorial, take note that in a GUI without without a glass pane, a tool tip will disappear when the mouse leaves the respective component, and the delay to reshow the tool tip will start over when the mouse enters the bounds of the next component. The problem with a glass pane that is rendering tool tips for underlying components is that the tool tip text doesn't automatically disappear when moving from one underlying component to another. Once the overlay's tool tip appears, moving from one underlying component to the next will change the tooltip text but will not hide the popup. I find this annoying—especially if the underlying component is something informationally dense like a table.

I address this issue in my tutorial by tracking the underlying component path and manipulating the tool tip timers when the underlying component changes to produce the natural expected effect, but if you don't care about hiding the tool tip popup and reinitializing the delay time between components, that script could be greatly simplified.