1 | sbool installInternalFrameSwitcher_v2_debug; |
2 | |
3 | svoid installInternalFrameSwitcher_v2(final JDesktopPane desktop) { |
4 | swing { |
5 | new DesktopListener desktopListener; |
6 | desktop.addContainerListener(desktopListener); |
7 | |
8 | new SwitchDispatcher<JInternalFrame>(new DesktopSwitcher(desktop), desktopListener).start(); |
9 | } |
10 | } |
11 | |
12 | sclass JInternalFrameCellRenderer extends DefaultListCellRenderer { |
13 | static int marginW = 20, marginH = 3; |
14 | |
15 | public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { |
16 | Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); |
17 | setText(((JInternalFrame) value).getTitle()); |
18 | setMargin(marginW, marginH, this); |
19 | ret c; |
20 | } |
21 | } |
22 | |
23 | sclass DesktopListener extends ContainerAdapter implements SwitchableComponentsListener<JInternalFrame> { |
24 | private final List<JInternalFrame> frames = new ArrayList<>(); |
25 | private final boolean titleOptional; |
26 | private final InternalFrameAdapter frameActivatedListener = new InternalFrameAdapter() { |
27 | @Override |
28 | public void internalFrameActivated(InternalFrameEvent e) { |
29 | JInternalFrame frame = e.getInternalFrame(); |
30 | frames.remove(frame); |
31 | frames.add(0, frame); |
32 | } |
33 | }; |
34 | |
35 | public DesktopListener() { |
36 | this(false); |
37 | } |
38 | |
39 | public DesktopListener(boolean titleOptional) { |
40 | this.titleOptional = titleOptional; |
41 | } |
42 | |
43 | @Override |
44 | public void componentAdded(final ContainerEvent e) { |
45 | if (e.getChild() instanceof JInternalFrame) { |
46 | final JInternalFrame frame = (JInternalFrame) e.getChild(); |
47 | if (isTitleOptional() || frame.getTitle() != null && !frame.getTitle().trim().isEmpty()) { |
48 | frames.add(frame); |
49 | frame.addInternalFrameListener(frameActivatedListener); |
50 | } |
51 | } |
52 | } |
53 | |
54 | @Override |
55 | public void componentRemoved(final ContainerEvent e) { |
56 | if (e.getChild() instanceof JInternalFrame) { |
57 | JInternalFrame frame = (JInternalFrame) e.getChild(); |
58 | frames.remove(frame); |
59 | frame.removeInternalFrameListener(frameActivatedListener); |
60 | } |
61 | } |
62 | |
63 | private boolean isTitleOptional() { |
64 | return titleOptional; |
65 | } |
66 | |
67 | public List<JInternalFrame> getSwitchableComponentList() { |
68 | return frames; |
69 | } |
70 | } |
71 | |
72 | sclass DesktopSwitcher implements Switcher<JInternalFrame> { |
73 | private final JDesktopPane jDesktopPane; |
74 | private SwitchDialog<JInternalFrame> switchDialog; |
75 | |
76 | public DesktopSwitcher(JDesktopPane jDesktopPane) { |
77 | this.jDesktopPane = jDesktopPane; |
78 | switchDialog = new SwitchDialog<>(SwingUtilities.getWindowAncestor(jDesktopPane), new ArrayList<JInternalFrame>()); |
79 | initMouseListener(); |
80 | } |
81 | |
82 | public void previous(List<JInternalFrame> switchableComponentList) { |
83 | showMenu(switchableComponentList); |
84 | switchDialog.selectPrevious(); |
85 | } |
86 | |
87 | public void next(List<JInternalFrame> switchableComponentList) { |
88 | showMenu(switchableComponentList); |
89 | switchDialog.selectNext(); |
90 | } |
91 | |
92 | public void dismiss() { |
93 | hideMenu(); |
94 | } |
95 | |
96 | @Override |
97 | public JDesktopPane getDesktop() { |
98 | return jDesktopPane; |
99 | } |
100 | |
101 | @Override |
102 | public boolean hasFocus() { |
103 | return switchDialog.hasFocus() || switchDialog.getList().hasFocus(); |
104 | } |
105 | |
106 | public SwitchDialog<JInternalFrame> getSwitchDialog() { |
107 | return switchDialog; |
108 | } |
109 | |
110 | private void showMenu(List<JInternalFrame> switchableComponentList) { |
111 | switchDialog.setSwitchableComponents(switchableComponentList); |
112 | if (!switchableComponentList.isEmpty()) { |
113 | switchDialog.pack(); |
114 | switchDialog.setLocationRelativeTo(jDesktopPane); |
115 | switchDialog.setVisible(true); |
116 | } |
117 | } |
118 | |
119 | private void hideMenu() { |
120 | if (switchDialog != null && switchDialog.isVisible()) { |
121 | pcall { |
122 | JInternalFrame selected = switchDialog.getSelected(); |
123 | if (selected != null) { |
124 | switchDialog.unselect(); |
125 | selected.setSelected(true); |
126 | selected.toFront(); |
127 | } |
128 | } |
129 | switchDialog.dispose(); |
130 | } |
131 | } |
132 | |
133 | private void initMouseListener() { |
134 | switchDialog.getList().addMouseListener(new MouseAdapter() { |
135 | @Override |
136 | public void mouseReleased(final MouseEvent e) { |
137 | hideMenu(); |
138 | } |
139 | }); |
140 | } |
141 | } |
142 | |
143 | sclass SwitchDispatcher<T> implements KeyEventDispatcher { |
144 | private static final KeyStroke nextStroke = KeyStroke.getKeyStroke("ctrl TAB"); |
145 | private static final KeyStroke previousStroke = KeyStroke.getKeyStroke("ctrl shift TAB"); |
146 | |
147 | private final Switcher<T> switcher; |
148 | private final SwitchableComponentsListener<T> desktopListener; |
149 | |
150 | public SwitchDispatcher(Switcher<T> switcher, SwitchableComponentsListener<T> switchableComponentsListener) { |
151 | this.switcher = switcher; |
152 | this.desktopListener = switchableComponentsListener; |
153 | } |
154 | |
155 | public void start() { |
156 | KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this); |
157 | } |
158 | |
159 | public void stop() { |
160 | KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this); |
161 | } |
162 | |
163 | public boolean dispatchKeyEvent(KeyEvent e) { |
164 | boolean shouldSwitch = isDesktopPaneFocused(e.getComponent(), switcher.getDesktop()) |
165 | || switcher.hasFocus(); |
166 | if (!shouldSwitch) { |
167 | return false; |
168 | } |
169 | |
170 | KeyStroke keyStrokeForEvent = KeyStroke.getKeyStrokeForEvent(e); |
171 | boolean next = nextStroke.equals(keyStrokeForEvent); |
172 | boolean previous = previousStroke.equals(keyStrokeForEvent); |
173 | boolean dismiss = e.getKeyCode() == KeyEvent.VK_CONTROL && !e.isControlDown(); |
174 | |
175 | if (installInternalFrameSwitcher_v2_debug) print("Have keystroke: " + keyStrokeForEvent + ", " + next + "/" + previous + "/" + dismiss); |
176 | |
177 | if (next) { |
178 | switcher.next(desktopListener.getSwitchableComponentList()); |
179 | } else if (previous) { |
180 | switcher.previous(desktopListener.getSwitchableComponentList()); |
181 | } else if (dismiss) { |
182 | switcher.dismiss(); |
183 | } |
184 | return next || previous || dismiss; |
185 | } |
186 | |
187 | private boolean isDesktopPaneFocused(Component component, JDesktopPane desktop) { |
188 | return getDesktopPaneParent(component) == desktop; |
189 | } |
190 | |
191 | private JDesktopPane getDesktopPaneParent(Component component) { |
192 | Component parent = component; |
193 | while (parent != null && !(parent instanceof JDesktopPane)) { |
194 | parent = parent.getParent(); |
195 | } |
196 | return (JDesktopPane) parent; |
197 | } |
198 | } |
199 | |
200 | sinterface SwitchableComponentsListener<T> { |
201 | /** |
202 | * Get a list of the components that the user can switch through. |
203 | * Ideally, this would be sorted by the last focused order. |
204 | * |
205 | * @return - list of switchable components of type T |
206 | */ |
207 | List<T> getSwitchableComponentList(); |
208 | } |
209 | |
210 | /** |
211 | * This class will do the actual switching based on the lists of components it receives. |
212 | * |
213 | * @param <T> - The type of the components that can be switched. {@link javax.swing.JInternalFrame} initially but |
214 | * will be extended for tabs and other components. |
215 | */ |
216 | sinterface Switcher<T> { |
217 | /** |
218 | * Trigger this to select the previous component in the list |
219 | * |
220 | * @param switchableComponentList - list of all the switchable components |
221 | */ |
222 | void previous(List<T> switchableComponentList); |
223 | |
224 | /** |
225 | * Trigger this to select the next component in the list |
226 | * |
227 | * @param switchableComponentList - list of all the switchable components |
228 | */ |
229 | void next(List<T> switchableComponentList); |
230 | |
231 | /** |
232 | * This method will hide the dialog that shows all the switchable components if it exists. |
233 | */ |
234 | void dismiss(); |
235 | |
236 | /** |
237 | * Returns the desktop pane on which the switcher is working |
238 | */ |
239 | JDesktopPane getDesktop(); |
240 | |
241 | /** |
242 | * Checks whether the switcher component currently has focus |
243 | * @return - true if the switch dialog is open and has focus; false otherwise |
244 | */ |
245 | boolean hasFocus(); |
246 | } |
247 | |
248 | sclass SwitchDialog<T> extends JDialog { |
249 | private final JList<T> list; |
250 | |
251 | public SwitchDialog(Window owner, List<T> titles) { |
252 | super(owner); |
253 | setUndecorated(true); |
254 | list = new JList<>(new Vector<>(titles)); |
255 | addFocusListener(focusLostListener); |
256 | initList(); |
257 | setContentPane(jCenteredSection(" Frames ", list)); |
258 | pack(); |
259 | } |
260 | |
261 | private void initList() { |
262 | list.setCellRenderer(new JInternalFrameCellRenderer); |
263 | list.setSelectedIndex(0); |
264 | list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
265 | list.addFocusListener(focusLostListener); |
266 | list.setSelectionModel(new DefaultListSelectionModel() { |
267 | public void removeSelectionInterval(int index0, int index1) {} |
268 | public void addSelectionInterval(int index0, int index1) { |
269 | super.setSelectionInterval(index0, index1); |
270 | } |
271 | }); |
272 | } |
273 | |
274 | public void selectNext() { |
275 | int indexToSelect = list.getSelectedIndex() != list.getModel().getSize() - 1 |
276 | ? list.getSelectedIndex() + 1 |
277 | : 0; |
278 | list.setSelectedIndex(indexToSelect); |
279 | } |
280 | |
281 | public void selectPrevious() { |
282 | int indexToSelect = list.getSelectedIndex() != 0 |
283 | ? list.getSelectedIndex() - 1 |
284 | : list.getModel().getSize() - 1; |
285 | list.setSelectedIndex(indexToSelect > -1 ? indexToSelect : 0); |
286 | } |
287 | |
288 | public T getSelected() { |
289 | return list.getSelectedValue(); |
290 | } |
291 | |
292 | public void unselect() { |
293 | list.setSelectedIndex(-1); |
294 | } |
295 | |
296 | public void setSwitchableComponents(List<T> switchableComponents) { |
297 | T oldSelection = list.getSelectedValue(); |
298 | list.setListData(new Vector<>(switchableComponents)); |
299 | list.setSelectedValue(oldSelection, false); |
300 | } |
301 | |
302 | public JList<T> getList() { |
303 | return list; |
304 | } |
305 | |
306 | private final FocusAdapter focusLostListener = new FocusAdapter { |
307 | public void focusLost(FocusEvent e) { |
308 | dispose(); |
309 | } |
310 | }; |
311 | } |
Began life as a copy of #1016825
download show line numbers debug dex old transpilations
Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1016948 |
Snippet name: | installInternalFrameSwitcher_v2 |
Eternal ID of this version: | #1016948/14 |
Text MD5: | f0c4e3ee36cf88d4eaff3456c7eb859f |
Author: | stefan |
Category: | javax / gui |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2018-09-26 17:21:32 |
Source code size: | 10033 bytes / 311 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 338 / 396 |
Version history: | 13 change(s) |
Referenced in: | [show references] |