I'm working on an on-screen keyboard for Windows and macOS and I've made a little test app. It has a single button and types the letter “M” to the active application. It works for Windows 10, but not Mac (I'm running macOS 10.12). In macOS, as soon as I press the button, whichever app I'm trying to send the "M" to loses focus (the cursor for text entry disappears), even though my single button "keyboard" has setFocusable(false) all over the place. I tried my own MouseListener on the button too.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class main {
private static Robot robot;
private static Rectangle rectangle;
public static void main(String[] args){
try {
robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
Button button = new Button("M");
button.setFocusable(false);
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(100, 100);
frame.add(button);
frame.setAlwaysOnTop(true);
//set everything I can think of to unfocusable!!!
frame.setFocusable(false);
frame.setAutoRequestFocus(false);
frame.setFocusableWindowState(false);
frame.getRootPane().setFocusable(false);
frame.setVisible(true);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
sendKeystroke();
}
});
//Instead of adding a listener to the button, I've also tried my own MouseListener.
/* button.addMouseListener(new MouseTrap());
rectangle = button.getBounds();*/
}
private static void sendKeystroke(){
robot.keyPress(KeyEvent.VK_M);
robot.keyRelease(KeyEvent.VK_M);
}
private static class MouseTrap extends MouseAdapter{
#Override
public void mouseClicked(MouseEvent e) {
if (rectangle.contains(e.getPoint())){
sendKeystroke();
}
}
}
}
It seems like macOS does let some apps have focus without taking the cursor from another. e.g. VMware or spotlight search from the system tray.
Cursor for VMware and IntelliJ at the same time
I've seen other answer which are non-Java:
Virtual Keyboard Cocoa & Objective C
But do I really have to go all native when Java works on Windows? Apart from the learning curve (not done anything native on a Mac), I want to keep Win and Mac versions versions as close as possible.
Anyone know how I could get this working using straight Java?
(Note: As was the case with the questioner for the above link, I can't just use a keyboardview, as I want send modified/additional data from the keyboard e.g. text predictions. I believe that would require additional native code again.)
I have tried to find the way very much. But it seems that it is not possible by pure Java without any native code.
Hi there.
I have a problem to one my PC. Source code is correct. My problem is in graphic render. This bug only on one laptop.
Example screens in 1 PC:
(screen in http://)
JLabel - i.gyazo.com/deafe8b111007562e47d93ab4f9728a3.png
JMenu - i.gyazo.com/1d5c5babd243d958073d2635f64b0e8a.png
JMenuItem - http://i.gyazo.com/aca39fd386d7d186c1f22bb2d629148e.png
JMenuItem - i.gyazo.com/d78f33834913e4548edf93f8bd996002.png
On other PC's there is no problems.: http://i.gyazo.com/51e9c928627d7091bbb302b5eb78b6a2.png
I thought that problem was in java JRE (I use v1.7) and I reinstalled it. But the problem remains. Also, I switched off Desktop Composition - but there is no luck either. What should I do now? Maybe the problem is in video driver or hardware acceleration?
Tell me how to solve the problem?
[message edited]
Example "bug code":
public class Example extends JFrame {
private static final long serialVersionUID = 1L;
public Example(){
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu mnNewMenu = new JMenu("\u041C\u0435\u043D\u044E");
menuBar.add(mnNewMenu);
JMenuItem mntmNewMenuItem = new JMenuItem("\u0424\u0430\u0439\u043B");
mnNewMenu.add(mntmNewMenuItem);
JMenuItem mntmNewMenuItem_1 = new JMenuItem("\u0412\u044B\u0445\u043E\u0434");
mnNewMenu.add(mntmNewMenuItem_1);
}
public static void main(String[] args){
Example form = new Example();
form.setSize(640,480);
form.setLocationRelativeTo(null);
form.setVisible(true);
}
}
screen bug: i.gyazo.com/9fcf258e62c498f8468d47b7f73759e1.png
screen normal: i.gyazo.com/ec27e655a0209e4613c3a19863fa0e18.png
Ok, this problem solved!
From my PC, i added lines:
System.setProperty("sun.java2d.d3d", "false");
System.setProperty("sun.java2d.ddoffscreen","false");
System.setProperty("sun.java2d.noddraw", "true");
This video driver error.
If a popup menu is still open when another component is clicked, then the component does not get the event, because it's probably consumed by the popup. This happens for all JPopupmenus in general.
This happens only in Java 7 with windows LAF (Windows7). Is there a workaround? Is it a known bug?
import javax.swing.*;
import java.awt.event.*;
public class Test
{
public static void main(String[] s)
throws Exception
{
String lookAnfFeelClassName = UIManager.getSystemLookAndFeelClassName();
UIManager.setLookAndFeel(lookAnfFeelClassName);
JMenu menu = new JMenu("TEST Menu");
JMenuItem menuItem = new JMenuItem("Menu Item 1");
JMenuBar menuBar = new JMenuBar();
menu.add(menuItem);
menuBar.add(menu);
final JButton b = new JButton("Test");
b.setBounds(5, 50, 60, 20);
b.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//If the Menu is open when I press the button, the putton is not pressed
//so I have to press it again.
JOptionPane.showMessageDialog(b, "Button Pressed");
}
}
);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(150, 150);
frame.setJMenuBar(menuBar);
frame.getContentPane().setLayout(null);
frame.getContentPane().add(b);
frame.setVisible(true);
}
}
Here is the magic line that fixes the problem:
UIManager.put("PopupMenu.consumeEventOnClose", Boolean.FALSE);
I found this after looking into the source code for the BasicPopupMenuUI class. Apparently this behaviour is a deliberate design choice according to the following comments in the code, but it sure feels like a bug to me.
// Ask UIManager about should we consume event that closes
// popup. This made to match native apps behaviour.
By the way, it happens in Java 5 and 6 too.
I would like to create a JFrame with two specifal features:
JFrame should not grab focus while maximized from minimized state.
When a JFrame created or became maximized from minimized state, it should flash in the Windows bar until a user will grant a focus to it. (like as in ICQ clients ).
Does anybody know how the second requirement can be implemented?
Little self-explained example:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class JFrameTest {
private static JFrame childFrame;
public static Container getParentContentPane() {
JPanel panel = new JPanel();
JButton button = new JButton("Create\\Restore child frame");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
createOrRestoreChildFrame();
}
});
panel.add(button);
return panel;
}
private static void createOrRestoreChildFrame() {
if (childFrame == null) {
childFrame = new JFrame("Child Frame");
childFrame.setLocation(200, 200);
childFrame.add(new JLabel("Child Frame"));
childFrame.pack();
setChildFrameVisible();
} else {
setChildFrameVisible();
}
}
private static void setChildFrameVisible() {
childFrame.setFocusableWindowState(false);
childFrame.setVisible(true);
flashInWindowsBar(childFrame);
childFrame.toFront();
childFrame.setFocusableWindowState(true);
}
/**
* Should Make child frame flash in Windows bar.
* Currently, it does not work for me.
* Could anybody help me to fix this please? )
*/
private static void flashInWindowsBar(JFrame childFrame) {
childFrame.setState(JFrame.ICONIFIED);
childFrame.toFront();
}
private static void createAndShowGUI() {
JFrame parentFrame = new JFrame("JFrame Demo");
parentFrame.setLocation(100, 100);
parentFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
parentFrame.setContentPane(getParentContentPane());
parentFrame.pack();
parentFrame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Thanks!
The following code worked for me exactly as you described:
f.setState(JFrame.ICONIFIED);
f.toFront();
f is a JFrame.
Unfortunately, this isn't something that you can do natively under any Java platform. Anyone who manages to get it working by using the kind of 'trickery' that you've shown, will be disappointed to find that it is unlikely to work on another version of Windows, or even another computer with the same version of Windows. The only times I've ever seen a Java window flash is due to some glitch in Swing when minimizing all windows to the taskbar.
As this article on making Java applications feel native shows, it's the same on Mac OS.
Your best bet is to use the techniques described in the above article to make a JNI which does the Windows API call, or get a license for JNIWrapper (search for it) which does it all for you (best option if you are making a commercial app, or making it for a client who is willing to pay for such a feature). It looks like you can get a 30-day trial for that.
The only other thing I could suggest is create a poor-man's equivalent of a pop-up notification system. When you want to alert the user, create a Frame without a border, put it in the bottom-right corner of the screen, make it non-focusable and show it for a brief period of time.
The JPanel does not flash. Try it instead of JFrame.
I'm trying to make a GUI in java, but JMenuBar has been giving me a hard time for two days. Can someone please tell me why it isn't showing up?
import java.awt.*;
import javax.swing.*;
import javax.swing.JPanel;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.util.Arrays;
import javax.imageio.ImageIO;
public class selectionFrame extends JFrame
{
Font name;
Font title;
public void setup() //can't use constructer because this isn't given a size until after it is constructed.
{
//getContentPane().add(menuBar);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setLayout(new FlowLayout());
//getContentPane().add(j);
setJMenuBar(createMenuBar());
//getContentPane().add(createMenuBar());
}
public JMenuBar createMenuBar()
{
JMenuBar menuBar;
JMenu m_file;
JMenuItem mi_save;
JMenuItem mi_load;
JMenu m_edit;
JMenuItem mi_tileHeight;
JMenuItem mi_tileWidth;
menuBar = new JMenuBar();
m_file = new JMenu("File");
m_edit = new JMenu("Edit");
mi_save = new JMenuItem("Save file", KeyEvent.VK_S);
mi_load = new JMenuItem("Load file", KeyEvent.VK_L);
mi_tileHeight = new JMenuItem("Set tile height", KeyEvent.VK_H);
mi_tileWidth = new JMenuItem("Set tile width", KeyEvent.VK_W);
menuBar.add(m_file);
m_file.add(mi_save);
m_file.add(mi_load);
menuBar.add(m_edit);
m_edit.add(mi_tileHeight);
m_edit.add(mi_tileWidth);
return menuBar;
}
public static void main(String[] args) //run
{
selectionFrame sel = new selectionFrame();
sel.setLocationRelativeTo(null);
sel.setSize((int) 400 + (sel.getInsets().left + sel.getInsets().right),(int) 400 + (sel.getInsets().top + sel.getInsets().bottom));
sel.setVisible(true);
sel.setTitle("Tiles/Meta");
sel.setResizable(false);
sel.setFocusable(true);
sel.getContentPane().setSize(sel.getSize());
sel.setLocation((int) sel.getX() - (sel.getWidth()/2),(int) sel.getY() - (sel.getHeight()/2));
sel.setup();
sel.repaint();
}
}
You have an awful lot of extra code there.
public class SelectionFrame extends JFrame
{
Font name;
Font title;
public SelectionFrame()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setJMenuBar(createMenuBar());
}
public JMenuBar createMenuBar()
{
JMenuBar menuBar;
JMenu m_file;
JMenuItem mi_save;
JMenuItem mi_load;
JMenu m_edit;
JMenuItem mi_tileHeight;
JMenuItem mi_tileWidth;
menuBar = new JMenuBar();
m_file = new JMenu("File");
m_edit = new JMenu("Edit");
mi_save = new JMenuItem("Save file", KeyEvent.VK_S);
mi_load = new JMenuItem("Load file", KeyEvent.VK_L);
mi_tileHeight = new JMenuItem("Set tile height",
KeyEvent.VK_H);
mi_tileWidth = new JMenuItem("Set tile width",
KeyEvent.VK_W);
menuBar.add(m_file);
m_file.add(mi_save);
m_file.add(mi_load);
menuBar.add(m_edit);
m_edit.add(mi_tileHeight);
m_edit.add(mi_tileWidth);
return menuBar;
}
public void main( String args[] )
{
SelectionFrame sel = new SelectionFrame();
sel.setLocationRelativeTo(null);
sel.setSize(400 + (sel.getInsets().left + > sel.getInsets().right), 400
+ (sel.getInsets().top + sel.getInsets().bottom));
sel.setTitle("Tiles/Meta");
sel.setResizable(false);
sel.setFocusable(true);
sel.getContentPane().add( new JLabel( "Content", SwingConstants.CENTER),
BorderLayout.CENTER );
sel.setLocation(sel.getX() - (sel.getWidth() / 2), sel.getY() - > (sel.getHeight() / 2));
sel.setVisible(true);
}
}
That shows up with a menu bar and everything. if you add your content to the CENTER of the content pane (by default a border layout), the center automatically fills the whole content area, you don't have to resize anything.
This shows up as a window with a menu bar and everything works fine.
What platform are you doing this on? I'm on Vista, i get what i expect to see.
What Java version are you using? Your menu bar shows up fine in 1.6.0_10 on my system. Try wrapping the body of your main method in an invokeLater() call so that it runs on the correct thread, like so:
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
selectionFrame sel = new selectionFrame();
sel.setLocationRelativeTo(null);
sel.setSize((int) 400 + (sel.getInsets().left + sel.getInsets().right),
(int) 400 + (sel.getInsets().top + sel.getInsets().bottom));
sel.setTitle("Tiles/Meta");
sel.setResizable(false);
sel.setFocusable(true);
sel.getContentPane().setSize(sel.getSize());
sel.setLocation((int) sel.getX() - (sel.getWidth() / 2),
(int) sel.getY() - (sel.getHeight() / 2));
sel.setup();
sel.setVisible(true); // Follow Kendrick's advice too.
}
});
}
Turns out you HAVE to set the JMenuBar inside the JFrame's constructor. I figured this out while looking at the differences between my code, and the marked answers code.
Thank you for your wonderful answer, John Gardner. Without you I would have most likely been stuck for months.
In my case I have tracked down a missing menu bar to a bug where I set the RootPane layout, tsk tsk. The RootPane (see eg http://download.java.net/jdk7/archive/b123/docs/api/javax/swing/JRootPane.html ) controls the layout of the menu bar, so when I changed its layout manager it lost the bar.
Instead, one should use the ContentPane to layout and add components to, eg:
frame.getContentPane().setLayout(...);
frame.getContentPane().add(...);
For future reference... this has nothing to do with the component being visible (as the OP said, the frame is visible but the menu bar is not), I have working code that sets the JMenuBar outside the constructor, and while being swing-thread-safe is Good Practice, it is not the cause of the problem.
sel.setVisible(true);
Should be the last thing you call......
Also, just before the call to sel.setVisible(true); pls invoke sel.pack();
Pls note that instead of setSize it is better to use setPreferredSize, which is leveraged during frame packing.
Not directly relevant to your question, but still -- the use of a good layout manager is a huge time and frustration saviour when using Swing. MigLayout is simply an excellent one-stop layout manager.
When I compile and run it, it shows up with a menu bar with file and edit menu items. Were you expecting more?
Also, capitalize your class- SelectionFrame
EDIT:
One thing I forgot to look at, your code and every answer here is technically wrong. Often, it happens to work, but you are not allowed to do anything with Swing components unless you are in the AWT worker thread.
Normally you don't have to think about the worker thread much because every event that comes from your window will be on the worker thread anyway, but there is a tendency to forget about it when you construct your initial frame--and more often than not it just works anyway.
Sun used to recommend that you can work with components outside the AWT thread until the window has been realized (with either pack() or setVisible(true)) but this is no longer considered safe.
The easiest way to fix this might be for your main to create a swing worker thread before newing your SelectionFrame.
There is only a 50-50 chance this will fix your problem, but you should still take it into consideration whenever working on a GUI.