Look & Feel troubles on Ubuntu - java

I'm writing an app that requires some widgets to use different look & feels. The rest of the app uses the system look & feel.
My approach to this was while initializing the app to swap the LF for the desired one, create the component and then reset the LF. This worked fine on Windows operating systems, but while testing it on Ubuntu it failed to set the LF for the component in a bizarre way.
On Ubuntu, UIManager.getSystemLookAndFeelClassName() reports that that the LF is a javax.swing.plaf.metal.MetalLookAndFeel. This is the first bit of trouble - I'd expect it to return com.sun.java.swing.plaf.gtk.GTKLookAndFeel. The strange part is that my approach works when the program is using the Metal LF, but fails when using the GTK LF, where it sets the entire frame and all components within it to the GTK LF.
The following SSCCE demonstrates the issue:
import javax.swing.*;
import java.awt.*;
import static javax.swing.UIManager.*;
public class LaFTest {
public static void main(String[] argv) throws ReflectiveOperationException, UnsupportedLookAndFeelException {
String[] keys = {
"java.specification.version",
"java.version",
"java.vm.version",
"java.specification.vendor",
};
for (String key : keys) {
System.out.printf("%-40s%s\n", key, System.getProperty(key));
}
System.out.println(UIManager.getSystemLookAndFeelClassName());
// Swap for "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"
setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JFrame frame = new JFrame("LaF Test");
Container content = frame.getContentPane();
content.setLayout(new GridLayout(2, 0));
JButton button = new JButton("System");
content.add(button);
LookAndFeel previous = getLookAndFeel();
for (LookAndFeelInfo feel : getInstalledLookAndFeels()) {
if (feel.getName().equals("Nimbus")) {
setLookAndFeel(feel.getClassName());
}
}
JButton second = new JButton("Nimbus");
content.add(second);
setLookAndFeel(previous);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Note, however, that I am unsure regarding the reproducibility of this issue. As quick Google shows no similar issues, it may be something specific to my machine. Regardless, it has persisted when updating from Ubuntu 12 to 13, and 13 to 14. From this I assume it's an issue with Swing and not the underlying system.
The output of the above program is, on my machine,
java.specification.version 1.7
java.version 1.7.0_51
java.vm.version 24.51-b03
java.specification.vendor Oracle Corporation
javax.swing.plaf.metal.MetalLookAndFeel
Using the Metal feel, the resulting window is
Swapping for the GTK feel,
I've been playing around with this for a while now, with no results. Is there something I'm missing? Can anyone reproduce this, and if so is it a bug in the Java distribution or my code? Is there a better way to achieve my goal?

I would have loved to debug this in depth, but a nearing deadline forced me to install the Oracle Java package, which fixed the issue. A disappointing answer, but it did fix the issue permanently.

Related

Why does JMenu not render correctly when using System L&F?

Bug Report
This has been confirmed as a bug. You can track it here:
https://bugs-stage.openjdk.java.net/browse/JDK-8258934
Background
There's no other code running besides what I've posted in the MCVE; there are no other JVMs active when running this program (though I don't believe that should matter).
I do not have any programs installed which customizes the UI of my system.
Target Environment
Java 1.8.0_201
Windows 10 Home Edition
Tested JDKs
JDK 8 to JDK 15
Tested by third party
George Z. - Windows 7 - Can reproduce bug
David Kroukamp - Windows 10, JDK 10 - Can reproduce bug
camickr - Windows 10, JDK 11 - Can reproduce bug
Canvas (Discord) - Windows 10 - Can reproduce bug
Pawnee (Discord) - macOS Catalina 10.15.7, JDK 15 - Unable to reproduce bug
MCVE
import javax.swing.*;
public class Demo {
public static void main(String[] args) {
SwingUtilities.invokeLater(Demo::launchUI);
}
private static void launchUI() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
JFrame frame = new JFrame();
JMenuItem report = new JMenuItem("Report");
JMenu newMenu = new JMenu("New...");
JMenu fileMenu = new JMenu("File");
JMenuBar bar = new JMenuBar();
newMenu.add(report);
fileMenu.add(newMenu);
bar.add(fileMenu);
frame.setJMenuBar(bar);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Reproduction Steps
Run the MCVE
Click through the menus
Resize the window
Click through the menus again
MCVE Results
Windows 10 machine targeting JDK 11 (though similar results on all tested major Java distributions):
Menus give different render results before & after resizing the window
There's overlap of the highlighting - the highlight of "New..." bleeds into the "Report" menu item.
Sometimes the arrow of "New..." will get overlapped by "Report" as shown below.
Windows 7 - image by George Z
Windows 10, JDK - image by Canvas (Discord)
New Findings
The highlight bug does not appear if part of the "Report" menu item is outside of the windows bounds.
This was achieved by updating the popout offset for menus:
// apply this before creating any components
UIManager.put("Menu.menuPopupOffsetX", 20);
UIManager.put("Menu.menuPopupOffsetY", 20);
Works on...
macOS Catalina 10.15.7 - image by Pawnee (Discord)
Question
Does JMenu work properly with System L&F, and if so, what am I missing to get proper results? (No extra spacing, no highlights bleeding into other components)

Java crashes when creating a JFrame

I am baffled at this issue. I just wanted to create a JFrame for testing, this is the only class:
import javax.swing.*;
public class TextPaneTest extends JFrame {
public TextPaneTest(){
setTitle("Test");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setSize(200, 200);
setVisible(true);
}
public static void main(String[] args){
new TextPaneTest();
}
}
I am using IntelliJ IDEA 2019.2.4 as my IDE.
The result is a small white JFrame opens up for 2 seconds and closes. You can't move or resize the window and the cursor remains in "wait" mode when you hover the frame.
This is my project structure:
And this is my run configuration:
There is no error message or exception. All the console shows is:
Process finished with exit code -1073740771 (0xC000041D)
I've already done a clean reinstall of both the JRE and JDK
This is my current java -version:
java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
My OS is Windows 10 Home Single Language 1903
I don't know what else to add. I've been using Java for the past 5 years as a hobbyist and I've never came across an issue so fundamental as this.
Update
Tried instantiating TextPaneTest() using SwingUtilities.invokeLater()
Tried building the JAR and running from a command window
None has worked so far. Exactly the same behaviour.
Update 2
Fixed it by switching the 64 bit JRE for the 32 one. Is this a bug with the 64 one or could there be an underlying problem?
I cannot reproduce the issue on my macintosh, but I notice you are doing everything on the main thread. You shouldn't do that. Make sure all events happen on the Event Dispatch Thread. For example,
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TextPaneTest();
}
});
}

