4

我必须到窗口(JFrame 和非模态 JDialog)并且我想将 JDialog 附加到 JFrame,这样当我移动 JFrame 时,JDialog 也会被移动。同步运动本身不是问题。我是通过 ComponentListener.componentMoved(.) 实现的。

但附件不应固定。用户应该可以通过将 JDialog 移离 JFrame 来分离它。只有当 JDialog 被直接放置到 JFrame 边框时,它才应该被附加。

为了帮助用户将 JDialog 恰好放置在 JFrame 旁边(附上它),我想实现“磁效应”:如果 JDialog 移动到 JFrames 边框的距离小于 10px,则 JDialog 应该自动放置在 JFrame 旁边。在 ComponentListener.componentMoved(.) 中检测这种情况也不是问题,而是设置新的 JDialog 位置:

当我通过 JDialog.setLocation(.) 首先设置新位置时,JDialog 被正确放置。但是当我完成对话框拖动(释放鼠标按钮)时,JDialog 被放回先前的位置(距离 JFrame 10px)

我认为这是因为在拖动结束时,“系统”也调用了 setLocation(.)。有什么想法可以防止这种行为吗?例如,从事件队列中删除任何以下鼠标移动或鼠标释放事件?

4

3 回答 3

7

我对这个问题很感兴趣,并决定制作我自己的小程序,该程序将创建一个主框架/码头和其他可停靠的容器。这些容器将跟随主框架以保持在它们的位置。

所以虽然回答了我想分享我的例子。

至于为什么 OPs 代码不能完美运行,我认为这是因为componentMoved每次移动都会重新触发,因此在可停靠的移动过程中出现了问题。

这是一个小示例,您现在可以将可停靠点添加到主机的北、东、南和/或西。Dockables 保持在其位置的方式由诸如StickyMagneticFollow Layout之类的布局管理。

Sticky Layout随主框架移动,而 Follow调用可停靠设备移动到主框架位置,Magnetic确实会吸引一定距离内的所有可停靠设备到主框架上指定的最终/停靠点(主框架的东顶部)的给定距离。

它使用 aComponentAdapter/ListenerSwing TimerwithComponent#setLocation(int x,int y)来实现结果。

下面的屏幕截图是 4 个可停靠的(北、西、东和南),中心屏幕是主框架。该类Docker有一个getDockToolbar允许我们添加具有预建停靠取消停靠按钮的工具栏的能力。

在此处输入图像描述

这是代码(下面显示磁性布局):

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class MagneticFrameAndDialog {

    public MagneticFrameAndDialog() {
        createAndShowGUI();

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new MagneticFrameAndDialog();
            }
        });
    }

    private void createAndShowGUI() {
        JFrame frame = new JFrame("JFrame (The Docks)");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Docker docker = new Docker();
        docker.registerDock(frame);
        docker.setLayout(Docker.MAGNETIC_LAYOUT);
        //docker.setMagneticFieldSize(250);//default is 150
        docker.setComponentMovedReactTime(50);//default is 100

        JDialog d1 = createAndShowDialog(300, 300);
        d1.setTitle("East Dockable");
        JDialog d2 = createAndShowDialog(300, 100);
        d2.setTitle("South Dockable");
        JDialog d3 = createAndShowDialog(100, 300);
        d3.setTitle("West Dockable");
        JDialog d4 = createAndShowDialog(300, 100);
        d4.setTitle("North Dockable");

        docker.registerDockee(d1, Docker.EAST_DOCKED);
        docker.registerDockee(d2, Docker.SOUTH_DOCKED);
        docker.registerDockee(d3, Docker.WEST_DOCKED);
        docker.registerDockee(d4, Docker.NORTH_DOCKED);

        frame.add(docker.getDockToolbar(), BorderLayout.SOUTH);

        frame.pack();
        frame.setVisible(true);
    }

    private JDialog createAndShowDialog(final int w, final int h) {
        JDialog dialog = new JDialog() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(w, h);
            }
        };
        dialog.setTitle("Dockable Dialog");
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        dialog.pack();
        dialog.setVisible(true);
        return dialog;
    }
}

class Docker {

    public static final String NORTH_DOCKED = "dock north", SOUTH_DOCKED = "dock south", WEST_DOCKED = " dock west", EAST_DOCKED = "dock east";
    public static final String FOLLOW_LAYOUT = "layout follow", STICKY_LAYOUT = "layout sticky", MAGNETIC_LAYOUT = "layout magnetic";
    private HashMap<Component, String> dockees = new HashMap<>();
    private Timer dockeeMoveTimer;
    private ComponentAdapter caDock, caDockee;
    private Component dock;
    private String layout = STICKY_LAYOUT;
    private int MAGNETIC_FIELD_SIZE = 150, movedReactTime = 100;

    public Docker() {
        initTimers();
        initComponentAdapters();
    }

    public void setLayout(String layout) {
        this.layout = layout;
    }

    void setComponentMovedReactTime(int milis) {
        movedReactTime = milis;
    }

    private void initComponentAdapters() {
        caDockee = new ComponentAdapter() {
            @Override
            public void componentMoved(ComponentEvent ce) {
                super.componentMoved(ce);
                if (layout.equals(MAGNETIC_LAYOUT)) {
                    createDockeeMovedTimer();
                } else {
                    iterateDockables();
                }
            }

            private void createDockeeMovedTimer() {
                if (dockeeMoveTimer.isRunning()) {
                    dockeeMoveTimer.restart();
                } else {
                    dockeeMoveTimer.start();
                }
            }
        };
        caDock = new ComponentAdapter() {
            @Override
            public void componentMoved(ComponentEvent ce) {
                super.componentMoved(ce);
                iterateDockables();
            }

            @Override
            public void componentResized(ComponentEvent ce) {
                super.componentResized(ce);
                iterateDockables();
            }
        };
    }

