Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

298
LINES

< > BotCompany Repo | #1019718 // installInternalFrameSwitcher_v3

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (4190L/28K).

1  
// Adapted from: https://github.com/topalavlad/ComponentSwitcher
2  
3  
sbool installInternalFrameSwitcher_v3_debug;
4  
5  
static SwitchDispatcher installInternalFrameSwitcher_v3(final JDesktopPane desktop) {
6  
  ret swing -> SwitchDispatcher {
7  
    new DesktopListener desktopListener;
8  
    desktop.addContainerListener(desktopListener);
9  
  
10  
    SwitchDispatcher dispatcher = new(new DesktopSwitcher(desktop), desktopListener);
11  
    dispatcher.start();
12  
    ret dispatcher;
13  
  };
14  
}
15  
16  
sclass JInternalFrameCellRenderer extends DefaultListCellRenderer {
17  
  static int marginW = 20, marginH = 3;
18  
  
19  
  public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
20  
    Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
21  
    setText(((JInternalFrame) value).getTitle());
22  
    setMargin(marginW, marginH, this);
23  
    ret c;
24  
  }
25  
}
26  
27  
sclass DesktopListener extends ContainerAdapter implements SwitchableComponentsListener {
28  
  private final List frames = new ArrayList<>();
29  
  private final boolean titleOptional;
30  
  private final InternalFrameAdapter frameActivatedListener = new InternalFrameAdapter() {
31  
      @Override
32  
      public void internalFrameActivated(InternalFrameEvent e) {
33  
          JInternalFrame frame = e.getInternalFrame();
34  
          frames.remove(frame);
35  
          frames.add(0, frame);
36  
      }
37  
  };
38  
39  
  public DesktopListener() {
40  
      this(false);
41  
  }
42  
43  
  public DesktopListener(boolean titleOptional) {
44  
      this.titleOptional = titleOptional;
45  
  }
46  
47  
  @Override
48  
  public void componentAdded(final ContainerEvent e) {
49  
      if (e.getChild() instanceof JInternalFrame) {
50  
          final JInternalFrame frame = (JInternalFrame) e.getChild();
51  
          if (isTitleOptional() || frame.getTitle() != null && !frame.getTitle().trim().isEmpty()) {
52  
              frames.add(frame);
53  
              frame.addInternalFrameListener(frameActivatedListener);
54  
          }
55  
      }
56  
  }
57  
58  
  @Override
59  
  public void componentRemoved(final ContainerEvent e) {
60  
      if (e.getChild() instanceof JInternalFrame) {
61  
          JInternalFrame frame = (JInternalFrame) e.getChild();
62  
          frames.remove(frame);
63  
          frame.removeInternalFrameListener(frameActivatedListener);
64  
      }
65  
  }
66  
67  
  private boolean isTitleOptional() {
68  
      return titleOptional;
69  
  }
70  
71  
  public List<JInternalFrame> getSwitchableComponentList() {
72  
      return frames;
73  
  }
74  
}
75  
76  
sclass DesktopSwitcher {
77  
  private final JDesktopPane jDesktopPane;
78  
  private SwitchDialog switchDialog;
79  
  bool instaSwitch;
80  
81  
  public DesktopSwitcher(JDesktopPane jDesktopPane) {
82  
      this.jDesktopPane = jDesktopPane;
83  
      switchDialog = new SwitchDialog(SwingUtilities.getWindowAncestor(jDesktopPane), new ArrayList<JInternalFrame>());
84  
      initMouseListener();
85  
  }
86  
87  
  public void previous(List<JInternalFrame> switchableComponentList) {
88  
      bool vis = isVisible();
89  
      showMenu(switchableComponentList);
90  
      switchDialog.selectPrevious();
91  
      if (instaSwitch && !vis) switchDialog.selectPrevious();
92  
  }
93  
94  
  public void next(List<JInternalFrame> switchableComponentList) {
95  
      bool vis = isVisible();
96  
      showMenu(switchableComponentList);
97  
      switchDialog.selectNext();
98  
      if (instaSwitch && !vis) switchDialog.selectNext();
99  
  }
100  
101  
  public void dismiss() {
102  
      hideMenu();
103  
  }
104  
105  
  public JDesktopPane getDesktop() {
106  
      return jDesktopPane;
107  
  }
108  
109  
  public boolean hasFocus() {
110  
      return switchDialog.hasFocus() || switchDialog.getList().hasFocus();
111  
  }
112  
113  
  public SwitchDialog getSwitchDialog() {
114  
      return switchDialog;
115  
  }
116  
117  
  private void showMenu(List<JInternalFrame> switchableComponentList) {
118  
      switchDialog.setSwitchableComponents(switchableComponentList);
119  
      if (!switchableComponentList.isEmpty()) {
120  
          switchDialog.pack();
121  
          switchDialog.setLocationRelativeTo(jDesktopPane);
122  
          switchDialog.setVisible(true);
123  
      }
124  
  }
125  
  
126  
  bool isVisible() {
127  
    ret switchDialog != null && switchDialog.isVisible();
128  
  }
129  
130  
  private void hideMenu() {
131  
    if (isVisible()) {
132  
      pcall {
133  
        JInternalFrame selected = switchDialog.getSelected();
134  
        if (selected != null) {
135  
            switchDialog.unselect();
136  
            selected.setSelected(true);
137  
            selected.toFront();
138  
        }
139  
      }
140  
      
141  
      switchDialog.dispose();
142  
    }
143  
  }
144  
145  
  private void initMouseListener() {
146  
      switchDialog.getList().addMouseListener(new MouseAdapter() {
147  
          @Override
148  
          public void mouseReleased(final MouseEvent e) {
149  
              hideMenu();
150  
          }
151  
      });
152  
  }
153  
}
154  
155  
sclass SwitchDispatcher implements KeyEventDispatcher {
156  
  private static final KeyStroke nextStroke = KeyStroke.getKeyStroke("ctrl TAB");
157  
  private static final KeyStroke previousStroke = KeyStroke.getKeyStroke("ctrl shift TAB");
158  
159  
  DesktopSwitcher switcher;
160  
  SwitchableComponentsListener desktopListener;
161  
  F0<Bool> shouldSwitch; // check for additional focused components here
162  
  bool instaSwitch = true; // actually switch to previous frame on first Ctrl+Tab (otherwise Ctrl+Tab just brings up the switcher list)
163  
164  
  public SwitchDispatcher(DesktopSwitcher switcher, SwitchableComponentsListener switchableComponentsListener) {
165  
      this.switcher = switcher;
166  
      this.desktopListener = switchableComponentsListener;
167  
  }
168  
169  
  public void start() {
170  
      KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
171  
  }
172  
173  
  public void stop() {
174  
      KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this);
175  
  }
176  
177  
  public boolean dispatchKeyEvent(KeyEvent e) {
178  
      boolean _shouldSwitch = isDesktopPaneFocused(e.getComponent(), switcher.getDesktop())
179  
        || switcher.hasFocus()
180  
        || isTrue(callF(shouldSwitch));
181  
        
182  
      if (!_shouldSwitch) false;
183  
      switcher.instaSwitch = instaSwitch; // copy config
184  
185  
      KeyStroke keyStrokeForEvent = KeyStroke.getKeyStrokeForEvent(e);
186  
      boolean next = nextStroke.equals(keyStrokeForEvent);
187  
      boolean previous = previousStroke.equals(keyStrokeForEvent);
188  
      boolean dismiss = e.getKeyCode() == KeyEvent.VK_CONTROL && !e.isControlDown();
189  
190  
      if (installInternalFrameSwitcher_v3_debug) print("Have keystroke: " + keyStrokeForEvent + ", " + next + "/" + previous + "/" + dismiss);
191  
      
192  
      if (next) {
193  
          switcher.next(desktopListener.getSwitchableComponentList());
194  
      } else if (previous) {
195  
          switcher.previous(desktopListener.getSwitchableComponentList());
196  
      } else if (dismiss) {
197  
          switcher.dismiss();
198  
      }
199  
      return next || previous || dismiss;
200  
  }
201  
202  
  private boolean isDesktopPaneFocused(Component component, JDesktopPane desktop) {
203  
      return getDesktopPaneParent(component) == desktop;
204  
  }
205  
206  
  private JDesktopPane getDesktopPaneParent(Component component) {
207  
      Component parent = component;
208  
      while (parent != null && !(parent instanceof JDesktopPane)) {
209  
          parent = parent.getParent();
210  
      }
211  
      return (JDesktopPane) parent;
212  
  }
213  
}
214  
215  
sinterface SwitchableComponentsListener {
216  
  /**
217  
   * Get a list of the components that the user can switch through.
218  
   * Ideally, this would be sorted by the last focused order.
219  
   *
220  
   * @return - list of switchable components of type JInternalFrame
221  
   */
222  
  List<JInternalFrame> getSwitchableComponentList();
223  
}
224  
225  
sclass SwitchDialog extends JDialog {
226  
  private final JList<JInternalFrame> list;
227  
228  
  public SwitchDialog(Window owner, List<JInternalFrame> titles) {
229  
    super(owner);
230  
    setUndecorated(true);
231  
    list = new JList<>(new Vector<>(titles));
232  
    addFocusListener(focusLostListener);
233  
    onWindowClosed(this, r {
234  
      /* AWT keeps references to stale components around! e.g.
235  
          last in java.util.LinkedList
236  
           keyEventDispatchers in java.awt.DefaultKeyboardFocusManager
237  
            value in sun.awt.MostRecentKeyValue
238  
             shadowMostRecentKeyValue in sun.awt.AppContext
239  
              appContext in java.awt.EventQueue
240  
         So we free the links to JInternalFrames: */
241  
      list.setModel(new DefaultListModel);
242  
    });
243  
    initList();
244  
    setContentPane(jCenteredSection(" Frames ", list));
245  
    pack();
246  
  }
247  
248  
  private void initList() {
249  
    list.setCellRenderer(new JInternalFrameCellRenderer);
250  
    list.setSelectedIndex(0);
251  
    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
252  
    list.addFocusListener(focusLostListener);
253  
    list.setSelectionModel(new DefaultListSelectionModel() {
254  
      public void removeSelectionInterval(int index0, int index1) {}
255  
      public void addSelectionInterval(int index0, int index1) {
256  
        super.setSelectionInterval(index0, index1);
257  
      }
258  
    });
259  
  }
260  
261  
  public void selectNext() {
262  
    int indexToSelect = list.getSelectedIndex() != list.getModel().getSize() - 1
263  
            ? list.getSelectedIndex() + 1
264  
            : 0;
265  
    setSelectedIndexAndScroll(list, indexToSelect);
266  
  }
267  
268  
  public void selectPrevious() {
269  
    int indexToSelect = list.getSelectedIndex() != 0
270  
            ? list.getSelectedIndex() - 1
271  
            : list.getModel().getSize() - 1;
272  
    setSelectedIndexAndScroll(list, indexToSelect > -1 ? indexToSelect : 0);
273  
  }
274  
275  
  public JInternalFrame getSelected() {
276  
    return list.getSelectedValue();
277  
  }
278  
  
279  
  public void unselect() {
280  
    list.setSelectedIndex(-1);
281  
  }
282  
283  
  public void setSwitchableComponents(List<JInternalFrame> switchableComponents) {
284  
    JInternalFrame oldSelection = list.getSelectedValue();
285  
    list.setListData(new Vector<>(switchableComponents));
286  
    list.setSelectedValue(oldSelection, false);
287  
  }
288  
289  
  public JList<JInternalFrame> getList() {
290  
    return list;
291  
  }
292  
293  
  private final FocusAdapter focusLostListener = new FocusAdapter {
294  
    public void focusLost(FocusEvent e) {
295  
      dispose();
296  
    }
297  
  };
298  
}

Author comment

Began life as a copy of #1016948

download  show line numbers  debug dex  old transpilations   

Travelled to 11 computer(s): bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1019718
Snippet name: installInternalFrameSwitcher_v3
Eternal ID of this version: #1019718/19
Text MD5: 66ed36a75126910349e8cff717f752c5
Transpilation MD5: cfaaec6151b77f21a70f02310626506b
Author: stefan
Category: javax / gui
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-10-06 14:03:30
Source code size: 10010 bytes / 298 lines
Pitched / IR pitched: No / No
Views / Downloads: 274 / 386
Version history: 18 change(s)
Referenced in: [show references]