Java error: "Can't open input server /Library/InputManagers/Inquisitor"

I'm just now getting into GUI's in Java and when experimenting with JFrame I get the following error:
java[3126:71534] Can't open input server /Library/InputManagers/Inquisitor
Despite the error the program runs fine, but I'd like to know what this is about as I couldn't find much about Inquisitor anywhere.
Running Netbeans 8.0.2 and Java 8 Update 40 on OS X Yosemite (10.10.2). The java code being run is:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Ikkuna extends JFrame implements ActionListener{
JTextField syöte;
JLabel vastaus;
JButton painike;
public void setTitle(String string){
super.setTitle(string);
}
public Ikkuna(){
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setTitle("Celsius / Fahrenheit -muunnos");
this.setSize(400, 200);
this.setResizable(false);
JPanel paneeli = (JPanel) getContentPane();
syöte = new JTextField(10);
vastaus = new JLabel("tuntematon");
painike = new JButton("Laske");
painike.addActionListener(this);
syöte.addActionListener(this);
paneeli.setLayout(new FlowLayout(FlowLayout.LEFT, 10,10));
paneeli.add(syöte);
paneeli.add(vastaus);
paneeli.add(painike);
setVisible(true);
}
public void actionPerformed(ActionEvent e){
vastaus.setText("" + ((Integer.parseInt(syöte.getText())*1.8+32)));
}
public static void main(String[] args){
Ikkuna i = new Ikkuna();
}
}
TLDR: Don't worry about it, it has nothing to do with your code, although you're probably better of removing Inquisitor extension as there's a good chance that it's not working properly.
InputManagers are intended to be Keyboard/Mouse extensions for applications. The idea is that the code is loaded into applications and can change their behavior in relation to Mouse/Keyboard input, but are generally used as a mechanism for extending applications. They became more restricted in 10.5, where they had to be in the system supported location only (/Library/InputManagers), and stopped being loaded entirely on 64bit mode.
Java 8 is a 64bit only application, so if you're seeing this error, then it's probably because the extension is not being loaded because it's a 64bit application and the system doesn't load extensions in this case.
The extension Inquisitor doesn't look to have been updated in a long time, and was used to extend the functionality of Safari, adding auto-complete to the search bar. The default search functionality of Safari now includes auto-complete, so I would consider it obsoleted.
If you want extended functionality for Safari, there are other plugins which extend the behaviour, such as Glims (I used to use it, but stopped because I felt it destabilized safari more than usual).
Opening the /Library/InputManagers folder (open a Finder window, hit Command-G to get a location field and type in the directory, then hit enter) will allow you to see what input managers are present on the system.

