---
category: swing
folder: ToolTipAfterMouseWheelEvent
title: JListのセルのToolTipをMouseWheelEventに対応した表示位置に修正
tags: [JToolTip, JList, JScrollPane, MouseWheelListener, ToolTipManager]
author: aterai
pubdate: 2022-01-24T09:33:09+09:00
description: JScrollPane内のJListをMouseWheelで移動した後に表示されるToolTipの対象セルとその位置に修正します。
image: https://drive.google.com/uc?id=1wqY3utjMezRBlJ9j8WIest2ResTWWTMF
hreflang:
    href: https://java-swing-tips.blogspot.com/2022/01/fix-jtooltip-display-of-jlist-cells-to.html
    lang: en
---
* 概要 [#summary]
`JScrollPane`内の`JList`を`MouseWheel`で移動した後に表示される`ToolTip`の対象セルとその位置に修正します。

#download(https://drive.google.com/uc?id=1wqY3utjMezRBlJ9j8WIest2ResTWWTMF)

* サンプルコード [#sourcecode]
#code(link){{
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);
});
}}

* 解説 [#explanation]
- `Default`
-- たとえば先頭の`0: 11111`セルにマウスカーソルを移動した直後にすばやくマウスホイールを下方向に回転して別セル上にマウスカーソルが配置された状態でもツールチップ表示される内容は`0: 11111`で、その表示位置も現在のマウスカーソル付近ではなく`JScrollPane`で隠されている`0: 11111`セルの位置になる
-- マウスホイール対応以前の実装なのでマウス移動イベント以外でカーソル直下のセルが変化することは想定されていない
-- `JTable`、`JTree`などでも同様
- `MouseWheelListener`
-- 親`JScrollPane`に`MouseWheelListener`を追加し、ホイールイベントを`SwingUtilities.convertMouseEvent(...)`で`JList`ソースのマウスイベントに変換して`ToolTipManager.sharedInstance().mouseMoved(...)`で実行
-- `JToolTip`表示後、マウスカーソルを移動せずにマウスホイールのみ回転して直下のセルが変更された場合でも同位置でツールチップテキストの更新が可能
--- `Windows`環境以外では位置の修正も内容の更新も動作しない?
-- `ToolTipManager`では`MouseEvent`の`ID`は考慮せずに位置のみ使用しているのでイベントソースの変更のみで十分
-- [https://community.oracle.com/tech/developers/discussion/1353509/tooltips-the-mouse-wheel-and-jscrollpane Tooltips, the mouse wheel and JScrollPane — oracle-tech]
- `getToolTipLocation`
-- `JList#getToolTipText()`と`JList#getToolTipLocation()`をオーバーライドしてそれぞれ`MouseEvent#getPoint()`と`Component#getMousePosition()`(内部で`MouseInfo.getPointerInfo()`を使用)で取得したマウス位置が異なる場合は後者で取得したマウス位置で`MouseEvent`を再作成して実行
-- [[Screen上にあるMouseの位置を取得する>Swing/MouseInfo]]

#code{{

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;
  }
}
}}

* 参考リンク [#reference]
- [https://community.oracle.com/tech/developers/discussion/1353509/tooltips-the-mouse-wheel-and-jscrollpane Tooltips, the mouse wheel and JScrollPane — oracle-tech]
- [[JListのセル上にToolTipを表示する>Swing/ToolTipOnCellBounds]]
- [[Screen上にあるMouseの位置を取得する>Swing/MouseInfo]]

* コメント [#comment]
#comment
#comment