Skip to content

Commit 850afee

Browse files
implement MarkableScrollPane.MarkerListener and use it to show entry tooltips for markers
1 parent 37fc9f6 commit 850afee

File tree

2 files changed

+126
-46
lines changed

2 files changed

+126
-46
lines changed

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

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -551,13 +551,46 @@ void tryMarking(Token token, TokenType type, TokenStore tokenStore) {
551551

552552
final int priority = Objects.requireNonNull(MARKER_PRIORITIES_BY_COLOR_CONFIG.get(colorConfig));
553553
final Color color = colorConfig.value();
554-
EditorPanel.this.editorScrollPane.addMarker(tokenPos, color, priority, new MouseAdapter() {
555-
// TODO show/hide tooltip on mouse enter/exit
556-
@Override
557-
public void mouseClicked(MouseEvent e) {
558-
EditorPanel.this.navigateToToken(token);
559-
}
560-
});
554+
EditorPanel.this.editorScrollPane.addMarker(
555+
tokenPos, color, priority,
556+
new MarkableScrollPane.MarkerListener() {
557+
@Override
558+
public void mouseClicked() {
559+
EditorPanel.this.navigateToToken(token);
560+
}
561+
562+
@Override
563+
public void mouseExited() {
564+
if (EditorPanel.this.tooltipManager.lastMouseTargetToken == null) {
565+
EditorPanel.this.tooltipManager.entryTooltip.close();
566+
}
567+
}
568+
569+
@Override
570+
public void mouseEntered() {
571+
// dont' resolve the token for markers
572+
final EntryReference<Entry<?>, Entry<?>> reference =
573+
EditorPanel.this.getReference(token);
574+
if (reference != null) {
575+
EditorPanel.this.tooltipManager.reset();
576+
EditorPanel.this.tooltipManager.openTooltip(reference.entry, false);
577+
}
578+
}
579+
580+
// This is used instead of just exit+enter because closing immediately before opening
581+
// causes the (slightly delayed) window lost focus listener to close the tooltip
582+
// for the new marker immediately after opening it.
583+
@Override
584+
public void mouseTransferred() {
585+
this.mouseEntered();
586+
}
587+
588+
@Override
589+
public void mouseMoved() {
590+
EditorPanel.this.tooltipManager.reset();
591+
}
592+
}
593+
);
561594
} catch (BadLocationException e) {
562595
Logger.warn("Tried to add marker for token with bad location: " + token);
563596
}

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

Lines changed: 86 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
import java.awt.Rectangle;
2121
import java.awt.event.ComponentEvent;
2222
import java.awt.event.ComponentListener;
23+
import java.awt.event.MouseAdapter;
2324
import java.awt.event.MouseEvent;
24-
import java.awt.event.MouseListener;
2525
import java.util.Collection;
2626
import java.util.Collections;
2727
import java.util.HashMap;
@@ -32,7 +32,7 @@
3232
import java.util.Optional;
3333
import java.util.Set;
3434
import java.util.TreeMap;
35-
import java.util.function.BiConsumer;
35+
import java.util.function.Consumer;
3636
import java.util.function.Predicate;
3737

