JFrameのデフォルトWindow装飾タイトルバー上にJButtonを配置する
Total: 475, Today: 1, 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座標をその分修正する必要がある
- 親