Swing/TabbedPaneSelectionFollowsFocus のバックアップ(No.1)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- Swing/TabbedPaneSelectionFollowsFocus へ行く。
- 1 (2023-04-03 (月) 03:48:42)
- category: swing folder: TabbedPaneSelectionFollowsFocus title: JTabbedPaneの選択タブとフォーカスタブを分離する tags: [JTabbedPane, UIManager, Focus, InputMap, LookAndFeel] author: aterai pubdate: 2023-04-03T03:44:40+09:00 description: JTabbedPaneのキー入力によるタブ移動で選択タブとフォーカスタブを一致させるか、または別々に扱うかを設定で切り替えます。 image: https://drive.google.com/uc?id=1mS3Bmh6B7dAqMMBMM3TiCTRLVoHZzxiy
概要
JTabbedPaneのキー入力によるタブ移動で選択タブとフォーカスタブを一致させるか、または別々に扱うかを設定で切り替えます。
Screenshot
Advertisement
サンプルコード
JTabbedPane tabs = new JTabbedPane() {
@Override public void updateUI() {
super.updateUI();
if (getUI() instanceof MetalTabbedPaneUI) {
setUI(new MetalTabbedPaneUI() {
@Override protected void navigateSelectedTab(int direction) {
super.navigateSelectedTab(direction);
focusIndex = getFocusIndex();
}
});
}
}
};
tabs.addChangeListener(e -> {
// System.out.println("Tab selected");
focusIndex = tabs.getSelectedIndex();
// focusIndex = -1;
tabs.repaint();
});
tabs.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
String help1 = "SPACE: selectTabWithFocus";
String help2 = "LEFT: navigateLeft";
String help3 = "RIGHT: navigateRight";
JTextArea textArea = new JTextArea(String.join("\n", help1, help2, help3));
textArea.setEditable(false);
tabs.addTab("help", new JScrollPane(textArea));
IntStream.range(0, 10).forEach(i -> tabs.addTab("title" + i, new JLabel("JLabel" + i)));
InputMap im = tabs.getInputMap(WHEN_FOCUSED);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "selectTabWithFocus");
String key = "TabbedPane.selectionFollowsFocus";
JCheckBox check = new JCheckBox(key, UIManager.getBoolean(key)) {
@Override public void updateUI() {
super.updateUI();
boolean b = UIManager.getLookAndFeelDefaults().getBoolean(key);
setSelected(b);
UIManager.put(key, b);
}
};
check.setFocusable(false);
check.addActionListener(e -> {
boolean b = ((JCheckBox) e.getSource()).isSelected();
UIManager.put(key, b);
SwingUtilities.updateComponentTreeUI(tabs);
});
add(new JLayer<>(tabs, new LayerUI<JTabbedPane>() {
@Override public void paint(Graphics g, JComponent c) {
super.paint(g, c);
if (c instanceof JLayer) {
JLayer<?> layer = (JLayer<?>) c;
JTabbedPane tabbedPane = (JTabbedPane) layer.getView();
if (focusIndex >= 0 && focusIndex != tabbedPane.getSelectedIndex()) {
Rectangle r = tabbedPane.getBoundsAt(focusIndex);
Graphics2D g2 = (Graphics2D) g.create();
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f));
g2.setPaint(Color.RED);
g2.fill(r);
g2.dispose();
}
}
}
}));
View in GitHub: Java, Kotlin解説
TabbedPane.selectionFollowsFocus
:true
MetalLookAndFeel
,WindowsLookAndFeel
,GTKLookAndFeel
などのデフォルトでカーソルキーなどによるタブ選択移動にフォーカスタブも同期して移動- マウスクリックでのタブ選択ではこの設定に関係なく選択タブとフォーカスタブは一致する
WindowsLookAndFeel
などのマウスカーソルによるロールオーバータブとフォーカスタブは別物
TabbedPane.selectionFollowsFocus
:false
NimbusLookAndFeel
のデフォルトだが、NimbusLookAndFeel
はこの設定を無視してカーソルキーなどによるタブ選択移動にフォーカスタブも同期して移動するMetalLookAndFeel
,WindowsLookAndFeel
ではフォーカスタブは選択タブと別々になるがフォーカスタブの描画は選択されていないタブと同じになって見分けがつかない- このサンプルでは
MetalLookAndFeel
を適用した場合のみ、テスト用として選択タブとフォーカスタブを半透明の赤色で描画するJLayer
をJTabbedPane
に設定している - BasicTabbedPaneUI#getFocusIndex()は
protected
、BasicTabbedPaneUI#setFocusIndex(...)
はパッケージプライベートメソッドなので代わりにBasicTabbedPaneUI#navigateSelectedTab(...)
をオーバーライドしてfocusIndex
をJLayer
に伝えている
- このサンプルでは
GTKLookAndFeel
ではフォーカスタブにラウンド矩形のBorder
が表示され、デフォルトでSPACEキーでフォーカスタブが選択タブになるAction
が実行される- このサンプルでは
GTKLookAndFeel
以外の場合でもこのselectTabWithFocus
アクションをSPACEキーで実行するよう以下のようなInputMap
を設定しているInputMap im = tabs.getInputMap(WHEN_FOCUSED); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "selectTabWithFocus");
- このサンプルでは