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: | 570 / 631 |
| Version history: | 13 change(s) |
| Referenced in: | [show references] |