Swing/NotPopupOverlapTaskBar の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/NotPopupOverlapTaskBar へ行く。
- Swing/NotPopupOverlapTaskBar の差分を削除
--- category: swing folder: NotPopupOverlapTaskBar title: JPopupMenuがTaskBarと重ならないように表示位置を調整する tags: [JPopupMenu, TaskBar] author: aterai pubdate: 2023-01-30T06:05:32+09:00 description: JPopupMenuがTaskBarと重なる場合、マウスカーソル位置がその右下隅になるよう表示位置を調整し、内部のJMenuItemの配置も反転します。 image: https://drive.google.com/uc?id=1qKrSZUuSGcLCP3cebYbv07ITdlv5w2_I --- * 概要 [#summary] `JPopupMenu`が`TaskBar`と重なる場合、マウスカーソル位置がその右下隅になるよう表示位置を調整し、内部の`JMenuItem`の配置も反転します。 #download(https://drive.google.com/uc?id=1qKrSZUuSGcLCP3cebYbv07ITdlv5w2_I) * サンプルコード [#sourcecode] #code(link){{ List<Component> list = Arrays.asList( new JMenuItem("JMenuItem 1"), new JMenuItem("JMenuItem 2"), new JMenuItem("JMenuItem 3"), new JMenuItem("JMenuItem 4"), new JMenuItem("JMenuItem 5")); JPopupMenu popup = new JPopupMenu() { @Override public void show(Component c, int x, int y) { Point popupLocation = getInvokerOrigin(x, y, c.getLocationOnScreen()); Rectangle scrBounds = getScreenBounds(c, popupLocation); Dimension popupSize = getPreferredSize(); long popupBottomY = (long) popupLocation.y + (long) popupSize.height; Point p = new Point(x, y); removeAll(); if (popupBottomY > scrBounds.y + scrBounds.height) { p.translate(-popupSize.width, -popupSize.height); for (int i = list.size() - 1; i >= 0; i--) { add(list.get(i)); } } else { list.forEach(this::add); } super.show(c, p.x, p.y); } }; list.forEach(popup::add); setComponentPopupMenu(popup); }} * 解説 [#explanation] - `JPopupMenu`の親コンポーネントが表示されている`GraphicsConfiguration`を取得 -- `JPopupMenu#getCurrentGraphicsConfiguration(Point)`を参考 #code{{ GraphicsConfiguration getCurrentGraphicsConfiguration2(Component c, Point p) { GraphicsConfiguration gc = null; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] gd = ge.getScreenDevices(); for (GraphicsDevice graphicsDevice : gd) { if (graphicsDevice.getType() == GraphicsDevice.TYPE_RASTER_SCREEN) { GraphicsConfiguration dgc = graphicsDevice.getDefaultConfiguration(); if (dgc.getBounds().contains(p)) { gc = dgc; break; } } } // If not found, and we have invoker, ask invoker about his gc if (gc == null && c != null) { gc = c.getGraphicsConfiguration(); } return gc; } }} - 取得した`GraphicsConfiguration`からスクリーン領域を取得し`TaskBar`などの余白を除去 -- デフォルトの`JPopupMenu`では`JPopupMenu#adjustPopupLocationToFitScreen(int xPosition, int yPosition)`メソッド内で`((SunToolkit) Toolkit.getDefaultToolkit()).canPopupOverlapTaskBar()`が`false`の場合`TaskBar`などの余白を除去してスクリーン領域サイズを計算しているが`Windows 10`環境では常に`true`でこれを変更する方法が不明 -- このためこのサンプルでは以下のように`((SunToolkit) Toolkit.getDefaultToolkit()).canPopupOverlapTaskBar()`を無視して`TaskBar`領域をスクリーン領域サイズから除去している -- デフォルトの`JPopupMenu`では`JPopupMenu#adjustPopupLocationToFitScreen(int xPosition, int yPosition)`メソッド内で`( (SunToolkit) Toolkit.getDefaultToolkit() ).canPopupOverlapTaskBar()`が`false`の場合`TaskBar`などの余白を除去してスクリーン領域サイズを計算しているが`Windows 10`環境では常に`true`でこれを変更する方法が不明 -- このためこのサンプルでは以下のように`( (SunToolkit) Toolkit.getDefaultToolkit() ).canPopupOverlapTaskBar()`を無視して`TaskBar`領域をスクリーン領域サイズから除去している #code{{ private static Rectangle getScreenBounds(Component c, Point popupLocation) { Rectangle scrBounds; GraphicsConfiguration gc = getCurrentGraphicsConfiguration2(c, popupLocation); Toolkit toolkit = Toolkit.getDefaultToolkit(); if (gc != null) { // If we have GraphicsConfiguration use it to get screen bounds scrBounds = gc.getBounds(); } else { scrBounds = new Rectangle(toolkit.getScreenSize()); } Insets scrInsets = toolkit.getScreenInsets(gc); scrBounds.x += scrInsets.left; scrBounds.y += scrInsets.top; scrBounds.width -= scrInsets.left + scrInsets.right; scrBounds.height -= scrInsets.top + scrInsets.bottom; return scrBounds; } }} - `JPopupMenu#show(...)`をオーバーライドして取得したスクリーン領域の外に`JPopupMenu`の下辺が表示される場合は、マウスカーソル位置が`JPopupMenu`の右下隅になるよう表示位置を調整 -- 左上隅から右下隅にマウスカーソル配置が変更されるので`JPopupMenu`内部の`JMenuItem`の配置もマウスカーソル移動距離が短くなるよう昇順から降順に変更 -- `JPopupMenu#show(...)`内などで参照されている`popupPostionFixDisabled`は`popupPositionFixDisabled`の`typo`? * 参考リンク [#reference] - [[JPopupMenuの表示を親コンポーネント領域内のみに制限する>Swing/AdjustPopupLocation]] * コメント [#comment] #comment #comment