Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switching from another look-and-feel sometimes creates inconsistent combobox visuals #17

Closed
kirill-grouchnikov opened this issue Oct 21, 2019 · 8 comments

Comments

@kirill-grouchnikov
Copy link

image

See how the background behind the arrows is darker than the rest of the combobox fill.

public class Test {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Throwable t) {
                    t.printStackTrace(System.err);
                }

                JComboBox comboHebrew = new JComboBox(new Object[] { "\u05e8\u05d0\u05e9\u05d9 1",
                        "\u05e8\u05d0\u05e9\u05d9 2", "\u05e8\u05d0\u05e9\u05d9 3",
                        "\u05e8\u05d0\u05e9\u05d9 4", "\u05e8\u05d0\u05e9\u05d9 5",
                        "\u05e8\u05d0\u05e9\u05d9 6", "\u05e8\u05d0\u05e9\u05d9 7",
                        "\u05e8\u05d0\u05e9\u05d9 8", "\u05e8\u05d0\u05e9\u05d9 9" });
                comboHebrew.setToolTipText("RTL combo");
                comboHebrew.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
                comboHebrew.setMaximumRowCount(6);

                JComboBox comboRegular = new JComboBox(new Object[] { "item 1",
                        "item 2", "item 3",
                        "item 4", "item 5",
                        "item 6", "item 7",
                        "item 8", "item 9" });
                comboRegular.setToolTipText("LTR combo");
                comboRegular.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
                comboRegular.setMaximumRowCount(6);

                JButton toFlat = new JButton("Set FlatDark!");
                toFlat.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    UIManager.setLookAndFeel("com.formdev.flatlaf.FlatDarkLaf");
                                    for (Window w : Window.getWindows()) {
                                        SwingUtilities.updateComponentTreeUI(w);
                                    }
                                } catch (Throwable t) {
                                    t.printStackTrace(System.err);
                                }
                            }
                        });
                    }
                });

                JButton toNimbus = new JButton("Set Nimbus!");
                toNimbus.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    UIManager.setLookAndFeel(new NimbusLookAndFeel());
                                    for (Window w : Window.getWindows()) {
                                        SwingUtilities.updateComponentTreeUI(w);
                                    }
                                } catch (Throwable t) {
                                    t.printStackTrace(System.err);
                                }
                            }
                        });
                    }
                });

                JButton toMetal = new JButton("Set Metal!");
                toMetal.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    UIManager.setLookAndFeel(new MetalLookAndFeel());
                                    for (Window w : Window.getWindows()) {
                                        SwingUtilities.updateComponentTreeUI(w);
                                    }
                                } catch (Throwable t) {
                                    t.printStackTrace(System.err);
                                }
                            }
                        });
                    }
                });

                JFrame frame = new JFrame("Combo test");
                frame.setLayout(new BorderLayout());

                JPanel flow = new JPanel(new FlowLayout());
                comboRegular.setPreferredSize(new Dimension(comboRegular.getPreferredSize().width + 50,
                        comboRegular.getPreferredSize().height));
                flow.add(comboRegular);
                comboHebrew.setPreferredSize(new Dimension(comboHebrew.getPreferredSize().width + 50,
                        comboHebrew.getPreferredSize().height));
                flow.add(comboHebrew);
                frame.add(flow, BorderLayout.NORTH);

                JPanel flowButtons = new JPanel(new FlowLayout());
                flowButtons.add(toNimbus);
                flowButtons.add(toMetal);
                flowButtons.add(toFlat);
                frame.add(flowButtons, BorderLayout.SOUTH);

                frame.setSize(400, 400);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}
@DevCharly
Copy link
Collaborator

Good catch 👍

The problem is that the AquaComboBoxUI changes opaque to false and does not restore it correctly. And FlatComboBoxUI does not paint correctly if opaque is false...

It is always a problem when a LaF changes something in the component and does not restore it correctly.

There is a similar problem with Windows LaF (in Java 9 and later), that sets a non-UIResouce EmptyBorder at the combobox. When switching from Windows LaF to FlatLaf the combobox borders are gone:

image

Probably have to remember what LaF was active before switching to FlatLaf to fix those issues...

@kirill-grouchnikov
Copy link
Author

I'm guilty of not restoring that in Substance as well (for combo boxes). I do it for some other controls (like buttons), storing the original opacity when the UI delegate is installed, and resetting it when it is uninstalled.

The thing I don't like about this is what happens when the application code calls setOpacity in the middle of the app "flow". I have an explicit FAQ entry saying that apps shouldn't do that. Probably I should restore the opacity bit for comboboxes.

@DevCharly
Copy link
Collaborator

For the opaque property, the solution is to use following in installDefaults() (after invoking super method):

LookAndFeel.installProperty( comboBox, "opaque", Boolean.FALSE);

(change last parameter to TRUE if needed).

This invokes JComponent.setOpaque(...) only if it was not set explicitly.

But this works only correctly if the BasicXyzUI (or all other LaFs) also invokes this method, to reset opaque when switching to another LaF. BasicComboBoxUI, BasicButtonUI and many other Basic UIs use this method.

@kirill-grouchnikov
Copy link
Author

Convenience method for installing a property with the specified name and value on a component if that property has not already been set by the developer

Unfortunately with how Substance paints the control visuals (rounded corners, partial translucency in some cases), I have to set opacity to false no matter what the application "preference" is.

@DevCharly
Copy link
Collaborator

I see. Because of the round corners?

In FlatLaf are also round corners. And "focused" borders outside of the components (in Darcula and IntelliJ themes). But the components are opaque.

image

The trick I use (borrowed from IDEA) is to fill the background of the component with the the background color of a non-opaque parent.

https://github.com/JFormDesigner/FlatLaf/blob/master/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java#L136-L157

Works fine as long as the panel uses a solid color. Will fail for gradient or image backgrounds...

@kirill-grouchnikov
Copy link
Author

Right, my preference is to say that Substance doesn't play well with application calls to setOpacity. It can get pretty complicated if the child component needs to paint the parent's background with nested multi-colored panels, toolbars etc.

JFormDesigner pushed a commit that referenced this issue Oct 25, 2019
…paque.

`JPasswordField`, `JScrollPane` and `JTextField` are non-opaque if they have
an outside focus border (e.g. IntelliJ and Darcula themes).
(issues #20 and #17)
@DevCharly
Copy link
Collaborator

fixed in master

LookAndFeel.installProperty( comboBox, "opaque", Boolean.FALSE); works well for me and the components now even paint well if opaque was explicitly set to true. But it is important to fill the whole background in MyUI.update(Graphics g, JComponent c) somehow if opaque is true. Otherwise I had some garbage (if single components are repainted).

@JFormDesigner
Copy link
Owner

fixed in 0.17

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants