4

ウィンドウ (JFrame および非モーダル JDialog) が必要で、JFrame を移動すると JDialog も移動するように、JDialog を JFrame にアタッチしたいと考えています。同期運動自体は問題ではありません。ComponentListener.componentMoved(.) で実現しました。

しかし、アタッチメントは固定されるべきではありません。ユーザーは、JFrame から離して JDialog を切り離すことができる必要があります。JDialog が JFrame 境界に直接配置されている場合にのみ、それを添付する必要があります。

ユーザーが JDialog を JFrame のすぐ隣に配置する (添付する) のを支援するために、「磁気効果」を実装したいと考えています。ComponentListener.componentMoved(.) でこの状況を検出することも問題ではありませんが、新しい JDialog の場所を設定します。

JDialog.setLocation(.) によって新しい場所を設定すると、最初に JDialog が正しく配置されます。しかし、ダイアログのドラッグを終了すると (マウス ボタンを放すと)、JDialog は前の場所 (JFrame から 10px 離れた場所) に戻されます

これは、ドラッグの最後に「システム」が setLocation(.) も呼び出すためだと思います。この動作を防ぐ方法はありますか? たとえば、次のマウス移動またはマウス解放イベントをイベント キューから削除しますか?

4

3 に答える 3

7

私はこの質問に興味があり、メイン フレーム/ドックおよびその他のドッキング可能なコンテナーを作成する独自の小さなプログラムを作成することにしました。これらのコンテナは、その位置にとどまるためにメイン フレームに従います。

答えはありますが、私の例を共有したいと思います。

OPコードが完全に機能していなかった理由については、componentMoved移動するたびに再起動するため、ドッキング可能なものの移動中に問題が発生したためだと思います。

これは、メイン フレームの北、東、南、および/または西にドッキング可能を追加できる小さな例です。ドッキング可能なものをその場所に保持する方法は、StickyMagneticFollow Layoutなどのレイアウトによって管理されます。

スティッキー レイアウトはメイン フレームと共に移動しますが、Followドッキング可能なものをメイン フレームの位置に移動するように呼び出し、 Magneticはその名前が想定するように、特定の距離内にあるすべてのドッキング可能なものを引き付けます。メイン フレーム上の指定された最終/ドッキング ポイント (メイン フレームの東の上部) からの指定された距離。

ComponentAdapter/ListenerSwing Timerwithを使用しComponent#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

効果のない検索と試行錯誤を何時間も繰り返した後、上記の質問を投稿しました。そして、さらに数分間検索した後、解決策を見つけました... D'oh!

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

ダイアログとフレームを疑似 Winamp スタイルにドッキングできる DockingManager というクラスを作成しました

また、さまざまなクラスターを作成し、磁気レベルを設定して、どのフレームが他のフレームをドラッグするかを選択することもできます)

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 に答える