I want to set the background color of JButton. For that I use
setBackground() method.
This method jsut sets the border color of the button and not the whole button of the specified color. Why so ? This is the only method to set background color of a button. Where am I making a mistake due to which it sets just the border of the button of specified color and not actual button ?
Code :
account_btn.setAction(actionMap.get("AccountingClicked")); // NOI18N
account_btn.setBackground(Utility.getBackgroundColor());
account_btn.setFont(Utility.getButtonFont());
account_btn.setForeground(Utility.getTextColor());
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(cashaccountingapp.CashAccountingApp.class).getContext().getResourceMap(MainPanel.class);
account_btn.setText(resourceMap.getString("account_btn.text")); // NOI18N
account_btn.setBorderPainted(false);
account_btn.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
account_btn.setName("account_btn"); // NOI18N
account_btn.setOpaque(true);
add(account_btn);
Result :
Have tried setting setOpaque(true) also. But you can see the results of account_btn i.e. "Accounting". setOpaque seems to have no effects.
Any idea.
SOLUTION :
Setting L&F
private void initLookandFeel() {
try {
System.out.println("DEFAULT Look & Feel = " + UIManager.getLookAndFeelDefaults().toString());
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
javax.swing.SwingUtilities.updateComponentTreeUI(this.mainPanel);
System.out.println("Look & Feel = " + UIManager.getLookAndFeel().toString());
} catch(Exception e) { ..... }
}
I call initLookandFeel() after initComponents() and also update my mainPanel. Also needed to update my dynamically added panel at initial stage then no need to set anything more.
import java.awt.*;
import javax.swing.*;
class ColoredButtons {
ColoredButtons() {
JPanel gui = new JPanel(new GridLayout(1,0,5,5));
JButton one = new JButton("One");
one.setBackground(Color.RED);
JButton two = new JButton("Two");
two.setBackground(Color.RED);
gui.add(one);
gui.add(two);
JOptionPane.showMessageDialog(null, gui);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ColoredButtons();
}
});
}
}
There is my SSCCE. The buttons are red. The PLAF is metal.
Which brings me back to: Where is your SSCCE? What PLAF are you using?
I believe the Jbutton background is controlled by the particular look-and-feel you are using. To change the background you may need to modify the
setUI(ComponentUI newUI)
with your own one.
Try setting border painted to false and opaque true
account_btn.setBorderPainted(false);
account_btn.setOpaque(true);
Related
I want to change my JLabel background to blue using mouseClicked. The name of my JLabel is lblKembali. I tried this code and when I tried to click the label it didnt change the background. Please help. Thank you.
lblKembali = new JLabel("Kembali");
lblKembali.setPreferredSize(new Dimension(400,30));
lblKembali.setMaximumSize(getPreferredSize());
lblKembali.addMouseListener(new java.awt.event.MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
lblKembali.setBackground(Color.BLUE);
}
});
By default a JLabel is non-opaque so its background is not painted. You need to make the label opaque when you create it:
lblKembali = new JLabel("Kembali");
lblKembali.setOpaque( true );
Also you can make your listener more generic so it can be shared by multiple components by doing:
public void mouseClicked(MouseEvent e)
{
Component c = e.getComponent();
c.setBackground(Color.BLUE);
}
When you set the icon for a Swing control (say a JButton), a disabled icon with grayed-darker colors automatically generates. I want to be able to imitate the way the icon colors are changed for the disabled state, so I can create my own disabled icon with a little twist, and set it as the disabled button.
Is there a way to achieve this (other then first instantiating the control and retrieving it's icon, I'm looking for more straight-forward less hacky way)?
Is there a way to achieve this (other then first instantiating the control and retrieving it's icon
Well the LAF is responsible for creating the disabled Icon. The getDisabledIcon() method from the AbstractButton button class looks something like this:
Icon disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, getIcon());
So in theory the LAF getDisabledIcon() method expects the component to be passed in as a parameter. So the answer to your question would be: "yes, the button needs to be created first".
However in practice it appears (for Metal and Windows at least) that the component is not actually used in the creation of the icon so you could do something like:
ImageIcon original = new ImageIcon( ... );
Icon disabled = UIManager.getLookAndFeel().getDisabledIcon(null, original);
Using this approach is risky because other LAF's may indeed need the component to create the disabled Icon.
However, instead of passing null, I guess you could create a single component and then call this method with multiple different icons, so it would still be a little better than creating a unique component for each Icon.
Here is the SSCCE I used to test this approach:
import javax.swing.*;
import javax.swing.plaf.metal.*;
import java.awt.*;
import java.awt.image.*;
public class ButtonDisabledIcon extends JPanel
{
public ButtonDisabledIcon()
{
ImageIcon original = new ImageIcon( "dukewavered.gif" );
JButton button1 = new JButton( "Original" );
button1.setIcon( original );
add(button1);
JButton button2 = new JButton( "Disabled" );
button2.setIcon(original);
button2.setEnabled(false);
add(button2);
JButton button3 = new JButton( "LAF Disabled" );
button3.setIcon( UIManager.getLookAndFeel().getDisabledIcon(null, original) );
add(button3);
}
private static void createAndShowUI()
{
/*
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e) { }
*/
JFrame frame = new JFrame("Button Disabled Icon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new ButtonDisabledIcon() );
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Amplifying on #camickr's answer, you can experiment with getGray() and this L&F toolbar.
JToggleButton jtb = new JToggleButton("Plain", icon);
jtb.setEnabled(true);
this.add(jtb);
jtb = new JToggleButton("Disabled", icon);
jtb.setEnabled(false);
this.add(jtb);
jtb = new JToggleButton("Gray enabled", getGray(icon));
jtb.setEnabled(true);
this.add(jtb);
jtb = new JToggleButton("Gray disabled", getGray(icon));
jtb.setDisabledIcon(getGray(icon));
jtb.setEnabled(false);
this.add(jtb);
In the below code I am attempting to move the three buttons to the left when you click the left button. When I click it; nothing happens currently. Can anyone explain to me what I am doing wrong here? Also, for some reason it has stopped compiling correctly and I am unsure why but I BELIEVE it is because of a mistake in my code while attempting to get the buttons to move to the left when you click the button. I do NOT want the window to move. Just the buttons within the window. Does any one see what I am doing wrong and can you explain it?
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Buttons extends JFrame {
//Control Definitions
JButton resetButton;
JButton leftButton;
JButton colorButton;
JPanel buttonPanel;
// Layout Definiton
eventHandle evt;
FlowLayout flt;
Point point; //to Hold Previous Window Position
Color color; //to Hold Previous Color
public Buttons() {
super("Buttons Window");
flt = new FlowLayout();//inialize the Flow Layout
buttonPanel = new JPanel(flt);
//inialize the buttonPanel With Flow Layout
//initialize buttons
resetButton = new JButton("Reset");
leftButton = new JButton("Left");
colorButton = new JButton("Blue");
evt = new eventHandle(); //initiate the eventhandle class
buttonPanel.add(leftButton); //add leftButton
buttonPanel.add(colorButton);//add colorButton
buttonPanel.add(resetButton);//add colorButton
getContentPane().add(buttonPanel);//buttonPanel
//add actionlistners
leftButton.addActionListener(evt);
colorButton.addActionListener(evt);
resetButton.addActionListener(evt);
setBounds(20, 120, 250, 70);
//following Initate the point with Center of Scren
point = new Point((Toolkit.getDefaultToolkit().
getScreenSize().width - getWidth()) / 2,
(Toolkit.getDefaultToolkit().getScreenSize().height
- getHeight()) / 2);
setLocation(point); //locates the window in center
color = buttonPanel.getBackground();//stores the initial color
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
class eventHandle implements ActionListener { //Event Handler
public void actionPerformed(ActionEvent e) {
{
if (e.getSource() == leftButton) ///if its from leftButton
{
leftButton.setAlignmentX(Component.LEFT_ALIGNMENT);
colorButton.setAlignmentX(Component.LEFT_ALIGNMENT);
resetButton.setAlignmentX(Component.LEFT_ALIGNMENT);
//setLocation( (point.x -150), point.y);//shift the window 150 pixels left
} else if (e.getSource() == colorButton) {
buttonPanel.setBackground(color.BLUE);
//sets the backgorund to Blue
} else {
leftButton.setAlignmentX(Component.CENTER_ALIGNMENT);
//sets the location to previous location
colorButton.setAlignmentX(Component.CENTER_ALIGNMENT);
resetButton.setAlignmentX(Component.CENTER_ALIGNMENT);
}
}
}
}
public static void main(String[] args) {
Buttons buttonwindow = new Buttons();
}
}
It has stopped compiling, because you deleted one accolade, so put one accolade "}" just above the method:
public static void main(String[] args)
and the code should compile. pls feedback.
EDIT:
Also rewrite your main method like this:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Buttons buttonwindow = new Buttons();
}
}
);
}
Every usage of Swing components must be done thorugh the Event Dispatch Thread (abbreviated EDT) or you will probably get unwanted visual effects. See here for explanation.
EDIT^2:
To achieve the desired behavior, rewrite the the action listener like this:
if (e.getSource() == leftButton) {
((FlowLayout)buttonPanel.getLayout()).setAlignment(FlowLayout.LEFT); //1
buttonPanel.revalidate(); //2
}
else if (e.getSource() == colorButton) {
buttonPanel.setBackground(color.BLUE);
}
else {
((FlowLayout)buttonPanel.getLayout()).setAlignment(FlowLayout.CENTER);
buttonPanel.revalidate();
}
Any change to the visual appereance to the Swing component must be done through the assigned layout manager, in this case FlowLayout - in line 1.
To see the change you must notify the Swing components layout manager to rearrange the components - in line 2 the revalidate() method "notifies" the layout manager to recalculate the new positions and eventually "notifies" the EDT to draw it on the screen.
You should update the layout manager to align the components to the left or right. Try something like;
((FlowLayout)getLayout()).setAlignment(FlowLayout.LEFT);
Instead
You code won't compile as the static main method appears inside the inner class eventHandle. You can fix simply by moving it into the class body of the outer class Buttons.
As you have all the objects references at class level, you could do the button alignment using, for instance:
flt.setAlignment(FlowLayout.RIGHT);
buttonPanel.revalidate();
...
Here you are adjusting the layout alignment of your FlowLayout and revalidating to visually reflect the updated changes on your panel.
I have a JTable that is wider than the JScrollPane it is contained in (essentially defined like this):
JTable table = new JTable(model);
// I change some things like disallowing reordering, resizing,
// disable column selection, etc.
// I set the default renderer to a DefaultTableCellRenderer
// getTableCellRendererComponent, and then changes the color
// of the cell text depending on the cell value
JPanel panel = new JPanel(new BorderLayout(0, 5));
panel.add(new JScrollPane(table), BorderLayout.CENTER);
// add other stuff to the panel
this.add(panel, BorderLayout.CENTER);
Before I changed the look and feel from the default to Nimbus, I was able to scroll left and right in the JTable. (I like the Mac LaF, but it isn't supported on Windows, and the Windows LaF is ugly in my opinion),
I took the following code straight from the Java Tutorials:
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception e) {
// If Nimbus is not available, you can set the GUI to another look
// and feel.
}
I recompiled and ran the code without changing any of the table definition stuff above, and I couldn't scroll horizontally in the JTable anymore.
I can't seem to find anything on what would cause this. Is this the normal behavior for Nimbus, or can I change it? If so, how? or should I just try a different look and feel?
EDIT:
I discovered two things:
I made a new class extending JTable to test this. I copied the code for getScrollableUnitIncrement from the JTable source, and added print statements. The orientation that is passed seems to always be SwingConstants.VERTICAL, while in the default Look and Feel (Mac Aqua or whatever), both horizontal and vertical scrolling works. I don't know why this is.
Another part of the project also relies on horizontal scrolling. I tested it with both LaFs, and it worked fine in the default, but Nimbus would not allow me to scroll horizontally, either.
Could this be a bug with Nimbus?
Either way, I guess I'm going to use a different Look and Feel...
EDIT #2:
I should have mentioned this before. I am able to scroll horizontally with the scroll bar in the window, but not with my track pad or scroll wheel on my mouse.
(Note: After writing this, I found a solution, which appears in the addendum of this post.)
To reproduce the problem, you need to make the scroll bars required. (This is why some people have trouble reproducing this bug.) This means the obvious workaround is to make your horizontal scroll bar optional. (This is not always practical.)
You will only see the bug when you drag the window's width out to more than 1200 pixels or so. Until then, the scroll bar will work fine.
And the problem only shows up in Nimbus. (It may show up in other L&Fs created from the SynthLookAndFeel, but I haven't investigated that yet.)
I've found that the spurious scroll bar thumb only shows up when you have no need to scroll, so it's just a visual bug. When you need to scroll, the scroll bar thumb will appear and will work properly, although it might not be the right size. This may be why it hasn't been fixed yet.
Here's an example where you can compare the different L&Fs. In this example, Choose Nimbus, then drag the width inward and watch how the size of the scroll bar changes. When you're wider than the background image, the spurious scroll bar will be visible. As soon as you get narrower, a valid scroll bar thumb will appear, but it will be a bit too small. As you get smaller, the scroll bar thumb will stay a constant size until you reach a certain point, (at viewport width of 1282 pixels) then it will start getting smaller like it's supposed to.
With any other L&F, as soon as you get narrower than the background image, a thumb will appear that almost fills its space. It gets smaller as the window gets smaller, like it's supposed to.
(This exercise will also reveal how Nimbus draws much more slowly than any other L&F.)
You can observe different, but still incorrect behavior by making the icon smaller. Try 800 x 450. The spurious scroll bar will appear when the viewport width is > 1035. (Viewport size is shown at the bottom of the window.)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* NimbusScrollBug
* <p/>
* #author Miguel Muñoz
*/
public class NimbusScrollBug extends JPanel {
private static final long serialVersionUID = -4235866781219951631L;
private static JFrame frame;
private static boolean firstTime = true;
private static Point location;
private static final UIManager.LookAndFeelInfo[] INFOS
= UIManager.getInstalledLookAndFeels();
private final JLabel viewPortLabel = new JLabel();
public static void main(final String[] args) {
makeMainFrame(new NimbusScrollBug(), "System");
}
public static void makeMainFrame(final NimbusScrollBug mainPanel,
final String name) {
if (firstTime) {
installLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
frame = new JFrame(name);
final Container contentPane = frame.getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(mainPanel, BorderLayout.CENTER);
contentPane.add(makeButtonPane(mainPanel), BorderLayout.LINE_START);
frame.setLocationByPlatform(firstTime);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setVisible(true);
if (firstTime) {
location = frame.getLocation();
} else {
frame.setLocation(location);
}
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentMoved(final ComponentEvent e) {
location = e.getComponent().getLocation();
}
});
firstTime = false;
}
private static JPanel makeButtonPane(final NimbusScrollBug mainPanel) {
JPanel innerButtonPanel = new JPanel(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.BOTH;
constraints.gridx = 0; // forces vertical layout.
for (final UIManager.LookAndFeelInfo lAndF : INFOS) {
final JButton button = new JButton(lAndF.getName());
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
frame.dispose();
installLookAndFeel(lAndF.getClassName());
makeMainFrame(new NimbusScrollBug(), lAndF.getName());
}
});
innerButtonPanel.add(button, constraints);
}
final String version = System.getProperty("java.version");
JLabel versionLabel = new JLabel("Java Version " + version);
innerButtonPanel.add(versionLabel, constraints);
JPanel outerButtonPanel = new JPanel(new BorderLayout());
outerButtonPanel.add(innerButtonPanel, BorderLayout.PAGE_START);
return outerButtonPanel;
}
private static void installLookAndFeel(final String className) {
//noinspection OverlyBroadCatchBlock
try {
UIManager.setLookAndFeel(className);
} catch (Exception e) {
//noinspection ProhibitedExceptionThrown
throw new RuntimeException(e);
}
}
private NimbusScrollBug() {
Icon icon = new Icon() {
#Override
public void paintIcon(final Component c, final Graphics g,
final int x, final int y) {
Graphics2D g2 = (Graphics2D) g;
g2.translate(x, y);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Stroke lineStroke = new BasicStroke(6.0f);
g2.setStroke(lineStroke);
g2.setColor(Color.white);
g2.fillRect(0, 0, getIconWidth(), getIconHeight());
g2.setColor(Color.RED);
g2.drawLine(0, 0, getIconWidth(), getIconHeight());
g2.drawLine(0, getIconHeight(), getIconWidth(), 0);
g2.dispose();
}
#Override
public int getIconWidth() {
return 1600;
}
#Override
public int getIconHeight() {
return 900;
}
};
JLabel label = new JLabel(icon);
setLayout(new BorderLayout());
final JScrollPane scrollPane = new JScrollPane(label,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
label.addHierarchyBoundsListener(new HierarchyBoundsAdapter() {
#Override
public void ancestorResized(final HierarchyEvent e) {
viewPortLabel.setText("ViewPort Size: "
+ scrollPane.getViewport().getSize());
}
});
add(scrollPane, BorderLayout.CENTER);
add(viewPortLabel, BorderLayout.PAGE_END);
}
}
Addendum: Further investigation revealed the problem. The NimbusDefaults class, which creates the UIDefaults instance for Nimbus, has this line:
d.put("ScrollBar.maximumThumbSize", new DimensionUIResource(1000, 1000));
Any other look and feel uses 4096 for both values (so, for really big monitors, they will show the same behavior).
The following method, which may be used to install any look and feel, will fix this problem:
private static void installLookAndFeel(final String className) {
//noinspection OverlyBroadCatchBlock
try {
Class<?> lnfClass = Class.forName(className, true,
Thread.currentThread().getContextClassLoader());
final LookAndFeel lAndF;
lAndF = (LookAndFeel) lnfClass.getConstructor().newInstance();
// Reset the defaults after instantiating, but before
// calling UIManager.setLookAndFeel(). This fixes the Nimbus bug
DimensionUIResource dim = new DimensionUIResource(4096, 4096);
lAndF.getDefaults().put("ScrollBar.maximumThumbSize", dim);
UIManager.setLookAndFeel(lAndF);
} catch (Exception e) {
final String systemName = UIManager.getSystemLookAndFeelClassName();
// Prevents an infinite recursion that's not very likely...
// (I like to code defensively)
if (!className.equals(systemName)) {
installLookAndFeel(systemName);
} else {
// Feel free to handle this any other way.
//noinspection ProhibitedExceptionThrown
throw new RuntimeException(e);
}
}
}
Of course, you can fix the problem for really big monitors by using a bigger value.
I confirmed that the vertical scroll bar has exactly the same problem, but is only seen when the window gets very large vertically. This is why this problem is usually only seen with the horizontal scroll bar.
Based on the information you provided, I'm not able to recreate your problem (and therefore not able to help you figure out what's going wrong). Here's a sscce that works for me. Can you reproduce the problem with this example? Perhaps the problem is trickling down from a different part of the application.
public static void main(String[] args){
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception e) {
// If Nimbus is not available, you can set the GUI to another look and feel.
}
//Create Frame
JFrame frame = new JFrame("Title");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create Table
JTable table = new JTable(0, 2);
((DefaultTableModel) table.getModel()).addRow(new Object[]{"Sample Text", "Hi Mom!"});
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
// Wrap table in Scroll pane and add to frame
frame.add(new JScrollPane(table), BorderLayout.CENTER);
// Finish setting up the frame and display
frame.setBounds(0, 0, 600,400);
frame.setPreferredSize(new Dimension(600, 400));
frame.pack();
frame.setVisible(true);
}
I have a JButton that I would like to change the background color of to white. When using the Metal Look And Feel, I achieve the desired effect with setBackground:
Unfortunately, the concept of "background color" is different when using the Windows LAF; the background color is the color drawn around the button:
I would like to use the Windows LAF, but allow the button color of this JButton to be changed to white. How do I do this?
You'll have to decide if it's worth the effort, but you can always create youe own ButtonUI, as shown in this example due to #mKorbel.
I use JDK 6 on XP. It looks like the Window UI doesn't follow the normal painting rules in more ways than 1. As you noticed setBackground() doesn't work. You should be able to do custom painting by telling the component not to fill in the content area:
import java.awt.*;
import javax.swing.*;
public class ButtonBackground extends JFrame
{
public ButtonBackground()
{
setLayout( new FlowLayout() );
JButton normal = new JButton("Normal");
add(normal);
JButton test1 = new JButton("Test 1")
{
#Override
public void paintComponent(Graphics g)
{
g.setColor( Color.GREEN );
g.fillRect(0, 0, getSize().width, getSize().height);
super.paintComponent(g);
}
};
test1.setContentAreaFilled(false);
add(test1);
}
public static void main(String[] args)
{
try
{
// UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
}
catch (Exception e2) {}
ButtonBackground frame = new ButtonBackground();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
When you run the code as is it seems to work properly. That is when you click on the button you see the Border change. However is you run with the Windows XP LAF, the Border never changes to you don't see the button click effect.
Therefore, I guess the issue is with the WindowUI and you would need to customize the UI which is probably too complex to do so I don't have a solution.
but I still think that (modified but by Darryl) is correct UIManager.get("Button.gradient"), because would be crossplatform
EDIT: correct answer would be - Nimbus or some Custom L&F, why reinvent the wheel (by Rob)
Your best option is using SwingX:
JXButton allows you to set a Painter for the background with .setBackgroundPainter(Painter) using a MattePainter achieves exactly what you want. Having that JXButton extends from JButton, the changes are minimal in your code:
Color bg = new Color(new Random().nextInt(16777215)); // Random color
JButton button = new JButton();
button.setBackground(bg);
would become
JXButton button = new JXButton();
button.setBackgroundPainter(new MattePainter(bg));
instead of messing with the button's background color, could you do whatever indication you're trying to show a different way?
displaying an Icon, making the button bold instead of plain text, etc.
Indicating something only through a different background color isn't always obvious, and depending on the user's system colors, may be jarring, or invisible.
for example, what if the user is running windows in "high contrast" mode?