Skip to content

Commit 93f4978

Browse files
move tooltip management to EnitorPanel.TooltipManager
add/remove EntryTooltip's global listeners on open/close reset TooltipManager on key presses
1 parent 44b9fe9 commit 93f4978

File tree

2 files changed

+184
-142
lines changed

2 files changed

+184
-142
lines changed

enigma-swing/src/main/java/org/quiltmc/enigma/gui/panel/EditorPanel.java

Lines changed: 152 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import java.awt.Component;
2323
import java.awt.GridBagConstraints;
2424
import java.awt.KeyboardFocusManager;
25+
import java.awt.Toolkit;
26+
import java.awt.event.AWTEventListener;
2527
import java.awt.event.ActionEvent;
2628
import java.awt.event.FocusAdapter;
2729
import java.awt.event.FocusEvent;
@@ -49,65 +51,11 @@
4951
import static java.awt.event.InputEvent.CTRL_DOWN_MASK;
5052

5153
public class EditorPanel extends BaseEditorPanel {
52-
private static final int MOUSE_STOPPED_MOVING_DELAY = 100;
53-
5454
private final NavigatorPanel navigatorPanel;
5555
private final EnigmaQuickFindToolBar quickFindToolBar = new EnigmaQuickFindToolBar();
5656
private final EditorPopupMenu popupMenu;
5757

58-
// DIY tooltip because JToolTip can't be moved or resized
59-
private final EntryTooltip entryTooltip = new EntryTooltip(this.gui);
60-
private final WindowAdapter guiLostFocusListener;
61-
62-
@Nullable
63-
private Token lastMouseTargetToken;
64-
65-
// avoid finding the mouse entry every mouse movement update
66-
private final Timer mouseStoppedMovingTimer = new Timer(MOUSE_STOPPED_MOVING_DELAY, e -> {
67-
if (Config.editor().entryTooltip.enable.value()) {
68-
this.consumeEditorMouseTarget(
69-
(token, entry, resolvedParent) -> {
70-
this.hideTooltipTimer.stop();
71-
if (this.entryTooltip.isVisible()) {
72-
this.showTooltipTimer.stop();
73-
74-
if (!token.equals(this.lastMouseTargetToken)) {
75-
this.lastMouseTargetToken = token;
76-
this.openTooltip(entry, resolvedParent);
77-
}
78-
} else {
79-
this.lastMouseTargetToken = token;
80-
this.showTooltipTimer.start();
81-
}
82-
},
83-
() -> consumeMousePositionIn(
84-
this.entryTooltip.getContentPane(),
85-
(absolute, relative) -> this.hideTooltipTimer.stop(),
86-
absolute -> {
87-
this.lastMouseTargetToken = null;
88-
this.showTooltipTimer.stop();
89-
this.hideTooltipTimer.start();
90-
}
91-
)
92-
);
93-
}
94-
});
95-
96-
private final Timer showTooltipTimer = new Timer(
97-
ToolTipManager.sharedInstance().getInitialDelay() - MOUSE_STOPPED_MOVING_DELAY, e -> {
98-
this.consumeEditorMouseTarget((token, entry, resolvedParent) -> {
99-
if (token.equals(this.lastMouseTargetToken)) {
100-
this.entryTooltip.setVisible(true);
101-
this.openTooltip(entry, resolvedParent);
102-
}
103-
});
104-
}
105-
);
106-
107-
private final Timer hideTooltipTimer = new Timer(
108-
ToolTipManager.sharedInstance().getDismissDelay() - MOUSE_STOPPED_MOVING_DELAY,
109-
e -> this.entryTooltip.close()
110-
);
58+
private final TooltipManager tooltipManager = new TooltipManager();
11159

11260
private final List<EditorActionListener> listeners = new ArrayList<>();
11361

@@ -142,17 +90,6 @@ public void focusLost(FocusEvent e) {
14290
this.popupMenu = new EditorPopupMenu(this, gui);
14391
this.editor.setComponentPopupMenu(this.popupMenu.getUi());
14492

145-
this.entryTooltip.addCloseListener(this::onTooltipClose);
146-
this.guiLostFocusListener = new WindowAdapter() {
147-
@Override
148-
public void windowLostFocus(WindowEvent e) {
149-
if (e.getOppositeWindow() != EditorPanel.this.entryTooltip) {
150-
EditorPanel.this.entryTooltip.close();
151-
}
152-
}
153-
};
154-
this.gui.getFrame().addWindowFocusListener(this.guiLostFocusListener);
155-
15693
this.editor.addMouseListener(new MouseAdapter() {
15794
@Override
15895
public void mouseClicked(MouseEvent e1) {
@@ -162,11 +99,6 @@ public void mouseClicked(MouseEvent e1) {
16299
}
163100
}
164101

165-
@Override
166-
public void mousePressed(MouseEvent mouseEvent) {
167-
EditorPanel.this.entryTooltip.close();
168-
}
169-
170102
@Override
171103
public void mouseReleased(MouseEvent e1) {
172104
switch (e1.getButton()) {
@@ -180,35 +112,8 @@ public void mouseReleased(MouseEvent e1) {
180112
}
181113
});
182114

183-
this.editor.addMouseMotionListener(new MouseAdapter() {
184-
@Override
185-
public void mouseMoved(MouseEvent e) {
186-
if (!EditorPanel.this.entryTooltip.hasRepopulated()) {
187-
EditorPanel.this.mouseStoppedMovingTimer.restart();
188-
}
189-
}
190-
});
191-
192115
this.editor.addCaretListener(event -> this.onCaretMove(event.getDot()));
193116

194-
this.editorScrollPane.getViewport().addChangeListener(e -> this.entryTooltip.close());
195-
196-
this.mouseStoppedMovingTimer.setRepeats(false);
197-
this.showTooltipTimer.setRepeats(false);
198-
this.hideTooltipTimer.setRepeats(false);
199-
200-
this.entryTooltip.setVisible(false);
201-
202-
this.entryTooltip.addMouseMotionListener(new MouseAdapter() {
203-
@Override
204-
public void mouseMoved(MouseEvent e) {
205-
if (Config.editor().entryTooltip.interactable.value()) {
206-
EditorPanel.this.mouseStoppedMovingTimer.stop();
207-
EditorPanel.this.hideTooltipTimer.stop();
208-
}
209-
}
210-
});
211-
212117
this.editor.addKeyListener(new KeyAdapter() {
213118
@Override
214119
public void keyTyped(KeyEvent event) {
@@ -246,21 +151,6 @@ public void keyTyped(KeyEvent event) {
246151
this.ui.putClientProperty(EditorPanel.class, this);
247152
}
248153

249-
private void onTooltipClose() {
250-
this.lastMouseTargetToken = null;
251-
this.mouseStoppedMovingTimer.stop();
252-
this.showTooltipTimer.stop();
253-
this.hideTooltipTimer.stop();
254-
}
255-
256-
private void openTooltip(Entry<?> target, boolean inherited) {
257-
final Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
258-
final Component eventReceiver = focusOwner != null && isDescendingFrom(focusOwner, this.gui.getFrame())
259-
? focusOwner : null;
260-
261-
this.entryTooltip.open(target, inherited, eventReceiver);
262-
}
263-
264154
public void onRename(boolean isNewMapping) {
265155
this.navigatorPanel.updateAllTokenTypes();
266156
if (isNewMapping) {
@@ -305,7 +195,7 @@ public static EditorPanel byUi(Component ui) {
305195
@Override
306196
public void destroy() {
307197
super.destroy();
308-
this.gui.getFrame().removeWindowFocusListener(this.guiLostFocusListener);
198+
this.tooltipManager.removeExternalListeners();
309199
}
310200

311201
public NavigatorPanel getNavigatorPanel() {
@@ -362,13 +252,13 @@ protected void setCursorReference(EntryReference<Entry<?>, Entry<?>> ref) {
362252
@Override
363253
public void offsetEditorZoom(int zoomAmount) {
364254
super.offsetEditorZoom(zoomAmount);
365-
this.entryTooltip.setZoom(zoomAmount);
255+
this.tooltipManager.entryTooltip.setZoom(zoomAmount);
366256
}
367257

368258
@Override
369259
public void resetEditorZoom() {
370260
super.resetEditorZoom();
371-
this.entryTooltip.resetZoom();
261+
this.tooltipManager.entryTooltip.resetZoom();
372262
}
373263

374264
public void addListener(EditorActionListener listener) {
@@ -402,4 +292,150 @@ public void actionPerformed(JTextComponent target, SyntaxDocument sDoc, int dot,
402292

403293
this.popupMenu.getButtonKeyBinds().forEach((key, button) -> putKeyBindAction(key, this.editor, e -> button.doClick()));
404294
}
295+
296+
private class TooltipManager {
297+
static final int MOUSE_STOPPED_MOVING_DELAY = 100;
298+
299+
// DIY tooltip because JToolTip can't be moved or resized
300+
private final EntryTooltip entryTooltip = new EntryTooltip(EditorPanel.this.gui);
301+
302+
private final WindowAdapter guiFocusListener = new WindowAdapter() {
303+
@Override
304+
public void windowLostFocus(WindowEvent e) {
305+
if (e.getOppositeWindow() != TooltipManager.this.entryTooltip) {
306+
TooltipManager.this.entryTooltip.close();
307+
}
308+
}
309+
};
310+
311+
private final AWTEventListener globalKeyListener = e -> {
312+
if (e.getID() == KeyEvent.KEY_TYPED || e.getID() == KeyEvent.KEY_PRESSED) {
313+
this.reset();
314+
}
315+
};
316+
317+
@Nullable
318+
private Token lastMouseTargetToken;
319+
320+
// Avoid finding the mouse entry every mouse movement update.
321+
// This also reduces the chances of accidentally updating the tooltip with
322+
// a new entry's content as you move your mouse to the tooltip.
323+
final Timer mouseStoppedMovingTimer = new Timer(MOUSE_STOPPED_MOVING_DELAY, e -> {
324+
if (Config.editor().entryTooltip.enable.value()) {
325+
EditorPanel.this.consumeEditorMouseTarget(
326+
(token, entry, resolvedParent) -> {
327+
this.hideTimer.stop();
328+
if (this.entryTooltip.isVisible()) {
329+
this.showTimer.stop();
330+
331+
if (!token.equals(this.lastMouseTargetToken)) {
332+
this.lastMouseTargetToken = token;
333+
this.openTooltip(entry, resolvedParent);
334+
}
335+
} else {
336+
this.lastMouseTargetToken = token;
337+
this.showTimer.start();
338+
}
339+
},
340+
() -> consumeMousePositionIn(
341+
this.entryTooltip.getContentPane(),
342+
(absolute, relative) -> this.hideTimer.stop(),
343+
absolute -> {
344+
this.lastMouseTargetToken = null;
345+
this.showTimer.stop();
346+
this.hideTimer.start();
347+
}
348+
)
349+
);
350+
}
351+
});
352+
353+
final Timer showTimer = new Timer(
354+
ToolTipManager.sharedInstance().getInitialDelay() - MOUSE_STOPPED_MOVING_DELAY, e -> {
355+
EditorPanel.this.consumeEditorMouseTarget((token, entry, resolvedParent) -> {
356+
if (token.equals(this.lastMouseTargetToken)) {
357+
this.entryTooltip.setVisible(true);
358+
this.openTooltip(entry, resolvedParent);
359+
}
360+
});
361+
}
362+
);
363+
364+
final Timer hideTimer = new Timer(
365+
ToolTipManager.sharedInstance().getDismissDelay() - MOUSE_STOPPED_MOVING_DELAY,
366+
e -> this.entryTooltip.close()
367+
);
368+
369+
TooltipManager() {
370+
this.mouseStoppedMovingTimer.setRepeats(false);
371+
this.showTimer.setRepeats(false);
372+
this.hideTimer.setRepeats(false);
373+
374+
this.entryTooltip.setVisible(false);
375+
376+
this.entryTooltip.addMouseMotionListener(new MouseAdapter() {
377+
@Override
378+
public void mouseMoved(MouseEvent e) {
379+
if (Config.editor().entryTooltip.interactable.value()) {
380+
TooltipManager.this.mouseStoppedMovingTimer.stop();
381+
TooltipManager.this.hideTimer.stop();
382+
}
383+
}
384+
});
385+
386+
this.entryTooltip.addCloseListener(TooltipManager.this::reset);
387+
388+
EditorPanel.this.editor.addKeyListener(new KeyAdapter() {
389+
@Override
390+
public void keyTyped(KeyEvent e) {
391+
TooltipManager.this.reset();
392+
}
393+
});
394+
395+
EditorPanel.this.editor.addMouseListener(new MouseAdapter() {
396+
@Override
397+
public void mousePressed(MouseEvent mouseEvent) {
398+
TooltipManager.this.entryTooltip.close();
399+
}
400+
});
401+
402+
EditorPanel.this.editor.addMouseMotionListener(new MouseAdapter() {
403+
@Override
404+
public void mouseMoved(MouseEvent e) {
405+
if (!TooltipManager.this.entryTooltip.hasRepopulated()) {
406+
TooltipManager.this.mouseStoppedMovingTimer.restart();
407+
}
408+
}
409+
});
410+
411+
EditorPanel.this.editorScrollPane.getViewport().addChangeListener(e -> this.entryTooltip.close());
412+
413+
this.addExternalListeners();
414+
}
415+
416+
void reset() {
417+
this.lastMouseTargetToken = null;
418+
this.mouseStoppedMovingTimer.stop();
419+
this.showTimer.stop();
420+
this.hideTimer.stop();
421+
}
422+
423+
void openTooltip(Entry<?> target, boolean inherited) {
424+
final Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
425+
final Component eventReceiver = focusOwner != null && isDescendingFrom(focusOwner, EditorPanel.this.gui.getFrame())
426+
? focusOwner : null;
427+
428+
this.entryTooltip.open(target, inherited, eventReceiver);
429+
}
430+
431+
void addExternalListeners() {
432+
EditorPanel.this.gui.getFrame().addWindowFocusListener(this.guiFocusListener);
433+
Toolkit.getDefaultToolkit().addAWTEventListener(this.globalKeyListener, KeyEvent.KEY_EVENT_MASK);
434+
}
435+
436+
void removeExternalListeners() {
437+
EditorPanel.this.gui.getFrame().removeWindowFocusListener(this.guiFocusListener);
438+
Toolkit.getDefaultToolkit().removeAWTEventListener(this.globalKeyListener);
439+
}
440+
}
405441
}

0 commit comments

Comments
 (0)