Java Swing macOS buttons are grey instead of blue when selected - java

I have a bug in my Swing program where the background of JButton and JToggleButton components is grey instead of blue, when the component is selected. Here is a screenshot of the issue on a JToggleButton, but the same issue occurs when pressing down on a JButton.
I have tried manually setting the background with
button.setBackground(Color.BLUE);
button.setOpaque(true);
but that just paints a small border around the button:
Also added button.setBorderPainted(false); which removes the border but also removes the macOS button look entirely + gives the text a darker background color:
It looks like the border (AquaButtonBorder) blocks the background color from changing or something? Because simply setting button.setBackground(Color.BLUE); does absolutely nothing.
The look and feel is set to UIManager.getSystemLookAndFeelClassName(), but also tried UIManager.getCrossPlatformLookAndFeelClassName() which was unsuccessful.
Java specifications: running on Java 11, but the same issue persists in Java 17
System specifications: M1 MacBook Pro, macOS version 12.4

Sooo, I spent WAY to much time digging into the source code for the "Aqua" look and feel, and I found that the AquaButtonUI has a "button type" client property which is used to (amongst a couple of other things) determine the border type.
Sooo, I pulled all the internal "types" and did a quick test...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.border.EmptyBorder;
public class Main {
public static void main(String[] args) {
System.out.println(System.getProperty("user.dir"));
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private String[] keys = new String[]{
"toolbar",
"icon",
"text",
"toggle",
"combobox",
"comboboxInternal",
"comboboxEndCap",
"square",
"gradient",
"bevel",
"textured",
"roundRect",
"recessed",
"well",
"help",
"round",
"texturedRound",
"segmented-first",
"segmented-middle",
"segmented-last",
"segmented-only",
"segmentedRoundRect-first",
"segmentedRoundRect-middle",
"segmentedRoundRect-last",
"segmentedRoundRect-only",
"segmentedTexturedRounded-first",
"segmentedTexturedRounded-middle",
"segmentedTexturedRounded-last",
"segmentedTexturedRounded-only",
"segmentedTextured-first",
"segmentedTextured-middle",
"segmentedTextured-last",
"segmentedTextured-only",
"segmentedCapsule-first",
"segmentedCapsule-middle",
"segmentedCapsule-last",
"segmentedCapsule-only",
"segmentedGradient-first",
"segmentedGradient-middle",
"segmentedGradient-last",
"segmentedGradient-only",
"disclosure",
"scrollColumnSizer"
};
public TestPane() {
setBorder(new EmptyBorder(32, 32, 32, 32));
setLayout(new BorderLayout());
JPanel contentPane = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
for (String key : keys) {
JToggleButton btn = new JToggleButton(key);
btn.putClientProperty("JButton.buttonType", key);
contentPane.add(btn, gbc);
}
add(new JScrollPane(contentPane));
}
}
}
Generally, I found that bevel and segmented-only will achieve your desired result (although segment-first/middle/last looks interesting)

Related

How to draw two JPanels on top of each other?

I am currently working on a N-Body simulation and I have made particles move on a black screen. My current problem is that there is no way of controlling it.
My plan:
Each color stands for a different JPanel. The blue one should contain the buttons and text fields, the red one the viewport.
But with my small knowledge in Java, I didn't succeed in creating this. I first tried with setBounds and setLayoutManager(null), in vain.
My structure goes like that:
Window class extends JFrame
Simulation class creating blueJPanel class (extends JPanel) and redJPanel,
adds them to the window.
But this is garbage code... So how would you draw these simple panels on top of each other?
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay particular attention to the Laying Out Components Within a Container section.
As I said in my comment, you create two JPanels. Here's an example.
Here's the complete runable code to create this example.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class BorderLayoutExampleGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new BorderLayoutExampleGUI());
}
#Override
public void run() {
JFrame frame = new JFrame("BorderLayout Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createButtonPanel(), BorderLayout.NORTH);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
panel.setPreferredSize(new Dimension(600, 100));
panel.setBackground(Color.blue);
// Add the buttons and text fields
return panel;
}
private JPanel createMainPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
panel.setPreferredSize(new Dimension(600, 380));
panel.setBackground(Color.red);
// Add the drawing code
return panel;
}
}

jPanel and jButton customization