    private void initTimers() {
        dockeeMoveTimer = new Timer(movedReactTime, new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                iterateDockables();
            }
        });
        dockeeMoveTimer.setRepeats(false);
    }

    private void iterateDockables() {
        //System.out.println("Dock will call for Dockees to come");
        for (Map.Entry<Component, String> entry : dockees.entrySet()) {
            Component component = entry.getKey();
            String pos = entry.getValue();
            if (!isDocked(component, pos)) {
                dock(component, pos);
            }
        }
    }

    void registerDock(Component dock) {
        this.dock = dock;
        dock.addComponentListener(caDock);
    }

    void registerDockee(Component dockee, String pos) {
        dockee.addComponentListener(caDockee);
        dockees.put(dockee, pos);
        caDock.componentResized(new ComponentEvent(dock, 1));//not sure about the int but w dont use it so its fine for now
    }

    void deregisterDockee(Component dockee) {
        dockee.removeComponentListener(caDockee);
        dockees.remove(dockee);
    }

    void setMagneticFieldSize(int sizeInPixels) {
        MAGNETIC_FIELD_SIZE = sizeInPixels;
    }

    private boolean isDocked(Component comp, String pos) {
        switch (pos) {
            case EAST_DOCKED:
                int eastDockedX = dock.getX() + dock.getWidth();
                int eastDockedY = dock.getY();
                if (layout.equals(MAGNETIC_LAYOUT)) {
                    if (comp.getLocation().distance(new Point(eastDockedX, eastDockedY)) <= MAGNETIC_FIELD_SIZE) {
                        return false;
                    } else {
                        return true;
                    }
                } else {
                    if (comp.getX() == eastDockedX && comp.getY() == eastDockedY) {
                        // System.out.println("is eastly docked");
                        return true;
                    }
                }
                break;
            case SOUTH_DOCKED:
                int southDockedX = dock.getX();
                int southDockedY = dock.getY() + dock.getHeight();
                if (layout.equals(MAGNETIC_LAYOUT)) {
                    if (comp.getLocation().distance(new Point(southDockedX, southDockedY)) <= MAGNETIC_FIELD_SIZE) {
                        return false;
                    } else {
                        return true;
                    }
                } else {
                    if (comp.getX() == southDockedX && comp.getY() == southDockedY) {
                        // System.out.println("is southly docked");
                        return true;
                    }
                }
                break;
            case WEST_DOCKED:
                int westDockedX = dock.getX() - comp.getWidth();
                int westDockedY = dock.getY();
                if (layout.equals(MAGNETIC_LAYOUT)) {
                    if (comp.getLocation().distance(new Point(westDockedX + comp.getWidth(), westDockedY)) <= MAGNETIC_FIELD_SIZE) {
                        return false;
                    } else {
                        return true;
                    }
                } else {
                    if (comp.getX() == westDockedX && comp.getY() == westDockedY) {
                        // System.out.println("is southly docked");
                        return true;
                    }
                }
                break;
            case NORTH_DOCKED:
                int northDockedX = dock.getX() + comp.getHeight();
                int northDockedY = dock.getY() - comp.getHeight();
                if (layout.equals(MAGNETIC_LAYOUT)) {
                    if (comp.getLocation().distance(new Point(northDockedX - comp.getHeight(), northDockedY + comp.getHeight())) <= MAGNETIC_FIELD_SIZE) {
                        return false;
                    } else {
                        return true;
                    }
                } else {
                    if (comp.getX() == northDockedX && comp.getY() == northDockedY) {
                        // System.out.println("is southly docked");
                        return true;
                    }
                }
                break;
        }
        return false;
    }
    private Timer eastTimer = null, southTimer = null, westTimer = null, northTimer = null;

    private void dock(final Component comp, String pos) {
        //System.out.println("Snapping Dockee back to the dock");
        switch (pos) {
            case EAST_DOCKED:
                int eastDockedX = dock.getX() + dock.getWidth();
                int eastDockedY = dock.getY();
                if (eastTimer == null) {
                    eastTimer = getTimer(comp, eastDockedX, eastDockedY);
                    eastTimer.start();
                } else {
                    if (!eastTimer.isRunning()) {
                        eastTimer = getTimer(comp, eastDockedX, eastDockedY);
                        eastTimer.start();
                    }
                }
                break;
            case SOUTH_DOCKED:
                int southDockedX = dock.getX();
                int southDockedY = dock.getY() + dock.getHeight();
                if (southTimer == null) {
                    southTimer = getTimer(comp, southDockedX, southDockedY);
                    southTimer.start();
                } else {
                    if (!southTimer.isRunning()) {
                        southTimer = getTimer(comp, southDockedX, southDockedY);
                        southTimer.start();
                    }
                }
                break;
            case WEST_DOCKED:
                int westDockedX = dock.getX() - comp.getWidth();
                int westDockedY = dock.getY();
                if (westTimer == null) {
                    westTimer = getTimer(comp, westDockedX, westDockedY);
                    westTimer.start();
                } else {
                    if (!westTimer.isRunning()) {
                        westTimer = getTimer(comp, westDockedX, westDockedY);
                        westTimer.start();
                    }
                }
                break;
            case NORTH_DOCKED:
                int northDockedX = dock.getX();
                int northDockedY = dock.getY() - comp.getHeight();
                if (northTimer == null) {
                    northTimer = getTimer(comp, northDockedX, northDockedY);
                    northTimer.start();
                } else {
                    if (!northTimer.isRunning()) {
                        northTimer = getTimer(comp, northDockedX, northDockedY);
                        northTimer.start();
                    }
                }
                break;
        }
    }

    private Timer getTimer(final Component comp, int finalX, int finalY) {
        Timer t = null;
        switch (layout) {
            case STICKY_LAYOUT:
                t = stickyDockableTimer(comp, finalX, finalY);
                break;
            case FOLLOW_LAYOUT:
                t = followDockableTimer(comp, finalX, finalY);
                break;
            case MAGNETIC_LAYOUT:
                t = followDockableTimer(comp, finalX, finalY);
                break;
        }
        return t;
    }

    private Timer followDockableTimer(final Component comp, final int finalX, final int finalY) {
        Timer t = new Timer(1, new AbstractAction() {
            int INCREMENT = 1, DECREMENT = 1;
            int x = comp.getX(), y = comp.getY();

            @Override
            public void actionPerformed(ActionEvent ae) {
                //System.out.println(finalX + "," + finalY);
                if (x < finalX) {
                    x += INCREMENT;
                } else if (x > finalX) {
                    x -= DECREMENT;
                }
                if (y < finalY) {
                    y += INCREMENT;
                } else if (y > finalY) {
                    y -= DECREMENT;
                }
                comp.setLocation(x, y);
                if (x == finalX && y == finalY) {
                    if (comp instanceof Window) {
                        ((Window) comp).toFront();
                    }
                    ((Timer) ae.getSource()).stop();
                }
            }
        });
        return t;
    }

    public JPanel getDockToolbar() {
        JPanel panel = new JPanel();
        for (Map.Entry<Component, String> entry : dockees.entrySet()) {
            final Component component = entry.getKey();
            String pos = entry.getValue();
            Docker.MyButton jtb = new Docker.MyButton("un-dock" + pos.replace("dock", ""), component, pos);
            panel.add(jtb);
            jtb.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent ae) {
                    Docker.MyButton jtb = (Docker.MyButton) ae.getSource();
                    String tmp = jtb.getText();
                    if (tmp.contains("un-dock")) {
                        jtb.setText(tmp.replace("un-dock", "dock"));
                        deregisterDockee(jtb.getComponent());
                    } else {
                        jtb.setText(tmp.replace("dock", "un-dock"));
                        registerDockee(jtb.getComponent(), jtb.getDockablePosition());
                    }
                }
            });
        }
        return panel;
    }

    private Timer stickyDockableTimer(final Component comp, final int finalX, final int finalY) {
        Timer t = new Timer(1, new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                comp.setLocation(finalX, finalY);
            }
        });
        t.setRepeats(false);
        return t;
    }

    private class MyButton extends JButton {

        private final Component c;
        private final String pos;

        public MyButton(String text, Component c, String pos) {
            super(text);
            this.c = c;
            this.pos = pos;
        }

        public Component getComponent() {
            return c;
        }

        public String getDockablePosition() {
            return pos;
        }
    }
}
于 2012-12-31T18:12:19.510 回答
4

经过数小时的无效搜索和反复试验后,我发布了上面提到的问题。经过几分钟的搜索后,我找到了解决方案……哦!

诀窍不是在 ComponentListener.componentMoved(.) 期间直接通过 JDialog.setLocation(.) 设置对话框的位置,而是简单地修改鼠标位置(仍然处于拖动模式!):

// Example ONLY for magnetic effect on EAST border of JFrame!!
int dockDistE = ...; // distance on east border of JFrame to west border of JDialog
...
try {
  // determine mouse location
  Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
  Robot robot = new Robot();
  // move mouse to west exactly the remaining distance to JFrame
  robot.mouseMove(mouseLoc.x-dockDistE, mouseLoc.y);
  // release mouse button to automatically finish dragging
  robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
} catch (AWTException e1) {
  // TODO Auto-generated catch block
  e1.printStackTrace();
}

修改鼠标位置会导致 JDialog 的窗口自动拖动/移动到附加位置。

在这里你可以找到我完整的 DockingListener: http ://wald.intevation.org/scm/viewvc.php/trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/DockingListener.java?root= schmitzm&view=markup 除了 JDK (1.6) 之外没有其他依赖项。

于 2012-12-30T23:59:27.563 回答
0

我创建了一个名为 DockingManager 的类,它可以让您停靠 Dialogs 和 Frames 伪 Winamp Style

它还可以让您制作不同的集群并设置磁性水平以选择哪个框架拖动其他框架)

package com.gsteren.docking;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.UIManager;

public class DockingManager {

    public static enum DockingMode {
        EAST, WEST, NORTH, SOUTH, NONE
    }

    int magneticDistance = 20;

    public static Map<Component, DockingApplier> monitoredComponents = new HashMap<Component, DockingApplier>();

    /**
     * Para saber cual es el último Component movido por el usuario.
     */
    private static DockingApplier focusedApplier;

    public void registerComponent(Component component) {
        this.registerComponent(component, 1);
    }

    public void registerComponent(Component component, int magneticLevel) {
        DockingApplier applier = new DockingApplier(component, magneticLevel);
        monitoredComponents.put(component, applier);
    }

    /**
     * Indica el grupo completo de componentes pegados al que pertenece un
     * componente.
     * 
     * @author Guillermo
     * 
     */
    protected class DockingApplierCluster {
        Set<DockingApplier> appliers = new HashSet<DockingApplier>();
        DockingApplier leader = null;