Moving JScrollPane horizontally results in blured text

I have a TextArea inside JScrollPane inside standard JPanel.
JPanel panelMain = new JPanel();
panelMain.setBorder(titledBorder1);
panelMain.setBounds(new Rectangle(2, 5, 970, 700));
panelMain.setLayout(null);
JTextArea fieldBody = new JTextArea();
JScrollPane fieldBodyScrollPane = new JScrollPane(fieldBody);
fieldBodyScrollPane.setBounds(70, 140, 790, 500);
panelMain.add(fieldBodyScrollPane);
When I type enough text in a single row the horizontal knob appears - so far good. But when I start moving the knob left and right, the text gets blured (see image). Interestingly, nothing weird happens when I move the textarea up and down.
I use Ubuntu 12.04 with Unity. This graphic artifact never appeared to me before. Any hints what could be the problem?
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.border.*;
public class CaseForLayoutsNumber547 {
CaseForLayoutsNumber547() {
Border titledBorder1 = new TitledBorder("Case for Layouts #547");
// START: code snippet variant
JPanel panelMain = new JPanel(new GridLayout());
panelMain.setBorder(titledBorder1);
JTextArea fieldBody = new JTextArea(5,40);
JScrollPane fieldBodyScrollPane = new JScrollPane(fieldBody);
panelMain.add(fieldBodyScrollPane);
// END: code snippet variant
JOptionPane.showMessageDialog(null, panelMain);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new CaseForLayoutsNumber547();
}
};
SwingUtilities.invokeLater(r);
}
}
I do not see any scroll artifacts in this SSCCE. Do you?
Here's #Andrew's SSCCE displaying itself; it looks the same with either Ambience or Radiance.
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 12.04.1 LTS
Release: 12.04
Codename: precise
$ java -version
java version "1.6.0_24"
OpenJDK Runtime Environment (IcedTea6 1.11.4) (6b24-1.11.4-1ubuntu0.12.04.1)
OpenJDK Client VM (build 20.0-b12, mixed mode, sharing)
Addendum: Looking closer at your screenshot, failing to honor the opacity property can cause such rendering artifact, and the default setting may vary among Look & Feel implementations.
This problem happens in OpenJDK (6 and 7, at least; and at least on Linux), and does not happen in Oracle Java 6 and 7 (on Linux).
The workaround suggested by mKorbel works for me:
scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
So I guess it's a bug in OpenJDK.

Swing on OSX: How to Trap command-Q?

