How to use the built-in PLAF with custom components? - java

I'm creating a custom button, and I'm having trouble getting it to look right on most of the built-in PLAFs.
Here's my code
public MyButton(String text, Icon icon) {
if (icon == null) icon = createDefaultIcon();
mainButton = new JButton(text);
popupButton = new JButton(icon);
removeBorder(mainButton);
removeBorder(popupButton);
setModel(new DefaultButtonModel());
setBorder(UIManager.getBorder("Button.border"));
int popupButtonWidth = popupButton.getPreferredSize().width;
int popupButtonHeight = mainButton.getPreferredSize().height;
Dimension popupButtonSize = new Dimension(popupButtonWidth, popupButtonHeight);
popupButton.setMinimumSize(popupButtonSize);
popupButton.setPreferredSize(popupButtonSize);
popupButton.setMaximumSize(popupButtonSize);
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
add(mainButton);
add(new JSeparator(VERTICAL));
add(popupButton);
}
private void removeBorder(JButton button) {
Border border = button.getBorder();
if (border instanceof CompoundBorder) {
button.setBorder(((CompoundBorder) border).getInsideBorder());
} else {
button.setBorder(BorderFactory.createEmptyBorder());
}
}
Here is how the button looks in the PLAFs installed on my computer
Metal
Nimbus
CDE/Motif
Mac OS X
CDE/Motif is the only one that works properly. I looked at the source for some of the ButtonUIs, and it seems they can ignore the background color and the borders. Unfortunately the background color and the borders are what I need to set. How do I get my custom button to support the built-in PLAFs correctly?
Edit:
As requested, here's the code I used to produce the images
public class MyButtonDemo implements Runnable {
public void run() {
// Change the array index to get a different PLAF
try {
UIManager.setLookAndFeel(UIManager.getInstalledLookAndFeels()[0].getClassName());
} catch (Exception ignored) { }
JFrame frame = new JFrame();
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(new MyButton("My Button", null);
frame.getContentPane().add(new JButton("Normal Button"));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new MyButtonDemo());
}
}

Welcome to the wonderful world of pluggable look & feel. The only real choices you have, that I can think of, is provide. UI delegate for each platform or have a look at at the Syththetic UI

Not sure what I did, but it's working now for all but Nimbus. Here's the code
public MyButton(String text, Icon icon) {
arrowIcon = createDefaultArrowIcon();
mainButton = new JButton(text, icon);
popupButton = new JButton(arrowIcon);
mainButton.setBorder(BorderFactory.createEmptyBorder());
popupButton.setBorder(BorderFactory.createEmptyBorder());
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
add(mainButton);
add(new JSeparator(VERTICAL));
add(popupButton);
setModel(new DefaultButtonModel());
init(null, null);
}
#Override
public void updateUI() {
setUI((ButtonUI)UIManager.getUI(this));
}
#Override
public String getUIClassID() {
return "ButtonUI";
}

Related

JPanel ontop of other JPanels

I would like to make a JPanel pop up over an other JPanel in java. However I can not find anything useful on the internet. I tried playing around with absolute positioning but things like buttons from the bottom layer are showing through the top layer.
I uploaded a really ugly drawing of what I want to do.
Is there an easy way of doing this?
Edit:
I tried to make what "Ulkra" suggested. Here is the code and a screenshot:
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(new Dimension(400, 400));
JPanel panel = new JPanel();
JPanel foregroundPanel = new JPanel();
foregroundPanel.setVisible(false);
foregroundPanel.setBackground(Color.BLUE);
foregroundPanel.setLayout(new BoxLayout(foregroundPanel, BoxLayout.Y_AXIS));
JPanel backgroungPanel = new JPanel();
backgroungPanel.setBackground(Color.RED);
backgroungPanel.setLayout(new BoxLayout(backgroungPanel, BoxLayout.Y_AXIS));
for (int i = 0; i < 10; i++) {
backgroungPanel.add(new JButton("BackgroundBtn " + i));
}
foregroundPanel.add(new JButton("ForegroundBtn 1"));
JButton makeVisibleBtn = new JButton("+");
makeVisibleBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
foregroundPanel.setVisible(true);
}
});
backgroungPanel.add(makeVisibleBtn);
JButton makeInvisibleBtn = new JButton("-");
makeInvisibleBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
foregroundPanel.setVisible(false);
}
});
foregroundPanel.add(makeInvisibleBtn);
panel.add(backgroungPanel);
panel.add(foregroundPanel);
frame.add(panel);
}
}
For this, you have to use a Panel (if you are using NetBeans, you can find it in Swing Containers) and in the constuctor of the Frame, you have to SET IT AS UNVISIBLE, so you need the buttom to see it: (In this example, i call my Panel as "jPanel1" and i put a buttom in it)
public Main() {
initComponents();
jPanel1.setVisible(false);//<-- This!
this.setLocationRelativeTo(null);
}
Then, you create the buttom to "activate it" and but this code in:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
jPanel1.setVisible(true);
}
(And of course, if you wanna set it as unvisible you just have to do the same as in the constructor).
I test it and it works:(Imgur is not working so excuse i have to use postimg.org)
https://postimg.org/gallery/1z6fpajva/
Now you could put a border so you can differentiate your new Panel from your other Frame:
https://postimg.org/image/mdpf7hvxx/