3838
public class MarkableScrollPane extends JScrollPane {
@@ -50,7 +50,7 @@ public class MarkableScrollPane extends JScrollPane {
5050

5151
@Nullable
5252
private PaintState paintState;
53-
private MouseListener viewMouseListener;
53+
private MouseAdapter viewMouseAdapter;
5454

5555
/**
5656
* Constructs a scroll pane with no view,
@@ -123,71 +123,104 @@ public void componentHidden(ComponentEvent e) {
123123
public void setViewportView(Component view) {
124124
final Component oldView = this.getViewport().getView();
125125
if (oldView != null) {
126-
oldView.removeMouseListener(this.viewMouseListener);
126+
oldView.removeMouseListener(this.viewMouseAdapter);
127+
oldView.removeMouseMotionListener(this.viewMouseAdapter);
127128
}
128129

129130
super.setViewportView(view);
130131

131-
this.viewMouseListener = new MouseListener() {
132-
private void tryMarkerListeners(MouseEvent e, BiConsumer<MouseListener, MouseEvent> eventAction) {
133-
if (MarkableScrollPane.this.paintState != null) {
134-
final Point relativePos = GuiUtil
135-
.getRelativePos(MarkableScrollPane.this, e.getXOnScreen(), e.getYOnScreen());
136-
MarkableScrollPane.this.paintState
137-
.findSpanContaining(
138-
relativePos.x, relativePos.y,
139-
span -> span.getMarker().mouseListener.isPresent()
140-
)
141-
.map(span -> span.getMarker().mouseListener.orElseThrow())
142-
.ifPresent(listener -> eventAction.accept(listener, e));
132+
this.viewMouseAdapter = new MouseAdapter() {
133+
static MouseEvent withId(MouseEvent e, int id) {
134+
return new MouseEvent(
135+
(Component) e.getSource(), id, e.getWhen(), e.getModifiersEx(),
136+
e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(),
137+
e.getClickCount(), e.isPopupTrigger(), e.getButton()
138+
);
139+
}
140+
141+
@Nullable
142+
MarkerListener lastEntered;
143+
144+
Optional<MarkerListener> findMarkerListener(MouseEvent e) {
145+
if (MarkableScrollPane.this.paintState == null) {
146+
return Optional.empty();
147+
} else {
148+
final Point relativePos =
149+
GuiUtil.getRelativePos(MarkableScrollPane.this, e.getXOnScreen(), e.getYOnScreen());
150+
return MarkableScrollPane.this.paintState
151+
.findSpanContaining(
152+
relativePos.x, relativePos.y,
153+
span -> span.getMarker().listener.isPresent()
154+
)
155+
.map(span -> span.getMarker().listener.orElseThrow());
143156
}
144157
}
145158

146-
@Override
147-
public void mouseClicked(MouseEvent e) {
148-
this.tryMarkerListeners(e, MouseListener::mouseClicked);
159+
void tryMarkerListeners(MouseEvent e, Consumer<MarkerListener> listen) {
160+
this.findMarkerListener(e).ifPresent(listen);
149161
}
150162

151163
@Override
152-
public void mousePressed(MouseEvent e) {
153-
this.tryMarkerListeners(e, MouseListener::mousePressed);
164+
public void mouseClicked(MouseEvent e) {
165+
this.tryMarkerListeners(e, MarkerListener::mouseClicked);
154166
}
155167

156168
@Override
157-
public void mouseReleased(MouseEvent e) {
158-
this.tryMarkerListeners(e, MouseListener::mouseReleased);
169+
public void mouseExited(MouseEvent e) {
170+
this.mouseExitedImpl();
159171
}
160172

161173
@Override
162-
public void mouseEntered(MouseEvent e) {
163-
this.tryMarkerListeners(e, MouseListener::mouseEntered);
174+
public void mouseMoved(MouseEvent e) {
175+
this.tryMarkerListeners(e, MarkerListener::mouseMoved);
176+
177+
this.findMarkerListener(e).ifPresentOrElse(
178+
listener -> {
179+
if (listener != this.lastEntered) {
180+
if (this.lastEntered == null) {
181+
listener.mouseEntered();
182+
} else {
183+
listener.mouseTransferred();
184+
}
185+
186+
this.lastEntered = listener;
187+
}
188+
},
189+
this::mouseExitedImpl
190+
);
164191
}
165192

166-
@Override
167-
public void mouseExited(MouseEvent e) {
168-
this.tryMarkerListeners(e, MouseListener::mouseExited);
193+
private void mouseExitedImpl() {
194+
if (this.lastEntered != null) {
195+
this.lastEntered.mouseExited();
196+
this.lastEntered = null;
197+
}
169198
}
170199
};
171200

172201
// add the listener to the view because this doesn't receive clicks within the view
173-
view.addMouseListener(this.viewMouseListener);
202+
view.addMouseListener(this.viewMouseAdapter);
203+
view.addMouseMotionListener(this.viewMouseAdapter);
174204
}
175205

176206
/**
177207
* Adds a marker with passed {@code color} at the given {@code pos}.
178208
*
179-
* @param pos the vertical center of the marker within the space of this scroll pane's view
180-
* @param color the color of the marker
181-
* @param priority the priority of the marker; if there are multiple markers at the same position, only up to
182-
* {@link #maxConcurrentMarkers} of the highest priority markers will be rendered
183-
* @return an object which may be used to remove the marker by passing it to {@link #removeMarker(Object)}
209+
* @param pos the vertical center of the marker within the space of this scroll pane's view
210+
* @param color the color of the marker
211+
* @param priority the priority of the marker; if there are multiple markers at the same position, only up to
212+
* {@link #maxConcurrentMarkers} of the highest priority markers will be rendered
213+
* @param listener a listener for events within the marker; may be null
214+
*
215+
* @return an object which may be used to remove the marker by passing it to {@link #removeMarker(Object)}
184216
*/
185-
public Object addMarker(int pos, Color color, int priority, @Nullable MouseListener mouseListener) {
217+
public Object addMarker(int pos, Color color, int priority, @Nullable MarkerListener listener) {
186218
if (pos < 0) {
187219
throw new IllegalArgumentException("pos must not be negative!");
188220
}
189221

190-
final Marker marker = new Marker(color, priority, Optional.ofNullable(mouseListener));
222+
final Marker marker = new Marker(color, priority, Optional.ofNullable(listener));
223+
191224
this.markersByPos.put(pos, marker);
192225

193226
if (this.paintState != null) {
@@ -200,7 +233,7 @@ public Object addMarker(int pos, Color color, int priority, @Nullable MouseListe
200233
/**
201234
* Removes the passed {@code marker} if it belongs to this scroll pane.
202235
*
203-
* @param marker an object previously returned by {@link #addMarker(int, Color, int, MouseListener)}
236+
* @param marker an object previously returned by {@link #addMarker(int, Color, int, MarkerListener)}
204237
*/
205238
public void removeMarker(Object marker) {
206239
if (marker instanceof Marker removing) {
@@ -363,8 +396,7 @@ boolean areaContains(int x, int y) {
363396
}
364397
}
365398

366-
private record Marker(Color color, int priority, Optional<MouseListener> mouseListener)
367-
implements Comparable<Marker> {
399+
private record Marker(Color color, int priority, Optional<MarkerListener> listener) implements Comparable<Marker> {
368400
@Override
369401
public int compareTo(@Nonnull Marker other) {
370402
return other.priority - this.priority;
@@ -422,4 +454,19 @@ void paint(Graphics graphics) {
422454
}
423455
}
424456
}
457+
458+
public interface MarkerListener {
459+
void mouseClicked();
460+
461+
void mouseExited();
462+
463+
void mouseEntered();
464+
465+
/**
466+
* Called when the mouse moves between two adjacent markers.
467+
*/
468+
void mouseTransferred();
469+
470+
void mouseMoved();
471+
}
425472
}

0 commit comments

Comments
 (0)