Предпочтительный размер JDesktopPane, установленный по содержимому

Я пытался приручить JDesktopPane чтобы хорошо работать с изменяемым размером графического интерфейса и панелью прокрутки, но у меня есть некоторые проблемы с этим. Кажется, что если не выбран режим перетаскивания, панель рабочего стола не будет изменять размер должным образом (когда внутренний фрейм перетаскивается за край панели рабочего стола) и, следовательно, не создает полосы прокрутки.

введите здесь описание изображения

Я делаю что-то очень глупое в этом источнике? Я пропустил гораздо лучший подход?

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class MDIPreferredSize {

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                final JDesktopPane dt = new JDesktopPane() {

                    @Override
                    public Dimension getPreferredSize() {
                        Dimension prefSize = super.getPreferredSize();
                        System.out.println("prefSize: " + prefSize);
                        // inititialize the max to the first normalized bounds
                        Rectangle max = getAllFrames()[0].getNormalBounds();
                        for (JInternalFrame jif : this.getAllFrames()) {
                            max.add(jif.getNormalBounds());
                        }
                        System.out.println("maxBounds(): "
                                + max);
                        int x1 = max.width + (max.x * 2) < prefSize.width
                                ? prefSize.width
                                : max.width + (max.x * 2);
                        int y1 = max.height + (max.y * 2) < prefSize.height
                                ? prefSize.height
                                : max.height + (max.y * 2);
                        System.out.println("x,y: "
                                + x1
                                + ","
                                + y1);
                        return new Dimension(x1, y1);
                    }
                };
                dt.setAutoscrolls(true);

                int xx = 5;
                int yy = 5;
                int vStep = 10;
                int yStep = 22;
                for (int ii = 0; ii < 3; ii++) {
                    JInternalFrame jif = new JInternalFrame(
                            "Internal Frame " + (ii + 1),
                            true,
                            true,
                            true);
                    dt.add(jif);
                    jif.setLocation(xx, yy);
                    xx += vStep;
                    yy += yStep;
                    jif.setSize(200, 75);
                    jif.setVisible(true);
                }

                ComponentListener componentListener = new ComponentListener() {

                    @Override
                    public void componentResized(ComponentEvent e) {
                        e.getComponent().validate();
                    }

                    @Override
                    public void componentMoved(ComponentEvent e) {
                        e.getComponent().validate();
                    }

                    @Override
                    public void componentShown(ComponentEvent e) {
                        e.getComponent().validate();
                    }

                    @Override
                    public void componentHidden(ComponentEvent e) {
                        // do nothing 
                    }
                };
                // causes maximized internal frames to be resized..
                dt.addComponentListener(componentListener);

                final JCheckBox outLineDragMode = new JCheckBox("Outline Drag Mode");
                ActionListener dragModeListener = new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (outLineDragMode.isSelected()) {
                            dt.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
                        } else {
                            dt.setDragMode(JDesktopPane.LIVE_DRAG_MODE);
                        }
                    }
                };
                outLineDragMode.addActionListener(dragModeListener);

                JPanel gui = new JPanel(new BorderLayout());
                gui.add(outLineDragMode, BorderLayout.PAGE_START);
                gui.setBorder(new EmptyBorder(2, 3, 2, 3));
                gui.add(new JScrollPane(dt), BorderLayout.CENTER);

                JFrame f = new JFrame("DTP Preferred");
                f.add(gui);
                // Ensures JVM closes after frame(s) closed and
                // all non-daemon threads are finished
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                // See http://stackoverflow.com/a/7143398/418556 for demo.
                f.setLocationByPlatform(true);

                // ensures the frame is the minimum size it needs to be
                // in order display the components within it
                f.pack();
                f.setMinimumSize(f.getSize());

                // should be done last, to avoid flickering, moving,
                // resizing artifacts.
                f.setVisible(true);

                printProperty("os.name");
                printProperty("java.version");
                printProperty("java.vendor");
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }

    public static void printProperty(String name) {
        System.out.println(name + ": \t" + System.getProperty(name));
    }
}

Редактировать

Среди напечатанной информации см. также 3 системных свойства:

os.name:    Windows 7
java.version:   1.7.0_21
java.vendor:    Oracle Corporation