How to use JColorChooser given Color in other classes?

I have been learning Java just a few weeks so bear that in mind.. I have managed to solve many issues myself until this one:
What I am trying to achieve is using JComponent in a tabbed view so that in one tab you're able to pick a colour (my println shows it actually gets an sRGB value). Then in another class I should be able to get this value and use it in coloring JPanels.
Can I just pass the Color object or what is the best way to achieve. What I am trying here is not working too well. Sorry for the messy code - I am a newbie...
Here is the main part of the color chooser:
public class Colors extends JPanel implements ChangeListener {
private JColorChooser jcc = null;
protected JLabel title;
static Color newColor;
public Colors() {
super(new BorderLayout());
//Set up the banner at the top of the window
title = new JLabel();
//Set up color chooser for setting text color
jcc = new JColorChooser(title.getForeground());
jcc.getSelectionModel().addChangeListener(this);
jcc.setBorder(BorderFactory.createTitledBorder(
"Choose New Color"));
AbstractColorChooserPanel[] panels=jcc.getChooserPanels();
for(AbstractColorChooserPanel p:panels) {
String displayName = p.getDisplayName();
switch (displayName) {
case "HSV":
jcc.removeChooserPanel(p);
break;
case "HSL":
jcc.removeChooserPanel(p);
break;
case "CMYK":
jcc.removeChooserPanel(p);
break;
case "RGB":
jcc.removeChooserPanel(p);
break;
}
}
add(jcc, BorderLayout.PAGE_END);
}
public void stateChanged(ChangeEvent e) {
Color newColor = jcc.getColor();
title.setForeground(newColor);
System.out.println("color = " + newColor);
}
public static Color getNewCol() {
System.out.println("this now =" + newColor);
return newColor;
}
public static Component createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Color Selector");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new Colors();
return newContentPane;
}
}
And then I am trying to get hold of the chosen color in the Main.Java class in this way (which is likely wrong). I also note that the value seems to remain always null - probably I am instantiating in a wrong way (value gets lost?)
a.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Click add");
value++;
if (value < 21) {
JButton jb1 = new JButton();
//How to get the COLOR from the JColorChooser class?
Color box = Colors.getNewCol();
jb1.setBackground(box);
jb1.setOpaque(true);
//FOR TEST ONLY jb1.setBackground(Color.BLUE);
panel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
panel.add(jb1);
panel.setVisible(true);
//window.add(paneeli);
window.pack();
d = new Dimension(700, 500);
window.setSize(d);
Probably the answer is too obvious but I just can't see it at the moment.
I'd get take your static newColor variable and make it non-static. In my ChangeListener I'd fire the JPanel's innate PropertyChangeSupport so that listeners can be notified. The key is to use an observer design pattern -- For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.event.*;
#SuppressWarnings("serial")
public class TestColors extends JPanel {
private JTabbedPane tabbedPane = new JTabbedPane();
private Colors colors = new Colors();
private JPanel colorDisplayPanel = new JPanel();
public TestColors() {
tabbedPane.add("Colors", colors);
tabbedPane.add("Color Display", colorDisplayPanel);
setLayout(new BorderLayout());
add(tabbedPane);
// add a PropertyChangeListener to our Colors isntance.
colors.addPropertyChangeListener(Colors.NEW_COLOR,
new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
Color color = (Color) evt.getNewValue();
colorDisplayPanel.setBackground(color);
}
});
}
private static void createAndShowGui() {
JFrame frame = new JFrame("TestColors");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new TestColors());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class Colors extends JPanel implements ChangeListener {
public static final String NEW_COLOR = "new color";
private JColorChooser jcc = null;
protected JLabel title;
private Color newColor = null;
public Colors() {
super(new BorderLayout());
// Set up the banner at the top of the window
title = new JLabel("This is my Banner!", SwingConstants.CENTER);
// Set up color chooser for setting text color
jcc = new JColorChooser(title.getForeground());
jcc.getSelectionModel().addChangeListener(this);
jcc.setBorder(BorderFactory.createTitledBorder("Choose New Color"));
add(jcc, BorderLayout.CENTER);
add(title, BorderLayout.PAGE_START);
}
public void stateChanged(ChangeEvent e) {
// Color newColor = jcc.getColor();
Color oldValue = newColor;
newColor = jcc.getColor();
// fire a notification to the Colors JPanel's property change support
// object. Any listeners will be notified of the color change
firePropertyChange(NEW_COLOR, oldValue, newColor);
title.setForeground(newColor);
}
public Color getNewCol() {
System.out.println("this now =" + newColor);
return newColor;
}
}

