This seems like a simple behavior, but I'm having difficulty making it happen. I'm working on software which graphs data. I want to redraw the graph when the user hits enter. Well more accurately I want to draw the graph when the user hits enter and doesn't have a text field selected; but for now I'll be satisfied with drawing whenever the user hits enter.
I tried installing a basic KeyListener to the frame first, but that doesn't work since the JFrame children, not the frame, receive the event.
I then tried to use the KeyEventDispatcher, but it's proving too global a concept. I can have two graphs on screen at once, and an old graph can be closed or replaced with a new graph. With the KeyEventDispatcher I have no easy way of knowing which plot I want to activate out of the multiple plots open at a time and the plots currently open now may not be the plots that were opened when I instantiated the key dispatcher. I know this solution could still work, but it requires my storing extra data as to what plot is currently active that doesn't fit well into the program architecture.
It seems as if there should be an easier method to capture any KeyEvents dispatched to a JFrame/JPanel or any of the JFrame's children; but ignore events dispatched to other frames. Preferable a method that can be added to the frame and thus is automatically disposed when the frame is disposed. Can anyone suggest a simpler method then what I've tried?
Don't use a KeyListener.
Add a Key Binding to the graph panel.
This works very well
this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "fecharAction");
this.getRootPane().getActionMap().put("fecharAction", new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
int resp = JOptionPane.showConfirmDialog(MainForm.this, "Encerrar sistema?", "Confirmação", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (resp == 0) {
MainForm.this.setVisible(false);
MainForm.this.dispose();
}
}
});
Related
All Swing components in my app (except labels) have tooltips that can be annoying once the user knows what's going on, so I have a Preferences menu that allows turning them off. I could name every component and set its tooltip text to "" [e.g., txtPattern.setToolTipText("");] (and 10 others), but I decided (with SO aid that started awhile back) to write code that would be more elegant (a learning experience):
private void tipsOff(Container container){
Component [] c = container.getComponents();
for (Component cc : c)
((JComponent)cc).setToolTipText("");
}
private void mniPrefTooltipsActionPerformed(java.awt.event.ActionEvent evt) {
if(! mniPrefTooltips.isSelected()){
tipsOff(gui.getContentPane());
tipsOff(gui.pnlLetters);
tipsOff(gui.mbrMenuBar);
}
else{
gui.dispose();
gui = new IO();
gui.setVisible(true);
}
}
I have a problem, which is that the tooltips are NOT turned off for the two large text areas at the bottom of the gui (highlighted in the Navigator pane). The two buttons (marked with green in Nav. pane) ARE processed correctly. These items are supposed to be processed via the first call to tipsOff, which processes gui.getContentPane()).
(I added the two lines bellow to try to rectify the problem. Nope.)
tipsOff(gui.scrOutput);
tipsOff(gui.scrScratch);
(Also tried this. Nope.)
tipsOff(gui.txaOutput);
tipsOff(gui.txaScratchwork);
How can I elegantly (i.e., assume I have many text areas, not just 2) turn off the text area tooltips?
P.S. I get the message Access of private field of another object for all but the first call to tipsOff. I don't care, owing to the nature of the task at hand.
Use ToolTipManager.sharedInstance().setEnabled( false ) to disable all tool tips in your Swing application.
Benefits compared to your approach
It works :-)
You do not clear the tooltips, so it is easy to re-enable them again. For example if you want to offer UI to your user to activate/de-activate the tooltips this approach will work. In your approach, you would have to restore all the tooltips you previously cleared, which would be difficult to do in a generic way.
I'm making a level editor for my java game using Java swing.
One of the features is that there is a a togglable button to turn the game on and off to test the level out. The game runs inside a jpanel, then you click the button again to untoggle it, and it turns the game off.
I only want the user to be able to change stuff or push buttons in the swing application when the game is NOT running, when it is running I set the focus to the game component. The only button in the swing application that they should be able push is the toggle button to turn the game back off.
The problem is, I can't think of a good way to do this. Using a recursive function I could easily loop through and find all components and do setEnabled(false), but when the game is turned back off it has no way to know what the previous enabled state was (along other issues, like other components responding to setEnabled being called on other components)
What I think I really need is just some kind of way to just outright "kill" user input into the swing application when the game is running.. But preferablly with a way to click the toggle button again to return the application's state, and the game which is running inside a Jpanel needs to be able to have focus...
Is there any way to do this sort of thing at all without massive amounts of "organizational" code to manage the components in the swing application?
You can place everything in a map, like this.
class ComponentState {
private JComponent component;
private bool on;
// Getters & Setters
}
private Map<String, ComponentState> components = new HashMap<>();
in order to add a new component to your game:
components.add("startbutton", new ComponentState(new JButton, true));
then to add all components to your screen:
for(String key : components.KeySet()) {
ComponentState comp = components.get(key);
if(comp.isOn()) { this.add(comp.getComponent()) };
}
and to disable/activate a component:
components.get("myActivatedComponent").disable(); // disable is a self defined method
You want a disableAll() method that sets every component to a disabled state, and a resetAll() method that will reset every component state back to its previous state. You need to save the status of every component when you disable it, in order to be able to restore it after. That will take O(n) space.
private final Map<JComponent, Boolean> components = new HashMap<JComponent, Boolean>();
public void disableAll(JComponent root) {
components.put(root, root.isEnabled());
root.setEnabled(false);
for (int i=0, n=root.getComponentCount(); i<n; i++) {
JComponent child = (JComponent) root.getComponentAt(i);
disableAll(child);
}
}
public void resetAll(JComponent root) {
boolean status = components.get(root);
root.setEnabled(status);
for (int i=0, n=root.getComponentCount(); i<n; i++) {
JComponent child = (JComponent) root.getComponentAt(i);
resetAll(child);
}
}
Another option is to use a GlassPane and "grey out" an area of components. You would also have to capture and ignore clicks in the pane for the area you don't want a users clicking.
See more with an example in the Java Tutorial here: http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html
This write up could also be helpful:
https://weblogs.java.net/blog/alexfromsun/archive/2006/09/a_wellbehaved_g.html
I'm working on a Java Game and I've come to a point where I'm having problems with the KeyListeners/KeyBinding. What I basically want to do is temporarily disable the keyboard/do not allow more inputs when an animation occurs. This animation is generated by updating data.
What I currently get is that I press the key for the animation, the animation starts, and I press another key that does some other function. It gets added to the stack/queue(?) of the keyboardlistener and triggers when the first animation finishes.
I'm using a JPanel that implements KeyListener.
To give an idea of the code:
public void keyPressed(KeyEvent arg0) {
//Prevents Repeated keys
pressed.add(arg0);
if (pressed.size() == 1) {
int key = ((KeyEvent) pressed.toArray()[0]).getKeyCode();
if (key == KeyEvent.VK_ENTER) {
doSomeAnimation();
} else if (key == KeyEvent.VK_SPACE) {
doADifferentAnimation();
}
Update();
}
}
Things I've tried:
1) Set the focusable(false) on the JPanel before the calls to the animations. Then set the focusable(true) and grab the focus when they've been completed.
2) Used a boolean to track when an animation occurred.
3) Use Key Bindings.
No matter what method I used, I always ended up with the problem that I would still take in input from the keyboard when the animation was occurring. Then, once that animation was finished, it'd go to the next element in the stack/queue(?) and process that. Also, these animations would need to occur more than once (so using an array of booleans to verify if it's been executed already wouldn't be helpful).
So, if you have any idea or help (or places to point me to) that would be greatly appreciated.
Some Extra Information: Java 1.6, IDE Eclipse, MVC structure. (This in question is the Controller/Model)
Assuming your animation is driven by an instance of javax.swing.Timer, the queue in question is the EventQueue, which runs events in "the same order as they are enqueued" by the Timer. Because it is impractical to stop other devices on the host platform from evoking your listeners, you have to track the effect in your application. As a concrete example, this game has several overloads of the model's move() method to handle input from disparate sources: keyboard, mouse or animation timer. The timer may be toggled on or off to see its effect. This example, which supplants the EventQueue with a custom implementation, may also offer some insight.
Apologies for the somewhat unclear question - couldn't think of a better way of putting it.
I use a JXTaskPane (from the Swing labs extension API) to display some information.
The user can "click" the title to expand the panel. The JXTaskPane is in a container JPanel, which is then added to a JFrame, my main application window.
I want my application window to resize to the size of the expanded task pane. To achieve this, I added a component listener to my container JPanel which would set size to the now expanded panel.
panel.addComponentListener(new ComponentListener()
{
public void componentResized(ComponentEvent e)
{
Dimension newSize = ((JXTaskPane)e.getSource()).getSize();
reSizeFrame(newSize);
}
}
private void reSizeFrame(Dimension newSize)
{
if ((newSize.height < maxSize.height) && (newSize.width < maxSize.width))
{
containerPanel.setSize(newSize);
appFrame.setSize(containerPanel.getSize());
appFrame.pack();
}
}
The problem is that the componentResized method is called as the task pane expands, as a result the resizeFrame method is called lots of times, and looks really awful on the screen.
How can I detect when the JXTaskpane has finished resizing? I thought of two approaches:
Put the resizeFrame() method in a SwingUtilities.invokeLate(..) call.
Put in a timer resizeFrame call, so any subsequent calls do not do anything until the timer fires. This should give enough time for the panel to resize.
What is the best way forward?
Also - This is my first serious Java GUI app after years of server side program. StackOverflow has been very helpful. So thanks!
I know you've already selected an answer, but overriding the paint method is definitely not correct, and while you may be able to hack something in place, it won't be ideal.
Looking at the source for JXTaskPane and specifically looking in setExpanded() (line 387), you can see it calls JXCollapsiblePane.setCollapsed(...) and then fires a property change event for expanded. A listener on that property won't be correct, because it'll fire before the animation is complete. So, if you go into JXCollapsiblePane and look at setCollapsed(...) (line 470) you'll see that if it's animated, it sets the paramaters and starts a timer. We want to know when the animation ends, so in that file, look at the animator (line 620, and specifically 652-667), which shows that when the animation ends, it fires a property change for ANIMATION_STATE_KEY with a value of "collapsed" or "expanded". This is the event you actually want. However, you don't have access to JXCollapsiblePane, so go back to JXTaskPane and search for ANIMATION_STATE_KEY, and you find line 208, which shows that JXTaskPane creates a listener on JXCollapsiblePane.ANIMATION_STATE_KEY and refires it as it's own event.
Since you do have access to JXTaskPane, you can listen for that event, so doing ...
taskPane.addPropertyChangeListener(JXCollapsiblePane.ANIMATION_STATE_KEY, new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if(e.getNewValue().equals("expanded") {
...
}
else if(e.getNewValue().equals("collapsed") {
...
}
}
}
should get your event exactly when you want it.
The correct way to listen for events in Swing is through property listeners. Unfortunately, the only way to find out what the correct properties and values are is by digging through source code.
As a suggestion, have you tried overriding the paint method, first calling super and then putting your resize code at the end of that if (and only if) the size has changed significantly.
I'm not familiar with JXTaskPane, but my first reaction is that maybe you're handling the wrong event. You want the frame to resize when the user clicks on the header - so why not handle that event (perhaps using EventQueue.invokeLater() to resize the frame after the task pane has been resized)?
But if that doesn't work and you need to use the approach you've outlined above, using a javax.swing.Timer is probably best. Set it for 200 milliseconds or so and just restart() it every time componentResized() fires.
I'm developing a grid based sim game in java, and I was wondering if there is a standard way of doing the following.
I have a panel which is the game panel, and there are many different things which could happen when the panel is clicked. For example, when building a room, there are several stages, where dragging the mouse and left clicking will have different actions.
Right now, the way I have done it, is to use booleans to check what's being built, and then what stage it is at.
Is there any better or standard way of handling something like this? I had a quick google, but as I have said before, people on Stack Overflow always give a better, more relevant, up to date answer.
I consider myself still rather new to java.
Thanks in advance.
You might try looking into something similar to the strategy pattern.
Basically, you start by clicking the room button on your toolbar. The toolbar goes through and tells the grid to use the 'room place' actionlistener. Presumably removing any previous action listener that was listening
The room place actionlistener would in turn implement all the interesting bit of logic for left/right clicking, dragging, etc.
If you have multiple stages to building a room (say, placing doors, then windows, then trap doors); the action listeners would be responsible for handing control off to the next stage: a bit of a finite state machine.
So, start by clicking 'room' button, 'place room' listener is added. Drag out the area you want the room to be, 'place room' modifies the game state, then changes the actionlistener to the 'place windows' listener. Ad infinitum... until you finish.
One very simple (non compilable) example:
class GridPanel extends JPanel
{
void SetMouseListener(MouseListener newListener)
{
for(MouseListener ml : getMouseListeners())
removeMouseListener(ml);
addMouseListener(newListener);
}
}
class ControlPanel extends JPanel
{
GridPanel gameGrid;
void OnRectangleButtonClicked(some stuff)
{
gameGrid.SetMouseListener(new PlaceRoomListener(gameGrid));
}
}
class PlaceRoomListener extends MouseAdapter
{
GridPanel gameGrid;
//constructor, etc
void OnClick(mouse event)
{
gameGrid.doCoolStuff();
gameGrid.SetMouseListener(new PlaceTrapDoorListener());
}
}
//etc
Now, that non-compilable example aside, Pyrolistical does have a point: you generally don't want to combine your game model and graphic interface into one single glob of classes. You want the model separated from the GUI, and to manipulate it through a well defined interface.
(Also, there are probably better methods for going about removing the mouse listener then just randomly removing all listeners... I was in a bit of a rush, sorry)
It sounds like you need to define your game model/state and keep it separate from your mouse actions.
Are you using MVC?