# Component outline on different z-order than component

**URL:** <https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128>\
**Category:** Module Development\
**Tags:** vision\
**Created:** [June 19, 2024, 9:02pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128 "2024-06-19T21:02:53Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mathias\_Melling](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mathias_melling/32/67682_2.png) [@Mathias\_Melling](https://forum.inductiveautomation.com/u/Mathias_Melling)\
**Post date:** [June 19, 2024, 9:02pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/1 "2024-06-19T21:02:53Z")

</div>

I'm drawing an outline around my component when the mouse hovers over it. Sometimes the component needs to be partially covered by another component, but I still want to show the full outline when the mouse hovers over it. The video below should make it clear what I'm trying to achieve. Does anyone have any advice?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [June 19, 2024, 9:19pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/2 "2024-06-19T21:19:11Z")

</div>

That's not really possible with Swing's transparency model, unfortunately.

---

<div class="post-metadata">

**Author:** ![Mathias\_Melling](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mathias_melling/32/67682_2.png) [@Mathias\_Melling](https://forum.inductiveautomation.com/u/Mathias_Melling)\
**Post date:** [June 19, 2024, 9:27pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/3 "2024-06-19T21:27:42Z")

</div>

Not the answer I was hoping for, but was fearing to get. I'll try to figure something out.

---

<div class="post-metadata">

**Author:** ![Transistor](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/transistor/32/3380_2.png) [@Transistor](https://forum.inductiveautomation.com/u/Transistor)\
**Post date:** [June 19, 2024, 10:47pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/4 "2024-06-19T22:47:58Z")

</div>

Position a copy of the grey-pink blocks on top of the existing one but make it completely transparent except for the hover outline. Remove the hover effect from the original one.

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [June 20, 2024, 2:10am UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/5 "2024-06-20T02:10:46Z")

</div>

I'm certain that this can be achieved in some way. Explain your hierarchy; what type of components are involved? Is the outline a separate component? If so, in Vision, the z-order is set in the designer using these controls:  
View ==\> Toolbars ==\> Shape Editing  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/0/b/0b279bb4a66108b585892bc82c17f83ddf1ce460.png)

---

<div class="post-metadata">

**Author:** ![Mathias\_Melling](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mathias_melling/32/67682_2.png) [@Mathias\_Melling](https://forum.inductiveautomation.com/u/Mathias_Melling)\
**Post date:** [June 20, 2024, 1:29pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/6 "2024-06-20T13:29:09Z")

</div>

I want to package it all into one component, the graphic itself as well as the outline. Having 2 components makes it too easy to not line them up properly, especially when there are multiple of these components on the HMI.

This is how it will eventually be used. The component is a slide gate, which is often used under conveyors to allow the conveyor to deliver grain to multiple different destinations. The pink color simply shows what the current route is.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/3/d3fb0e71a3cb96329abbe773fb6527d042f4d5b7.jpeg)

However it can also be used in other locations, where the whole slide gate is seen. Like this.

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/0/d/0d86fac26c777498ed8a575623f5d57b64dbd8a6.png)

---

<div class="post-metadata">

**Author:** ![Mathias\_Melling](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mathias_melling/32/67682_2.png) [@Mathias\_Melling](https://forum.inductiveautomation.com/u/Mathias_Melling)\
**Post date:** [June 20, 2024, 1:36pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/7 "2024-06-20T13:36:39Z")

</div>

I could give the slide gate a property to not show the top of it, something like this.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/a/f/af5d577007cacc9fd00c78394767ce8753be7f38.png)

I could then attach it to the bottom of a conveyor and make it's z-order higher than that of the conveyor, and then the outline would show up correctly. It's just that this way the user really has to pay attention to z-order, as it's not obvious which component is in front until Preview Mode is turned on and the mouse is hovered over it.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 20, 2024, 1:43pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/8 "2024-06-20T13:43:56Z")

</div>

> [@Mathias\_Melling](#):
>
> to not show the top of it

Clever.

---

<div class="post-metadata">

**Author:** ![michael.flagler](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/michael.flagler/32/26272_2.png) [@michael.flagler](https://forum.inductiveautomation.com/u/michael.flagler)\
**Post date:** [June 20, 2024, 2:35pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/9 "2024-06-20T14:35:59Z")

</div>

Do you mind sharing how you accomplished the outline on hover? We have a client coming from Wonderware that has that on all their plants and I have a feeling they're going to want that. I was curious if you had a simple way you were doing it.

---

<div class="post-metadata">

**Author:** ![Mathias\_Melling](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mathias_melling/32/67682_2.png) [@Mathias\_Melling](https://forum.inductiveautomation.com/u/Mathias_Melling)\
**Post date:** [June 20, 2024, 3:21pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/10 "2024-06-20T15:21:30Z")

</div>

That's actually what we're doing as well. Moving from Wonderware to Ignition, and we're trying to keep that outline effect.

Here is how I achieve this. I created the following method in a class called `Utilities`

```python
/**
 * Expand or shrink an area in all directions by a defined offset.
 *
 * @param A Area
 * @param offset Offset to expand/shrink.
 * @param join Method to use for line joins.
 * @return New area that is expanded or shrunk by the specified amount.
 */
public static Area areaOutline(Area A, float offset, int join) {
    if (offset < 0.1 && offset > -0.1)
        return A;
    
    Area newArea = new Area(A);
    Stroke stroke = new BasicStroke(Math.abs(2f * offset),
                                    BasicStroke.CAP_SQUARE, 
                                    join);
    Area strokeShape = new Area(stroke.createStrokedShape(newA));

    if (offset > 0.0)
        newArea.add(strokeShape);
    else
        newArea.subtract(strokeShape);
    
    return newArea;
}

```

which is then implemented like this under paintComponent:

```python
if (mouseEntered) {
    Area outline = Utilities.areaOutline(componentArea,3,BasicStroke.JOIN_ROUND);
    
    g.setStroke(new BasicStroke(2f));
    g.setColor(Color.BLACK);
    g.draw(outline);
    
    g.setStroke(new BasicStroke(1.5f));
    g.setColor(Color.WHITE);
    g.draw(outline);            
}

```

---

<div class="post-metadata">

**Author:** ![michael.flagler](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/michael.flagler/32/26272_2.png) [@michael.flagler](https://forum.inductiveautomation.com/u/michael.flagler)\
**Post date:** [June 20, 2024, 3:40pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/11 "2024-06-20T15:40:36Z")

</div>

Now I'm confused. This looks to be all java, so did you have to build out a java JAR file that you included this in or is there a way to do it through the project library scripts?

---

<div class="post-metadata">

**Author:** ![Mathias\_Melling](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mathias_melling/32/67682_2.png) [@Mathias\_Melling](https://forum.inductiveautomation.com/u/Mathias_Melling)\
**Post date:** [June 20, 2024, 4:05pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/12 "2024-06-20T16:05:48Z")

</div>

I'm creating our own module with a bunch of components, and yes that's all written in Java.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/e/1e801cd0785b2221957398ed400a3e33b4bc332b.png)

---

<div class="post-metadata">

**Author:** ![michael.flagler](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/michael.flagler/32/26272_2.png) [@michael.flagler](https://forum.inductiveautomation.com/u/michael.flagler)\
**Post date:** [June 20, 2024, 6:02pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/13 "2024-06-20T18:02:04Z")

</div>

Ok, that's beyond my level. I'm not a Java programmer at all and was hoping there was something easy to do in Vision to replicate it on all of our clickable templates.

---

<div class="post-metadata">

**Author:** ![Mathias\_Melling](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mathias_melling/32/67682_2.png) [@Mathias\_Melling](https://forum.inductiveautomation.com/u/Mathias_Melling)\
**Post date:** [June 21, 2024, 1:03pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/14 "2024-06-21T13:03:11Z")

</div>

I don't think there is a switch anywhere that turns on automatic outlines for clickable objects. You would have to draw an outline around your template yourself, then toggle its `Visible` property via mouse events.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/b/e/be231d992ab7fc300de10bfa5c15207bd422d155.png)

---

<div class="post-metadata">

**Author:** ![michael.flagler](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/michael.flagler/32/26272_2.png) [@michael.flagler](https://forum.inductiveautomation.com/u/michael.flagler)\
**Post date:** [June 21, 2024, 2:17pm UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/15 "2024-06-21T14:17:29Z")

</div>

Yeah, that was going to be my alternative if they wanted similar functionality.

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [June 23, 2024, 3:57am UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/16 "2024-06-23T03:57:04Z")

</div>

The paintable canvas can be used as an overlay to provide this effect without any regard to z-order or component positioning. Simply overlay the entire container with a paintable canvas, and use the canvas's mouse moved event to detect when the cursor intersects a component, so a top level outline can be painted around it:

**Example:**  
Add the following custom properties to the canvas:  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/6/166f67ce85219b1976dd0e182bd35d6d6babc714.png)

Add the following custom methods to the canvas:

> **passMouseEvent**
>
> ```python
> #def passMouseEvent(self, event):
> """
> Passes mouse event to the underlying component
> """
> from java.awt import Rectangle
> from java.awt.event import MouseEvent
> for component in event.source.parent.components:
> if component != event.source: 
> 			
> # Components that use relative coordinates must be handled differently
> if not hasattr(component, 'relX') and component.bounds.contains(event.point):
> componentEvent = MouseEvent(
> component,
> event.getID(),
> event.when,
> event.modifiers,
> event.x - component.x,
> event.y - component.y,
> event.getXOnScreen(),
> event.getYOnScreen(),
> event.clickCount,
> event.isPopupTrigger(),
> event.button)
> component.dispatchEvent(componentEvent)
> return
> elif hasattr(component, 'relX') and Rectangle(int(component.relX), int(component.relY), int(component.relWidth), int(component.relHeight)).contains(event.point):
> componentEvent = MouseEvent(
> component,
> event.getID(),
> event.when,
> event.modifiers,
> event.x - int(component.relX),
> event.y - int(component.relY),
> event.getXOnScreen(),
> event.getYOnScreen(),
> event.clickCount,
> event.isPopupTrigger(),
> event.button)
> component.dispatchEvent(componentEvent)
> return
> 
> ```

> **mapOutline**
>
> ```python
> def mapOutline(self, event):
> """
> Sets the custom properties on the paintable canvas that define an outline
> ...and in doing so, triggers the repaint event
> """
> from java.awt import Rectangle
> for component in event.source.parent.components:
> # Filter out components that shouldn't be outlined
> if component. __class__. __name__ not in ['PMIPaintableCanvas', 'PMILabel'] or 'Polygon' in component.name:
> 			
> # Handle components that use relative coordinates differently than standard components
> if not hasattr(component, 'relX') and component.bounds.contains(event.point):
> event.source.outlineX = component.x - event.source.outlineGap
> event.source.outlineY = component.y - event.source.outlineGap
> event.source.outlineWidth = component.width + (2 * event.source.outlineGap)
> event.source.outlineHeight = component.height + (2 * event.source.outlineGap)
> return
> 				
> elif hasattr(component, 'relX') and Rectangle(int(component.relX), int(component.relY), int(component.relWidth), int(component.relHeight)).contains(event.point):
> event.source.outlineX = component.relX - event.source.outlineGap
> event.source.outlineY = component.relY - event.source.outlineGap
> event.source.outlineWidth = component.relWidth + (2 * event.source.outlineGap)
> event.source.outlineHeight = component.relHeight + (2 * event.source.outlineGap)
> return
> event.source.outlineX = None
> 
> ```

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/c/4/c4b7e2130421340fe0bbcdfb0fe43a0230b182da.png)

Both custom methods will take a single parameter called event. The  
`passMouseEvent` will be used to provide to the underlying components any mouse event _that would otherwise be consumed by the overlay_.

The `mapOutline` will be used to set the custom properties on the canvas that define the outline. The top level `if` statement in the `mapOutline` method should be used to determine which components receive the outline.

Call the `passMouseEvent` from all paintable canvas mouse event handlers in this way:

```python
event.source.passMouseEvent(event)

```

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/6/d/6d3c3427f86ad7cb9b655b6fd14ec16176c866ab.png)

call the `mapOutline` method from the `mouseMoved` event handler in this way:

```python
# All mouse events will have passMouseEvent,
#...but only mouseMoved will have mapOutline
event.source.passMouseEvent(event)
event.source.mapOutline(event)

```

Finally, use the following script on the canvas's repaint event handler to paint the outlines:

```python
if event.source.outlineX is not None:
	from java.awt import BasicStroke
	graphics = event.graphics
	graphics.setStroke(BasicStroke(5))
	graphics.setColor(system.gui.color('yellow'))
	x = event.source.outlineX
	y = event.source.outlineY
	width = event.source.outlineWidth
	height = event.source.outlineHeight
	radius = event.source.outlineGap
	graphics.drawRoundRect(x, y, width, height, radius, radius)

```

**Result:**  
 ![PaintableCanvasFloatingOutlineDemo](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/b/db35740bde6ed1da2cc04e3f32d44cc4b28fd6dc.gif)

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [June 24, 2024, 1:13am UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/17 "2024-06-24T01:13:53Z")

</div>

> [@Mathias\_Melling](#):
>
> Having 2 components

It's possible that this would work if the separate outline component's existence and position were determined at run time by the focusable component it was outlining. Since the last component added will be painted on top, I imagine the focusable component itself could dynamically create an outline component and add it to the parent container using the mouse entered event, and since the focusable component would have its own coordinates to determine the outline placement, alignment would maintained automatically. Furthermore, adding and removing the outline component at run time eliminates the need to maintain a proper z-order for the secondary component.

Lastly, if for some reason the overlay component interferes with the focusable component's mouse events, see the `passMouseEvent` method I put together for the paintable canvas overlay example. It simply converts the overlay's mouse events to what the underlying component's mouse event would have been, and passes the converted mouse event to the underlying component's `dispatchEvent` method.

---

<div class="post-metadata">

**Author:** ![michael.flagler](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/michael.flagler/32/26272_2.png) [@michael.flagler](https://forum.inductiveautomation.com/u/michael.flagler)\
**Post date:** [June 24, 2024, 1:58am UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/18 "2024-06-24T01:58:05Z")

</div>

I'm going to play with this more I think as I don't want it to highligh every component, but only those that can be interacted with. Most of my vessels and lines which are static would probably trigger this outline also, so I may just have to do some binding on the border to get this to work as I'd like, but not sure if I'll have time to mess with it this week.

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [June 24, 2024, 3:34am UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/19 "2024-06-24T03:34:27Z")

</div>

> [@michael.flagler](#):
>
> but only those that can be interacted with.

Perhaps simply checking focusability would be enough:

```python
if component != event.source and component.focusable:

```

...or something like this:

```python
if component != event.source and component.focusable and component.visible and component.enabled:

```

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [June 24, 2024, 4:56am UTC](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128/20 "2024-06-24T04:56:50Z")

</div>

Experimenting with focusability, I was also able to make a more concise list of only the components that can receive focus from the tab key in this way:

```python
from java.awt import KeyboardFocusManager
keyboardFocusPolicy = KeyboardFocusManager.getCurrentKeyboardFocusManager().defaultFocusTraversalPolicy
parentContainer = event.source.parent
startingComponent = keyboardFocusPolicy.getFirstComponent(parentContainer)
nextComponent = keyboardFocusPolicy.getComponentAfter(parentContainer, startingComponent)
lastComponent = keyboardFocusPolicy.getLastComponent(parentContainer)
tabFocusableComponents = [startingComponent]
for component in parentContainer.components:
	tabFocusableComponents.append(nextComponent)
	if nextComponent == lastComponent:
		break
	else:
		nextComponent = keyboardFocusPolicy.getComponentAfter(parentContainer, nextComponent)

# Possibly use this list for validating?
print tabFocusableComponents

```

There is probably some code golfing to be done there, and there is the hurdle of figuring out an efficient way to use it, but since it looks like it could be useful, I've decided to go ahead and post it.

[Next page](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128.md?page=2)