        public DockingApplierCluster(DockingApplier applier) {
            this.addToCluster(applier);
        }

        public DockingApplierCluster(Set<DockingApplier> appliers) {
            for (DockingApplier applier : appliers) {
                this.addToCluster(applier);
            }
        }

        public void addToCluster(DockingApplier applier) {
            appliers.add(applier);
            applier.setCluster(this);
            if (null == leader || applier.getMagneticLevel() < leader.getMagneticLevel()) {
                this.setLeader(applier);
            }
        }

        public int getSize() {
            return getAppliers().size();
        }

        public void mergeCluster(DockingApplierCluster cluster) {
            DockingApplierCluster bigCluster;
            DockingApplierCluster smallCluster;
            if (getSize() > cluster.getSize()) {
                bigCluster = this;
                smallCluster = cluster;
            } else {
                bigCluster = cluster;
                smallCluster = this;
            }
            for (DockingApplier applier : smallCluster.getAppliers()) {
                bigCluster.addToCluster(applier);
            }

        }

        public Set<DockingApplier> getAppliers() {
            return this.appliers;
        }

        /**
         * @return the leader
         */
        public DockingApplier getLeader() {
            return leader;
        }

        /**
         * @param leader
         *            the leader to set
         */
        public void setLeader(DockingApplier leader) {
            this.leader = leader;
        }

        public void remove(DockingApplier applier) {
            if (this.getAppliers().size() == 1) {
                /* No sacamos el único elemento */
                return;
            }

            this.getAppliers().remove(applier);

            if (this.leader == applier) {
                this.leader = findLeader();
            }

            /* Pude haber dividido en varios clusters */
            this.recalculateClustersAfterRemoval();
        }

        public void recalculateClustersAfterRemoval() {
            Set<DockingApplier> myAppliersCopy = new HashSet<DockingManager.DockingApplier>(getAppliers());
            Set<DockingApplier> visitedAppliers = new HashSet<DockingManager.DockingApplier>();
            for (DockingApplier applier : myAppliersCopy) {
                if (visitedAppliers.contains(applier)) {
                    continue;
                }
                Set<DockingApplier> newClusterComponents = findClusteredAppliers(applier);
                if (newClusterComponents.size() == myAppliersCopy.size()) {
                    /* No se dividieron los clusters */
                    return;
                }
                visitedAppliers.addAll(newClusterComponents);
                /* Creo un nuevo cluster, y le agrego los elementos */
                new DockingApplierCluster(newClusterComponents);
            }

        }

        /**
         * Devuelve todos los DockingAppliers anexos entre si.
         * 
         * @param anApplier
         * @return
         */
        public Set<DockingApplier> findClusteredAppliers(DockingApplier anApplier) {
            return findClusteredAppliers(anApplier, new HashSet<DockingManager.DockingApplier>());
        }

        public Set<DockingApplier> findClusteredAppliers(DockingApplier anApplier, Set<DockingApplier> currentSet) {
            currentSet.add(anApplier);
            for (DockingApplier applier : anApplier.getAttachedComponents()) {
                if (currentSet.contains(applier)) {
                    continue;
                }
                currentSet.add(applier);
                currentSet.addAll(findClusteredAppliers(applier, currentSet));
            }
            return currentSet;
        }

        private DockingApplier findLeader() {
            DockingApplier leaderCandidate = null;
            for (DockingApplier applier : getAppliers()) {
                if (leaderCandidate == null || applier.getMagneticLevel() < leaderCandidate.getMagneticLevel()) {
                    leaderCandidate = applier;
                    if (applier.getMagneticLevel() == 1) {
                        /* Encontramos óptimo */
                        break;
                    }
                }
            }
            return leaderCandidate;
        }

    }

    protected class DockingApplier implements ComponentListener, FocusListener {

        private Component component = null;
        private DockingApplierCluster cluster;

        public DockingApplier(Component component, int magneticLevel) {
            this.component = component;
            this.cluster = new DockingApplierCluster(this);
            this.magneticLevel = magneticLevel;
            this.updateLastLocation();

            /*
             * Esto no es necesario ya que registerComponent es quien crea el
             * applier
             */
            if (!monitoredComponents.containsKey(component)) {
                monitoredComponents.put(component, this);
            }

            this.component.addComponentListener(this);
            this.component.addFocusListener(this);
            componentFinishedMovingDetector.setRepeats(false);
        }

        public void setCluster(DockingApplierCluster cluster) {
            this.cluster = cluster;
        }

        /**
         * @return the cluster
         */
        public DockingApplierCluster getCluster() {
            return cluster;
        }

        boolean isClusterLeader() {
            return getCluster().getLeader() == this;
        }

        /**
         * @return the magneticLevel
         */
        public Integer getMagneticLevel() {
            return magneticLevel;
        }

        /**
         * @param magneticLevel
         *            the magneticLevel to set
         */
        public void setMagneticLevel(Integer magneticLevel) {
            this.magneticLevel = magneticLevel;
        }

        /**
         * @return the isDocking
         */
        protected boolean isDocking() {
            return isDocking;
        }

        /**
         * @param isDocking
         *            the isDocking to set
         */
        protected void setDocking(boolean isDocking) {
            this.isDocking = isDocking;
        }

        /**
         * @return the attachedComponents
         */
        public Set<DockingApplier> getAttachedComponents() {
            return attachedComponents;
        }

        int northY;
        int southY;
        int westX;
        int eastX;

        public void recalculateBorderCoordinates() {
            Point compLoc = component.getLocation();
            Dimension compDim = component.getSize();
            northY = compLoc.y;
            southY = northY + compDim.height;
            westX = compLoc.x;
            eastX = westX + compDim.width;
        }

        public DockingMode calculateWhereToDock(DockingApplier other, int magneticDistance) {
            return calculateWhereToDock(this, other, magneticDistance);
        }

        /**
         * Indica si me debería lockearse con other
         * 
         * @param me
         * @param other
         * @return
         */
        public DockingMode calculateWhereToDock(DockingApplier me, DockingApplier other, int magneticDistance) {

            /* Talvez innecesario */
            me.recalculateBorderCoordinates();
            other.recalculateBorderCoordinates();

            if (me.getAttachedComponents().contains(other)) {
                /* Ya estan conectados */
                return DockingMode.NONE;
            }

            int dockDistN = me.northY - other.southY;
            int dockDistS = other.northY - me.southY;
            int dockDistW = me.westX - other.eastX;
            int dockDistE = other.westX - me.eastX;

            if (dockDistN > 0 && magneticDistance > dockDistN && checkOverlappingEastWest(me, other)) {
                return DockingMode.NORTH;
            } else if (dockDistS > 0 && magneticDistance > dockDistS && checkOverlappingEastWest(me, other)) {
                return DockingMode.SOUTH;
            } else if (dockDistW > 0 && magneticDistance > dockDistW && checkOverlappingNorthSouth(me, other)) {
                return DockingMode.WEST;
            } else if (dockDistE > 0 && magneticDistance > dockDistE && checkOverlappingNorthSouth(me, other)) {
                return DockingMode.EAST;
            }
            return DockingMode.NONE;
        }

        /**
         * Checks whether components overlap in north/south direction.
         */
        protected boolean checkOverlappingEastWest(DockingApplier me, DockingApplier other) {
            return checkOverlappingEastWest_aux(me, other) || checkOverlappingEastWest_aux(other, me);
        }

        /**
         * Checks whether components overlap in east/west direction.
         */
        protected boolean checkOverlappingEastWest_aux(DockingApplier me, DockingApplier other) {
            return me.westX >= other.westX && me.westX <= other.eastX || me.eastX >= other.westX
                    && me.eastX <= other.eastX;
        }

        /**
         * Checks whether components overlap in north/south direction.
         */
        protected boolean checkOverlappingNorthSouth(DockingApplier me, DockingApplier other) {
            return checkOverlappingNorthSouth_aux(me, other) || checkOverlappingNorthSouth_aux(other, me);
        }

        /**
         * Checks whether components overlap in north/south direction.
         */
        protected boolean checkOverlappingNorthSouth_aux(DockingApplier me, DockingApplier other) {
            return me.northY >= other.northY && me.northY <= other.southY || me.southY >= other.northY
                    && me.southY <= other.southY;
        }

