Created
June 10, 2023 18:19
-
-
Save siggemannen/4affdff4b1892a15e481c626a190efab to your computer and use it in GitHub Desktop.
Tab Switcher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.sigge.test; | |
import java.awt.BorderLayout; | |
import java.awt.Component; | |
import java.awt.Dimension; | |
import java.awt.event.KeyEvent; | |
import java.awt.event.KeyListener; | |
import java.awt.event.WindowEvent; | |
import java.awt.event.WindowFocusListener; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Stack; | |
import javax.swing.JButton; | |
import javax.swing.JDialog; | |
import javax.swing.JFrame; | |
import javax.swing.JList; | |
import javax.swing.JPanel; | |
import javax.swing.JScrollPane; | |
import javax.swing.JSplitPane; | |
import javax.swing.JTabbedPane; | |
import javax.swing.JTextArea; | |
import javax.swing.ListSelectionModel; | |
import javax.swing.WindowConstants; | |
import com.sigge.test.TabSwitcherTest.LowLevelKeyListener.ILowLevelKeyPressedEvent; | |
import com.sigge.test.TabSwitcherTest.LowLevelKeyListener.ILowLevelKeyReleasedEvent; | |
import com.sigge.test.TabSwitcherTest.LowLevelKeyListener.LowLevelKeyEventListener; | |
/** | |
* Test | |
*/ | |
public class TabSwitcherTest | |
{ | |
public static void main(String[] args) | |
{ | |
JFrame f = new JFrame("Test"); | |
f.setSize(new Dimension(500, 500)); | |
f.setPreferredSize(new Dimension(500, 500)); | |
JTabbedPane jt = new JTabbedPane(); | |
jt.setFocusable(false); | |
jt.setFocusTraversalKeysEnabled(false); | |
LowLevelKeyListener l = new LowLevelKeyListener(); | |
JPanel panel1 = createTab(f, "Test1", l); | |
JPanel panel2 = createTab(f, "Test2", l); | |
jt.addTab("Tab1", panel1); | |
jt.addTab("Tab2", panel2); | |
f.getContentPane().add(jt); | |
Integer[] lm = new Integer[] {0, 1}; | |
Stack<TabsDialog> dialogs = new Stack<>(); | |
l.addListener(new LowLevelKeyEventListener() | |
{ | |
private int state = 0; | |
@Override | |
public boolean released(ILowLevelKeyReleasedEvent event) | |
{ | |
if (!event.isCtrlPressed() && state > 0) | |
{ | |
//Select new tab... | |
if (dialogs.size() > 0 && state > 1) | |
{ | |
TabsDialog td = dialogs.peek(); | |
Integer tc = (Integer) td.list.getSelectedValue(); | |
dispose(dialogs); | |
if (tc != null) | |
{ | |
jt.setSelectedIndex(tc); | |
} | |
} | |
state = 0; | |
dispose(dialogs); | |
return false; | |
} | |
return false; | |
} | |
@Override | |
public boolean pressed(ILowLevelKeyPressedEvent event) | |
{ | |
switch (state) | |
{ | |
case 0: | |
if (isCtrl(event.keyPressed())) | |
{ | |
state = 1; | |
} | |
break; | |
case 1: | |
if (isTab(event.keyPressed())) | |
{ | |
TabsDialog td = createDialog(f, lm); | |
td.list.setFocusTraversalKeysEnabled(false); | |
td.setFocusTraversalKeysEnabled(false); | |
td.list.addKeyListener(l); | |
td.list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); | |
td.list.setSelectedValue(jt.getSelectedIndex() == 0 ? 1 : 0, true); | |
dialogs.push(td); | |
state = 2; | |
td.addWindowFocusListener(new WindowFocusListener() | |
{ | |
@Override | |
public void windowLostFocus(WindowEvent e) | |
{ | |
dispose(dialogs); | |
} | |
@Override | |
public void windowGainedFocus(WindowEvent e) | |
{ | |
} | |
}); | |
td.setVisible(true); | |
return false; | |
} | |
break; | |
case 2: | |
if (event.isTabPressed()) | |
{ | |
if (event.isShiftPressed()) | |
{ | |
prevLine(); | |
} | |
else | |
{ | |
nextLine(); | |
} | |
return false; | |
} | |
case 3: | |
} | |
return false; | |
} | |
private void dispose(Stack<TabsDialog> dialogs) | |
{ | |
if (dialogs.size() > 0) //dispose previous | |
{ | |
TabsDialog jd = dialogs.pop(); | |
jd.setVisible(false); | |
jd.dispose(); | |
} | |
} | |
private void prevLine() | |
{ | |
JList tabList = dialogs.peek().list; | |
int idx = tabList.getSelectedIndex(); | |
idx--; | |
if (idx < 0) | |
{ | |
idx = lm.length - 1; | |
} | |
tabList.setSelectedIndex(idx); | |
tabList.ensureIndexIsVisible(idx); | |
} | |
private void nextLine() | |
{ | |
JList tabList = dialogs.peek().list; | |
int idx = tabList.getSelectedIndex(); | |
idx++; | |
if (idx == lm.length) | |
{ | |
idx = 0; | |
} | |
tabList.setSelectedIndex(idx); | |
tabList.ensureIndexIsVisible(idx); | |
} | |
private boolean isTab(String key) | |
{ | |
return "Tab".equals(key); | |
} | |
private boolean isCtrl(String key) | |
{ | |
return "Ctrl".equals(key); | |
} | |
}); | |
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); | |
f.setLocationRelativeTo(null); | |
f.pack(); | |
f.setVisible(true); | |
} | |
private static JPanel createTab(JFrame f, String name, LowLevelKeyListener l) | |
{ | |
JPanel jp = new JPanel(new BorderLayout()); | |
jp.setSize(f.getSize()); | |
jp.setMinimumSize(f.getSize()); | |
jp.addKeyListener(l); | |
JSplitPane comp = new JSplitPane(); | |
JTextArea comp2 = new JTextArea(); | |
comp2.setText(name); | |
JScrollPane rtext = new JScrollPane(comp2); | |
comp.add(rtext, JSplitPane.LEFT); | |
JButton button = new JButton("Dummy"); | |
comp.add(button, JSplitPane.RIGHT); | |
comp2.setFocusTraversalKeysEnabled(false); | |
button.setFocusTraversalKeysEnabled(false); | |
comp2.requestFocusInWindow(); | |
comp.setSize(jp.getSize()); | |
comp.setMinimumSize(jp.getSize()); | |
comp.setOrientation(JSplitPane.VERTICAL_SPLIT); | |
jp.add(comp); | |
button.addKeyListener(l); | |
comp2.addKeyListener(l); | |
jp.setFocusTraversalKeysEnabled(false); | |
comp2.setSize(300, 200); | |
comp.setDividerLocation(0.5); | |
return jp; | |
} | |
private static <E> TabsDialog createDialog(JFrame frame, E... items) | |
{ | |
return new TabsDialog(frame, true, items); | |
} | |
private static class TabsDialog<E> extends JDialog | |
{ | |
private final JList list; | |
public TabsDialog(JFrame frame, boolean modal, E... items) | |
{ | |
super(frame, modal); | |
list = new JList(items); | |
list.setMinimumSize(new Dimension(300, 300)); | |
this.setMinimumSize(new Dimension(300, 300)); | |
getContentPane().add(list); | |
setLocationRelativeTo(null); | |
pack(); | |
} | |
} | |
public static class LowLevelKeyListener implements KeyListener | |
{ | |
private boolean ctrlPressed; | |
private boolean tabPressed; | |
private boolean shiftPressed; | |
private final List<LowLevelKeyEventListener> listeners = new ArrayList<>(); | |
public LowLevelKeyListener() | |
{ | |
} | |
public void addListener(LowLevelKeyEventListener listener) | |
{ | |
listeners.add(listener); | |
} | |
public void removeListener(LowLevelKeyEventListener listener) | |
{ | |
listeners.remove(listener); | |
} | |
@Override | |
public void keyTyped(KeyEvent e) | |
{ | |
} | |
@Override | |
public void keyPressed(KeyEvent e) | |
{ | |
//LOGGER.debug("Pressed"); | |
if (e.isConsumed()) | |
{ | |
return; | |
} | |
String keyText = KeyEvent.getKeyText(e.getKeyCode()); | |
if ("Ctrl".equals(keyText)) | |
{ | |
ctrlPressed = true; | |
} | |
else if ("Shift".equals(keyText)) | |
{ | |
shiftPressed = true; | |
} | |
else if ("Tab".equals(keyText)) | |
{ | |
tabPressed = true; | |
} | |
if (!e.isControlDown() && ctrlPressed) | |
{ | |
ctrlPressed = false; | |
} | |
listeners.stream().forEachOrdered(l -> l.pressed(new LowLevelKeyPressedEvent(e.getComponent(), keyText, ctrlPressed, tabPressed, shiftPressed))); | |
//LOGGER.debug("Done pressed"); | |
} | |
@Override | |
public void keyReleased(KeyEvent e) | |
{ | |
// LOGGER.debug("Released"); | |
String keyText = KeyEvent.getKeyText(e.getKeyCode()); | |
if ("Ctrl".equals(keyText)) | |
{ | |
ctrlPressed = false; | |
} | |
else if ("Shift".equals(keyText)) | |
{ | |
shiftPressed = false; | |
} | |
else if ("Tab".equals(keyText)) | |
{ | |
tabPressed = false; | |
} | |
listeners.stream().forEachOrdered(l -> l.released(new LowLevelKeyReleasedEvent(e.getComponent(), keyText, ctrlPressed, tabPressed, shiftPressed))); | |
//LOGGER.debug("Done released"); | |
} | |
public static interface LowLevelKeyEventListener | |
{ | |
/** | |
* Process a key release | |
* | |
* @param event low level event | |
* @return true if further AWT processing of events should be finished | |
*/ | |
boolean released(ILowLevelKeyReleasedEvent event); | |
/** | |
* Process a key press | |
* | |
* @param event low level event | |
* @return true if further AWT processing of events should be finished | |
*/ | |
boolean pressed(ILowLevelKeyPressedEvent event); | |
} | |
public interface ILowLevelKeyPressedEvent extends ILowLevelKeyEvent | |
{ | |
String keyPressed(); | |
} | |
public interface ILowLevelKeyReleasedEvent extends ILowLevelKeyEvent | |
{ | |
String keyReleased(); | |
} | |
private class LowLevelKeyReleasedEvent extends ALowLevelKeyEvent implements ILowLevelKeyReleasedEvent | |
{ | |
private final String keyReleased; | |
private final Component source; | |
LowLevelKeyReleasedEvent(Component source, String keyReleased, boolean ctrlPressed, boolean tabPressed, boolean shiftPressed) | |
{ | |
this.source = source; | |
this.keyReleased = keyReleased; | |
this.ctrlPressedInner = ctrlPressed; | |
this.tabPressedInner = tabPressed; | |
this.shiftPressedInner = shiftPressed; | |
} | |
@Override | |
public String keyReleased() | |
{ | |
return keyReleased; | |
} | |
@Override | |
public String toString() | |
{ | |
return String.format("Released, source: %s: %s, ctrl: %b tab: %b shift: %b", source, keyReleased, ctrlPressedInner, tabPressedInner, shiftPressedInner); | |
} | |
} | |
private class LowLevelKeyPressedEvent extends ALowLevelKeyEvent implements ILowLevelKeyPressedEvent | |
{ | |
private final String keyPressed; | |
private final Component source; | |
LowLevelKeyPressedEvent(Component source, String keyPressed, boolean ctrlPressed, boolean tabPressed, boolean shiftPressed) | |
{ | |
this.source = source; | |
this.keyPressed = keyPressed; | |
this.ctrlPressedInner = ctrlPressed; | |
this.tabPressedInner = tabPressed; | |
this.shiftPressedInner = shiftPressed; | |
} | |
@Override | |
public String keyPressed() | |
{ | |
return keyPressed; | |
} | |
@Override | |
public String toString() | |
{ | |
return String.format("Pressed, source: %s, pressed: %s, ctrl: %b tab: %b shift: %b", source, keyPressed, ctrlPressedInner, tabPressedInner, shiftPressedInner); | |
} | |
} | |
private abstract class ALowLevelKeyEvent implements ILowLevelKeyEvent | |
{ | |
protected boolean ctrlPressedInner; | |
protected boolean tabPressedInner; | |
protected boolean shiftPressedInner; | |
@Override | |
public boolean isCtrlPressed() | |
{ | |
return ctrlPressedInner; | |
} | |
@Override | |
public boolean isTabPressed() | |
{ | |
return tabPressedInner; | |
} | |
@Override | |
public boolean isShiftPressed() | |
{ | |
return shiftPressedInner; | |
} | |
} | |
private interface ILowLevelKeyEvent | |
{ | |
public boolean isCtrlPressed(); | |
public boolean isTabPressed(); | |
public boolean isShiftPressed(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment