Let me first explain what I'm looking to achieve. I'm creating a data entry form in Swing, made up of a number of JComboBoxes & JTextFields. A validation routine iterates over those components and determines if the values specified for each control are 'valid' (the details of the validation are irrelevant for the purposes of this example).
When the routine identifies that a component contains an invalid value, I want to change the background color of that field, and also the foreground/text color of that field - to make it clear to the user that there is a problem with that field.
Where a field is deemed 'valid', I want to set the background of the control to be white - and the foreground/text to be black.
All pretty straightforward so far, and all achievable within the attached demo code below.
When a Combo Box contains a valid value, and is focused - the background of the editor within the combo is set to a bluish color, which I'm perfectly happy with.
However, what I'm trying to achieve is to change the color that is used to highlight a focused combo box when that combo box contains an invalid value. Despite having changed the background color of the combo box to be pink, if the control is focused, it still uses the blue color to indicate that it is focused.
Example of invalid field that is focused: http://postimg.org/image/ne9xgjch3/
Though I appreciate that this is perfectly normal behaviour, what I would like to do, is change the color that is used to highlight one of the 'invalid' fields to a darker shade of the color that a non-focused & invalid control would have - so that the user can still see which control is focused, and it still be pink all over. I appreciate that this might seem petty, but my end user is insistant that the whole field remains pink (or rather, a different shade of pink) when focused. This is what my eutopia, a focused and 'invalid' field, would look like:
http://postimg.org/image/9793bqcfj/
I've tried extending the DefaultListCellRenderer & BasicComboBoxEditor classes, and setting those against the combo box as the renderer and editor respectively. I was under the impression that the Editor would be where I needed to focus my attentions, so within the getEditorComponent method of the class, I would return a label with an appropriate background and foreground - however from within that method, I have no way of knowing whether or not the control has focus, so have no way of determining how I should format the returned label. Additionally, as soon as I started setting an Editor against the combobox, I seemed to lose the ability to focus the control at all - though that may have been my lack of knowledge on how to implement the editor.
I've been reading about the BasicComboBoxUI too, but nothing I've come across has stood out as the solution.
Please can someone kindly point me in the right direction, I've spent days tinkering with this, and it's really starting to bug me. Please excuse the netbeans generated demo code, it was just to allow me to knock together a demo quickly.
package com.test;
import java.awt.*;
public class TestForm extends javax.swing.JFrame {
public TestForm()
{
initComponents();
}
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
cboOne = new javax.swing.JComboBox();
txtOne = new javax.swing.JTextField();
txtTwo = new javax.swing.JTextField();
btnValidate = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
cboOne.setBackground(new java.awt.Color(255, 255, 255));
cboOne.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Valid Value", "Invalid Value", "Another Invalid Value" }));
txtOne.setText("123");
txtTwo.setText("123");
btnValidate.setText("Validate");
btnValidate.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnValidateActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(cboOne, 0, 376, Short.MAX_VALUE)
.addComponent(txtOne)
.addComponent(txtTwo)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addComponent(btnValidate)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(cboOne, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtOne, javax.swing.GroupLayout.PREFERRED_SIZE, 51, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtTwo, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(btnValidate)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void btnValidateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnValidateActionPerformed
//Check if the selection in the ComboBox is valid...
if (((String)cboOne.getSelectedItem()).equals("Valid Value"))
{
//Selected Value is Valid.
//We want the combo box to appear with a white background
//and black text.
cboOne.setBackground(Color.white);
cboOne.setForeground(Color.black);
}
else
{
//The value specified is invalid.
//We want to highlight the field in pink to identify an issue,
//and change the color of the text to red too:
cboOne.setBackground(Color.pink);
cboOne.setForeground(Color.red);
}
//Check if the value entered into the first Text Field is valid...
if (txtOne.getText().equals("123"))
{
//Selected Value is Valid.
//We want the text box to appear with a white background
//and black text.
txtOne.setBackground(Color.white);
txtOne.setForeground(Color.black);
}
else
{
//Selected Value is invalid.
//We want the text box to appear with a pink background
//and red text.
txtOne.setBackground(Color.pink);
txtOne.setForeground(Color.red);
}
//Check if the value entered into the second Text Field is valid...
if (txtTwo.getText().equals("123"))
{
//Selected Value is Valid.
//We want the text box to appear with a white background
//and black text.
txtTwo.setBackground(Color.white);
txtTwo.setForeground(Color.black);
}
else
{
//Selected Value is invalid.
//We want the text box to appear with a pink background
//and red text.
txtTwo.setBackground(Color.pink);
txtTwo.setForeground(Color.red);
}
}//GEN-LAST:event_btnValidateActionPerformed
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new TestForm().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnValidate;
private javax.swing.JComboBox cboOne;
private javax.swing.JComboBox jComboBox1;
private javax.swing.JComboBox jComboBox2;
private javax.swing.JTextField txtOne;
private javax.swing.JTextField txtTwo;
// End of variables declaration//GEN-END:variables
}