Adding keyboard shortcuts in applet - java

How can I add keyboard shortcuts to a Java applet?
I had an assignment (I am taking Honors Computer Science) to make my name spin in a circle.
I used an infinite loop, and every time I want to end, I have to go to Task Manager and end the program. Can I add a shortcut that quits the applet?
For example, if I press the space bar, it will change to another part of the program.
I looked over "How to Use Key Bindings" at Oracle's website but I could not understand it.

If you are using Swing (if you are creating a JApplet), then yes, Key Bindings are the way to go. What about them confuses you?
By the way, I know that this isn't directly related to your question, but if your applet is a Swing JApplet, I wouldn't use an infinite loop to do the animation, but rather I'd use a Swing Timer. If you use an infinite loop, you must take care to a) do the infinite loop in a background thread, and b) make most Swing calls from within the loop on the Swing event dispatch thread (or EDT) else you risk freezing the main Swing thread. A Swing Timer does all of this for you, and is thus much easier to use.

All that tutorial is saying that you need a KeyStroke, some key Object, and an Action to create a key binding.
To create your KeyStroke, take a look at the static helper methods, they should explain how to get the correct KeyStroke you need.
You can use any old Object as the key (Object key = new Object()).
The last thing you need is to create your Action. I would suggest extending AbstractAction since it already implements many of the methods of Action for you.
To put your key binding together do the following:
JComponent c = ...
KeyStroke keyStroke = ...
Object key = ...
Action a = ...
c.getInputMap().put(keyStroke, key);
c.getActionMap().put(key, a);

Related

Java Swing: How to distinguish events triggered by user?

I'd like to update GUI elements (JComboBox, JLabel, etc.) from code which shouldn't trigger change event. Is it possible to find out from java.awt.event.ActionEvent or java.awt.event.ItemEvent if the change was caused by an user or by running code like this?
combo.setSelectedItem("my item")
The answer is: no.
But in some cases you can try to analyze the current InputEvent. To get it, use EventQueue.getCurrentEvent(). For example if user has triggered the change on clicking of another component, you can compare the component of the input event and the component of the action event (OK I know: it's unsafe. But in some cases it can help to avoid incrementing of application complexity).
For a button you can get the event modifiers:
int buttonModifiers = evt.getModifiers();
If the button event was generated with a call to doClick() the modifier is 0, otherwise not.
By the way, you can find out such differences relatively easy by logging / printing using evt.toString()
.

Listeners in forms with JComponents

Right now, when I have a form with many JComponents, mainly JTextFields, JTextAreas, JComboboxes, JCheckBoxes and JButtons and want to control their behaviour, for instance the change of focus after a certain key was released, I do the following:
I put all my components in a JComponent[] and cycle through it, adding the appropriate listener. When an event is registered by said listener, I check with "instanceof" what kind of JComponent fired the event and assign the proper reaction.
I use this method for instance to cycle with VK_ENTER through the form, or to "firePropertyChange(..)" after a DocumentListener fires, or to add UndoRedoListeners and so on.
My question : is there a better way to do this and if yes, can you explain to me the benefits ?
but my question refers to the general practice of putting all
JComponents in an array and cycling through them for every listener
and every fired event. It works fine enough, but it feels a bit
"uneconomic",so I wanted to know if it is recommended practice, or if
there is a better way of doing it.
I usually write a custom listener (often as an anonymous class) per type/ instance if I have type/ instance specific behavior so that I can avoid instanceof and other other checks.
You'll want to customise the focus tranfersal system.
Take a look at How to Use the Focus Subsystem, in particular Customizing Focus Traversal

Java: setText GUI code in a text based rpg game

I am currently making this text-based rpg game with a simple GUI. I was happy to find the good start at it but then there is something I stumbled upon that made me stop coding for a while.
If you are making this in console, you could easily use this code to pause the movements of the characters for a while, like:
System.out.println("[enemy]");
Thread.sleep(1000);
System.out.println("The local guard waves his sword and tries to stab you in the back, but you quickly parried and tried for a counterattack but you failed.");
If you are doing this on a JTextArea, you'd use the setText but
if you use Thread.sleep it doesnt work and coding setText again, would erase the old text and replace it with the new text, so the records of the fight will not be fully displayed on the game. Is there a way to fix this?
You can use append to append instead of replace. That is the easy part.
The hard part: You have to change your program flow. In Swing there exists a single thread for dispatching GUI events, the event dispatching thread. You should not set the EDT to sleep or do any other long-running operations in it. This will freeze the GUI, it can't respond to anything and will not repaint.
Instead you should either start a new thread for the logic flow and dispatch operations that have to be executed on the EDT (everything that manipulates the GUI) with SwingUtilities.invokeLater or, in this case maybe better, SwingUtilities.invokeAndWait.
Or you should embrace an event driven control flow, e.g. you could use a Timer to output the second text later.
A program flow that works well with single-threaded console programs is not the right approach for multi-threaded GUI applications (and every GUI application is automatically multi-threaded).
for the setText part, you should have a variable which will hold the text and when you want to add a string, you append it and set the text again:
String text ="[enemy]";
textfield.setText(text);
text+= "\nblablabla ..";
textfield.setText(text);
UPDATE:
Some are suggesting to use the append method which is relatively good. Sometimes in the game you would like to append and sometimes replace the whole text (a new character talks), I would recommend something like this:
textfield.setText("[enemy]\n");
textfield.append("blablabla");
//When someone else wanna talk:
thread.Sleep(1000);
textfield.setText("[me]\n");
textfield.append("moreblablabla");
You could also use append() function. See JavaDoc.
jTextField.append("Foo\n");
jTextField.append("Bar\n");
You can use append() instead of setText(). Method append() will append new text at the end of the old text.

JTextField problem with ActionEvent getModifiers method

When I hit the enter key into a JTextField and I'm pressing one modifier key (like Ctrl or Alt) the ActionEvent is not fired and thus I can't check the modifier key just pressed.
Why is it a bug?
For Swing widgets, key binding should be done through InputMap and ActionMap (although the "obsolete" registerKeyboardAction is simpler). See Keyboard Bindings in Swing in the old Swing Connection, and the JComponent API docs.
The Pluggable Look & Feel rather gets in the way of mapping input events onto actions. Exactly what happens with ActionEvent is up to the current PL&F. Application code directly registering KeyListener generally isn't a good idea in Swing. Some components are made up of other smaller components, meaning InputEvents may or may not get passed on. Input events may get interpreted to perform more than one operation. Generally the whole thing is a mess.

Can multiple accelerators be defined for a JMenuItem?

I've a problem with setAccelerator(). Right now, I have the code that works for Ctrl+X for DELETE operation. I want to set the accelerator to Shift+Delete as well for same JMenuItem.
My code as follows:
JMenuItem item = new JMenuItem(menuText);
item.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_X, KeyEvent.CTRL_MASK));
item.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_DELETE, KeyEvent.SHIFT_MASK));
but this is working only for Shift+Delete operation. Seems it is overriding the Ctrl+X operation. Can we make both these keystrokes work at the same time?
Please guide.
Yes it can be done. Behind the scenes the setAccelerator() is just creating a Key Binding, however as you noticed the second binding replaces the first.
So, you need to create an Action (not an ActionListener) to add the to the menu item. Read the section from the Swing tutorial on How to Use Actions for more information. Now that you have created the Action, you can share the Action with another KeyStroke by manually creating a Key Binding. You can read the section from the Swing tutorial on How to Use Key Bindings for a detailed explanation. Or you can read my blog on Key Bindings which give some simple code examples.
This second binding will not show up on the menu item itself.
From: http://java.sun.com/j2se/1.4.2/docs/api/java/awt/AWTEvent.html
The masks are also used to specify to which types of events an AWTEventListener should listen.
So you can combine the mask for two keys, but not the KeyEvents.
item.setAccelerator(
KeyStroke.getKeyStroke(
KeyEvent.VK_X, KeyEvent.CTRL_MASK + KeyEvent.SHIFT_MASK));
A workaround solution would be to catch the KeyEvent in the middle (after your component fired it, but before your listeners will act on it) and check, whether its one of the two combinations. Then fire one event, on which you programmatically agree to represent the action you wanted.
The second call indeed overrides the accelerator. If the method starts with set, there will be only one. If the method starts with add, you can have more than one (for example for a number of listeners).
If you want multiple keystrokes to do the same, I think you should add a keyListener to the top frame (or panel, dialog, ...) which invokes the action listeners added to the menuItem.

Categories