JTabbedPaneの選択タブとフォーカスタブを分離する
Total: 617
, Today: 2
, Yesterday: 1
Posted by aterai at
Last-modified:
概要
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");
- このサンプルでは
参考リンク
- BasicTabbedPaneUI#getFocusIndex() (Java Platform SE 8)
- JLayerを使ってJTabbedPaneのタブの挿入位置を描画する
- JTabbedPaneのカーソルキーによるタブ選択遷移の動作を変更する