# 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:** 1\
**Showing post:** 16

<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)

---

_[View the full topic](https://forum.inductiveautomation.com/t/component-outline-on-different-z-order-than-component/92128)._
