Question

I can't figure out how to coerce Quaqua into using a unified toolbar on my window.

I have done everything I have seen other post suggesting to fix. Most people seem to forget the setFloatable(false) but ours always had that.

Anyway, I condensed it down into a simple example:

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;

import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import ch.randelshofer.quaqua.QuaquaManager;

public class QuaquaUnifiedToolbarTest implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new QuaquaUnifiedToolbarTest());
    }

    @Override
    public void run() {
        try {
            UIManager.setLookAndFeel(QuaquaManager.getLookAndFeel());
        } catch (UnsupportedLookAndFeelException e) {
            // Ignore it.
        }

        JToolBar toolBar = new JToolBar();
        toolBar.putClientProperty("Quaqua.ToolBar.style", "title");
        toolBar.setFloatable(false);

        ButtonGroup paneButtonGroup = new ButtonGroup();
        for (int i = 1; i <= 3; i++) {
            JToggleButton button = new JToggleButton("Section " + i);
            button.setIcon(new PlaceholderIcon());
            button.setHorizontalTextPosition(SwingConstants.CENTER);
            button.setVerticalTextPosition(SwingConstants.BOTTOM);
            button.putClientProperty("JComponent.sizeVariant", "small");
            button.putClientProperty("Quaqua.Button.style", "toolBarTab");
            paneButtonGroup.add(button);
            toolBar.add(button);
        }

        JDialog dialog = new JDialog(null, "Preferences", Dialog.ModalityType.MODELESS);
        dialog.setLayout(new BorderLayout());
        dialog.add(toolBar, BorderLayout.PAGE_START);
        dialog.setSize(800, 600);
        dialog.setVisible(true);
    }

    private static class PlaceholderIcon implements Icon {
        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            Graphics2D g2d = (Graphics2D) g;
            g2d.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_SQUARE,
                                          BasicStroke.JOIN_BEVEL, 1.0f,
                                          new float[] {  2.0f, 2.0f }, 1.0f));
            g2d.draw(new Rectangle(x, y, 32, 32));
        }

        @Override
        public int getIconWidth() {
            return 32;
        }

        @Override
        public int getIconHeight() {
            return 32;
        }
    }
}

The result looks like this:

screenshot of my example

This is what Transmission looks like:

screenshot of Transmission

Our example toolbar lacks the unified toolbar shading. Also, you can't drag the toolbar with the mouse to drag the window. I'm sure both of these issues stem from the same problem, Quaqua somehow not realising that I want this toolbar unified.

I have tried setting apple.awt.brushMetalLook on the root pane to true. This has a negative effect of making the window always look disabled and does nothing to help it show as a unified toolbar.

(( Miscellaneous other visual issues not related to this question:

  1. The antialiasing on the text isn't using LCD-style antialiasing (this is a bug in Java 7 with no known workaround.)
  2. There is no embossing (I think I know how to fix this but it's going to involve making my own ButtonUI.)
  3. The active button is just shaded darker, whereas in the real one, there is a bit of a "pushed in" look (oddly, the default AquaToggleButtonUI gets this right, so it's a rare case where Quaqua has provided a less native look than the default.) ))
Was it helpful?

Solution

Here's an extremely hacky solution which relies on disabling bits of Quaqua to get the root pane to do the right thing, since I have discovered that the default Aqua look and feel does work correctly.

Odd things:

  • Quaqua removes the border on the toolbar button.
  • If you try to use JDialog instead of JFrame, you get the original issue.

.

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.HashSet;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.WindowConstants;

import ch.randelshofer.quaqua.QuaquaManager;

public class SheetTest implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new SheetTest());
    }

    @Override
    public void run() {
        try {
            QuaquaManager.setExcludedUIs(new HashSet<>(Arrays.<String>asList(
                "RootPane"
            )));
            UIManager.setLookAndFeel(QuaquaManager.getLookAndFeel());
        } catch (Exception ignored) {}

        JButton button = new JButton("Sheet me!");
        JToolBar toolBar = new JToolBar();
        JScrollPane content = new JScrollPane();
        JFrame frame = new JFrame();

        toolBar.setFloatable(false);
        toolBar.setOpaque(false);
        toolBar.add(button);

        frame.setLayout(new BorderLayout());
        frame.add(toolBar, BorderLayout.PAGE_START);
        frame.add(content, BorderLayout.CENTER);
        // Under current Aqua L&F, this doesn't actually look like brushed metal.
        // It looks like normal window titles.
        frame.getRootPane().putClientProperty("apple.awt.brushMetalLook", true);
        frame.setSize(400, 300);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top