Swing/ExtraToolBarInTitleBar の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/ExtraToolBarInTitleBar へ行く。
- Swing/ExtraToolBarInTitleBar の差分を削除
---
category: swing
folder: ExtraToolBarInTitleBar
title: JFrameのデフォルトWindow装飾タイトルバー上にJButtonを配置する
tags: [JFrame, JWindow, JButton, WindowListener]
author: aterai
pubdate: 2025-09-22T16:47:35+09:00
description: JFrameがデフォルトWindow装飾を使用する場合、JButtonなどを配置したJWindowをタイトルバー内のアイコン化ボタン位置に連動して表示します。
image: https://drive.google.com/uc?id=1xtEpIPfhARrCvMt0IzawZ448jeA1YefK
---
* Summary [#summary]
`JFrame`がデフォルト`Window`装飾を使用する場合、`JButton`などを配置した`JWindow`をタイトルバー内のアイコン化ボタン位置に連動して表示します。
#download(https://drive.google.com/uc?id=1xtEpIPfhARrCvMt0IzawZ448jeA1YefK)
* Source Code Examples [#sourcecode]
#code(link){{
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);
}
}
}}
* Description [#description]
- `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`のとなりに配置する
-- [https://forums.oracle.com/ords/apexds/post/how-to-add-icon-to-jframe-s-title-bar-5581 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`座標をその分修正する必要がある
* Reference [#reference]
- [https://forums.oracle.com/ords/apexds/post/how-to-add-icon-to-jframe-s-title-bar-5581 How to add icon to JFrame's title bar - Oracle Forums]
- [[JFrameがデフォルトのウィンドウ装飾を使用する場合のタイトルバー背景色を変更>Swing/WindowTitleBackground]]
* Comment [#comment]
#comment
#comment