        public Point calculateDockedLocation(DockingApplier other, DockingMode mode) {
            return calculateDockedLocation(this, other, mode);
        }

        public Point calculateDockedLocation(DockingApplier me, DockingApplier other, DockingMode mode) {
            final Point meLoc = me.getComponent().getLocation();
            final Point otherLoc = other.getComponent().getLocation();
            final Dimension otherDim = other.getComponent().getSize();
            final Dimension meDim = me.getComponent().getSize();

            /* Posiciones relativas a other */
            switch (mode) {
            case NORTH:
                return new Point(meLoc.x, otherLoc.y + otherDim.height);
            case SOUTH:
                return new Point(meLoc.x, otherLoc.y - meDim.height);
            case WEST:
                return new Point(otherLoc.x + otherDim.width, meLoc.y);
            case EAST:
                return new Point(otherLoc.x - meDim.width, meLoc.y);
            default:
                return new Point(meLoc.x - otherLoc.x, meLoc.y);
            }
        }

        /**
         * Retrasa la accion a ejecutar en onComponentFinishedMoving hasta que
         * pasan 10ms sin que se mueva el componente afectado.
         */
        Timer componentFinishedMovingDetector = new Timer(300, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onComponentFinishedMoving();
            }
        });

        /**
         * Resetea el componentFinishedMovingDetector, se debe llamar cada vez
         * que se mueve o cambia el tamaño de la ventana.
         */
        protected void startComponentFinishedMovingDetector() {
            if (componentFinishedMovingDetector.isRunning()) {
                componentFinishedMovingDetector.restart();
            } else {
                componentFinishedMovingDetector.start();
            }
        }

        /* Mientras es menor, más probable es ser lider */
        int magneticLevel = 1;

        /* Indica si el componente esta en proceso de hacer dock */
        boolean isDocking = false;

        /* Para desconectarlos en resize */
        Set<DockingApplier> attachedComponents = new HashSet<DockingApplier>();
        /* Indica la posición del componente */
        private Point lastLocation;

        public boolean isDocked() {
            return getCluster().getSize() > 1;
        }

        /**
         * @return the component
         */
        protected Component getComponent() {
            return component;
        }

        public void componentResized(ComponentEvent e) {
            startComponentFinishedMovingDetector();
            this.recalculateBorderCoordinates();
            this.unDock();
        }

        public void componentMoved(ComponentEvent e) {

            this.recalculateBorderCoordinates();

            /*
             * Si el movimiento es consecuencia de hacer dock lo ignoro y marco
             * como que el docking se completó
             */
            if (this.isDocking()) {
                this.setDocking(false);
                return;
            }

            startComponentFinishedMovingDetector();

            if (this != focusedApplier) {
                return;
            }

            if (getCluster().getSize() == 1) {
                return;
            }

            if (!this.isClusterLeader()) {
                this.updateLastLocation();
                this.unDock();
                return;
            }

            positionAttachedComponents();

        }

        public void onComponentFinishedMoving() {
            this.recalculateBorderCoordinates();
            for (DockingApplier otherDockingApplier : getMonitoredComponents().values()) {
                if (otherDockingApplier == this) {
                    continue;
                }
                DockingMode dockMode = calculateWhereToDock(this, otherDockingApplier);
                if (!DockingMode.NONE.equals(dockMode)) {
                    System.out.println("shouldAttach");
                    this.dock(otherDockingApplier, dockMode);                   
                    this.updateLastLocation();
                } else {
                    System.out.println("shouldNotAttach");
                }
            }
        }

        public void setLocation(int x, int y) {
            this.setLocation(new Point(x, y));
        }

        public void setLocation(Point location) {
            this.getComponent().removeComponentListener(this);
            this.getComponent().setLocation(location);
            this.setLastLocation(location);
            this.getComponent().addComponentListener(this);
        }

        private void setLastLocation(Point location) {
            this.lastLocation = location;
        }

        public Point getLocation() {
            return this.getComponent().getLocation();
        }

        /**
         * @return the lastLocation
         */
        public Point getLastLocation() {
            return lastLocation;
        }

        protected void dock(DockingApplier otherDockingApplier, DockingMode dockMode) {
            this.setDocking(true);
            Point dockInfo = this.calculateDockedLocation(otherDockingApplier, dockMode);
            this.setLocation(dockInfo.x, dockInfo.y);
            this.bindAppliers(otherDockingApplier);
            /* Uno los clusters */
            otherDockingApplier.getCluster().mergeCluster(this.getCluster());
        }

        public void bindAppliers(DockingApplier otherDockingApplier) {
            this.getAttachedComponents().add(otherDockingApplier);
            otherDockingApplier.getAttachedComponents().add(this);
        }

        public void unDock() {
            if (this.getCluster().getSize() == 1) {
                return;
            }
            /*
             * Primero lo quito de sus vecinos, luego del cluster, el orden es
             * importante para el calculo de clusters en caso de división.
             */
            Set<DockingApplier> attachedComponentsCopy = new HashSet<DockingManager.DockingApplier>(
                    this.getAttachedComponents());
            for (DockingApplier applier : attachedComponentsCopy) {
                this.unbind(applier);
            }

            this.getCluster().remove(this);
            this.setCluster(new DockingApplierCluster(this));
        }

        public void unbind(DockingApplier dockingApplier) {
            this.getAttachedComponents().remove(dockingApplier);
            dockingApplier.getAttachedComponents().remove(this);
        }

        private DockingMode calculateWhereToDock(DockingApplier component, DockingApplier otherDockingApplier) {
            return this.calculateWhereToDock(otherDockingApplier, magneticDistance);
        }

        public void componentShown(ComponentEvent e) {
            getMonitoredComponents().get(component).recalculateBorderCoordinates();
            // positionAttachedComponents(e);
        }

        public void componentHidden(ComponentEvent e) {
            getMonitoredComponents().get(component).recalculateBorderCoordinates();
            // positionAttachedComponents(e);
        }

        private void positionAttachedComponents() {

            /* El lider del cluster debe arrastrar a los otros componentes */
            Point currentLocation = getComponent().getLocation();
            int xDiff = currentLocation.x - getLastLocation().x;
            int yDiff = currentLocation.y - getLastLocation().y;

            if (xDiff == 0 && yDiff == 0) {
                return;
            }

            this.updateLastLocation();

            for (DockingApplier otherApplier : getCluster().getAppliers()) {
                if (otherApplier == this) {
                    continue;
                }
                Point otherComponentLocation = otherApplier.getComponent().getLocation();
                otherApplier.getComponent().removeComponentListener(otherApplier);
                otherApplier.setDocking(true);
                otherApplier.getComponent().setLocation(otherComponentLocation.x + xDiff,
                        otherComponentLocation.y + yDiff);
                otherApplier.getComponent().addComponentListener(otherApplier);
            }
        }

        public void updateLastLocation() {
            this.setLastLocation(getComponent().getLocation());
        }

        @Override
        public void focusGained(FocusEvent e) {
            DockingManager.setFocusedApplier(this);
        }

        @Override
        public void focusLost(FocusEvent e) {
            // TODO Auto-generated method stub

        }
    }

    public DockingManager() {

    }

    public static void setFocusedApplier(DockingApplier applier) {
        DockingManager.focusedApplier = applier;
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
        }
        JFrame frame1 = new JFrame("Frame 1");
        JDialog dialog2 = new JDialog((JFrame) null, "Dialog 2");
        frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame1.setSize(200, 50);
        dialog2.setSize(200, 50);
        frame1.setVisible(true);
        dialog2.setVisible(true);

        JDialog dialog1 = new JDialog((JFrame) null, "Dialog 1");
        dialog1.setSize(200, 50);
        dialog1.setVisible(true);

        DockingManager manager = new DockingManager();

        manager.registerComponent(frame1, 1);
        manager.registerComponent(dialog2, 2);
        manager.registerComponent(dialog1, 2);

    }

    /**
     * @return the monitoredComponents
     */
    protected static Map<Component, DockingApplier> getMonitoredComponents() {
        return monitoredComponents;
    }
}
于 2014-07-01T07:08:30.177 回答