Set the background button of the nimbus button

I am working on an application using Nimbus Look and Feel. There the table and one column contain buttons (using the Column button table from Rob Camick ). This works, but the result is not what I expected. I tried to fix the look, but to no avail.

So the question is: how do I change the “background” (the area outside the rounded rectangle) of the Nimbus button? Preferably not hacks: ")

Using the default table column button, the result is as follows:

Buttons with incorrect background

As you can see, the background (and by this I mean the area outside the rounded rectangle) is incorrect for odd (white) lines. The code that produces this output is:

public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (isSelected) { renderButton.setForeground(table.getSelectionForeground()); renderButton.setBackground(table.getSelectionBackground()); } else { renderButton.setForeground(table.getForeground()); renderButton.setBackground(table.getBackground()); } if (hasFocus) { renderButton.setBorder( focusBorder ); } else { renderButton.setBorder( originalBorder ); } // <snip some code> renderButton.setOpaque(true); return renderButton; } 

renderButton is the default instance of JButton . I tried to spoil the background color of the button, but this did not work, as I expected at the beginning:

  Color alternate = (Color)LookAndFeel.getDesktopPropertyValue("Table.alternateRowColor", Color.lightGray); Color normal = (Color)LookAndFeel.getDesktopPropertyValue("Table.background", Color.white); if (row % 2 == 0) { renderButton.setBackground(normal); } else { renderButton.setBackground(alternate); } 

This gives:

Also incorrect button background

This time, the buttons that look good in the first image are now bad and vice versa. The button’s inner backgrounds (the areas inside the rounded rectangles) appear to have the correct color according to the background color property (which really changed when setBackground() called). But the area outside is all wrong. Ok, let me combine the two:

  Color alternate = table.getBackground(); Color normal = (Color)LookAndFeel.getDesktopPropertyValue("Table.background", Color.white); if (row % 2 == 0) { renderButton.setBackground(normal); } else { renderButton.setBackground(alternate); } 

Result:

Still incorrect buttons

So, now the "background" looks correct, but the buttons are no longer similar to the Nimbus buttons. How to make the “background” have the right color, still looking like Nimbus buttons?

+6
source share
2 answers

Do not install the JButton background. Use JPanel to migrate JButton and set the background to JPanel. That would be obvious if you used more buttons in a single JTable column.

To set the correct JPanel background color, I did (you should):

  • Keep a link to the original renderer
  • Allows the original render to display its own component (for each render)!
  • Use the background of the rendered component to set the JPanel background (for each rendering)!

Thus, you do not need to choose the right color

You also need to override paintComponent to correctly draw the white JPanel background:

 @Override protected void paintComponent(Graphics g) { Color background = getBackground(); setBackground(new Color(background.getRGB())); super.paintComponent(g); } 

Edit: as @kleopatra suggests , you do not need to override paintComponent, set the background color to not-uiresource ( shown in the full example )

Here is a complete example:

 import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.table.TableCellRenderer; public class Test { public static void main(String[] args) throws Throwable { for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { UIManager.setLookAndFeel(info.getClassName()); break; } } String[] columnNames = new String[]{"c1"}; Object[][] data = new Object[4][1]; data[0][0] = "First"; data[1][0] = "Second"; data[2][0] = "Third"; data[3][0] = "Fourth"; JTable table = new JTable(data, columnNames){ @Override public javax.swing.table.TableCellRenderer getCellRenderer(int row, int column) { final TableCellRenderer ori = super.getCellRenderer(row, column); final TableCellRenderer mine = new TableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component c = ori.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); JPanel p = new JPanel(); if(value == null){ value = ""; } p.add(new JButton(value.toString())); p.setBackground(new Color(c.getBackground().getRGB())); return p; } }; return mine; }; }; table.setRowHeight(50); JFrame f = new JFrame(); f.add(table); f.setVisible(true); f.pack(); } } 

Result:

Result

+1
source

Below is a hacky way, following @Piro's recommendations: using JPanel with a button as a child component. Which in itself is a good idea, given that we really don't want to touch the “internal” background images of the button.

This hacking occurs when it forces the Nimbus internals not to use the default JPanel background to fill their area, but instead to use the background of this panel instance. This requires the use of implementation details, especially a background background search engine. This happens in SynthStyle.getColor ():

 // If the developer has specified a color, prefer it. Otherwise, get // the color for the state. Color color = null; if (!id.isSubregion()) { if (type == ColorType.BACKGROUND) { color = c.getBackground(); } .... } if (color == null || color instanceof UIResource) { // Then use what we've locally defined color = getColorForState(context, type); } 

Translated: it does indeed request the color of the instance, but overrides it by default if the color of the instance is a UIResource, which usually occurs if used as a visualization tool. So the trick (SynthBooleanRenderer tried unsuccessfully, but this is another story ;-) should make the instance color not UIResource. Another feature is that a UIRsource is needed to ensure that the striping color, which is not a UIResource type, haha ​​- is applied ... intuitively, isn't it?

 public class RendererPanel implements TableCellRenderer { private JComponent panel; private JButton button; public RendererPanel() { panel = new JPanel(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(3, 10, 2, 10)); button = new JButton(); panel.add(button); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { // suggestion by Piro - use background of default DefaultTableCellRenderer dt = (DefaultTableCellRenderer) table.getDefaultRenderer(Object.class); dt.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); // first try: set the color as-is - doesn't work // panel.setBackground(dt.getBackground()); // second try: set color as not ui-resource // that working because at this point we already have the color that will be used // let hinder synth background color searching to fall back to component defaults panel.setBackground(new Color(dt.getBackground().getRGB())); // hack: unwrap ui-resource as needed // updateBackground(isSelected ? table.getSelectionBackground() : table.getBackground(), row); button.setText(String.valueOf(value)); return panel; } private void updateBackground(Color color, int row) { Color hack = row % 2 == 0 ? unwrap(color) : color; panel.setBackground(hack); } private Color unwrap(Color c) { if (c instanceof UIResource) { return new Color(c.getRGB()); } return c; } } 

Screenshot: with unpacking hack

enter image description here

Screenshot: Using default colors (from the renderer installed for Object.class)

enter image description here

An unsafe exit may be (I have not tried it here, but remember that I did it once) to register a Region with a style, similar to what NimbusDefaults does inside:

 register(Region.PANEL, "Table:\"Table.cellRenderer\""); 

The problem is that there is no public api for this (or it may be that I just don't know enough about Synth;)

+2
source

Source: https://habr.com/ru/post/951589/


All Articles