Move Popup Within Bounds of Client Window

I have a popup view that I'm opening via an onClick event and I'd like to open it in the relative mouse position.

I've achieved this, but now there's a case where the button is close enough to the edge of the client window that it opens partially off the screen.

I want to limit the max X and Y position based on the window size, but this is not so easy because there can be docked view (in this case, a docked north navigation view) that throws things off.

#x and y position for popup, relative to entire screen
xOpen, yOpen = system.gui.convertPointToScreen(event.x, event.y, event)

#open window
window = system.nav.openWindowInstance('Popups/PopUp')

#parent window
parentWindow = system.gui.getParentWindow(event)

#calculate where the end of the poup is
xEnd = xOpen + window.width
yEnd = yOpen + window.height

#how do I find x and y max (width and height of screen)??
#xMax = 1920
#yMax = 1080

#make sure end of popup is not off-screen
if xEnd > xMax:
	xOpen = xOpen - (xEnd - xMax)
	
if yEnd > yMax:
	yOpen = yOpen - (yEnd - yMax)

#set window location
window.setLocation(xOpen,yOpen)

How can I make sure the popup opens within the bounds of the window every time?

Here is a script I developed some time ago for this purpose:

# Developed to be called from a popup window's internalFrameActivated event handler

# Import the necessary Javax Swing classes
from javax.swing import JFrame,SwingUtilities

# Get an unambiguous reference to the 'popupWindow' component from the event source
popupWindow = event.source

# Find the nearest ancestor JFrame of the 'popupWindow' component which will be the parent window that the popup was opened from
parentJFrame = SwingUtilities.getAncestorOfClass(JFrame, popupWindow)

# Set a margin value for positioning the 'popupWindow'
parentJFrameMargin = 100

# Try to get the mouse position relative to the 'parent' JFrame
try:
	popupWindowX = parentJFrame.getMousePosition(True).x
	popupWindowY = parentJFrame.getMousePosition(True).y
	
# If an exception occurs (e.g., mouse position not available), set default values of (0,0) [Upper left corner] plus the specified margin
except:
	popupWindowX = parentJFrameMargin
	popupWindowY = parentJFrameMargin
	
# Ensure that the 'popupWindow' is within the boundaries of the 'parent' JFrame
if popupWindowX < parentJFrameMargin:
	popupWindowX = parentJFrameMargin
elif popupWindowX + popupWindow.width > parentJFrame.width - parentJFrameMargin:
	popupWindowX = parentJFrame.width - popupWindow.width - parentJFrameMargin
if  popupWindowY < parentJFrameMargin:
	popupWindowY = parentJFrameMargin
elif popupWindowY + popupWindow.height > parentJFrame.height - parentJFrameMargin:
	popupWindowY = parentJFrame.height - popupWindow.height - parentJFrameMargin 
	
# Set the new location of the 'popupWindow' based on the calculated positions
popupWindow.setLocation(popupWindowX, popupWindowY)

It finds the Jframe of the window the popup was launched from, and calculates if the window can be positioned at the mouse cursor without overlapping the side of the window. If it can't be done, it positions it as close as possible. It also has an adjustable margin parameter which is currently set to 100 pixels, so the window will actually be contained within a 100 pixels of the edge of the parent window.

2 Likes

Thanks for the script! I'll try to adapt this for my use.

I have one call that opens the popup and another that repositions the popup, all within the onActionPerformed for a button, like so:

if event.source.parent.parent.clickable:
	window = Nav.popup1(event)
	Nav.moveWindowRelative(event, window)
def popup1(event):
	'''
		Calls pop with path defined on the root container
		
		Args:
			event (obj)				:	event handler (typically mouse click or button action)
			
		Returns:
			window (obj)			:	window that was opened
	'''
	
	#get component path from root container
	param1 = event.source.parent.parent.path
	
	#call popup with path
	window = system.nav.openWindowInstance('Popups/PopUp', {'path' : param1})
	system.nav.centerWindow(window)
	
	return window

If I use your code as-is

