概要

JScrollPane内のJListMouseWheelで移動した後に表示されるToolTipの対象セルとその位置に修正します。

サンプルコード

JList<String> list1 = new JList<String>(model) {
  @Override public void updateUI() {
    super.updateUI();
    setCellRenderer(new TooltipListCellRenderer<>());
  }
};
JScrollPane scroll1 = new JScrollPane(list1);
scroll1.addMouseWheelListener(e -> {
  JScrollPane scrollPane = (JScrollPane) e.getComponent();
  Component view = scrollPane.getViewport().getView();
  MouseEvent event = SwingUtilities.convertMouseEvent(scrollPane, e, view);
  ToolTipManager.sharedInstance().mouseMoved(event);
});
View in GitHub: Java, Kotlin

解説

  • Default
    • たとえば先頭の0: 11111セルにマウスカーソルを移動した直後にすばやくマウスホイールを下方向に回転して別セル上にマウスカーソルが配置された状態でもツールチップ表示される内容は0: 11111で、その表示位置も現在のマウスカーソル付近ではなくJScrollPaneで隠されている0: 11111セルの位置になる
    • マウスホイール対応以前の実装なのでマウス移動イベント以外でカーソル直下のセルが変化することは想定されていない
    • JTableJTreeなどでも同様
  • MouseWheelListener
    • JScrollPaneMouseWheelListenerを追加し、ホイールイベントをSwingUtilities.convertMouseEvent(...)JListソースのマウスイベントに変換してToolTipManager.sharedInstance().mouseMoved(...)で実行
    • JToolTip表示後、マウスカーソルを移動せずにマウスホイールのみ回転して直下のセルが変更された場合でも同位置でツールチップテキストの更新が可能
    • ToolTipManagerではMouseEventIDは考慮せずに位置のみ使用しているのでイベントソースの変更のみで十分
    • Tooltips, the mouse wheel and JScrollPane — oracle-tech
  • getToolTipLocation
    • JList#getToolTipText()JList#getToolTipLocation()をオーバーライドしてそれぞれMouseEvent#getPoint()Component#getMousePosition()(内部でMouseInfo.getPointerInfo()を使用)で取得したマウス位置が異なる場合は後者で取得したマウス位置でMouseEventを再作成して実行
    • Screen上にあるMouseの位置を取得する
class TooltipList<E> extends JList<E> {
  protected TooltipList(ListModel<E> m) {
    super(m);
  }

  @Override public String getToolTipText(MouseEvent e) {
    Point p0 = e.getPoint();
    Point p1 = getMousePosition();
    if (p1 != null && !p1.equals(p0)) {
      int i = locationToIndex(p1);
      Rectangle cellBounds = getCellBounds(i, i);
      if (i >= 0 && cellBounds != null && cellBounds.contains(p1.x, p1.y)) {
        MouseEvent event = new MouseEvent(
            e.getComponent(),
            MouseEvent.MOUSE_MOVED,
            e.getWhen(),
            e.getModifiers(),
            p1.x,
            p1.y,
            e.getClickCount(),
            e.isPopupTrigger()
        );
        return super.getToolTipText(event);;
      }
    }
    return super.getToolTipText(e);
  }

  @Override public Point getToolTipLocation(MouseEvent e) {
    Point p0 = e.getPoint();
    Point p1 = getMousePosition();
    if (p1 != null && !p1.equals(p0)) {
      int i = locationToIndex(p1);
      Rectangle cellBounds = getCellBounds(i, i);
      if (i >= 0 && cellBounds != null && cellBounds.contains(p1.x, p1.y)) {
        return new Point(p1.x, p1.y + cellBounds.height);
      }
    }
    return null;
  }
}

参考リンク

コメント