Search code examples
javaswingawtjscrollpanemixing

Java ScrollPane and Canvas - Lock Canvas to JScrollPane size


I am currently stuck on a dilemma regarding JScrollPane and a child component. In essence I need strict control over the resizing of a child component within a JScrollPane such that it either locks to the size of the JScrollPane (so that no scrollbars appear) or stays fixed to a predefined size (and let JScrollPane show scrollbars when appropriate). This control must be able to be dynamically toggled (specifically by a togglebox from another JFrame window). The JScrollPane is directed locked to the parent JFrame window (fills it completely and locks to its resize via a BorderLayout).

At the present I am using a Canvas object as the JScrollPane's child component due to its triple buffering capabilities (createBufferStrategy(3);). I have seen in many places that Canvas and JScrollPane do not mingle very well so answers that can both fix the issue above and scrap the use of Canvas would be highly appreciated.

The layout of my components are as follows:

JFrame (Custom Class) -> JScrollPane -> Canvas

Not sure if this helps but the Canvas rendering code is as follows:

//This is a method from a nested class inside the JFrame class.
public void run() {
    long MaxFrameTime;
    long Time;
            //This is the Canvas Object
    RXDisplayCanvas.createBufferStrategy(3);
    BufferStrategy BS = RXDisplayCanvas.getBufferStrategy();
    Graphics2D G2D;

    while(isVisible()){
        MaxFrameTime = Math.round(1000000000.0 / FPSLimit);
        Time = System.nanoTime();

        //Render Frame from a source from another thread via a AtomicReference<BufferedImage> named 'Ref'
        BufferedImage Frame = Ref.get();
        if(Frame != null){
            G2D = (Graphics2D)BS.getDrawGraphics();

            int X0 = 0;
            int Y0 = 0;
            int W = RXDisplayCanvas.getWidth();
            int H = RXDisplayCanvas.getHeight();
            double Width = Frame.getWidth();
            double Height = Frame.getHeight();
            double ImgW = Width;
            double ImgH = Height;

            if(ImgW > W){
                ImgW = W;
                ImgH = ImgW / (Width / Height);
            }

            if(ImgH > H){
                ImgH = H;
                ImgW = ImgH * (Width / Height);
            }

            int CenterX = (int)Math.round((W / 2.0) - (ImgW / 2.0)) + X0;
            int CenterY = (int)Math.round((H / 2.0) - (ImgH / 2.0)) + Y0;
            G2D.setBackground(Color.BLACK);
            G2D.clearRect(0, 0, W, H);
            G2D.drawImage(Frame, CenterX, CenterY, (int)Math.round(ImgW), (int)Math.round(ImgH), null);

            //Additional Drawing Stuff Here


            G2D.dispose();

            if(!BS.contentsLost()){
                BS.show();
            }
        }

        Time = System.nanoTime() - Time;
        if(Time < MaxFrameTime){
            try{
                Thread.sleep(Math.round((MaxFrameTime - Time)/1000000.0));
            }catch(InterruptedException N){}
        }
    }
}

My current implementation of my problem which doesn't work very well (doesn't 'relock' with the parent JScrollPane; 'FixedDim' is a dimension object previously set):

/**
 * Sets whether to lock the size of the canvas object to a predefined dimension.
 * @param b If true, the canvas becomes non-resizable and scrollbars will appear when appropriate. 
 * If false, the canvas will resize with the enclosing scrollpane.
 */
public void setLockResize(boolean b){
    CurrentlyLocked = b;
    if(b){
        RXDisplayCanvas.setMinimumSize(FixedDim);
        RXDisplayCanvas.setMaximumSize(FixedDim);
        RXDisplayCanvas.setPreferredSize(FixedDim);
    }else{
        RXDisplayCanvas.setMinimumSize(Min);
        RXDisplayCanvas.setMaximumSize(Max);
        RXDisplayCanvas.setPreferredSize(FixedDim);
    }
}

Solution

  • and scrap the use of Canvas

    Swing is double buffered by default. Try just using a JPanel.

    I need strict control over the resizing of a child component within a JScrollPane such that it either locks to the size of the JScrollPane (so that no scrollbars appear) or stays fixed to a predefined size (and let JScrollPane show scrollbars when appropriate)

    You might be able to use the Scrollable Panel. You might be able to toggle between NONE and FIT for the ScrollableSizeHint.