• category: swing folder: DnDLayerTabbedPane title: JLayerを使ってJTabbedPaneのタブの挿入位置を描画する tags: [JLayer, JTabbedPane, DragAndDrop, TransferHandler, JWindow, JLabel] author: aterai pubdate: 2012-01-23T18:01:31+09:00 description: JLayerを使って、タブのドラッグ&ドロップでの移動先をJTabbedPane上に描画します。 image: https://lh3.googleusercontent.com/-xX0rzgauC5c/Txz4AxE_u2I/AAAAAAAABIM/jHQdxU1yP9g/s800/DnDLayerTabbedPane.png hreflang:
       href: https://java-swing-tips.blogspot.com/2012/01/sharing-tabs-between-2-jframes.html
       lang: en

概要

JLayerを使って、タブのドラッグ&ドロップでの移動先をJTabbedPane上に描画します。

サンプルコード

class DropLocationLayerUI extends LayerUI<DnDTabbedPane> {
  private static final int LINEWIDTH = 3;
  private final Rectangle lineRect = new Rectangle();
  @Override public void paint(Graphics g, JComponent c) {
    super.paint(g, c);
    if (c instanceof JLayer) {
      JLayer layer = (JLayer) c;
      DnDTabbedPane tabbedPane = (DnDTabbedPane) layer.getView();
      DnDTabbedPane.DropLocation loc = tabbedPane.getDropLocation();
      if (loc != null && loc.isDroppable() && loc.getIndex() >= 0) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setComposite(
            AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f));
        g2.setColor(Color.RED);
        initLineRect(tabbedPane, loc);
        g2.fill(lineRect);
        g2.dispose();
      }
    }
  }
  private void initLineRect(JTabbedPane tabbedPane, DnDTabbedPane.DropLocation loc) {
    int index = loc.getIndex();
    int a = index == 0 ? 0 : 1;
    Rectangle r = tabbedPane.getBoundsAt(a * (index - 1));
    if (tabbedPane.getTabPlacement() == JTabbedPane.TOP
        || tabbedPane.getTabPlacement() == JTabbedPane.BOTTOM) {
      lineRect.setBounds(
        r.x - LINE_WIDTH / 2 + r.width * a, r.y, LINE_WIDTH, r.height);
    } else {
      lineRect.setBounds(
        r.x, r.y - LINE_WIDTH / 2 + r.height * a, r.width, LINE_WIDTH);
    }
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、JTabbedPaneのタブをドラッグ&ドロップや、JTabbedPane間でタブのドラッグ&ドロップ移動のようにGlassPaneを使用する代わりに、JDK 1.7.0で導入されたJLayerを使用して、タブの挿入先を描画しています。JLayerを使用することで、別ウィンドウにあるJTabbedPaneへのタブ移動などの描画が簡単になります。


メニューバーから、ドラッグ中の半透明タブイメージの描画方法を切り替えてテストできます。

  • Lightweight
    • JDK1.7.0で導入された、TransferHandler#setDragImage(...)メソッドを使用して描画
    • ウィンドウの外では非表示
  • Heavyweight
    • 半透明のJWindowJLabelを追加して表示
    • ウィンドウの外でも表示可能
    • 表示位置のオフセットが(0, 0)の場合、DragOverイベントが元のJFrameに伝わらない?
      • オフセットが(0, 0)でも、JLabel#contains(...)が常にfalseなら問題なし
private final JLabel label = new JLabel() {
  @Override public boolean contains(int x, int y) {
    return false;
  }
};
private final JWindow dialog = new JWindow();
public TabTransferHandler() {
  dialog.add(label);
  //dialog.setAlwaysOnTop(true); // Web Start
  dialog.setOpacity(.5f);
  //com.sun.awt.AWTUtilities.setWindowOpacity(dialog, .5f); // JDK 1.6.0
  DragSource.getDefaultDragSource().addDragSourceMotionListener(
      new DragSourceMotionListener() {
    @Override public void dragMouseMoved(DragSourceDragEvent dsde) {
      Point pt = dsde.getLocation();
      pt.translate(5, 5); // offset
      dialog.setLocation(pt);
    }
  });
//...

参考リンク

コメント