Most windows users may remember that every windows 98 properties/settings window had a little question mark button next to other window buttons:
If you clicked on that button, all click events were overriden by different callback for that window. And that new callback would display element's individual help text.
I'd like to do the very same. My idea was to do it using class which holds all JComponent and Help associations:
public interface Help {
/** based on implementation, displays help to the used. May use
* JDialog, url redirection or maybe open document on the computer.**/
public void getHelp(JComponent comp, ActionEvent evt);
}
public class HelpLibrary {
public HashMap<JComponent, Help> helpLib;
public void getHelp(JComponent comp, ActionEvent evt) {
Help help = helpLib.get(comp);
if(help!=null) {
help.getHelp(comp, evt);
}
}
}
Writing these two classes was the easy part. The hard one is this:
How to override all click events in particular window and then remove override after help was called?
How to ensure help cursor will override all other cursors, and again, safely remove this setting?
I have no idea where to start with this. I really do not want to change the GUI structure or used classes just because of this, which is why I want to store the help and do the overrides from the outside.
public class HelpLibrary {
/**
* Overrides click events on the given window and displays help cursor.
* User then may click a JComponent, such as button, to initiate
* help callback for that element. If no help exists for that element,
* do nothing and stop the help mode.
* #param window the window to get help for
**/
public void waitForHelp(JFrame window) {
???
}
}
You can try following:
Register a global MouseListener using
Toolkit.getDefaultToolkit().addAWTEventListener(myListener, AWTEvent.MOUSE_EVENT_MASK)
Cast the incoming event to MouseEvent and check the event type using the getID() method
If the event is a click for a component, which has help, you need to show help, consume event and remove this listener from the global listener list.
You can also override mouseEnter/Exit event in this
listener for components which have help text, and set the cursor to
question/normal type (don't forget to consume this event).
This is only idea for you, I've not tested whether it works.
You could use a GlassPane.
Read the section from the Swing tutorial on How to Use Root Panes. The Glass Pane demo shows how to intercept mouse events and redispatch the event to the underlying components. YoOu would obviously change this code to find the component under the mouse click and then display the help context.
The glass pane can be toggeled on/off by making it visible or not.
Related
My user story is the following:
In order to upgrade a tower, the player has to drag a gun from the shop and drop it on the tower.
In practice, the GameView contains both the BattlefieldCanvas and the ShopView. Besides, the ShopView contains a GunSelector for each buyable gun. All those guys are sub-classes of JPanel.
I'm currently using a MousListener to handle several actions performed on the BattlefieldCanvas; I thought I could use the same stuff to handle a mouse trip from one of the GunSelector to the BattlefieldCanvas ( = across several panels ), so I tried to add the same MousListener to the gun selectors and the battlefield.
Problem: doesn't work. The getSource() method of the event object returns a reference to the gun selector while the mouse is actually released on the battlefield.
PS: Unlike gun selectors, towers are not swing components but images drawn by the paintComponent method.
1) To answer to your original question as to why source is still the component mouse was clicked on. You just need to read the JavaDoc:
public Object getSource()
The object on which the Event initially occurred.
Returns:
The object on which the Event initially occurred.
2) Now, how can we get the actual component that mouse is released on. You can try this approach:
#Override
public void mouseReleased(MouseEvent e) {
Component theCOmponentMouseIsReleasedOn = frame.findComponentAt( e.getLocationOnScreen() );
}
You don't need to always call findComponentAt on frame, you can call on the container that contains your BattlefieldCanvas.
So I am building an login frame using Netbeans 8.1. The problem is that after the user has typed in their username, I want them to be able to press the tab key and then have the password textField selected, but instead of this, the 'Login' button is selected.
On startup the username field is selected.
Then when I press tab it looks like this
But instead I want it to look like this when I press tab
Any help is welcome, but I would prefer it if I can do this without getting into the code and can use the Netbeans GUI instead(the project is only about designing the GUI, and nothing about the actual code).
You need order your component indexes, it calls tab index.
NetBeans 8.0 I believe the first component (ie. textBox) you add to the design window should be the one the cursor defaults to when you run the program. You can set the order for tabbing from component to component when you run the program in the design window. Select the first component. In the Properties window, go to nextFocusableComponent and choose the next component you want to tab to in the dropdown list. Repeat for rest of components
I hope this helps you!
Look at this answer.
You can implement your own FocusTraversalPolicy which will manage the tab order. You can then assign it to your frame with the setFocusTraversalPolicy method.
You can find some more explanations in Oracle's doc.
No Tab key-pressed or key-released events are received by the key event listener. This is because the focus subsystem consumes focus traversal keys, such as Tab and Shift Tab.
See: http://docs.oracle.com/javase/tutorial/uiswing/events/keylistener.html
To solve this, apply the following to the component that is firing the key events (e.g., the TextArea):
.setFocusTraversalKeysEnabled(false)
Using this method, you must then handle focus traversal explicitly. Alternatively, the KeyEventDispatcher class can be used to pre-listen to all key events.
you can do following way also
txtUserName.addKeyListener(new KeyAdapter()
{
public void keyReleased(KeyEvent arg)
{
if (arg.getKeyCode() == KeyEvent.VK_ENTER)
{
txtPassword.requestFocus();
}
}
});
txtPassword.addKeyListener(new KeyAdapter()
{
public void keyReleased(KeyEvent arg)
{
if (arg.getKeyCode() == KeyEvent.VK_ENTER)
{
btnLogin.doClick();
}
}
});
so I searched online and tried things and common functions to focus on window but whenever I click on Exit button, it won't return to main JFrame.
When I remove the this.setEnabled(true), it does it but what I really wanted to do is to disable the main JFrame when jButton9 is clicked and show the JInternalFrame. Then close the JInternalFrame when Exit button is clicked.
I tried the instructions on this website http://www.coderanch.com/t/334157/GUI/java/JInternalFrame-Focus
Here's what I've done so far. I'd appreciate any help.
private void jButton9ActionPerformed(java.awt.event.ActionEvent evt) {
AddTo_Assigned_Subjects_InternalFrame.setVisible(true);
this.setEnabled(false);
AddTo_Assigned_Subjects_InternalFrame.requestFocusInWindow();
}
private void ExitActionPerformed(java.awt.event.ActionEvent evt) {
this.requestFocusInWindow();
AddTo_Assigned_Subjects_InternalFrame.setVisible(false);
AddTo_Assigned_Subjects_InternalFrame.dispose();
}
"this" is my MainFrame
"Add_To_Assigned_Subjects_InternalFrame" holds the exit button.
Thanks.
Form Javadoc on requestFocusWindow()
This method cannot be used to set the focus owner to no Component at all. Use KeyboardFocusManager.clearGlobalFocusOwner() instead.
The focus behavior of this method can be implemented uniformly across platforms, and thus developers are strongly encouraged to use this method over requestFocus when possible. Code which relies on requestFocus may exhibit different focus behavior on different platforms.
new to Java here but i have been experimenting...
I am trying to achieve this in Greenfoot: I want to have a mouse click on an object (Actor) and it disappears from the world. This is my code so far:
public void act()
{
disappear();
}
public void disappear(){
if(Greenfoot.mouseClicked(this)){
getWorld().removeObject(this);
}
}
From my understanding, when the mouse is clicked, then it will remove the object from the world... but the object still doesn't disappear, what am i missing here?
Many thanks!
You can regist the action
getWorld().removeObject(this);
to the mouse clicked event list.
Maybe show us the class archi of your system could give us more information.
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.