概要

JTabbedPaneのタブをJFrame外にドラッグアウトした場合はそのタブを含む新規JFrameを作成し、ドラッグ元のJTabbedPaneが空になる場合はその親JFrameごと破棄を実行します。

サンプルコード

protected TabTransferHandler() {
  super();
  dialog.add(label);
  dialog.setOpacity(.5f);
  DragSource.getDefaultDragSource().addDragSourceMotionListener(e -> {
    Point pt = e.getLocation();
    pt.translate(5, 5); // offset
    dialog.setLocation(pt);
    source.pointOnScreen.setLocation(pt);
  });
}

@Override protected void exportDone(
    JComponent c, Transferable data, int action) {
  DnDTabbedPane src = (DnDTabbedPane) c;
  if (src.pointOnScreen.x > 0) {
    createNewFrame(src);
  }
  src.updateTabDropLocation(null, false);
  src.repaint();
  if (src.getTabCount() == 0) {
    Optional.ofNullable(SwingUtilities.getWindowAncestor(src))
        .ifPresent(Window::dispose);
  }
  if (mode == DragImageMode.HEAVYWEIGHT && dialog != null) {
    dialog.dispose();
  }
}

private static void createNewFrame(DnDTabbedPane src) {
  int index = src.dragTabIndex;
  final Component cmp = src.getComponentAt(index);
  // final Component tab = src.getTabComponentAt(index);
  final String title = src.getTitleAt(index);
  final Icon icon = src.getIconAt(index);
  final String tip = src.getToolTipTextAt(index);
  src.remove(index);
  DnDTabbedPane tabs = new DnDTabbedPane();
  tabs.setTransferHandler(src.getTransferHandler());
  tabs.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
  tabs.addTab(title, icon, cmp, tip);
  // tabs.setTabComponentAt(0, tab);
  tabs.setTabComponentAt(0, new ButtonTabComponent(tabs));
  DropTargetListener listener = new TabDropTargetAdapter();
  try {
    tabs.getDropTarget().addDropTargetListener(listener);
  } catch (TooManyListenersException ex) {
    ex.printStackTrace();
    UIManager.getLookAndFeel().provideErrorFeedback(tabs);
  }
  JFrame frame = new JFrame();
  frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
  frame.getContentPane().add(
      new JLayer<>(tabs, new DropLocationLayerUI()));
  frame.setSize(320, 240);
  frame.setLocation(src.pointOnScreen);
  frame.setVisible(true);
  EventQueue.invokeLater(frame::toFront);
}
View in GitHub: Java, Kotlin

解説

  • JFrame外へのタブドラッグアウトで新規JFrame作成とJFrame間でのタブ移動
  • 異なるJFrame間でのタブ移動
    • ドロップ先のJFrameが前面にくるようWindow#toFront()を実行
    • 異なるJFrame間でドラッグアイコンを表示するためのJWindowJWindow#setVisible(...)で表示・非表示するとすべてのJFrame(JFrame#setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE)を設定)を閉じてもアプリケーションを終了することができなくなるので、ドラッグ開始時にnew JWindow()で生成、終了時にJWindow#dispose()で破棄するよう変更
  • JFrameの破棄
    • TransferHandler#exportDone(...)実行時、ドラッグ元のJTabbedPaneのタブ数が0になるかどうかをチェックして親JFrameJTabbedPaneの破棄を実行
    • JLayerを使ってJTabbedPaneのタブの挿入位置を描画するなどとは異なり、ドラッグ元のJTabbedPaneのタブ数が1でもドラッグ開始可能に変更している
    • タブ内に配置したクローズボタンをクリックしてJTabbedPaneのタブ数が0になる場合の親JFrame破棄には対応していない

参考リンク

コメント