I have a dropdown that partially overlaps another component that can also be clicked on. I would like to disable the component below the dropdown box if the dropdown box is open.
Just off-hand I think I could achieve this with the onClick and focusLost extension functions on the dropdown, but is there a way to just check if the dropdown is "open"?
edit: focus is not lost when the clicking off of the component. I was thinking that would work for the case where the user clicks any space outside of the dropdown box to close the dropdown.
if event.propertyName == 'componentRunning':
from javax.swing.event import PopupMenuListener
class PopupCloseListener(PopupMenuListener):
en = True
def popupMenuCanceled(self, subEvent, en):
en = True
def popupMenuWillBecomeVisible(self, subEvent, en):
en = False
def popupMenuWillBecomeInvisible(self, subEvent, en):
en = True
event.source.parent.getComponent('Switch 2POS Buttons').enabled = en
event.source.addPopupMenuListener(PopupCloseListener())
Yeah, definitely store the class definition in a project library script or something. Just trying to demonstrate the concept.
Here's the same example but commented to explain what's going on in more detail:
if event.propertyName == 'componentRunning':
from javax.swing.event import PopupMenuListener
# define a class that extends from the Java interface we care about
class PopupCloseListener(PopupMenuListener):
# give this class a custom constructor that takes one argument, 'target'
def __init__(self, target):
# store the target parameter we get on construction into a class-internal field, `self.target`
self.target = target
# to override a Java interface method, you must match the signature of the Java method exactly
# (plus the `self` parameter, because Python)
# Java has strict overloading, so adding parameters and stuff doesn't work - this is why your first
# example didn't work
def popupMenuCanceled(self, event):
# this will be called automatically because we're providing a listener
# so when the popup menu is canceled, we modify the enabled property of our self.target field
self.target.enabled = True
def popupMenuWillBecomeVisible(self, event):
self.target.enabled = False
def popupMenuWillBecomeInvisible(self, event):
self.target.enabled = True
# construct an instance of our listener class, passing in our 'target' component as a reference
# this is what will get stored in the 'target' field inside the class
listener = PopupCloseListener(event.source.parent.getComponent('Switch 2POS Buttons'))
# then add our instance to the actual class we care about
event.source.addPopupMenuListener(listener)
My example was for multiple properties in a single component, you cans modify it appropriately for multiple components single attribute, or multiple of both.
You could go fully functional - accept a function during construction of your listener. Call that function inside the listener with true or false to represent the state of the boolean.
Where you actually use the listener, also define the callback to modify the enabled, visible, other properties. Slightly more brain breaking, but a lot more flexible.
Something like this, to get maximally silly with it:
# in project library
class PopupMenuAdapter(PopupMenuListener):
def __init__(self, callback):
self.callback = callback
def popupMenuCanceled(self, event):
self.callback(event.source.popupVisible)
def popupMenuWillBecomeVisible(self, event):
self.callback(event.source.popupVisible)
def popupMenuWillBecomeInvisible(self, event):
self.callback(event.source.popupVisible)
##############################
# in component event
if event.propertyName == 'componentRunning':
# define the function to execute when the popup menu is opened or closed
# the popupVisible argument will reflect the current state and can be used to drive other logic
def callback(popupVisible):
# we can access event.source here because the callback is defined in the same scope
event.source.parent.getComponent('Switch 2POS Buttons').visible = not popupVisible
listener = PopupMenuAdapter(callback)
event.source.addPopupMenuListener(listener)
I know I've written this a 100 times in the forum, but it's something I need to go back through and correct. The componentRunning property is a bool, and it's propertyChange event fires twice during the normal life cycle of the component. Once at initialization when the property becomes True and once at window close when the property becomes False.
For this reason, I now write it this way:
# Only occurs once at initialization
if event.propertyName == 'componentRunning' and event.newValue:
If the initialization script schedules anything with invokeLater or makes any asynchronous calls, null pointer exceptions and other unexpected behaviors can occur. Overall, I'd say that 99% of the time it probably doesn't matter if an initialization script runs at window close, but it's still an unnecessary inefficiency that should be corrected.
I would assume I need to somehow pass a list of components and a list of lists of properties (one list for each component), but I'm not quite sure how to make that change with the self.target and self.attrs
Something like this?
if event.propertyName == 'componentRunning' and event.newValue:
from javax.swing.event import PopupMenuListener
class PopupCloseListener(PopupMenuListener):
def __init__(self, target,attrs):
self.target = target
self.attrs = attrs
def popupMenuCanceled(self, event):
for i in range(0,len(target)):
for attr in attrs:
setattr(self.target[i],attr,True)
def popupMenuWillBecomeVisible(self, event):
for i in range(0,len(target)):
for attr in attrs:
setattr(self.target[i],attr,False)
def popupMenuWillBecomeInvisible(self, event):
for i in range(0,len(target)):
for attr in attrs:
setattr(self.target[i],attr,True)
listener = PopupCloseListener([event.source.parent.getComponent('Switch 2POS Buttons')],[["Enabled","Visible"]])
event.source.addPopupMenuListener(listener)
(still just doing this in the component script for now, until I get it working)