Skip to content

Instantly share code, notes, and snippets.

@siggemannen
Created June 10, 2023 18:19
Show Gist options
  • Save siggemannen/4affdff4b1892a15e481c626a190efab to your computer and use it in GitHub Desktop.
Save siggemannen/4affdff4b1892a15e481c626a190efab to your computer and use it in GitHub Desktop.
Tab Switcher
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