Translucent loading overlay for JFrame

I have a JFrame containing various components and I would like to add a translucent grey overlay over the top while the application is initializing various things. Ideally it would prevent interaction with the underlying components and would be able to display some "Loading..." text or a spinning wheel or something similar.
Is there a simple way to do this using Java and Swing?
Take a look at JRootPane and JLayeredPane http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html#layeredpane
What you're asking about specifically sounds like a Glass Pane.
http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html#glasspane
The Glass Pane prevents interaction with underlying components and can be used to display something on top of your JFrame.
As #David said, you can use the glass pane for displaying some loading text or image above the rest of the application.
As for the grey overlay: why don't you use the built in ability to disable components as long as your application is loading? Disabled components will get grayed out automatically and cannot be interacted with by the user.
Something like this:
public class LoadingFrame extends JFrame{
JButton button;
public LoadingFrame() {
button = new JButton("ENTER");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Application entered");
}
});
setLayout(new BorderLayout());
add(button, BorderLayout.CENTER);
}
public void startLoading(){
final Component glassPane = getGlassPane();
final JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
final JLabel label = new JLabel();
panel.add(label, BorderLayout.SOUTH);
setGlassPane(panel);
panel.setVisible(true);
panel.setOpaque(false);
button.setEnabled(false);
Thread thread = new Thread(){
#Override
public void run() {
for (int i = 5; i > 0; i--) {
label.setText("Loading ... " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// loading finished
setGlassPane(glassPane);
button.setEnabled(true);
}
};
thread.start();
}
public static void main(String[] args) {
LoadingFrame frame = new LoadingFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.startLoading();
frame.setVisible(true);
}
}

Get JPopupMenu size before it is shown

I have the JPopupMenu shown on right mouse click. I want JPopupMenu' top right corner to be at the click location (not top left one, as default). To perform this, I need to set the X coordinate as mouseEvent.getX() - popupMenu.getWidth(). The problem is, before popup is shown first time, its width equals 0.
SSCCE:
public class PopupTest2 {
public static void main(String[] a) {
final JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.RED));
final JPopupMenu menu = new JPopupMenu();
for (int i = 0; i < 10; i++) {
JMenuItem item = new JMenuItem("Item #"+String.valueOf(i));
menu.add(item);
}
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
// first time works wrong
menu.show(panel, e.getX() - menu.getWidth(), e.getY());
}
}
});
frame.setContentPane(panel);
frame.setUndecorated(true);
frame.setBackground(new Color(50, 50, 50, 200));
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.setVisible(true);
}
});
}
}
Using the preferredSize (as already mentioned) is the way to go for getting the location.
But (being on a quest here :-): manually showing the popup is not the recommended approach. A really clean implementation will use the componentPopupMenu property and implement getPopupLocation as needed, something like
JPanel panel = new JPanel(new BorderLayout()) {
#Override
public Point getPopupLocation(MouseEvent event) {
JPopupMenu menu = getComponentPopupMenu();
if (menu == null || event == null) return null;
return new Point(event.getX() - menu.getPreferredSize().width, event.getY());
}
};
JPopupMenu menu = new JPopupMenu();
panel.setComponentPopupMenu(menu);
What's preferred size of the JPopupMenu before showing?
Probably you can determine the width of context menu as the highest preferred width of ist elements?
Using popup.getPreferredSize().width and popup.getPreferredSize().height you can get popup size before it is shown.
This is an example locating the popup menu at the top of a button:
but_menu = new JButton("");
but_menu.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
Point p = but_menu.getLocation();
Point dest = new Point();
dest.x = 0 - popup.getPreferredSize().width + but_menu.getWidth();
dest.y = 0 - popup.getPreferredSize().height;
popup.show(but_menu,dest.x,dest.y);
}
});

