Swing/PopupVolumeSlider の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/PopupVolumeSlider へ行く。
- Swing/PopupVolumeSlider の差分を削除
--- category: swing folder: PopupVolumeSlider title: JPopupMenuに垂直JSliderを配置してJToggleButtonの上部に表示する tags: [JSlider, JPopupMenu, JToolTip, JToggleButton] author: aterai pubdate: 2021-10-04T11:17:36+09:00 description: JPopupMenuに垂直JSliderを配置し、JToggleButtonに設定したJToolTipを表示するときにその上部に重ねて表示します。 image: https://drive.google.com/uc?id=1JlCYec_huorcrZoTG5_-vs7oFTK0TRhF hreflang: href: https://java-swing-tips.blogspot.com/2021/11/add-vertical-jslider-in-jpopupmenu-and.html lang: en --- * 概要 [#summary] `JPopupMenu`に垂直`JSlider`を配置し、`JToggleButton`に設定した`JToolTip`を表示するときにその上部に重ねて表示します。 #download(https://drive.google.com/uc?id=1JlCYec_huorcrZoTG5_-vs7oFTK0TRhF) * サンプルコード [#sourcecode] #code(link){{ JPopupMenu popup = new JPopupMenu(); popup.setLayout(new BorderLayout()); popup.addMouseWheelListener(InputEvent::consume); UIManager.put("Slider.paintValue", Boolean.TRUE); UIManager.put("Slider.focus", UIManager.get("Slider.background")); JSlider slider = new JSlider(SwingConstants.VERTICAL, 0, 100, 80); slider.addMouseWheelListener(e -> { JSlider s = (JSlider) e.getComponent(); if (s.isEnabled()) { BoundedRangeModel m = s.getModel(); m.setValue(m.getValue() - e.getWheelRotation() * 2); } e.consume(); }); popup.add(slider); JToggleButton button = new JToggleButton("🔊") { @Override public JToolTip createToolTip() { JToolTip tip = super.createToolTip(); tip.addHierarchyListener(e -> { long flg = e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED; if (flg != 0 && e.getComponent().isShowing()) { Dimension d = popup.getPreferredSize(); popup.show(this, (getWidth() - d.width) / 2, -d.height); } }); return tip; } @Override public Point getToolTipLocation(MouseEvent e) { return new Point(getWidth() / 2, -getHeight()); } @Override public void setEnabled(boolean b) { super.setEnabled(b); setText(b ? "🔊" : "🔇"); } }; button.setToolTipText(""); button.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (!button.isEnabled()) { slider.setValue(80); button.setEnabled(true); } Component b = (Component) e.getSource(); Dimension d = popup.getPreferredSize(); popup.show(b, (b.getWidth() - d.width) / 2, -d.height); } @Override public void mouseEntered(MouseEvent e) { if (!popup.isVisible()) { ToolTipManager.sharedInstance().setEnabled(true); } } @Override public void mouseExited(MouseEvent e) { if (!popup.isVisible()) { ToolTipManager.sharedInstance().setEnabled(true); } } }); popup.addPopupMenuListener(new PopupMenuListener() { @Override public void popupMenuCanceled(PopupMenuEvent e) { /* not needed */ } @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { EventQueue.invokeLater(() -> ToolTipManager.sharedInstance().setEnabled(false)); } @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { button.setSelected(false); } }); }} * 解説 [#explanation] - `JPopupMenu` -- レイアウトを`BorderLayout`に変更して垂直`JSlider`をひとつだけ追加 -- `JPopupMenu#addMouseWheelListener(InputEvent::consume)`を追加して余白などでマウスホイールを回転しても閉じないよう設定 -- `PopupMenuListener`を追加して`JPopupMenu`が開いたあとは`JToolTip`を無効化、閉じる前に`JToggleButton`の選択状態を解除 - `JSlider` -- `MouseWheelListener`を追加してマウスホイールの回転で値を変更可能に設定 -- `ChangeListener`を追加して値が`0`(最小値)になったら`JToggleButton`を無効化 -- `UIManager.put("Slider.paintValue", Boolean.TRUE)`で垂直`JSlider`上部に値を常時表示 --- [[JSliderの上部に現在値を常時表示する>Swing/SliderPaintValue]] - `JToggleButton` -- `JToggleButton#setToolTipText("")`で空文字(`JPopupMenu`の幅より短くなる文字)を`ToolTipText`として設定 -- `JToggleButton#createToolTip()`をオーバーライドし`JToolTip`に`HierarchyListener`を追加 -- `JToggleButton#getToolTipLocation()`をオーバーライドし常に`JPopupMenu`の背面に表示されるよう位置を調整 -- `JToggleButton#setEnabled(...)`をオーバーライドし、有効なら`🔊`、無効なら`🔇`にテキストを変更 -- `JSlider`の値が`0`で`JToggleButton`が無効状態でも`JToolTip`は有効 -- 無効状態の`JToggleButton`をクリックした場合も`JPopupMenu`を開くようにするため`ActionListener`ではなく`MouseListener`を追加 --- `MouseListener#mousePressed(...)`で`JPopupMenu`を開く前に`JToggleButton`が無効状態の場合は値をデフォルトの`80`まで戻す --- マウスカーソルが出入りしたとき`JPopupMenu`が非表示の場合は`JPopupMenu`を開くときに無効化した`JToolTip`を有効に戻す - `JToolTip` -- 追加した`HierarchyListener`で`JToolTip`の表示後、重ねて`JPopupMenu`を表示するよう設定 --- `JPopupMenu`の表示後に`JPopupMenu`に追加した上記の`PopupMenuListener#popupMenuWillBecomeVisible(...)`で`EventQueue.invokeLater( () -> ToolTipManager.sharedInstance().setEnabled(false))`を実行して`JToolTip`を無効化 * 参考リンク [#reference] - [[JSliderの上部に現在値を常時表示する>Swing/SliderPaintValue]] - [[JSliderのUIや色を変更する>Swing/VolumeSlider]] - [https://github.com/checkstyle/checkstyle/issues/10837 WhitespaceAround misrecognizes white spaces after emojis · Issue #10837 · checkstyle/checkstyle] -- 「🔊(`U+1F50A`)(`\uD83D\uDD0A`)」などの`0x0A`(`CR/LF`)が誤判定されているようで、このサンプルコードでも`WhitespaceAround`の警告が発生するが`checkstyle-9.0.2`で修正されそう? -- 「🔊(`U+1F50A`)(`\uD83D\uDD0A`)」などの`0x0A`(`CR/LF`)が誤判定されているようで、このサンプルコードでも`WhitespaceAround`の警告が発生するが`checkstyle-9.0.2`で修正され%%そう?%%た * コメント [#comment] #comment #comment