Swing/ThreeDotsMenuButton の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/ThreeDotsMenuButton へ行く。
- Swing/ThreeDotsMenuButton の差分を削除
--- category: swing folder: ThreeDotsMenuButton title: JListのセルがハイライト表示中にのみJButtonを表示する tags: [JList, JLayer, JButton] author: aterai pubdate: 2024-12-09T08:19:05+09:00 description: JListのセルハイライト表示とコンテキストメニュー表示用のJButtonをJLayer上で描画します。 image: https://drive.google.com/uc?id=16fRrYb7io_62_SL3QoDEAvwI7QTD1cj4 --- * 概要 [#summary] JListのセルハイライト表示とコンテキストメニュー表示用のJButtonをJLayer上で描画します。 `JList`のセルハイライト表示とコンテキストメニュー表示用の`JButton`を`JLayer`上で描画します。 #download(https://drive.google.com/uc?id=16fRrYb7io_62_SL3QoDEAvwI7QTD1cj4) * サンプルコード [#sourcecode] #code(link){{ class RolloverLayerUI extends LayerUI<JScrollPane> { private final JPanel renderer = new JPanel(); private int rolloverIdx = -1; private final Point loc = new Point(-100, -100); private final JButton button = new JButton(new ThreeDotsIcon()) { @Override public void updateUI() { super.updateUI(); setBorderPainted(false); setContentAreaFilled(false); setFocusPainted(false); setFocusable(false); setOpaque(false); setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4)); } }; @Override public void installUI(JComponent c) { super.installUI(c); if (c instanceof JLayer) { ((JLayer<?>) c).setLayerEventMask( AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); } } @Override public void uninstallUI(JComponent c) { if (c instanceof JLayer) { ((JLayer<?>) c).setLayerEventMask(0); } super.uninstallUI(c); } @Override protected void processMouseEvent(MouseEvent e, JLayer<? extends JScrollPane> l) { super.processMouseEvent(e, l); Component c = e.getComponent(); if (c instanceof JList) { JList<?> list = (JList<?>) c; int id = e.getID(); if (id == MouseEvent.MOUSE_CLICKED && SwingUtilities.isLeftMouseButton(e)) { Rectangle r = list.getCellBounds(rolloverIdx, rolloverIdx); Dimension d = button.getPreferredSize(); r.width = l.getView().getViewportBorderBounds().width - d.width; JPopupMenu popup = ((JComponent) c).getComponentPopupMenu(); Point pt = e.getPoint(); if (popup != null && !r.contains(pt)) { popup.show(c, pt.x, pt.y); } } else if (id == MouseEvent.MOUSE_EXITED) { list.repaint(list.getCellBounds(rolloverIdx, rolloverIdx)); rolloverIdx = -1; loc.setLocation(-100, -100); } } } @Override protected void processMouseMotionEvent(MouseEvent e, JLayer<? extends JScrollPane> l) { super.processMouseMotionEvent(e, l); Component c = e.getComponent(); if (e.getID() == MouseEvent.MOUSE_MOVED && c instanceof JList) { JList<?> list = (JList<?>) c; Point pt = e.getPoint(); loc.setLocation(pt); rolloverIdx = list.locationToIndex(pt); Rectangle r = list.getCellBounds(rolloverIdx, rolloverIdx); r.width = l.getView().getViewportBorderBounds().width; r.grow(0, r.height); list.repaint(r); } } @Override public void paint(Graphics g, JComponent c) { super.paint(g, c); JList<?> list = getList(c); if (list != null && rolloverIdx >= 0) { JScrollPane scroll = (JScrollPane) ((JLayer<?>) c).getView(); Graphics2D g2 = (Graphics2D) g.create(); Rectangle cellBounds = list.getCellBounds(rolloverIdx, rolloverIdx); Component rc = getRendererComponent(list, rolloverIdx); Dimension d = button.getPreferredSize(); cellBounds.width = scroll.getViewportBorderBounds().width - d.width; boolean buttonRollover = !cellBounds.contains(loc); button.getModel().setRollover(buttonRollover); Rectangle rect = SwingUtilities.convertRectangle(list, cellBounds, c); SwingUtilities.paintComponent(g2, rc, renderer, rect); rect.x += rect.width; rect.width = d.width; g2.setPaint(rc.getBackground()); g2.fill(rect); SwingUtilities.paintComponent(g2, button, renderer, rect); g2.dispose(); } } private static JList<?> getList(JComponent layer) { JList<?> list = null; if (layer instanceof JLayer) { JScrollPane scroll = (JScrollPane) ((JLayer<?>) layer).getView(); Component view = scroll.getViewport().getView(); if (view instanceof JList) { list = (JList<?>) view; } } return list; } private static <E> Component getRendererComponent(JList<E> list, int idx) { E value = list.getModel().getElementAt(idx); ListCellRenderer<? super E> r = list.getCellRenderer(); boolean isSelected = list.isSelectedIndex(idx); boolean cellHasFocus = list.getSelectionModel().getLeadSelectionIndex() == idx; Component c = r.getListCellRendererComponent(list, value, idx, isSelected, cellHasFocus); if (!isSelected) { c.setBackground(Color.GRAY); c.setForeground(Color.WHITE); } return c; } } }} * 解説 [#explanation] - `JList`の親`JScrollPane`に`JLayer`を設定 -- セルのハイライト表示を`ListCellRenderer`ではなく`LayerUI#paint(...)`をオーバーライドして、`JList#getListCellRendererComponent(...)`で取得した描画用コンポーネントの文字色、背景色を変更し`SwingUtilities.paintComponent(...)`で描画 -- 描画領域をセル全体の幅ではなくコンテキストメニュー表示用の`JButton`の幅を除外するよう縮小する --- [[JListのセルをカーソル移動でロールオーバー>Swing/RollOverListener]] -- コンテキストメニュー表示用の`JButton`も同様に`SwingUtilities.paintComponent(...)`でセル右端に描画 -- `SwingUtilities.paintComponent(...)`での描画で`JButton`の`MouseListener`などは反応しないため、`LayerUI#processMouseEvent(...)`中でクリックされたかを判別し`JList#getComponentPopupMenu()`で取得した`JPopupMenu`を表示 --- `Windows`環境で右クリックだけではなく、この`JButton`の左クリックで一般的?な`Web`アプリのように`JPopupMenu`が表示可能になる * 参考リンク [#reference] - [[JListのセルをカーソル移動でロールオーバー>Swing/RollOverListener]] - [[JListのセル内にJButtonを配置する>Swing/ButtonsInListCell]] - [[JListのセルに項目選択チェックボックスを追加する>Swing/ListCellItemCheckBoxes]] * コメント [#comment] #comment #comment