2222import java .awt .Component ;
2323import java .awt .GridBagConstraints ;
2424import java .awt .KeyboardFocusManager ;
25+ import java .awt .Toolkit ;
26+ import java .awt .event .AWTEventListener ;
2527import java .awt .event .ActionEvent ;
2628import java .awt .event .FocusAdapter ;
2729import java .awt .event .FocusEvent ;
4951import static java .awt .event .InputEvent .CTRL_DOWN_MASK ;
5052
5153public 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