Reputation: 1
I am trying to write an application which, for UX purposes, I want the UI to have a light mode and a dark mode... as a result, I have two color variables, and I have a getForeground() and a getBackground() that returns the appropriate colors, based on the setting of my boolean DarkMode. What I would like is to find a way to make the existing components in the window re-initialize their settings, IE: reset Background and Foreground, without having to dispose and reopen the frame. My button works to toggle the flag, and new panels opened and drawn after it's clicked are properly toggled between light and dark mode, but I can't figure out how to make the less-often-updated panels redraw themselves without loosing their current contents.
I have tried using
frame.invalidate(); frame.validate(); frame.repaint();
I have also tried
frame.setVisible(false); frame.setVisible(true);
and both those options on the JPanels as well, nothing changes the existing color palette save moving to a different panel. I feel like there must be something simple I'm missing.
JPanel navPanel = new JPanel();//Navigation panel for the left side of the app
/*
* buttons for broad concept areas of the application to be always pinned at the left
*/
NavButton employeeButton = new NavButton("Employees", Main.getEmployeesIcon());
NavButton settingsButton = new NavButton("Settings", Main.getSettingsIcon());
NavButton scheduleButton = new NavButton("Schedules", Main.getScheduleIcon());
NavButton saveButton = new NavButton("Save Data", Main.getSaveIcon());
navPanel.setBackground(Main.background());
navPanel.setOpaque(true);
navPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
navPanel.setPreferredSize(new Dimension(180,180));
navPanel.add(employeeButton);
navPanel.add(scheduleButton);
navPanel.add(saveButton);
navPanel.add(settingsButton);
navPanel.add(new NavButton("Mode Change", Main.getSaveIcon()) {
{
addActionListener(ae ->{
Main.toggleDarkMode();
SwingUtilities.updateComponentTreeUI(mainWindow);
});
}
});
Upvotes: 0
Views: 89
Reputation: 347314
Switching the look & feel at run time is a fluke side effect and the API was never designed to actually support it, just beware that this could break in unexpected ways.
The "real" answer is to design for it from the start. That is, design an actual look and feel which supports light and dark mode (or design two which can be switched between). This allows you to define the "default" values every component should use and makes it significantly easier to switch between them.
I mean, as yourself the question, if the background color has been defined programmatically, should you be changing it?
"Another" solution would be to modify the default values used by the current look and feel. This is some what problematic as not all look and feels use the same key sets to define there default values and don't even get me started on JButton
.
The following is an overly simplified concept of the idea. When I say "simplified", I'm not kidding. There were some 719 key's been used by the UIManager
and I didn't start looking at things like the Border
properties.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.ColorUIResource;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
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 {
public TestPane() {
setBorder(new EmptyBorder(32, 32, 32, 32));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JLabel("Hello"), gbc);
add(new JTextField("Some text", 20), gbc);
JButton flip = new JButton("Flip");
add(flip, gbc);
flip.addActionListener(new ActionListener() {
private boolean isDark = false;
@Override
public void actionPerformed(ActionEvent e) {
isDark = !isDark;
System.out.println("IsDark " + isDark);
// It would be better to store the "default" values at launch,
// to make it easier to switch back to
UIManager.put("Panel.background", isDark ? new ColorUIResource(Color.BLACK) : new ColorUIResource(new Color(238, 238, 238)));
UIManager.put("Label.foreground", isDark ? new ColorUIResource(Color.WHITE) : new ColorUIResource(Color.BLACK));
UIManager.put("Label.disabledForeground", isDark ? new ColorUIResource(new Color(192, 192, 192)) : new ColorUIResource(Color.BLACK));
UIManager.put("Label.background", isDark ? new ColorUIResource(Color.BLACK) : new ColorUIResource(new Color(238, 238, 238)));
UIManager.put("TextField.background", isDark ? new ColorUIResource(new Color(64, 64, 64)) : new ColorUIResource(Color.WHITE));
UIManager.put("TextField.foreground", isDark ? new ColorUIResource(Color.WHITE) : new ColorUIResource(Color.BLACK));
SwingUtilities.updateComponentTreeUI(TestPane.this);
JRootPane rootPane = SwingUtilities.getRootPane(TestPane.this);
if (rootPane == null) {
return;
}
rootPane.updateUI();
}
});
}
}
}
You might also consider having a look around and seeing what else is available.
For example...
Upvotes: 0
Reputation: 11030
According to Oracle it's:
Changing the Look and Feel After Startup
You can change the L&F with setLookAndFeel even after the program's GUI is visible. To make existing components reflect the new L&F, invoke the SwingUtilities updateComponentTreeUI method once per top-level container. Then you might wish to resize each top-level container to reflect the new sizes of its contained components. For example:
UIManager.setLookAndFeel(lnfName);
SwingUtilities.updateComponentTreeUI(frame);
frame.pack();
https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html#dynamic
Upvotes: 1