Вот такие значения.

MouseMotionListener исправленный код

Благодаря предложению Джонатана Драпо о MouseListener, этот фиксированный пример фактически использует MouseMotionListener, чтобы разрешить активное изменение размера панели рабочего стола во время перетаскивания. Он может страдать некоторыми причудами, помимо использования MouseListener, которые вызывают проблемы (пока не известно), если это так, вернитесь к более простой технике «изменить размер панели рабочего стола при пропущенном внутреннем кадре» (только MouseListener).

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicInternalFrameTitlePane;

public class MDIPreferredSize {

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                final JDesktopPane dt = new JDesktopPane() {

                    @Override
                    public Dimension getPreferredSize() {
                        Dimension prefSize = super.getPreferredSize();
                        System.out.println("prefSize: " + prefSize);
                        // inititialize the max to the first normalized bounds
                        Rectangle max = getAllFrames()[0].getNormalBounds();
                        for (JInternalFrame jif : this.getAllFrames()) {
                            max.add(jif.getNormalBounds());
                        }
                        System.out.println("maxBounds(): "
                                + max);
                        int x1 = max.width + (max.x * 2) < prefSize.width
                                ? prefSize.width
                                : max.width + (max.x * 2);
                        int y1 = max.height + (max.y * 2) < prefSize.height
                                ? prefSize.height
                                : max.height + (max.y * 2);
                        System.out.println("x,y: "
                                + x1
                                + ","
                                + y1);
                        return new Dimension(x1, y1);
                    }
                };

                int xx = 5;
                int yy = 5;
                int vStep = 10;
                int yStep = 22;
                for (int ii = 0; ii < 3; ii++) {
                    JInternalFrame jif = new JInternalFrame(
                            "Internal Frame " + (ii + 1),
                            true,
                            true,
                            true);
                    dt.add(jif);
                    jif.setLocation(xx, yy);
                    xx += vStep;
                    yy += yStep;
                    jif.setSize(200, 75);
                    jif.setVisible(true);
                }

                /*final MouseListener mouseListener = new MouseAdapter() {

                    @Override
                    public void mouseReleased(MouseEvent e) {
                        dt.revalidate();
                    }
                };
                */
                final MouseMotionListener mouseMotionListener = new MouseMotionAdapter() {

                    @Override
                    public void mouseDragged(MouseEvent e) {
                        dt.revalidate();
                    }
                };
                for (JInternalFrame jif : dt.getAllFrames()) {
                    for (Component comp : jif.getComponents()) {
                        if (comp instanceof BasicInternalFrameTitlePane) {
                            //comp.addMouseListener(mouseListener);
                            comp.addMouseMotionListener(mouseMotionListener);
                        }
                    }
                }

                dt.setAutoscrolls(true);


                final JCheckBox outLineDragMode =
                        new JCheckBox("Outline Drag Mode");
                ActionListener dragModeListener = new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (outLineDragMode.isSelected()) {
                            dt.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
                        } else {
                            dt.setDragMode(JDesktopPane.LIVE_DRAG_MODE);
                        }
                    }
                };
                outLineDragMode.addActionListener(dragModeListener);

                JPanel gui = new JPanel(new BorderLayout());
                gui.add(outLineDragMode, BorderLayout.PAGE_START);
                gui.setBorder(new EmptyBorder(2, 3, 2, 3));
                gui.add(new JScrollPane(dt), BorderLayout.CENTER);

                JFrame f = new JFrame("DTP Preferred");
                f.add(gui);
                // Ensures JVM closes after frame(s) closed and
                // all non-daemon threads are finished
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                // See http://stackoverflow.com/a/7143398/418556 for demo.
                f.setLocationByPlatform(true);

                // ensures the frame is the minimum size it needs to be
                // in order display the components within it
                f.pack();
                f.setMinimumSize(f.getSize());

                // should be done last, to avoid flickering, moving,
                // resizing artifacts.
                f.setVisible(true);

                printProperty("os.name");
                printProperty("java.version");
                printProperty("java.vendor");
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }

    public static void printProperty(String name) {
        System.out.println(name + ": \t" + System.getProperty(name));
    }
}

причуды

Помимо использования MouseListener, у него могут быть некоторые особенности, вызывающие проблемы (пока неизвестны).

Это было тогда..

  1. В режиме полного рендеринга панель рабочего стола будет динамически увеличиваться по мере того, как пользователь перетаскивает внутреннюю рамку (даже за пределами графического интерфейса). (Хорошо.) В режиме структуры контейнер будет изменять размер только при перетаскивании, а не при перетаскивании. (Менее хорошо, но, по крайней мере, полосы прокрутки появляются/исчезают надежно.)

person Andrew Thompson    schedule 30.08.2013    source источник
comment
Я протестировал ваш код и не могу даже в режиме схемы отобразить полосы прокрутки, если не взаимодействую с главным окном. Я получил их дважды, но, похоже, это потому, что мне каким-то образом удалось обновить главное окно. Также это может быть связано с тем, что я изменил размер внутренней рамки, которую перетаскивал. Пусть рамка в любом режиме пересекает одну сторону и изменяет ее размер, чтобы появились полосы прокрутки.   -  person Jonathan Drapeau    schedule 30.08.2013
comment
Хах. Вместе с потоком, связанным mKorbel (в комментарии, который теперь удален), я подозреваю, что это очень версия/JRE зависимый. Через несколько секунд вы увидите изменения. Вопрос изменен. Какие 3 свойства вы тестируете?   -  person Andrew Thompson    schedule 30.08.2013
comment
Я тестировал с использованием Oracle java 1.6.0_33 под Ubuntu. Я думаю, что заметил, что если у меня нет полосы прокрутки, функция автопрокрутки не работает. Я не могу перетащить рамку дальше границы основного фрейма (мышь останавливается там, в отличие от полос прокрутки, где она будет двигаться дальше).   -  person Jonathan Drapeau    schedule 30.08.2013
comment
Протестировано еще раз с 1.7.0_25, и я воспроизвожу то же поведение, в режиме oultine я получаю полосы прокрутки при пересечении границы, но функция автопрокрутки по-прежнему не работает без полос прокрутки. Есть разница, но я сомневаюсь, что ваша проблема на самом деле связана с версией Java.   -  person Jonathan Drapeau    schedule 30.08.2013
comment
См. также Создание виртуального рабочего стола с возможностью прокрутки в Swing в мире Java.   -  person Andrew Thompson    schedule 30.08.2013
comment
Единственное, что работает, — это обходной путь для этой ошибки. Добавление ComponentListenerк JInternalFrame и вызов dt.revalidate(); вместо проверки компонента.   -  person Jonathan Drapeau    schedule 30.08.2013


Ответы (3)


Добавление MouseListener к панели заголовка JInternalFrame, в то время как в JDesktopPane.LIVE_DRAG_MODE к revalidate JDesktopPane после выпуска, — это способ получить точно такое же поведение в каждом режиме.

            final MouseListener testList = new MouseListener() {

              @Override
              public void mouseReleased(MouseEvent e) {
                dt.revalidate();
              }

              @Override
              public void mousePressed(MouseEvent e) {
              }

              @Override
              public void mouseExited(MouseEvent e) {
              }

              @Override
              public void mouseEntered(MouseEvent e) {
              }

              @Override
              public void mouseClicked(MouseEvent e) {
              }
            };
            // causes maximized internal frames to be resized..
            dt.addComponentListener(componentListener);

            for (JInternalFrame jif : dt.getAllFrames()) {
              for (Component comp : jif.getComponents()) {
                if (comp instanceof BasicInternalFrameTitlePane) {
                  comp.addMouseListener(testList);
                }
              }
            }        

            final JCheckBox outLineDragMode = new JCheckBox("Outline Drag Mode");
            ActionListener dragModeListener = new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                  if (outLineDragMode.isSelected()) {
                    dt.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
                    for (JInternalFrame jif : dt.getAllFrames()) {
                      for (Component comp : jif.getComponents()) {
                        if (comp instanceof BasicInternalFrameTitlePane) {
                          comp.removeMouseListener(testList);
                        }
                      }
                    }
                  } else {
                    dt.setDragMode(JDesktopPane.LIVE_DRAG_MODE);
                    for (JInternalFrame jif : dt.getAllFrames()) {
                      for (Component comp : jif.getComponents()) {
                        if (comp instanceof BasicInternalFrameTitlePane) {
                          comp.addMouseListener(testList);
                        }
                      }
                    }
                  }
                }
            };

Я удаляю их в JDesktopPane.OUTLINE_DRAG_MODE, так как он уже правильно реагирует.

person Jonathan Drapeau    schedule 30.08.2013
comment
Хороший. Он работает так, как я ожидаю, либо в режиме контура, либо в стандартном режиме перетаскивания рендеринга. Может быть, я добавлю MouseMotionListener и посмотрю, практично ли «живое перетаскивание» далеко за пределы видимых границ текущего рабочего стола, но на данный момент это будет очень хорошо. :) - person Andrew Thompson; 31.08.2013
comment
Я добавил к вопросу код, подтверждающий, что здесь также может работать MouseMotionListener. - person Andrew Thompson; 31.08.2013

Вы должны иметь возможность использовать макет перетаскивания для управления изменением размера панель рабочего стола при перетаскивании компонентов.

person camickr    schedule 30.08.2013
comment
К сожалению, это не приводит к улучшению здесь. Рамки крошечные и не изменяются по размеру, и если их развернуть, они застревают в левом верхнем углу панели (все еще крошечные). - person Andrew Thompson; 30.08.2013

Интересная задача для субботнего утра :-)

Полного решения нет, только пара комментариев и набросок альтернативного подхода:

  • полагаться на мышь/движение/слушатель неполно, поскольку он не обрабатывает движения, управляемые клавиатурой
  • per-internalframe componentListener на помощь: отлично работает, если не в режиме структуры
  • в режиме структуры повторная проверка не может работать в любом случае, поскольку она зависит от фактического местоположения кадра, которое не меняется во время перетаскивания.

Таким образом, настоящая проблема заключается в режиме контура, необходимо

  • проследить промежуточные границы перетаскиваемого кадра
  • пусть расчет prefSize рабочего стола учитывает эти промежуточные границы
  • прорисовка контура (неожиданно для меня, см. ниже [*])

Соавтор, который отвечает за перемещение фрейма, — это DesktopManager.dragFrame: его реализация по умолчанию сбрасывает границы фрейма, если не в режиме структуры, или отслеживает промежуточное положение и рисует прямоугольник схемы, если в режиме схемы.

Очевидной идеей является собственный DesktopManager, который переопределяет dragFrame:

  • пусть супер делает свое дело
  • в режиме структуры получить промежуточное местоположение кадра и сохранить его где-нибудь на самом кадре, т.е. как свойство клиента

Теперь кто-то, т.е. PropertyChangeListener может прослушивать изменения промежуточного местоположения и запускать повторную проверку. И вычисление prefSize для desktopPane может учитывать промежуточные границы в дополнение к реальным границам, что-то вроде

public static class MyDesktopManager extends DefaultDesktopManager {
    private Point currentLoc;

    @Override
    public void dragFrame(JComponent f, int newX, int newY) {
        // let super handle outline drawing
        super.dragFrame(f, newX, newY);
        if (isOutline(f)) {
            // take over the drawing
            currentLoc = new Point(newX, newY);
            Rectangle bounds = new Rectangle(currentLoc, f.getSize());
            f.putClientProperty("outlineBounds", bounds);
        } else {
            // call super only if not outline
            // handle outline drawing ourselves
            // super.dragFrame(f, newX, newY);
        }
    }

    @Override
    public void beginDraggingFrame(JComponent f) {
        super.beginDraggingFrame(f);
        if (isOutline(f)) {
            currentLoc = f.getLocation();
            RootPaneContainer r = (RootPaneContainer) SwingUtilities.getWindowAncestor(f);
            // do the painting in the glassPane
            // r.getGlassPane().setVisible(true);
        }
    }

    @Override
    public void endDraggingFrame(JComponent f) {
        super.endDraggingFrame(f);
        f.putClientProperty("outlineBounds", null);
        if (isOutline(f)) {
            RootPaneContainer r = (RootPaneContainer) SwingUtilities.getWindowAncestor(f);
            r.getGlassPane().setVisible(false);
        }
    }

    protected boolean isOutline(JComponent f) {
        return ((JInternalFrame) f).getDesktopPane().getDragMode() == 
             JDesktopPane.OUTLINE_DRAG_MODE;
    }
}

Использование:

final JDesktopPane dt = new JDesktopPane() {

    @Override
    public Dimension getPreferredSize() {
        Dimension prefSize = super.getPreferredSize();
        System.out.println("prefSize: " + prefSize);
        // inititialize the max to the first normalized bounds
        Rectangle max = getAllFrames()[0].getNormalBounds();
        for (JInternalFrame jif : this.getAllFrames()) {
            max.add(jif.getNormalBounds());
            Rectangle outline = (Rectangle) jif.getClientProperty("outlineBounds");
            if (outline != null) {
                max.add(outline);
            }
        }
        int x1 = max.width + (max.x * 2) < prefSize.width ? prefSize.width
                : max.width + (max.x * 2);
        int y1 = max.height + (max.y * 2) < prefSize.height ? prefSize.height
                : max.height + (max.y * 2);
        return new Dimension(x1, y1);
    }
};
dt.setDesktopManager(new MyDesktopManager());
dt.setAutoscrolls(true);
int xx = 5;
int yy = 5;
int vStep = 10;
int yStep = 22;

// oer-internalframe componentListener
ComponentListener il = new ComponentAdapter() {

    @Override
    public void componentMoved(ComponentEvent e) {
        dt.revalidate();
    }

};
// per-internalframe outlineListener
PropertyChangeListener propertyL = new PropertyChangeListener() {

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        dt.revalidate();
    }
};
for (int ii = 0; ii < 3; ii++) {
    JInternalFrame jif = new JInternalFrame(
            "Internal Frame " + (ii + 1),
            true,
            true,
            true);
    dt.add(jif);
    jif.addComponentListener(il);
    jif.addPropertyChangeListener("outlineBounds", propertyL);
    jif.setLocation(xx, yy);
    xx += vStep;
    yy += yStep;
    jif.setSize(200, 75);
    jif.setVisible(true);
}

[*] Рисунок контура по умолчанию мерцает (до такой степени, что он невидим) - причина в том, что реализация по умолчанию использует ... getGraphics() ... Итак, нам нужно взять на себя рисование контура, т.е. в специальном стекле (это делается с помощью прокомментированного кода) или, возможно, лучше с помощью LayerUI на рабочем столе.

Грубая стеклянная панель, как и poc, которая не обрезается правильно и имеет некоторые проблемы, когда фрейм перемещается обратно в видимый прямоугольник:

public static class OutlinePanel extends JPanel {

    private JDesktopPane desktop;

    public OutlinePanel(JDesktopPane desktop) {
        this.desktop = desktop;
    }

    @Override
    public boolean isOpaque() {
        return false;
    }

    @Override
    protected void paintComponent(Graphics g) {
        JInternalFrame selected = desktop.getSelectedFrame();
        Rectangle outline = (Rectangle) selected.getClientProperty("outlineBounds");
        if (outline == null) return;
        Rectangle bounds = SwingUtilities.convertRectangle(desktop, outline, this);
        g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
    }

}

Обновить

Версия с LayerUI — теперь полностью новое поведение (регистрация слушателя, рисование контура при необходимости, установка менеджера) мы оставляем на украшение. Преимущества:

  • упрощенное использование
  • одно место для всех грязных деталей

СлойUI:

public class DesktopLayerUI extends LayerUI<JDesktopPane> {

