防止 JInternalFrame 移出 JDesktopPane

发布于 2024-12-15 18:53:52 字数 793 浏览 3 评论 0原文

当我在 JDesktopPane 中有 JInternalFrame 时,JInternalFrame 是可移动的(这很好)。但是,可以将其移到 JDesktopPane 的可见范围之外(我不太喜欢)

要亲自查看,这里有一些示例代码:

public static void main(String[] args) {

  JFrame frame = new JFrame("JDesktopPane");
  JDesktopPane tableDisplay = new JDesktopPane();

  JInternalFrame internalFrame = new JInternalFrame("JInternalFrame",true,true,true,true);
  internalFrame.setContentPane(new JLabel("Content"));
  internalFrame.pack();
  internalFrame.setVisible(true);
  tableDisplay.add(internalFrame, JDesktopPane.POPUP_LAYER);

  frame.setContentPane(tableDisplay);
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  frame.setMinimumSize(new Dimension(400, 300));
  frame.setVisible(true);
}

是否可以设置 JInternalFrame 或 JDesktopPane,以便它们不会'不允许这样吗?

When I have a JInternalFrame in a JDesktopPane, the JInternalFrame is movable (which is good). However, it's possible to move it outside of the visible scope of the JDesktopPane (which I'm not so fond of)

To see for yourself, here's some sample code:

public static void main(String[] args) {

  JFrame frame = new JFrame("JDesktopPane");
  JDesktopPane tableDisplay = new JDesktopPane();

  JInternalFrame internalFrame = new JInternalFrame("JInternalFrame",true,true,true,true);
  internalFrame.setContentPane(new JLabel("Content"));
  internalFrame.pack();
  internalFrame.setVisible(true);
  tableDisplay.add(internalFrame, JDesktopPane.POPUP_LAYER);

  frame.setContentPane(tableDisplay);
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  frame.setMinimumSize(new Dimension(400, 300));
  frame.setVisible(true);
}

Is it possible to set either the JInternalFrame or JDesktopPane so that they won't allow this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

捎一片雪花 2024-12-22 18:53:52

负责移动/调整大小的协作者是 DesktopPaneManager。所以我会尝试将移动限制在窗格内。这是一个快速&肮脏的概念证明:

    JDesktopPane background = new JDesktopPane();
    JInternalFrame internalFrame = new JInternalFrame("Internal Frame",
            true, true, true, true);
    DesktopManager manager = new DefaultDesktopManager() {
        /** This moves the <code>JComponent</code> and repaints the damaged areas. */
        @Override
        public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
            boolean didResize = (f.getWidth() != newWidth || f.getHeight() != newHeight);
            if (!inBounds((JInternalFrame) f, newX, newY, newWidth, newHeight)) return;
            f.setBounds(newX, newY, newWidth, newHeight);
            if(didResize) {
                f.validate();
            } 
        }

        protected boolean inBounds(JInternalFrame f, int newX, int newY, int newWidth, int newHeight) {
            if (newX < 0 || newY < 0) return false;
            if (newX + newWidth > f.getDesktopPane().getWidth()) return false;
            if (newY + newHeight > f.getDesktopPane().getHeight()) return false;
            return true;
        }

    };
    background.setDesktopManager(manager);

显然,有一些问题需要解决:-) Fi

  • 使用适合 LAF 的管理器,这可以通过实现一个包装器 DesktopManager 来完成,该包装器将其他所有内容委托给 LAF 安装的
  • 副作用检查(拖动在撞到墙上后出现无响应,可能还需要其他东西)

编辑

只是为了澄清:“无响应”我的意思是用户必须再次释放并按下/拖动(一旦内部框架已触及桌面边界)进一步移动。这并不奇怪,因为 BorderListener(即 BasicInternalFrame 安装的 mouseListener)保留与初始按下相关的一些状态,然后请求相对于该初始位置重新定位。在框架卡在某处的情况下拖动鼠标会混淆内部状态。

有趣的是,查看代码,似乎有意限制移动而不将其推到外面,

    // Make sure we stay in-bounds
    if(newX + i.left <= -__x)
        newX = -__x - i.left + 1;
    if(newY + i.top <= -__y)
        newY = -__y - i.top + 1;
    if(newX + __x + i.right >= pWidth)
        newX = pWidth - __x - i.right - 1;
    if(newY + __y + i.bottom >= pHeight)
        newY =  pHeight - __y - i.bottom - 1;

不过这是相对于当前鼠标位置的。

The collaborator which is responsible for doing the move/resize is the DesktopPaneManager. So I would try to limit the movement to within the pane. Here's a quick & dirty proof of concept:

    JDesktopPane background = new JDesktopPane();
    JInternalFrame internalFrame = new JInternalFrame("Internal Frame",
            true, true, true, true);
    DesktopManager manager = new DefaultDesktopManager() {
        /** This moves the <code>JComponent</code> and repaints the damaged areas. */
        @Override
        public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
            boolean didResize = (f.getWidth() != newWidth || f.getHeight() != newHeight);
            if (!inBounds((JInternalFrame) f, newX, newY, newWidth, newHeight)) return;
            f.setBounds(newX, newY, newWidth, newHeight);
            if(didResize) {
                f.validate();
            } 
        }

        protected boolean inBounds(JInternalFrame f, int newX, int newY, int newWidth, int newHeight) {
            if (newX < 0 || newY < 0) return false;
            if (newX + newWidth > f.getDesktopPane().getWidth()) return false;
            if (newY + newHeight > f.getDesktopPane().getHeight()) return false;
            return true;
        }

    };
    background.setDesktopManager(manager);

There are some issues to solve, obviously :-) F.i.

  • use the manager as appropriate for the LAF, which could be done by implementing a wrapper DesktopManager which delegates everything else to the LAF installed
  • check for side-effects (the drag appears a unresponsive after having hit a wall, there might be other things needed)

Edit

just to clarify: with "unresponsive" I mean that the user has to release and press/drag again (once the internal frame has hit the desktop bounds) to further move the. That's not overly surprising, as the BorderListener (that's the mouseListener installed by BasicInternalFrame) keeps some state related to the initial press and then requests re-locates relative to that initial location. Dragging the mouse with the frame stuck somewhere confuses that internal state.

Interestingly, looking at the code, it seems like there had been intentions to limit the movement to not push it to the outside,

    // Make sure we stay in-bounds
    if(newX + i.left <= -__x)
        newX = -__x - i.left + 1;
    if(newY + i.top <= -__y)
        newY = -__y - i.top + 1;
    if(newX + __x + i.right >= pWidth)
        newX = pWidth - __x - i.right - 1;
    if(newY + __y + i.bottom >= pHeight)
        newY =  pHeight - __y - i.bottom - 1;

that's relative to the current mouse location, though.

浴红衣 2024-12-22 18:53:52

完全归功于克利奥帕特拉为我指明了正确的方向。

我想我已经解决了无响应问题,并在这里分享我的解决方案。根据 kleopatra 的回答,如果鼠标点位于窗格之外,则内部框架位于窗格的边缘。此外,它继续跟随鼠标 - 即,如果您将鼠标从窗格底部移开,然后绕到窗格的右侧,框架将沿着窗格底部,然后向右上方移动窗格中的沙子。

public class BoundedDesktopManager extends DefaultDesktopManager {

  @Override
  public void beginDraggingFrame(JComponent f) {
    // Don't do anything. Needed to prevent the DefaultDesktopManager setting the dragMode
  }

  @Override
  public void beginResizingFrame(JComponent f, int direction) {
    // Don't do anything. Needed to prevent the DefaultDesktopManager setting the dragMode
  }

  @Override
  public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
    boolean didResize = (f.getWidth() != newWidth || f.getHeight() != newHeight);
    if (!inBounds((JInternalFrame) f, newX, newY, newWidth, newHeight)) {
      Container parent = f.getParent();
      Dimension parentSize = parent.getSize();
      int boundedX = (int) Math.min(Math.max(0, newX), parentSize.getWidth() - newWidth);
      int boundedY = (int) Math.min(Math.max(0, newY), parentSize.getHeight() - newHeight);
      f.setBounds(boundedX, boundedY, newWidth, newHeight);
    } else {
      f.setBounds(newX, newY, newWidth, newHeight);
    }
    if(didResize) {
      f.validate();
    }
  }

  protected boolean inBounds(JInternalFrame f, int newX, int newY, int newWidth, int newHeight) {
    if (newX < 0 || newY < 0) return false;
    if (newX + newWidth > f.getDesktopPane().getWidth()) return false;
    if (newY + newHeight > f.getDesktopPane().getHeight()) return false;
    return true;
  }
}

Full credit to kleopatra for pointing me in the right direction.

I think I've solved the unresponsiveness issue, and am sharing my solution here. Following kleopatra's answer, if the mouse point is outside of the pane, the internal frame is at the edge of the pane. Also, this continues to follow the mouse - i.e. if you move the mouse off the bottom of the pane, and then round to the right hand side of the pane, the frame will follow along the bottom of the pane, and then up the right sand of the pane.

public class BoundedDesktopManager extends DefaultDesktopManager {

  @Override
  public void beginDraggingFrame(JComponent f) {
    // Don't do anything. Needed to prevent the DefaultDesktopManager setting the dragMode
  }

  @Override
  public void beginResizingFrame(JComponent f, int direction) {
    // Don't do anything. Needed to prevent the DefaultDesktopManager setting the dragMode
  }

  @Override
  public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
    boolean didResize = (f.getWidth() != newWidth || f.getHeight() != newHeight);
    if (!inBounds((JInternalFrame) f, newX, newY, newWidth, newHeight)) {
      Container parent = f.getParent();
      Dimension parentSize = parent.getSize();
      int boundedX = (int) Math.min(Math.max(0, newX), parentSize.getWidth() - newWidth);
      int boundedY = (int) Math.min(Math.max(0, newY), parentSize.getHeight() - newHeight);
      f.setBounds(boundedX, boundedY, newWidth, newHeight);
    } else {
      f.setBounds(newX, newY, newWidth, newHeight);
    }
    if(didResize) {
      f.validate();
    }
  }

  protected boolean inBounds(JInternalFrame f, int newX, int newY, int newWidth, int newHeight) {
    if (newX < 0 || newY < 0) return false;
    if (newX + newWidth > f.getDesktopPane().getWidth()) return false;
    if (newY + newHeight > f.getDesktopPane().getHeight()) return false;
    return true;
  }
}
末蓝 2024-12-22 18:53:52

尝试设置JFrame的FlowLayout并使用add()方法添加JDesktopPane。

frame.setLayout(new FlowLayout());
tableDisplay.setPreferredSize(new Dimension(100,100));
frame.add(tableDisplay);

Try to set FlowLayout of JFrame and use add() method to add JDesktopPane.

frame.setLayout(new FlowLayout());
tableDisplay.setPreferredSize(new Dimension(100,100));
frame.add(tableDisplay);
奈何桥上唱咆哮 2024-12-22 18:53:52

JInternalFrame 的顶部不要被隐藏,这一点很重要,因为那是其抓取栏和控制按钮所在的位置。因此,作为设置边界之前的最后一步,请确保 y 值不为负:例如,使用 y = Math.max(0, y) 之类的语句。

It is important that the top of the JInternalFrame not get hidden, since that is where its grab bar and control buttons are located. So, as a last step before setting bounds, make sure that your y value is not negative: for example, with a statement like y = Math.max(0, y).

深爱不及久伴 2024-12-22 18:53:52

这完全满足了我的需求。它使拖动的框架完全可见,并且当鼠标光标移到桌面窗格区域的边缘时,它会“滑动”窗口。

JDesktopPane desktop = new JDesktopPane();
DesktopManager manager = new DefaultDesktopManager() {
    @Override
    public void dragFrame(JComponent f, int newX, int newY) {
        JDesktopPane desktop = ((JInternalFrame) f).getDesktopPane();
        newX = Math.clamp(newX, 0, desktop.getWidth() - f.getWidth());
        newY = Math.clamp(newY, 0, desktop.getHeight() - f.getHeight());
        super.dragFrame(f, newX, newY);
    }
};
background.setDesktopManager(desktop);

Math.clamp 在 Java 21 中可用。如果需要,自己实现

This fully satisfies my needs. It keeps the dragged frame fully visible and it "slides" the window on the edge of the desktop pane area when the mouse cursor is moved beyond it.

JDesktopPane desktop = new JDesktopPane();
DesktopManager manager = new DefaultDesktopManager() {
    @Override
    public void dragFrame(JComponent f, int newX, int newY) {
        JDesktopPane desktop = ((JInternalFrame) f).getDesktopPane();
        newX = Math.clamp(newX, 0, desktop.getWidth() - f.getWidth());
        newY = Math.clamp(newY, 0, desktop.getHeight() - f.getHeight());
        super.dragFrame(f, newX, newY);
    }
};
background.setDesktopManager(desktop);

Math.clamp is available in Java 21. Implement it yourself if needed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文