How to detect JPanel child component paint event? - java

I have a JPanel where I place components in it. I need to get notified every time when a sub component (even nested) is repainted. Is there an event for it that I can register when a component is added to the JPanel?
My purpose is to draw a scroll bar on the right and bottom edges of the JPanel but when the sub component is redrawn, it overlaps the scroll bars and makes a part of scroll bar invisible. I want to redraw the scroll bar when a component refreshes its view so it stays behind the scroll bar.
To make it a little bit more clear, this two are the screenshots of the UI.
The first image is incorrect but this happens only if I hover the components with the mouse. Otherwise, it's looking good. The image on the right is when the container is loaded. I want the components always appear behind the scroll bar but I just cannot detect when a specific component is repainted.

For those who encounter similar problem like this,
I've found the answer within Swing's own painting mechanism. If you want to receive component paint events from a container's child, override isPaintingOrigin() method.
#Override
protected boolean isPaintingOrigin() {
return true;
}
Only this will be sufficient and painting event will be called by the underlying container.

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.

Does Java repaint the components when resize or mouse over?

I am not asking how to use Java swing nor I am asking for suggestion on using layout managers. I am just curious how Java behaves.
All along it has been a myth and many people speculate that Java automatically repaints the components when you resize the frame OR mouse over the components in the frame.
So my question is: Is it true that Java does the repainting automatically when we carry out one of the above actions?
There has been several post with similar title such as: Java repainting a component at mouse-over.
But no one can give a definite answer whether Java does the repainting automatically upon certain user actions (such as resizing & mouse over).
All along it has been a myth and many people speculate
There is no myth or speculation.
automatically repaints the components when you resize
This makes sense because the layout manager is invoked and the size or location may change which means some components may need to be repainted.
automatically repaints the components when you mouse over the components in the frame.
It depends on the component. If a MouseListener has been added to the component to do special processing (ie. rollover a button) then the component may be repainted, otherwise nothing happens. But there is no default painting unless it has been specifically added as part of the UI for the component.
These question is easily verified. Just override the paintCompent() method of your components to display a message when the component is painted and see what happens.

How do I set the component background to a specific color in JGraphX?

I want to make the background of a JGraphX graph component (https://github.com/jgraph/jgraphx) a specific color all over. I tried the standard call for any Swing component:
graphComponent.setBackground(Color.BLACK);
but this has no effect. I tried forcing a repaint of the component also, no luck. Is the call incorrect, or is there some specific way to force a refresh?
Since mxGraphComponent extends JScrollPane change the background of the view port:
graphComponent.getViewport().setOpaque(true);
graphComponent.getViewport().setBackground(Color.BLACK);
From JScrollPane docs:
This can be accomplished by setting the background
color of the viewport, via scrollPane.getViewport().setBackground().
The reason for setting the color of the viewport and not the
scrollpane is that by default JViewport is opaque which, among other
things, means it will completely fill in its background using its
background color. Therefore when JScrollPane draws its background the
viewport will usually draw over it.

How do I make a Swing JComponent a bigger mouse target?

I have a JPanel that contains a bunch of Swing JComponents, including some JSeparators that may be only one or two pixels wide. I want to let my users drag the items around, but it can be rather difficult to hit a one or two pixel wide line. Is there a way that I can give those JSeparators a wider "target" region for mouse clicks? The only thing I've been able to think of is to have my mouse handler listen for clicks on the JPanel, and if it gets any, run through the list of JSeparators, looking to see if any of them are within a couple of pixels of the mouse click.
Should that work? Is there a better way?
Add a fat EmptyBorder to the component.
If it already has a border, you can set a compound border using the current border then the empty border, or simpler, add the empty border (and listener) to a panel that contains the component. The latter will work better for components such as JButton, which have borders that change according to state and focus.

Is it possible to have "movable"/"draggable" components like JButtons, JTextFields in a JFrame?

Basically I plan to place some buttons, textfields, labels, etc. on a JFrame and I would like to make it possible that a user can move the different components around on that JFrame with the mouse.
I have seen various ways with MouseListeners, subclassed JComponent code, DropSource/DropTarget implementations and so on, but I'm unsure which is the "recommended" way (I don't need to support "drag and drop" between different Frames/Applications which is what most examples seem to do).
The Component Mover can do this for you.
Use the GlassPane:
http://download.oracle.com/javase/tutorial/uiswing/components/rootpane.html
It's an invisible panel that sits on top of all other components. You can attach mouse listeners to it and then use SwingUtilities.getDeepestComponentAt() to figure out which component was clicked on beneath the GlassPane. Then use a mouseDragged listener on the glasspane and set the component location based on the mouse dragged event.
You will need to set the layout of your container to "null" so the components' setLocation will work.

Categories

Resources