TITLE:JLayerを使ってJTabbedPaneのタブの挿入位置を描画する

Posted by at 2012-01-23

JLayerを使ってJTabbedPaneのタブの挿入位置を描画する

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

  • &jnlp;
  • &jar;
  • &zip;
DnDLayerTabbedPane.png

サンプルコード

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);
      }
      Graphics2D g2 = (Graphics2D)g.create();
      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
      g2.setColor(Color.RED);
      g2.fill(lineRect);
      g2.dispose();
    }
  }
}
View in GitHub: Java, Kotlin

解説

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


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

  • Lightweight
    • JDK1.7.0で導入された、TransferHandler#setDragImage(...)メソッドを使用して描画
    • ウインドウの外では非表示
  • Heavyweight
    • 半透明のJWindowにJLabelを追加して表示
    • ウインドウの外でも表示可能
    • 表示位置のオフセットが(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(0.5f);
          //com.sun.awt.AWTUtilities.setWindowOpacity(dialog, 0.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);
            }
          });
        //...
        

参考リンク

コメント

  • JFrameの外にドロップした場合は、新しいフレームとJTabbedPaneを作成して表示したいけど、ESCキーや右クリックでのキャンセルと区別がつかない?ので、難しそう。 -- aterai