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 | } |
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: | 438 / 605 |
Version history: | 18 change(s) |
Referenced in: | [show references] |