Java ComponentResized - Detect whether user resized the window or it was resized programatically

I have a JFrame in which I remove and add components and each time I do this, I resize the frame accordingly. I added a Component Listener but of course it gets triggered both by the user resizing the window and also by my setSize() methods.
Is there any way to see from the ComponentEvent whether the user resized the window or I did through setSize()?
The posible solutions I found are:
1. Use a flag - boolean resizing - which I set to true before setSize() and to false after that.
2. Add a mouseDragged listener and compare sizes before and after the drag.
The second one is definitely not a good choice. The first one would work but I would like to know if I can find in a simple way whether the user is the one who resized the window or not.
I resize the frame accordingly
Whats wrong with using pack()?
I remove and add components and each time I do this,
Then this is where you should set your Boolean value:
programResize == true:
panel.add(...);
frame.setSize(...); // this should cause the ComponentListener to fire
// the ComponentListener would then use programResize == false;
Or a better option option could be:
component.removeComponentListener(...);
panel.add(...);
frame.setSize(...);
component.addComponentListener(...);
I like this approach better because all the logic based on the manual update is self contained in one place and there is no need to define a Boolean variable.
Is there any way to see from the ComponentEvent whether the user
resized the window or I did through setSize()?
yes it is, use boolean flag reseted by Swing Timer
in the case that user resized window then ComponentListener firing a new event per every pixel, bunch of events
by determine the contianer set(Xxx)Size is this event fired only once time
example about events from ComponentListner
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
public class ComponentEventDemo extends JPanel
implements ComponentListener, HierarchyListener,
ItemListener {
private JFrame frame;
private static final long serialVersionUID = 1L;
private JTextArea display;
private JLabel label;
private JButton button = new JButton("Change Size");
private String newline = "\n";
public ComponentEventDemo() {
display = new JTextArea();
display.setEditable(false);
JScrollPane scrollPane = new JScrollPane(display);
scrollPane.setPreferredSize(new Dimension(350, 200));
label = new JLabel("This is a label", JLabel.CENTER);
label.addComponentListener(this);
JCheckBox checkbox = new JCheckBox("Label visible", true);
checkbox.addItemListener(this);
checkbox.addComponentListener(this);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Dimension dim = frame.getPreferredSize();
if (!dim.equals(new Dimension(800, 600))) {
frame.setPreferredSize(new Dimension(800, 600));
frame.pack();
} else {
frame.setPreferredSize(new Dimension(400, 300));
frame.pack();
}
}
});
JPanel panel = new JPanel(new GridLayout(1, 3));
panel.add(label);
panel.add(checkbox);
panel.add(button);
panel.addComponentListener(this);
frame = new JFrame("ComponentEventDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane, BorderLayout.CENTER);
frame.add(panel, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
label.setVisible(true);
label.revalidate();
label.repaint();
} else {
label.setVisible(false);
}
}
protected void displayMessage(String message) {
//If the text area is not yet realized, and
//we tell it to draw text, it could cause
//a text/AWT tree deadlock. Our solution is
//to ensure that the text area is realized
//before attempting to draw text.
// if (display.isShowing()) {
display.append(message + newline);
display.setCaretPosition(display.getDocument().getLength());
//}
}
#Override
public void componentHidden(ComponentEvent e) {
//displayMessage(e.getComponent().getClass().getName() + " --- Hidden");
}
#Override
public void componentMoved(ComponentEvent e) {
//displayMessage(e.getComponent().getClass().getName() + " --- Moved");
}
#Override
public void componentResized(ComponentEvent e) {
displayMessage(e.getComponent().getClass().getName() + " --- Resized ");
}
#Override
public void componentShown(ComponentEvent e) {
//displayMessage(e.getComponent().getClass().getName() + " --- Shown");
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ComponentEventDemo componentEventDemo = new ComponentEventDemo();
}
});
}
#Override
public void hierarchyChanged(HierarchyEvent e) {
displayMessage(e.getComponent().getClass().getName() + " --- Hierarchy changed");
}
}

Categories

Resources