After being convinced ("schooled") that Swing apps on Mac do look native, I'm trying to make mine look as native as possible. Everything looks great, but when I hit command+Q or do it from the menu, my windowStateChanged(WindowEvent e) is not firing on my main JFrame (if I exit in any other way, it does fire). How can I respond to the real Apple quit?
You can implement com.apple.eawt.ApplicationListener and respond to the Quit event. An example may be found in the Mac OS X Reference Library example, OSXAdapter.
Addendum: See Java for Mac OS X 10.6 Update 3 and 10.5 Update 8 Release Notes for information on deprecation, the redesigned com.apple.eawt.Application class, and the location of API documentation for the Apple Java extensions. Control-click or right-click on the .jdk file to Show Package Contents. You can browse the classes of com.apple.eawt among the OpenJDK sources.
As shown in this complete example, you can specify the desired
QuitStrategy; a WindowListener will respond to ⌘Q:
Application.getApplication().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
As noted here, you can set the property from the command line
java -Dapple.eawt.quitStrategy=CLOSE_ALL_WINDOWS -cp build/classes gui.QuitStrategyTest
or early in the program, before posting any GUI events:
System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
EventQueue.invokeLater(new QuitStrategyTest()::display);
Console, after ⌘Q:
java.vendor: Oracle Corporation
java.version: 1.8.0_60
os.name: Mac OS X
os.version: 10.11
apple.eawt.quitStrategy: CLOSE_ALL_WINDOWS
java.awt.event.WindowEvent[WINDOW_CLOSING,opposite=null,oldState=0,newState=0] on frame0
Code:
package gui;
import java.awt.EventQueue;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JTextArea;
/**
* #see https://stackoverflow.com/a/7457102/230513
*/
public class QuitStrategyTest {
private void display() {
JFrame f = new JFrame("QuitStrategyTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.out.println(e);
}
});
f.add(new JTextArea(getInfo()));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private String getInfo() {
String[] props = {
"java.vendor",
"java.version",
"os.name",
"os.version",
"apple.eawt.quitStrategy"
};
StringBuilder sb = new StringBuilder();
for (String prop : props) {
sb.append(prop);
sb.append(": ");
sb.append(System.getProperty(prop));
sb.append(System.getProperty("line.separator"));
}
System.out.print(sb);
return sb.toString();
}
public static void main(String[] args) {
System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
EventQueue.invokeLater(new QuitStrategyTest()::display);
}
}
The top voted answer is excellent but just to fill in the "best way":
System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
This will trigger the standard window closing callback event which should work really nicely for portable code.
As a result of the discussion below it seems that its crucial to do this really early in the app. I wrote this early in the static initializer of the main class before any UI code was executed.
This is a pretty good question, and I must admit I don't have the answer. However, a couple years ago when I was working on a Java app and faced this problem, I solved it by registering a shutdown hook with the runtime that would do what I wanted the app to do before quitting. It's a heavy-handed solution but it worked. You can take a look at my code and see if it helps.
I was originally seeing a 'access restriction' violation when trying to access the com.apple.eawt.Application and com.apple.eawt.* subclasses.
(Note: I'm programming on a MAC, using Eclipse, with Java 1.6 using Swing)
So I needed to modify my java build path to allow access to the apple subclasses by adding "com/apple/eawt/**" access rule. After that this code below was able to compile and work for me:
//NOTE: This code only works for MAC OS. If you run this on Windows
//the application never starts (so you literally need to remove this block of code)
import com.apple.eawt.*;
import com.apple.eawt.QuitHandler;
Application a = Application.getApplication();
a.setQuitHandler(new QuitHandler() {
#Override
public void handleQuitRequestWith(com.apple.eawt.AppEvent.QuitEvent qe, com.apple.eawt.QuitResponse qr) {
// TODO Auto-generated method stub
int res = JOptionPane.showConfirmDialog(frame, "Are you sure you want to exit the program?", "Quit ?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (res == JOptionPane.YES_OPTION)
qr.performQuit();
else
qr.cancelQuit();
}
});
Have you tried setting up command-Q as an accelerator in your menu? Can you make your app respond to it?
I'm not positive, but I think this works in Linux and probably Windows with the equivalent Alt-F4. My app responds to the "killing" keystroke, I process some cleanup code and then I do a programmatic System.exit().
If you're "just" after graceful exit handling, you may also want to catch the WindowEvent WINDOW_CLOSING, where traditionally "are you sure?" stuff gets done.
Looking at the link to Java for Mac OS X 10.6 Update 3 and 10.5 Update 8 Release Notes I noticed that there is a section on Default Quit Action. This describes a system property to request that all windows are closed in response to the "Quit" menu item, which sounds like exactly what is needed? I have used this in my own application (using Info.plist to set the property on OS X only), and it seems to work as described. This would presumably only work on recent Java/OS X versions, but for those platforms seems like a neat solution, and doesn't require any code changes.

Categories

Resources