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