JFrameのデフォルトWindow装飾タイトルバー上にJButtonを配置する
Total: 15
, Today: 15
, Yesterday: 0
Posted by aterai at
Last-modified:
Summary
JFrame
がデフォルトWindow
装飾を使用する場合、JButton
などを配置したJWindow
をタイトルバー内のアイコン化ボタン位置に連動して表示します。
Screenshot

Advertisement
Source Code Examples
class ExtraBarPositionHandler extends WindowAdapter implements ComponentListener {
private final JWindow extraBar;
protected ExtraBarPositionHandler(JWindow extraBar) {
this.extraBar = extraBar;
}
@Override public void windowActivated(WindowEvent e) {
extraBar.setBackground(UIManager.getColor("activeCaption"));
}
@Override public void windowDeactivated(WindowEvent e) {
extraBar.setBackground(UIManager.getColor("inactiveCaption"));
}
@Override public void windowOpened(WindowEvent e) {
setLocationRelativeTo(e.getWindow());
extraBar.setVisible(true);
}
@Override public void windowClosed(WindowEvent e) {
extraBar.setVisible(false);
}
@Override public void windowIconified(WindowEvent e) {
extraBar.setVisible(false);
}
@Override public void windowDeiconified(WindowEvent e) {
setLocationRelativeTo(e.getWindow());
extraBar.setVisible(true);
}
@Override public void componentResized(ComponentEvent e) {
// System.out.println("componentResized");
// ???: setLocationRelativeTo(e.getComponent());
EventQueue.invokeLater(() -> setLocationRelativeTo(e.getComponent()));
}
@Override public void componentMoved(ComponentEvent e) {
setLocationRelativeTo(e.getComponent());
}
@Override public void componentShown(ComponentEvent e) {
setLocationRelativeTo(e.getComponent());
extraBar.setVisible(true);
}
@Override public void componentHidden(ComponentEvent e) {
extraBar.setVisible(false);
}
private void setLocationRelativeTo(Component p) {
EventQueue.invokeLater(() -> updateExtraBarLocation(p));
}
private void updateExtraBarLocation(Component p) {
JRootPane root = SwingUtilities.getRootPane(p);
Icon iconifyIcon = UIManager.getIcon("InternalFrame.iconifyIcon");
Insets zeroIns = new Insets(0, 0, 0, 0);
Border bdr = root.getBorder();
Insets ins = bdr == null ? zeroIns : bdr.getBorderInsets(root);
if (p instanceof Frame &&
((Frame) p).getExtendedState() == Frame.MAXIMIZED_BOTH) {
ins = zeroIns;
}
JButton iconifyIconButton = SwingUtils
.descendants(root)
.filter(JButton.class::isInstance)
.map(JButton.class::cast)
.filter(b -> Objects.equals(b.getIcon(), iconifyIcon))
.findFirst()
.orElse(null);
Point pt = iconifyIconButton == null
? new Point()
: iconifyIconButton.getLocation();
SwingUtilities.convertPointToScreen(pt, root);
int x = pt.x - extraBar.getWidth();
int y = p.getY() + ins.top + 1;
extraBar.setLocation(x, y);
}
}
View in GitHub: Java, KotlinDescription
JFrame.setDefaultLookAndFeelDecorated(true)
を指定してJFrame
がデフォルトのWindow
装飾を使用するよう設定- このため
MetalLookAndFeel
以外には未対応で、LookAndFeel
の切り替えにも対応していない
- このため
- デフォルトの
Window
装飾を使用する場合、アイコン化JButton
はUIManager.getIcon("InternalFrame.iconifyIcon")
アイコンが適用されるので、JRootPane
内でこれを使用するJButton
を探し、その位置を取得する - アイコン化
JButton
の位置をスクリーン相対にSwingUtilities.convertPointToScreen(pt, root)
で変換して、Dialog.ModalExclusionType.APPLICATION_EXCLUDE
な子JWindow
をアイコン化JButton
のとなりに配置する- How to add icon to JFrame's title bar - Oracle Forums
JWindow#setAlwaysOnTop(true)
を設定すると別アプリケーションより手前に子JWindow
が表示されてしまう
WindowAdapter
を継承、かつComponentListener
を実装するイベントリスナーを作成して、常に親JFrame
の移動やリサイズに子JWindow
の位置が追従するよう設定- 親
JFrame
のリサイズで子JWindow
のサイズ以下になると、JButton
がタイトルバーの外に表示されるので、これを防ぐためにJFrame#setMinimumSize(...)
で最小サイズを設定 - 親
JFrame
がアイコン化される場合は子JWindow
を非表示に変更する WindowAdapter#windowActivated(...)
が実行されたら子JWindow
の背景色をUIManager.getColor("activeCaption")
、WindowAdapter#windowDeactivated(...)
が実行されたらUIManager.getColor("inactiveCaption")
に切り替える- 最大化解除時に子
JWindow
の位置が正しく更新されない場合がある?ため、ComponentListener#componentResized(...)
が実行されたらEventQueue.invokeLater(...)
を二重に掛けて子JWindow
の位置更新を実行している - 親
JFrame
が最大化される場合はそのJRootPane
の余白が無くなるので、子JWindow
のy
座標をその分修正する必要がある
- 親