• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JLayerを使ってJTabbedPaneのタブの挿入位置を描画する
#navi(../)
#tags(JLayer, JTabbedPane, DragAndDrop, TransferHandler, JWindow, JLabel)
RIGHT:Posted by &author(aterai); at 2012-01-23
*JLayerを使ってJTabbedPaneのタブの挿入位置を描画する [#t75e37f6]
``JLayer``を使って、タブのドラッグ&ドロップでの移動先を``JTabbedPane``上に描画します。
---
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
---
* 概要 [#summary]
`JLayer`を使って、タブのドラッグ&ドロップでの移動先を`JTabbedPane`上に描画します。

-&jnlp;
-&jar;
-&zip;
#download(https://lh3.googleusercontent.com/-xX0rzgauC5c/Txz4AxE_u2I/AAAAAAAABIM/jHQdxU1yP9g/s800/DnDLayerTabbedPane.png)

//#screenshot
#ref(https://lh3.googleusercontent.com/-xX0rzgauC5c/Txz4AxE_u2I/AAAAAAAABIM/jHQdxU1yP9g/s800/DnDLayerTabbedPane.png)

**サンプルコード [#d057781e]
* サンプルコード [#sourcecode]
#code(link){{
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);
    JLayer layer = (JLayer)c;
    DnDTabbedPane tabbedPane = (DnDTabbedPane)layer.getView();
    DnDTabbedPane.DropLocation loc = tabbedPane.getDropLocation();
    if(loc != null && loc.isDropable() && loc.getIndex()>=0) {
      int index = loc.getIndex();
      boolean isZero = index==0;
      Rectangle r = tabbedPane.getBoundsAt(isZero?0:index-1);
      if(tabbedPane.getTabPlacement()==JTabbedPane.TOP ||
         tabbedPane.getTabPlacement()==JTabbedPane.BOTTOM) {
        lineRect.setRect(
            r.x-LINEWIDTH/2+r.width*(isZero?0:1), r.y,LINEWIDTH,r.height);
      }else{
        lineRect.setRect(
            r.x,r.y-LINEWIDTH/2+r.height*(isZero?0:1), r.width,LINEWIDTH);
    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();
      }
      Graphics2D g2 = (Graphics2D)g.create();
      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
      g2.setColor(Color.RED);
      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);
    }
  }
}
}}

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

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

- ``Lightweight``
-- ``JDK1.7.0``で導入された、``TransferHandler#setDragImage(...)``メソッドを使用して描画
-- ウインドウの外では非表示
- `Lightweight`
-- `JDK1.7.0`で導入された、`TransferHandler#setDragImage(...)`メソッドを使用して描画
-- ウィンドウの外部では非表示
- `Heavyweight`
-- 半透明の`JWindow`に`JLabel`を追加して表示
-- ウィンドウの外部でも表示可能
-- 表示位置のオフセットが`(0, 0)`の場合、`DragOver`イベントが元の`JFrame`に伝わらない?
--- オフセットが`(0, 0)`でも、`JLabel#contains(...)`が常に`false`なら問題なし

- ``Heavyweight``
-- 半透明の``JWindow``に``JLabel``を追加して表示
-- ウインドウの外でも表示可能
-- 表示位置のオフセットが(0, 0)の場合、``DragOver``イベントが元の``JFrame``に伝わらない?
--- オフセットが(0, 0)でも、``JLabel#contains(...)``が常に``false``なら問題なし

#code{{
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(0.5f);
  //com.sun.awt.AWTUtilities.setWindowOpacity(dialog, 0.5f); // JDK 1.6.0
  // 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);
    }
  });
//...
// ...
}}

**参考リンク [#t09438dd]
* 参考リンク [#reference]
- [[JTabbedPaneのタブをドラッグ&ドロップ>Swing/DnDTabbedPane]]
- [[JTabbedPane間でタブのドラッグ&ドロップ移動>Swing/DnDExportTabbedPane]]
- [http://free-the-pixel.blogspot.com/2010/04/ghost-drag-and-drop-over-multiple.html Free the pixel: GHOST drag and drop, over multiple windows]

**コメント [#b93fe731]
- ``JFrame``の外にドロップした場合は、新しいフレームと``JTabbedPane``を作成して表示したいけど、``ESC``キーや右クリックでのキャンセルと区別がつかない?ので、難しそう。 -- [[aterai]] &new{2012-01-25 (水) 19:57:10};
* コメント [#comment]
#comment
- `JFrame`の外にドロップした場合は、新しいフレームと`JTabbedPane`を作成して表示したいけど、KBD{Esc}キーや右クリックでのキャンセルと区別がつかない?ので、難しそう。 -- &user(aterai); &new{2012-01-25 (水) 19:57:10};

#comment