def moveWindowRelative(event, window):
	from javax.swing import JFrame,SwingUtilities
	# Get an unambiguous reference to the 'popupWindow' component from the event source
	popupWindow = event.source
	
	# Find the nearest ancestor JFrame of the 'popupWindow' component which will be the parent window that the popup was opened from
	parentJFrame = SwingUtilities.getAncestorOfClass(JFrame, popupWindow)
	
	# Set a margin value for positioning the 'popupWindow'
	parentJFrameMargin = 100
	
	# Try to get the mouse position relative to the 'parent' JFrame
	try:
		popupWindowX = parentJFrame.getMousePosition(True).x
		popupWindowY = parentJFrame.getMousePosition(True).y
	
	# If an exception occurs (e.g., mouse position not available), set default values of (0,0) [Upper left corner] plus the specified margin
	except:
		popupWindowX = parentJFrameMargin
		popupWindowY = parentJFrameMargin
	
	# Ensure that the 'popupWindow' is within the boundaries of the 'parent' JFrame
	if popupWindowX < parentJFrameMargin:
		popupWindowX = parentJFrameMargin
	elif popupWindowX + popupWindow.width > parentJFrame.width - parentJFrameMargin:
		popupWindowX = parentJFrame.width - popupWindow.width - parentJFrameMargin
	if  popupWindowY < parentJFrameMargin:
		popupWindowY = parentJFrameMargin
	elif popupWindowY + popupWindow.height > parentJFrame.height - parentJFrameMargin:
		popupWindowY = parentJFrame.height - popupWindow.height - parentJFrameMargin 
	
	# Set the new location of the 'popupWindow' based on the calculated positions
	popupWindow.setLocation(popupWindowX, popupWindowY)

The window always just opens at 0,0.

If I replace the las popupWindow.setLocation with my window.setLocation it still opens off the screen.

I'll try and do some troubleshooting with the console.

*edit: so, when I print the JFrame that is found it looks like this:

com.inductiveautomation.ignition.designer.IgnitionDesigner[frame1,1912,-8,1936x1056,invalid,layout=java.awt.BorderLayout,title=Grain_SCC - Ignition-LPTP134 - Ignition Designer,resizable,maximized,defaultCloseOperation=DO_NOTHING_ON_CLOSE,rootPane=javax.swing.JRootPane[,8,31,1920x1017,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.synth.SynthBorder@7071c1,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]

If you're calling my script from the button that launches the popup, it won't work "as is".

My script was developed to be called from the pop-up window itself. Specifically, the internalFrameActivated event handler.

Right, that's what I'm going to try and change. I just don't know enough about JFrames

Delete the popupWindow = event.source line, and rename your passed in window variable popupWindow, and the script will probably work.

1 Like

There we go! I replaced it in the setLocation call, but nowhere else. That's perfect.

# Import the necessary Javax Swing classes
from javax.swing import JFrame,SwingUtilities

def moveWindowRelative(window):
	'''
		Moves a popup window to a position
	'''
	
	
	# Find the nearest ancestor JFrame of the window component which will be the parent window that the popup was opened from
	parentJFrame = SwingUtilities.getAncestorOfClass(JFrame, window)
	
	# Set a margin value for positioning the window
	parentJFrameMargin = 30
	#initially assume a window move is required
	moveRequired = True
	
	# Try to get the mouse position relative to the parent JFrame
	try:
	    windowX = parentJFrame.getMousePosition(True).x
	    windowY = parentJFrame.getMousePosition(True).y
	
	# If an exception occurs (e.g., mouse position not available), do not move the window
		moveRequired = False
		
	if moveRequired:
	
		# Ensure that the window is within the boundaries of the parent JFrame
		if windowX < parentJFrameMargin:
		    windowX = parentJFrameMargin
		elif windowX + window.width > parentJFrame.width - parentJFrameMargin:
		    windowX = parentJFrame.width - window.width - parentJFrameMargin
		if  windowY < parentJFrameMargin:
		    windowY = parentJFrameMargin
		elif windowY + window.height > parentJFrame.height - parentJFrameMargin:
		    windowY = parentJFrame.height - window.height - parentJFrameMargin 
		
		# Set the new location of the window based on the calculated positions
		window.setLocation(windowX, windowY)
1 Like

I needed just this for my popups, and determined that the best event handler on the popup is the visionWindowOpened event. Your script works perfectly otherwise, and helped me a ton!

When I used internalFrameActivated, if the user clicks off of the popup and back to it, the script triggers again causing the popup to jump to a new location based on where the user clicks on the popup.

You also can't use internalFrameOpened unless your cache policy on the popup is set to never, as any cached windows/popups will not run the script a second time and the popup will show in the last location.

I finally moved it to visionWindowOpened, and it works consistenly with or without caching and without jumping when clicking off and back on a popup.

That makes sense. In my application, I close the pop-up with internal frame deactivated, so its counterpart internal frame activated seemed appropriate for my initialization script. If I were leaving the popup open, I would definitely have to take your advice and change the way that part of the script is called.