    @Override
    public void installUI(JComponent c) {
        super.installUI(c);
        final JDesktopPane dt = getDesktopPane(c);
        //dt.setBorder(BorderFactory.createLineBorder(Color.RED));
        dt.setDesktopManager(new MyDesktopManager());
        // per-internalframe componentListener
        ComponentListener il = new ComponentAdapter() {

            @Override
            public void componentMoved(ComponentEvent e) {
                dt.revalidate();
            }

        };
        // per-internalframe outlineListener
        PropertyChangeListener propertyL = new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                dt.revalidate();
            }
        };
        for (JInternalFrame jif : dt.getAllFrames()) {
            jif.addComponentListener(il);
            jif.addPropertyChangeListener("outlineBounds", propertyL);
        }
        // TBD: register container listener to update frame listeners on adding/removing
        // TBD: componentListener on desktop that handles maximizing frame
        //   (JW: didn't really understand what that one is doing in the original)
    }


    @Override
    public Dimension getPreferredSize(JComponent c) {
        JDesktopPane dt = getDesktopPane(c);
        Dimension prefSize = super.getPreferredSize(c);
        //System.out.println("prefSize: " + prefSize);
        // inititialize the max to the first normalized bounds
        Rectangle max = dt.getAllFrames()[0].getNormalBounds();
        for (JInternalFrame jif : dt.getAllFrames()) {
            max.add(jif.getNormalBounds());
            Rectangle outline = (Rectangle) jif
                    .getClientProperty("outlineBounds");
            if (outline != null) {
                max.add(outline);
            }
        }
        // TBD: cope with frames at negative locations
        //System.out.println("maxBounds(): " + max);
        int x1 = max.width + (max.x * 2) < prefSize.width ? prefSize.width
                : max.width + (max.x * 2);
        int y1 = max.height + (max.y * 2) < prefSize.height ? prefSize.height
                : max.height + (max.y * 2);
        //System.out.println("x,y: " + x1 + "," + y1);
        return new Dimension(x1, y1);
    }


    @Override
    public void paint(Graphics g, JComponent c) {
        super.paint(g, c);
        JDesktopPane desktop = getDesktopPane(c);
        JInternalFrame selected = desktop.getSelectedFrame();
        if (selected == null) return;
        Rectangle outline = (Rectangle) selected.getClientProperty("outlineBounds");
        if (outline == null) return;
        Rectangle bounds = outline; //SwingUtilities.convertRectangle(, outline, this);
        g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
    }

    protected JDesktopPane getDesktopPane(JComponent c) {
        JDesktopPane desktop = ((JLayer<JDesktopPane>) c).getView();
        return desktop;
    }

    public static class MyDesktopManager extends DefaultDesktopManager {
        private Point currentLoc;

        @Override
        public void dragFrame(JComponent f, int newX, int newY) {
            if (isOutline(f)) {
                // take over the outline drawing
                currentLoc = new Point(newX, newY);
                Rectangle bounds = new Rectangle(currentLoc, f.getSize());
                f.putClientProperty("outlineBounds", bounds);
            } else {
                // call super only if not outline
                // handle outline drawing ourselves
                super.dragFrame(f, newX, newY);
            }
        }

        @Override
        public void beginDraggingFrame(JComponent f) {
            super.beginDraggingFrame(f);
            if (isOutline(f)) {
                currentLoc = f.getLocation();
                f.putClientProperty("outlineBounds", f.getBounds());
            }
        }

        @Override
        public void endDraggingFrame(JComponent f) {
            if (isOutline(f) && currentLoc != null) {
                setBoundsForFrame(f, currentLoc.x, currentLoc.y, f.getWidth(), f.getHeight() );
                f.putClientProperty("outlineBounds", null);
            } else {
                super.endDraggingFrame(f);
            }
        }

        protected boolean isOutline(JComponent f) {
            return ((JInternalFrame) f).getDesktopPane().getDragMode() == 
                JDesktopPane.OUTLINE_DRAG_MODE;
        }
    }

}

использование:

JDesktopPane dt = new JDesktopPane(); 
// add internalframes
...
// decorate the pane with the layer
JLayer<JDesktopPane> layer = new JLayer<>(dt, new DesktopLayerUI());
gui.add(new JScrollPane(layer), BorderLayout.CENTER);

Есть небольшая загвоздка (читай: пока не придумал, как это исправить): JLayer реализует Scrollable — его реализация возвращает false для trackXX (если декорированный компонент сам по себе не является Scrollable — JDesktopPane не является), а это означает, что рабочий стол внутри scrollPane всегда имеет размер до своего prefSize, который показывает сероватое окно просмотра в конечной/нижней области, если scrollPane больше.

person kleopatra    schedule 31.08.2013