views:

32

answers:

1

I'm creating a slider bar, which will be a subclass of JSlider, as part of a logging gui. What I'd like to do is be able to draw some items above the slider bar (e.g. a "volume" display, indicating how much logging info will be spit out by a particular setting). I have overridden the paintComponent() method, but I'm getting a bit confused about how the clipping rectangle is calculated for the Graphics object that is passed in to paintComponent.

Basically, what I want to do is create more space within the visible area of the component that can be drawn, but only ABOVE what is already drawn. So, for example, in the following image I have three slider bars:

slider bars

The clipping rectangle is shown in red for each of these. (These are somewhat hand-drawn, but I did actually use the drawRect(g.getClipBounds()) and came up with similar rectangles. The top slider bar is what I get if I don't augment the slider bar at all. The middle rectangle is what I get if I override getPreferredSize() to add some amount (I call it the 'volume height' - by default, 20) to the height of the component. The bottom image is the one I want. Basically, it's the middle slider bar, but with the clipping rectangle translated by 1/2 the volume height toward the top of the component.

So, my question is 'How are clipping rectangles computed within the Graphics object when passed in to a paintComponent() method?' I can't seem to find out how the system determines what the clipping bounds should be for a given component.

Thanks,

~Scott

+1  A: 

The java.awt.Graphics API mentions, "The combination of the user clip and device clip defines the composite clip, which determines the final clipping region," but clipping is not really relevant to your question. You probably want to use a suitable layout, instead. Box layout, which enables Using Invisible Components as Filler, may be useful in this context.

Addendum:

I'd rather ... override paintComponent() in JSlider.

AFAIK, paintComponent() just delegates to a subclass of javax.swing.plaf.SliderUI. This example may suggest an approach.

alt text

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;

/** @see http://stackoverflow.com/questions/3971972 */
public class JSliderTest extends JPanel {

    public JSliderTest() {
        super(new GridLayout(0, 1));
        this.setPreferredSize(new Dimension(500, 400));
        this.add(genSliderPanel());
        this.add(genSliderPanel());
        this.add(genSliderPanel());
    }

    private JPanel genSliderPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        panel.setBorder(BorderFactory.createLineBorder(Color.red));
        JSlider slider = new JSlider(JSlider.HORIZONTAL);
        slider.setValue(40);
        slider.setMajorTickSpacing(20);
        slider.setPaintTicks(true);
        slider.setPaintLabels(true);
        panel.add(Box.createVerticalGlue());
        panel.add(slider);
        panel.add(Box.createRigidArea(new Dimension(16, 16)));
        return panel;
    }

    private void display() {
        JFrame f = new JFrame("JSliderTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new JSliderTest().display();
            }
        });
    }
}
trashgod
Thanks. This is good advice. I actually wanted this object to extend JSlider, rather than JPanel, though. I'd rather it be treated as a fully contained component, and override paintComponent() in JSlider, rather than extend JPanel.
jwir3
@jwir3: I'm wary of using the UI delegate to do layout, but I've suggested an approach above. Using your own layout manager may be another alternative.
trashgod
So, I just want to be clear. You would favor making the VolumeSlider a subclass of JPanel, because you think that the issue is layout? I sort of approached the problem a bit differently I think by looking at it as a special painting issue.
jwir3
One thing I did notice, though, is that if I override paintComponent(), and translate the graphics object passed in prior to the UI delegate's paintTrack() method call, (i.e. I perform the downward translation of the the painted components prior to any painting, to make room for the volume graphic) it works as I want, but I am then unable to click and drag the thumb graphic. I can click on the track to move it, but I can't click and drag. The problem appears to be with the translation prior to paintThumb() in MetalSliderUI.
jwir3
@jwir3: No, I would favor a `JPanel` (or subclass) _containing_ a `JSlider` (or subclass). The UI delegate assumes a consistent geometry for track and thumb.
trashgod