I'm trying to learn how to code Conway's game of life in Java, and I'm getting stuck creating the GUI. I want to make a JPanel within the JFrame, with a larger border at South, and then two buttons in the south border, one for "Play" and one for "Restart." But the Design element won't let me resize or move anything around. I was able to resize the JPanel by going into the code and creating a larger border in the South, but I can't figure out how to resize the JButton. Any ideas?
(I'm using Eclipse Kepler...I hear NetBeans is better at this kind of stuff, should I just ditch Eclipse and try it with NetBeans?)
Here's my code so far:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import java.awt.Color;
import java.awt.FlowLayout;
import javax.swing.JButton;
public class GameOfLife extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GameOfLife frame = new GameOfLife();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public GameOfLife() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 518, 508);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(10, 10, 50, 10));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JPanel panel = new JPanel();
panel.setBackground(Color.GRAY);
contentPane.add(panel, BorderLayout.CENTER);
JButton btnNewButton = new JButton("New button");
contentPane.add(btnNewButton, BorderLayout.SOUTH);
}
}
Basically, instead of doing BorderLayout.SOUTH, I want to manually place it where I want it within the frame. I'd also love to be able to do that with the JPanel--the whole North/West/South/East/Center thing in general seems very constricting. What's the way around it?
Maybe you should look at Swing Layouts in Java documentation:
A Visual Guide to Layout Managers
And the layout which is able to give you the most flexibility is the GridBagLayout but you will write much code to display the User Interface as your needs.
You will have a detailled way to go with the following official tutorial:
How to Use GridBagLayout
In your code, you are using the simple BorderLayout which is very simple but not so much configurable.

Fixed sized items in Swing

I am rather new to this whole Swing, but nevertheless it already got me quite annoyed.
I am trying to do something simple, that behaves like WPF's list with custom item template. That is, item are of fixed size and as it overflows the given area a scroll bar pops up.
And I've been trying and trying, but I just can't get it to work. The closest I got was with BoxLayout, the problem there however, is that if there are too few items to take available space, they get stretched -.-
I bet there is some simple way to achieve that, I just don't know about. Thanks in advance.
Here's the code I got (java):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class App
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new MainFrame();
}
});
}
}
class MainFrame extends JFrame
{
private JPanel itemsPanel;
private JButton addButton;
public MainFrame()
{
// create components
itemsPanel = new JPanel();
addButton = new JButton("Add");
// layout
itemsPanel.setLayout(new BoxLayout(itemsPanel, BoxLayout.Y_AXIS));
JPanel buttons = new JPanel(new FlowLayout(FlowLayout.LEFT));
buttons.add(addButton);
setLayout(new BorderLayout());
add(new JScrollPane(itemsPanel), BorderLayout.CENTER);
add(buttons, BorderLayout.SOUTH);
// actions
addButton.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0)
{
itemsPanel.add(new SampleItem());
itemsPanel.revalidate();
}
});
// frame size and close action
setDefaultCloseOperation(EXIT_ON_CLOSE);
Dimension size = new Dimension(300, 300);
setMinimumSize(size);
setSize(size);
setVisible(true);
}
}
class SampleItem extends JPanel
{
public SampleItem()
{
setBorder(BorderFactory.createLineBorder(Color.black));
setPreferredSize(new Dimension(200, 100));
}
}
EDIT:
I ended up writing custom renderer and editor thanks to rcook's answer.
EDIT2:
Eh, after turning it in, I got scolded really badly for this... Apparently the problem is that JScrollPane resizes viewport so that the control fills all available space and the solution is to create JPanel implements Scrollable and return false in public boolean getScrollableTracksViewportHeight(). Oh well, I hope someone will find it useful, editors are just so much pain.
Use a JList, put it inside a JScrollPane, put that within a pane in the middle part of a BorderLayout; BorderLayout is the default for a JFrame, so you may not need to create one. Put the lower button on the bottom portion of the BorderLayout.

TextArea is overflowing bounds assigned to it as well as the center of the Borderlayout and covering the entire window

