Swing/RolloverTreeOnlyWhenPopupHidden の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/RolloverTreeOnlyWhenPopupHidden へ行く。
- Swing/RolloverTreeOnlyWhenPopupHidden の差分を削除
--- category: swing folder: RolloverTreeOnlyWhenPopupHidden title: JTreeに設定したJPopupMenuが非表示の場合のみJTreeの行をロールオーバー状態で描画する tags: [JTree, JPopupMenu] author: aterai pubdate: 2025-03-31T05:38:46+09:00 description: JTreeにマウスカーソル下の行をロールオーバー描画する機能を追加し、JPopupMenuが表示されている場合はそのロールオーバー状態を維持するよう設定します。 image: https://drive.google.com/uc?id=11qlW-Yy5eLtrsTD7qG4RTSNGM4x2E4Pe --- * Summary [#summary] `JTree`にマウスカーソル下の行をロールオーバー描画する機能を追加し、`JPopupMenu`が表示されている場合はそのロールオーバー状態を維持するよう設定します。 #download(https://drive.google.com/uc?id=11qlW-Yy5eLtrsTD7qG4RTSNGM4x2E4Pe) * Source Code Examples [#sourcecode] #code(link){{ class FileSystemViewTree extends JTree { private static final Color SELECTED_COLOR = new Color(0x00_78_D7); private static final Color ROLLOVER_COLOR = new Color(0x64_96_C8); protected int rollOverRowIndex = -1; protected transient MouseAdapter rolloverHandler; private transient FileSystemView fileSystemView; protected FileSystemViewTree() { super(); } @Override protected void paintComponent(Graphics g) { int[] sr = getSelectionRows(); if (sr == null) { super.paintComponent(g); return; } g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); Graphics2D g2 = (Graphics2D) g.create(); if (rollOverRowIndex >= 0) { g2.setPaint(ROLLOVER_COLOR); Rectangle rect = getRowBounds(rollOverRowIndex); g2.fillRect(0, rect.y, getWidth(), rect.height); } g2.setPaint(SELECTED_COLOR); Arrays.stream(sr).mapToObj(this::getRowBounds) .forEach(r -> g2.fillRect(0, r.y, getWidth(), r.height)); super.paintComponent(g); if (hasFocus()) { Optional.ofNullable(getLeadSelectionPath()).ifPresent(path -> { Rectangle r = getRowBounds(getRowForPath(path)); g2.setPaint(SELECTED_COLOR.darker()); g2.drawRect(0, r.y, getWidth() - 1, r.height - 1); }); } g2.dispose(); } @Override public void updateUI() { setCellRenderer(null); removeMouseListener(rolloverHandler); removeMouseMotionListener(rolloverHandler); super.updateUI(); setUI(new WholeRowSelectTreeUI()); UIManager.put("Tree.repaintWholeRow", Boolean.TRUE); fileSystemView = FileSystemView.getFileSystemView(); addTreeSelectionListener(new FolderSelectionListener(fileSystemView)); DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer(); setCellRenderer((tree, value, selected, expanded, leaf, row, hasFocus) -> { setCellRenderer((tree, value, selected, expanded, leaf, row, focus) -> { Component c = renderer.getTreeCellRendererComponent( tree, value, selected, expanded, leaf, row, false); boolean rollover = row == rollOverRowIndex; updateFgc(c, renderer.getTextSelectionColor(), rollover); updateBgc(c, tree.getBackground(), selected, rollover); updateIcon(c, value, selected); return c; }); setOpaque(false); rolloverHandler = new RolloverHandler(); addMouseListener(rolloverHandler); addMouseMotionListener(rolloverHandler); EventQueue.invokeLater(() -> setModel(makeFileTreeModel(fileSystemView))); } public void updateRolloverIndex() { EventQueue.invokeLater(() -> { Point pt = getMousePosition(); if (pt == null) { clearRollover(); } else { updateRolloverIndex(pt); } }); } public void updateRolloverIndex(Point pt) { int row = getRowForLocation(pt.x, pt.y); boolean isPopupVisible = getComponentPopupMenu().isVisible(); if (rollOverRowIndex != row && !isPopupVisible) { rollOverRowIndex = row; repaint(); } } private void clearRollover() { rollOverRowIndex = -1; repaint(); } protected class RolloverHandler extends MouseAdapter { @Override public void mouseMoved(MouseEvent e) { updateRolloverIndex(e.getPoint()); } @Override public void mouseEntered(MouseEvent e) { updateRolloverIndex(e.getPoint()); } @Override public void mouseExited(MouseEvent e) { boolean isPopupVisible = getComponentPopupMenu().isVisible(); if (!isPopupVisible) { clearRollover(); } } } } }} * Explanation [#explanation] - `JTree`に`MouseAdapter`を追加し、マウスカーソルが移動したら`JTree#getRowForLocation(x, y)`メソッドでマウスカーソル下の行番号を記憶し、その行の文字色はセルレンダラー、背景色は`JTree#paintComponent(...)`をオーバーライドして変更 -- デフォルトでは`JTree#setComponentPopupMenu(JPopupMenu)`で設定した`JPopupMenu`が表示されると`mouseExited`イベントが発生したり、その`JPopupMenu`が表示中でも`JTree`上にマウスカーソルが移動すると`mouseMoved`イベントが発生してロールオーバー状態の行番号が変化してしまう -- このサンプルではマウスカーソル移動時に`JPopupMenu`が表示中出ない場合のみロールオーバー状態の行番号を変更するよう設定してこれを回避 -- さらに`JPopupMenu`側には`PopupMenuListener`を追加し、`JPopupMenu`が非表示になる際にマウスカーソル位置を`JTree#getMousePosition()`で取得してロールオーバー状態の行番号を更新する --- `JTree#getMousePosition()`で取得した`Point`が`null`の場合、マウスカーソルは`JTree`の外に存在するのでロールオーバー状態はクリアする * Reference [#reference] - [[JTreeのノードをハイライト>Swing/RollOverTree]] - [[JTableの行を右クリックで選択して同時にJPopupMenuを開く>Swing/RightClickRowSelectionAndPopupMenu]] * Comment [#comment] #comment #comment