Change the background and icon color of the Dropdown Component's button:

If I change the background color of the dropdown component, I get this result where the background of the button changes with the component:
image

The result I want is this:
image

I've made several unsuccessful attempts to extend the SynthButton UI, and some of the attempts caused gui issues.
Here is one of the attempts that was not successful:

Unsuccessful script
from java.awt import Color
from javax.swing.plaf.synth import SynthArrowButton, SynthButtonUI
dropdown = event.source.parent.getComponent('Dropdown')
class customUI(SynthButtonUI):
    def paint(self, c, g):
    	print 'here'
        g.setBackground(Color.WHITE)
        super(customUI, self).paint(c, g)
from javax.swing.plaf.synth import SynthArrowButton
def getButton(dropdown):
	if dropdown.componentCount > 0:
		for component in dropdown.getComponents():
			if isinstance(component, SynthArrowButton):
				return component
			else:
				button = getButton(component)
				if button is not None:
					return button
	return None
button = getButton(dropdown)
button.background = Color.white
button.setIcon(None)
button.setUI(customUI())
button.repaint()
button.foreground = Color.white
button.text = u'▼'

I can create the illusion of changing the color by assigning a dummy UI, and painting a buffered image in the place of the button
Here is the script:

Painted Button Script
from java.awt import Color
from javax.swing.plaf.synth import SynthArrowButton, SynthButtonUI
from java.awt.image import BufferedImage
from javax.swing import ImageIcon
dropdown = system.gui.getParentWindow(event).getComponentForPath('Root Container.Dropdown')
def getButton(dropdown):
	if dropdown.componentCount > 0:
		for component in dropdown.getComponents():
			if isinstance(component, SynthArrowButton):
				return component
			else:
				button = getButton(component)
				if button is not None:
					return button
	return None
button = getButton(dropdown)
UI = SynthButtonUI()
button.setUI(UI)
size =  button.getHeight()
image = BufferedImage(size, size, BufferedImage.TYPE_INT_RGB)
g = image.getGraphics()
g.setColor(Color.WHITE)
g.fillRect(0, 0, size, size)
g.setColor(Color.BLACK);
g.drawString(u'▼', size/4, (3*size)/4);
icon = ImageIcon(image)
button.setIcon(icon)

Here is the result:
image

I can do an overlay button, but I don't like it because of all the things that have to be done to make it work with the dropdown component's necessary focus listeners.

Here is the procedure:
• Set the text property of the button to this: ▼
• Add a custom property to the button called: 'popupVisible'
• Add the following code to the overlay button's actionPerformed event handler:

dropdown = system.gui.getParentWindow(event).getComponentForPath('Root Container.Dropdown')
if not event.source.popupVisible:
	dropdown.requestFocusInWindow()
	dropdown.setPopupVisible(True)
	event.source.popupVisible = True
else:
	dropdown.parent.requestFocusInWindow()
	event.source.popupVisible = False

• Add the following code to the overlay button's mouseEntered event handler:

dropdown = system.gui.getParentWindow(event).getComponentForPath('Root Container.Dropdown')
if dropdown.isPopupVisible():
	event.source.popupVisible = True
else: 
	event.source.popupVisible = False

Here is the result:

Is there something I've missed? Does anybody know a simple way to do this?

I have figured out a better way of doing this. Instead of targeting the button, the new method changes the background of the text area and dropdown menu without effecting the button or the border. Therefore, the button color is still controlled by the traditional background property while the rest of the component is changed through scripting by replacing the dropdown's cell renderer with a custom renderer.

Here is the result:
image

image

Here is the script:

from com.inductiveautomation.plaf import ComboListCellRenderer
if event.propertyName == 'componentRunning':
	# Override the cell renderer for custom functionality
	class ColorCellRenderer(ComboListCellRenderer):
		def getListCellRendererComponent(self, list, value, index, isSelected, cellHasFocus):
			ComboListCellRenderer.getListCellRendererComponent(self, list, value, index, isSelected, cellHasFocus)
			if cellHasFocus or isSelected:
				self.background = event.source.selectionBackground
			else:	
				self.background = system.gui.color('black')
			return self
	renderer = ColorCellRenderer()
	event.source.setRenderer(renderer)

Edit: Updated this code, so it has proper interactions with the Ignition look and feel in versions 8.0.x and 8.1.x

1 Like

What about 'selectionBackground'...? :face_with_monocle:

1 Like

Excellent point; I hadn't considered that! I've adjusted the code to include that behavior.

1 Like