So I asked something similar to this earlier and got the answer to the question I asked, but I apparently didn't ask the right question. So, what I'm trying to accomplish here is have the text area populate only in the center of the jFrame. As you can see, it is set up as a borderlayout, and there are buttons on the bottom, a label on the top (which will then - on the full version of this program - be copied into the text frame when it is replaced with text from the action listener on the button)
The problem is, the text area fills the entire window and covers up every other component on the window. I've tried to use preferred size, and I tried to specify columns/rows and I've read the tutorials on docs.oracle, although I suppose since I'm still having trouble i may have missed one.
Also, the offset commented lines I found in the docs.oracle information and it would be a good idea for this to text-wrap because it's going to be a log of what has occurred. I tried adding all the imports suggested on that website but netbeans still gives me a red underline that they're not recognized as commands. Have they been deprecated, did I not use them right, or am I missing an import?
I know I'm asking a lot, but thanks for your time and patience!
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package theproblem;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.TextArea;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
/**
*
* #author Heather
*/
public class TheProblem {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
JFrame window2 = new JFrame();
TextArea battleLogging = new TextArea(3,10);
JScrollPane logScrollPane = new JScrollPane(battleLogging);
JLabel BattleLog = new JLabel();
JLabel p1HPLabel= new JLabel();
JLabel p2HPLabel= new JLabel();
String attack1ButtonContents = "Just an attack";
String attack2ButtonContents = "Just another attack";
JButton attack1=new JButton(attack1ButtonContents);
JButton attack2=new JButton(attack2ButtonContents);
window2.setLayout(new BorderLayout());
window2.setSize(400,400);
JPanel attackPanel = new JPanel();
attackPanel.add(attack1);
attackPanel.add(attack2);
window2.add(battleLogging, BorderLayout.CENTER);
battleLogging.setEditable(false);
logScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
logScrollPane.setPreferredSize(new Dimension(50, 50));
//battleLogging.setLineWrap(true);
//battleLogging.setWrapStyleWord(true);
window2.add(BattleLog, BorderLayout.NORTH);
window2.add(p1HPLabel, BorderLayout.WEST);
window2.add(p2HPLabel, BorderLayout.EAST);
window2.setVisible(true);
window2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Problems:
When adding any component BorderLayout.CENTER, it fills the center position, no matter what size or preferred size you give it.
You shouldn't even be setting sizes.
Don't use TextAreas with Swing apps. Use JTextAreas
Set the JTextArea's column and row count and let that do its sizing for you.
Don't forget to pack your GUI before displaying it.
For example:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.*;
public class TheProblem2 {
private static void createAndShowGUI() {
int rows = 5;
int cols = 20;
JTextArea textArea = new JTextArea(rows, cols);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JButton button1 = new JButton("Button 1");
JButton button2 = new JButton("Button 1");
JPanel btnPanel = new JPanel(new GridLayout(1, 0, 5, 0));
btnPanel.add(button1);
btnPanel.add(button2);
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.add(scrollPane);
mainPanel.add(btnPanel, BorderLayout.SOUTH);
JFrame frame = new JFrame("EG 2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

Java-Eclipse-Trying to use JTextArea

I have spent the last few hours of the past two days trying to figure out why I can't get my GUI to work right. Here is where I come to all of you to see if you can help me at all.
Now what i'm trying to do is a Text Based game, simple right? Well I want to use buttons lined up on the left side of the screen, with your health and all that fun stuff on the bottom, with the main meat of the game, the TEXT, in the center, but no matter what i did i couldn't get it to fit right, then i stumbled upon pack() so i tried it, and yea...it did what it was supposed to do, sadly right now i have ONE WORD and the screen is tiny, any ideas as to how i could make the screen bigger, and remain big?
Going to edit this and throw you bits and peices of my code, including for the textarea (please note i have been using java for a whopping two days while not at work)
JTextArea textarea = new JTextArea("example");
frame.add(textarea);
//frame.pack();
And here i have pack commented out because i'm playing around with it.
Anything else you guys want to take a peek at?
There are so many things that could be going wrong, without more code it's difficult to pin point an exact problem...
A few things jump to mind...
Use appropriate layout managers, even compound layout managers
Provide additional information to the JTextArea to allow it to define a usable space, such as rows and columns
Add the JTextArea to a JScrollPane to ensure that it doesn't suddenly occupy more space then is actually available
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestLayout21 {
public static void main(String[] args) {
new TestLayout21();
}
public TestLayout21() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
JPanel actions = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill= GridBagConstraints.HORIZONTAL;
actions.add(new JButton("Walk"), gbc);
actions.add(new JButton("Run"), gbc);
actions.add(new JButton("Jump"), gbc);
actions.add(new JButton("Eat"), gbc);
actions.add(new JButton("Drink"), gbc);
add(actions, BorderLayout.WEST);
add(new JProgressBar(), BorderLayout.SOUTH);
JTextArea textArea = new JTextArea(20, 40);
add(new JScrollPane(textArea));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
You could try setting the preferred size on your objects or the panel/frames themselves:
myPanel.preferredSize(new Dimension(1000,500));
or
myFrame.preferredSize(new Dimension(1000,500));
or
myTextArea.preferredSize(new Dimension(300,200));
Pack will indeed just make your frame or panel only as big as needed to fit the components who are in it. Try preferredSize() method, and maybe post some code if it still isn't working.

Categories