twilker
twilker

Reputation: 1427

Is it possible to change the component style at runtime?

I use swing + nimbus to style my component. I want to change the style of a component at runtime with "Nimbus.Overrides".

private void SetExceptionState() {
    //password.setBackground(new Color(200,0,0,120));
    UIDefaults overrides = new UIDefaults();
    overrides.put("PasswordField.background", Color.red);
    password.putClientProperty("Nimbus.Overrides", overrides);
    password.revalidate();
    password.updateUI();
}

private void ResetExceptionState() {
    //password.setBackground(Color.white);
    UIDefaults overrides = new UIDefaults();
    overrides.put("PasswordField.background", Color.white);
    password.putClientProperty("Nimbus.Overrides", overrides);
}

The first time I set the overrides, lets say with SetExceptionState() method it works. I get a red background. The second time I use this nothing happens. It seems, that the overrides are evaluated only once.

What I want is to introduce a new state ofthe passwordfield and style it different. Is there any possibility to do so?

Best regards,

Yggdrasil

Upvotes: 0

Views: 372

Answers (1)

kleopatra
kleopatra

Reputation: 51524

Yes, it is possible and Nimbus actually listens to changes of the "Nimbus.Overrides" - just: it does not uninstall some properties if they are !instanceof UIResource That's the case for background, foreground, font at least (might be others as well)

In your context, you did install a not-uiresource of RED initially, effectively telling the laf to not touch it again - and it complies :-)

The only way I could make it work, was to null the background before setting the new overrides, like in:

private void setExceptionState(JComponent password) {
    password.setBackground(null);
    UIDefaults overrides = new UIDefaults();
    overrides.put("PasswordField.background", Color.RED);
    password.putClientProperty("Nimbus.Overrides", overrides);
}

private void resetExceptionState(JComponent password) {
    password.setBackground(null);
    UIDefaults overrides = new UIDefaults();
    overrides.put("PasswordField.background", Color.WHITE);
    password.putClientProperty("Nimbus.Overrides", overrides);
}

Update

Actually, the above doesn't answer the real question:

introduce a new state of the passwordfield and style it different

Nimbus indeed does allow to add custom states (though the outcome is somewhat unpredicable, as often with that unloved youngest child of Synth ;-) The way to go is

  • implement a custom State
  • register the additonal state with the ui-defaults
  • attach properties for that state as needed

All this configuration has to be done after the LAF is installed and before the first JPasswordField is instantiated, most probably (didn't test) posing problems if the LAF is switched at runtime.

protected void installCustomPasswordFieldState() {
    // implement a custom state
    State<JPasswordField> state = new State<JPasswordField>("Invalid") {

        @Override
        protected boolean isInState(JPasswordField c) {
            Object invalid = c.getClientProperty("Invalid");
            return Boolean.TRUE.equals(invalid);
        }

    };
    UIDefaults defaults = UIManager.getLookAndFeelDefaults();
    // register available states 
    // note: couldn't find a way to grab the already available states
    // so this is guesswork
    defaults.put("PasswordField.States", "Enabled, Focused, Invalid");
    // install the custom state
    defaults.put("PasswordField.Invalid", state);
    // install the properties for the custom state
    // note: background has no effect
    defaults.put("PasswordField[Invalid].background", 
            Color.RED); 
    javax.swing.Painter<JComponent> p = new javax.swing.Painter<JComponent>() {

        @Override
        public void paint(Graphics2D g, JComponent object, int width, int height) {
            g.setColor(Color.RED);
            // this is crude - overpainting the complete area, do better!
            g.fillRect(0, 0, width, height);
        }

    };
    // using a painter has an effect 
    defaults.put("PasswordField[Invalid].backgroundPainter", p);
}

// example usage, toggling
// a new property (for simplicity implemented as clientProperty
// to toggle the invalid state
Action reset = new AbstractAction("reset") {
    @Override
    public void actionPerformed(ActionEvent e) {
        boolean isInvalid = Boolean.TRUE.equals(field.getClientProperty("Invalid")); 
        if (isInvalid) {
            field.putClientProperty("Invalid", null); 
        } else {
            field.putClientProperty("Invalid", Boolean.TRUE); 
        }
        field.repaint();
    }
};

Upvotes: 6

Related Questions