N
N
Nikolai Stupak2014-03-14 17:03:54
Java
Nikolai Stupak, 2014-03-14 17:03:54

Why is the window (java.awt.Window) redrawn incorrectly?

I'm not satisfied with the standard toolbar docking mechanism in Swing, so I'm making my own implementation.
When dragging a toolbar from one edge of the component to the other, a window (java.awt.Window) appears, which takes the size of the dragged toolbar, taking into account its orientation.

The position of the window is determined by the current coordinates of the cursor. Depending on whether the toolbar can be moved to its current position, the color of the lines and the background color in the window change.

Here are screenshots of what it looks like.
Window near the horizontal edge:
4100bf89d1794525856baa77445d551d.png

Window indicating that the toolbar cannot be placed here
a254f23f4a0648cc8929be8ec7311665.png

. Window near the vertical edge:
a004a802556644849343d4139d13197b.png

The problem is the following. When a toolbar is dragged and the cursor moves from one edge of the component to another (for example, from the left to the top), this window shows both the newly drawn state and part of the previous one. It looks as if a new state is being drawn, and an old state is being drawn on top of it, but due to the fact that the window size has changed to the opposite, the rendered old state does not fit completely, and only a part of it is visible.

Here is a screenshot of such a state:
3419359802f9493d8753a7ffa86ae077.png

Here is the code:

package ru.kih.gui.toolbar;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import ru.kih.gui.graph.Colors;

public class DragWindowHandler implements DragEffectHandler {
    
    private final ToolbarSupport toolbarSupport;
    private final DragWindow dragWindow;

    public DragWindowHandler(ToolbarSupport toolbarSupport) {
        this.toolbarSupport = toolbarSupport;
        this.dragWindow = new DragWindow(SwingUtilities.getWindowAncestor(toolbarSupport.getRoot()));
    }
    
    @Override
    public void showDragEffect(Dimension size, Point p, final boolean canDock) {
        final Point location = getLocation(p, size);
        final Rectangle rect = new Rectangle(location, size);
        
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                dragWindow.setCanDock(canDock);
                dragWindow.setBounds(rect);

                if ( !dragWindow.isVisible() ) {
                    dragWindow.setVisible(true);
                }
            }
        });
    }
    
    private Point getLocation(Point p, Dimension size) {
        Point location = new Point(p);
        SwingUtilities.convertPointToScreen(location, toolbarSupport.getRoot());
        
        int offsetX = size.width / 2;
        int offsetY = size.height / 2;
        
        location.x -= offsetX;
        location.y -= offsetY;
        return location;
    }
    
    @Override
    public void hideDragEffect() {
        dragWindow.setVisible(false);
    }
    
    private class DragWindow extends Window {

        private boolean canDock;
        private Color frameColor;
        private int lineTickness = 3;
        
        public DragWindow(Window owner) {
            super(owner);
        }

        public boolean isCanDock() {
            return canDock;
        }

        public void setCanDock(boolean canDock) {
            if ( this.canDock == canDock ) {
                return;
            }
            this.canDock = canDock;
            repaint();
        }

        @Override
        public void paint(Graphics g) {
            //super.paint(g);
            Graphics2D g2 = (Graphics2D) g;
            Dimension size = getSize();
            final Color color = getFrameColor();
            
            Color fillColor = canDock ? new Colors(color).a(.5f).build() : getBackground();
            Color lineColor = canDock ? color : new Colors(color).a(.2f).build();

            g2.setColor(getBackground());
            g2.fillRect(0, 0, size.width, size.height);
            
            g2.setColor(fillColor);
            g2.fillRect(0, 0, size.width, size.height);

            g2.setColor(lineColor);
            g2.setStroke(new BasicStroke(lineTickness));
            g2.drawRect(lineTickness / 2, lineTickness / 2, size.width - lineTickness, size.height - lineTickness);
        }
        
        private Color getFrameColor() {
            if(frameColor == null) {
                frameColor = UIManager.getColor("Tree.dropLineColor");
                if(frameColor == null) {
                    frameColor = Color.RED;
                }
            }
            return frameColor;
        }
        
    }
    
}


I've been struggling with this bug for the second day and I can't figure out what's wrong. At first I thought that the problem was that changing the size and position of the window is called in the general code, and then after that the window is redrawn somewhere else with the old values. Wrapped the code in a Runnable and called it in SwingUtilities.invokeLater(Runnable) (in the showDragEffect method). Did not help. Tried to use other AlphaComposites when drawing the window (in DragWindow.paint(Graphics)). But that doesn't help either.
Tell me, please, what could be the problem?

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question