Stop Swing components relocating to initial coordinates upon ChangeEvent? - java

I have some movable objects (JLabels) and the user will reposition them within the parent ChartPanel during runtime. To move the objects I've implemented the (really helpful) ComponentMover class, found here: http://tips4java.wordpress.com/2009/06/14/moving-windows/).
As they are moved, their (x,y) coordinates are displayed back to the user. I'm using a JTabbedPane and the coordinates of two objects are displayed per tab (I tried to post a screenshot but my lack of reputation won't allow it).
The problem is when I change tabs, the objects reposition themselves to the coordinates I had initially set them to. I think this is something to do with revalidate() or repaint()?
I tried to implement a ChangeListener
public static final ChangeListener
relocateObjects = new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
labelPole.setAlignmentX(polePositionX);
labelPole.setAlignmentY(polePositionY);
labelZero.setAlignmentX(zeroPositionX);
labelZero.setAlignmentY(zeroPositionY);
}
};
on the JTabbedPane
tabbedPane.addChangeListener(ComponentMover.relocateObjects);
to relocate the object to coordinates stored just before the event, but to no avail.
I tried to find a way to prevent the objects from listening (and thus responding) to a state change or simply update their default positions to override their initial coordinates, which you'd think would be an option, but I have failed.
I've literally spent all day on this, so I would love a solution or a push in the right direction. Thank you.

The problem is when I change tabs, the objects reposition themselves to the coordinates I had initially set them to.
You need to use a null layout on the panel that contains the components you are dragging:
panel.setLayout( null );

Related

Can you make the background of a JScrollBar transparent?

I have a series of column labels that scrolls independently from the data that is displayed in a matrix below. I can make the whole scrollbar transparent except on hover. The labels are right up against the data, which I like, however, upon hover, unless I shift the vertical scroll (which I'd rather not do), the scrollbar obscures the beginning of all the labels.
I would like to set the background of the scrollbar as transparent so that only the "grabber" (or whatever it's called) is the only thing that is drawn. (It will obscure the beginning of the labels it is over, but would be a lot less so.)
Is there any way to do that? Here is what I tried:
Color bg = new Color(255,255,255,0);
colLabelScroll.setBackground(bg);
This does not seem to make the background of the scrollbar transparent.
What I'm shooting for is like how the iPhone's scrollbar grabber hovers over info in some apps. Is that even possible with JScrollBars?
Transparent JScrollBar can do it, but consider this: if column labels are related to the data and you can scroll them independently, beginner users may not understand what is going on and associate column labels with whatever is visually aligned beneath it. Either you will need some sort of visual indicator that makes it clear that the labels are disconnected from the data, or you should change the way labels are scrolled that never leaves them statically in 1 place.
Here's how I ended up making the relationship between the labels and the data clearer:
Instead of allowing the user to independently and intentionally scroll the labels, I decided to control the label scroll position via mouse hover. This eliminates the need for the obtrusive scrollbar.
I created a scroll-bar-like indicator that shows the portion of the data the labels represent.
I highlighted the currently hovered label that corresponds to the data below it, i.e. the only label that is ever correctly aligned with the data is the one that is under (or directly above) the cursor.
When the mouse is not hovered over (or dragging from) the column labels, do not display any labels. This helps prevent invalid label/data associations by the user.
A few nuanced notes: Implementing your own scrollbar-like indicator is somewhat involved, especially if your labels are painted and then rotated, because the paint position of 0 is at the bottom of the pane, yet the vertical scroll position of the pane is at the top. You will have to track the vertical scroll position to be able to recover it again when the cursor returns since you are blanking the labels on mouse out.
When developing a plugin for IntelliJ, I accomplished it with:
scrollPane.getVerticalScrollBar().setUI(ButtonlessScrollBarUI.createTransparent());
It takes advantage of the the:
ButtonlessScrollBarUI.createTransparent()
method, which is an IntelliJ specific method. However, if you can find a ScrollBarUI which has a transparent background, you can use the same trick.
Since I got a bit lost myself at first after reading #hepcat72's answer I'm posting a little explanation about the BasicScrollBarUI class:
JScrollBar scrollbar = scrollPaneConversation.getVerticalScrollBar();
scrollbar.setUI(new BasicScrollBarUI(){
// This function returns a JButton to be used as the increase button
// You could create your own customized button or return an empty(invisible) button
#Override
protected JButton createIncreaseButton(int orientation){
}
// Same as above for decrease button
#Override
protected JButton createDecreaseButton(int orientation){
}
// This function paints the "track" a.k.a the background of the scrollbar
// If you want no background just return from this function without doing anything
// If you want a custom background you can paint the 'Graphics g' object as you like
#Override
protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
{
}
// This function paints the "thumb" a.k.a the thingy that you drag up and down
// You can override this function to paint it as you like
#Override
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
{
}
});
Refer to the Transparent JScrollBar link posted by #hepcat72 for hints about what to do exactly in these functions.

Set correct dialog location without knowing parent component

I am trying to figure out how to get rid of parents passed in application just for the reason to center dialogs over window or frame.
Application I am responsible for is quite large and not written nicely. Refactoring is in process however it's not easy to decouple some things. Part I am trying to change now is window with tabbed pane. Tabs are same class and consists of several panels which are responsible for different things, so they are also in different packages and classes. Good thing that each class is responsible for single thing only now, bad one is that there are lots of dialog (error, info, question...) which require parent to be centered correctly over window.
I would like not to use these parents all the way down, however at this moment it seems impossible to move all dialogs up in hierarchy. Just setting parent null also doesn't solve problem as they appears somewhere else then window (may be problem on dual screen).
I was thinking about static method in main window which returns current location (or null if doesn't exist). Then set all references to parent as null and set modality type to application. Finaly setLocation on point given by static method. I feel it's not the best solution, but I think it's much better then passing whole window as parameter cca 10 levels down.
What do you think about it? Is it acceptable or do you have some better idea how to handle parent in large GUI application?
EDIT: To specify my intention more deeply - I need dialog boxes to be centered over it's parent. In other words, it doesn't need to be centered on the screen, it should be located directly over the window (not necessarily in the center of window bot just over it). If I set parent to null or location to center of the screen, it can happen that dialog box will display somewhere else than parent window.
EDIT2: So I did it for now as I suggest above. I have created static field JFrame parent = null. Next static method was created public static Point getCenterPosition(Dimension dimension). If something calls this method and parent doesn't exist yet, topleft of the screen is returned (however it can't happen).
As parameter is passed size of dialog I want to center, which is used for inner calculation. As a result method return point, so I can use dialog.setLocation(ParentFrame.getCenterPosition(dialog.getSize()));
Now I have dialog nicely centered and I don't need passed parent at all. It's not very nice solution, but it works.
While I am not entirely sure what specific effect you are trying to create without some form of illustration I believe you are trying to get a dialog or pane centered in the screen.
Common practice for the creation of GUI is to base your sizes on the screen resolution that is being used. (this helps your application scale to different resolution monitors)
If you know the screen resolution it is also relatively easy to determine the center point of the screen where you want to display your dialog or pane.
An out of the box solution for determining screen size can be found in the Toolkit (Java7)
I propose you use getScreenSize() or getScreenResolution() (depending on your preferred approach) and then take half of each dimension to find the center point of the screen.
subtract half of your dialog or pane size and you will know the origin point for your pane.
You could put inside of the dialog which has to be centered something like
Window ancestor = SwingUtilities.getWindowAncestor(this);
setLocationRelativeTo(ancestor);
I use it all the time and it avoids having to pass the parent explicitly.
Of course the dialog has to be called by the parent you want to center to.

Problem with mouseListener on JPanel

I have problem with mouseListener in JPanel.
I add JLabel which contain image(size 600 x 600) on JPanel, and I add mouseListener for the JPanel.
Everytimes I click on the image, the image should change to another image, and this is working fine. However, the problem is that, only if I click on the right side or at the center of the image, then the image will change to another image. The image doesn't change when I click on the top or on the left side. This make me confused. I want the image to change to another image when I click everywhere within the image display.
private final int SECOND= 1;
private final int FIRST = 0;
int imageCounter = 0;
JLabel picture;
JPanel panel;
...
private mainLayout () {
GridBagLayout m = new GridBagLayout();
Container c = (Container)getContentPane();
GridBagConstraints con;
c.setLayout (m);
picture = new JLabel();
picture.setIcon(getImages(myImage.get(imageCounter).get(FIRST))); //first Image
panel = new JPanel();
con = new GridBagConstraints();
con.anchor=GridBagConstraints.CENTER;
con.gridy = 1; con.gridx = 0;
m.setConstraints(panel, con);
c.add(panel);
panel.add(picture); //add the pictures
panel.addMouseListener(l);
....
}
MouseListener l = new MouseAdapter(){
public void mouseClicked (MouseEvent e)
{
Point p = e.getPoint();
if((panel.getBounds().contains(p))
picture.setIcon(getImages(myImage.get(imageCounter).get(SECOND)));
}
};
The problem looks as if it might be in this unnecessary code:
Point p = e.getPoint();
if((panel.getBounds().contains(p))
The mouse listener is on the panel, so the mouse coordinates will be relative to the panel top left. panel.getBounds() gets the bounds of the panel relative to whatever its parent container is.
It's worth noting the mouse event behave very strangely. They "bubble up" until they hit a component with a mouse listener attached. So, adding a mouse listener actually changes the behaviour of a component. Adding the listener to a parent will potentially miss events depending upon the exact way the component is set up (which may change arbitrarily). There are a number of ways around this, none of them good.
The likely problem you have is the same I have encountered, that your event is getting gobbled up in your hierarchy as a previous poster implied. In reference to camickr's reply, it really doesn't matter if you extend JPanel or JLabel as long as you're aware of the consequences. JPanel offers you a lot more capability and has many useful things already built into it. But let's take the simple example where you have the following:
A JFrame that contains a JPanel, call it's JParent. JParent has a set of mouse listeners dedicated to it.
JParent contains a child JPanel that's added to its components, called JChild. JChild also has its own set of mouse listeners.
What happened when you click on an area that is not occupied by JChild? You get the response of JParent's listeners. If you click in an area with JChild visible, only JChild's mouse events will fire.
So be very careful in debugging as Java's swing may have different behavior than you may be used to from other implementations of drawing simple forms/GUIs. Events are basically one-shot and the lowest level component gets first dibs. If you try to fire the parent's events anyway, your x/y coords will be based on the child and not the parent. I'm still trying to come up with a good work around myself because I need access specifically to the parent to move an object elsewhere.
The best I can think of that isn't a mess to debug/think about are these two ideas:
a) have each of your individual components contain their own exclusive set of events and try to prevent the 'bubbling up' effect from coming into play.
b) handle all events on the parent
I'd prefer a as it avoids nasty things such as checking component classes just to figure out what kind of reaction you want (i.e. things like what kind of right-click menu to show).
Edit: So you can handle this either way I've discovered. You can use the dispatchEvent message to forward the events back upward the chain. In my case I have to gobble up the first event (or alter it) and send the x-y positions as if they are relative to the parent (using offsets). I'd say the better way to do it is through forwarding because then all your individual components can be encapsulated.
Are you positive that you are actually clicking? The mouse click event only fires if the mouse is pressed and released in the exact same spot. If your mouse is moving even minimally while you are clicking, the event won't be fired.
Out of curiosity, why are you using a JLabel to display an image? You may be better off writing an ImagePanel class that extends JPanel and overrides the paint() method to do your own drawing. Then you can attach MouseListeners right to the panel.

How do I control the display of a JComponent's Tooltip?

I have a JComponent that's painting various shapes on itself. I'm detecting whenever the mouse enters one of these shapes and changing the tooltip accordingly.
The problems I'm having are:
The tooltip doesn't follow the mouse as the user tracks the mouse across the shape. It
stays where it was first set and then only jumps whenever another shape changes the tooltip.
It takes about a second for the tooltip to appear, but I'd like it to appear immediately.
Can someone suggest a way of getting these behaviours without writing a custom tooltip mechanism?
Take a look at the ToolTipManager.
You can register your component with that manager and then adjust a number of settings. Its pretty straight forward to use.
That at least can solve your initialdelay problem.
For your first problem you can overide the createTooltip command from your component to get a hold of the JTooltip instance. and then its easy make the position change whenever you move your mouse(aka follow your mouse) as its a subclass of the JComponent class.
To solve your first issue of where the tooltip doesn't follow the mouse, if you override the getToolTipLocation(MouseEvent e) in JComponent, you can return the point for where you want to the display the tooltip. The MouseEvent will allow you to retrieve the x and y.

Find "real" height/width of Swing/AWT object

Because Canvas3D doesn't have the ability to resize dynamically with the parent frame, I would like to be able to track when a user resizes a window and then resize it manually myself. (If this ends up crashing Canvas3D, as some docs suggest, I will simply destroy and recreate it when the user resizes their window). Part of this procedure involves being able to accurately tell how big the container panel is to begin with.
The two methods I've tried:
panel.getHeight();
panel.getPreferredSize().height;
Don't seem to accurately report things: getHeight() is invariably zero, and getPreferredSize() returns numbers that don't actually have anything to do with the actual size of the panel.
Any ideas?
Edit: So, I took a debugger to the panel object and manually inspected the non-object properties and I didn't see anything that resembled width/height. Granted, there are sub-objects that I didn't look at. Also, maybe the window has to be visible (it isn't, at the point I'm interfacing the object) when I query for height/object?
Edit 2: So, Swing classes are subclasses of AWT classes, so I imagine if you're able to find the height/width of those, the approach would generalize. I've amended the title accordingly.
To determine the size of a component you have to either:
have set it manually at some point
run the layout manager responsible for layouting the component
Generally, you get the exact size of a component via the getSize() method, which returns a Dimension object containing width and height, but getWidth/Height() should work too. But this can only work, if one of the two preconditions are met. If a window has never been made visible, has no layout manager or the component (you want to know the size of) has been added after the window/container has been made visible, the size usually is zero.
So to get the correct size, you have to make the container/frame visible (after you have added the component) or call validate() or doLayout() on the container to recalculate the layout, if you added the component after the last layout was done. Another thing to keep in mind is setting and probably configuring a layout manager on the container. If no layout manager ist set (null), even making a container visible oder calling validate() does not set a size on its children.
The minimumSize/preferredSize/maximumSize properties are hints to the layout manager, how the component should be sized, but it does not have to obey them (most layout managers don't).
Edit 2: After I read your other question about the same subject, I think you should read Using Layout Managers from The Java Tutorials
Edit: I don't know if you already figured that out, but to react to the resizing of the window, you can do something like this:
public class WindowResizeTest extends JFrame {
public static void main(String[] args) {
new WindowResizeTest();
}
public WindowResizeTest() {
this.setSize(640, 480);
JPanel panel = new JPanel();
panel.setBackground(Color.RED);
this.add(panel);
this.addComponentListener(new ComponentListener() {
public void componentResized(ComponentEvent e) {
System.out.println(e.getComponent().getSize());
}
public void componentHidden(ComponentEvent e) {}
public void componentMoved(ComponentEvent e) {}
public void componentShown(ComponentEvent e) {}
});
this.setVisible(true);
}
}
I found out that if you extend by JFrame, this code can be used also to save time, effort and space.
int windowWidth = getWidth();
int windowHeight = getHeight();
I know you already got an answer but if you ever need an alternative, here it is.

Categories

Resources