Skip to content

Commit ef0a631

Browse files
authored
Merge pull request #9028 from mbien/debug-tooltip-ignore-area
Improve editor tooltip mouse ignore area computation.
2 parents 8270397 + a5dbc59 commit ef0a631

File tree

5 files changed

+60
-72
lines changed

5 files changed

+60
-72
lines changed

ide/editor.lib/nbproject/project.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# under the License.
1717

1818
javac.compilerargs=-Xlint:unchecked
19-
javac.source=1.8
19+
javac.release=17
2020
spec.version.base=4.38.0
2121
is.autoload=true
2222

ide/editor.lib/src/org/netbeans/editor/ext/ToolTipSupport.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
package org.netbeans.editor.ext;
2121

22-
import org.netbeans.api.editor.StickyWindowSupport;
2322
import java.awt.Color;
2423
import java.awt.Container;
2524
import java.awt.Dimension;
@@ -35,6 +34,7 @@
3534
import java.awt.event.MouseEvent;
3635
import java.awt.event.MouseListener;
3736
import java.awt.event.MouseMotionListener;
37+
import java.awt.geom.Rectangle2D;
3838
import java.beans.PropertyChangeEvent;
3939
import java.beans.PropertyChangeListener;
4040
import java.beans.PropertyChangeSupport;
@@ -156,7 +156,11 @@ public class ToolTipSupport {
156156
private static final String HTML_PREFIX_UPPERCASE = "<HTML"; //NOI18N
157157

158158
private static final String LAST_TOOLTIP_POSITION = "ToolTipSupport.lastToolTipPosition"; //NOI18N
159+
160+
/// Key for ignored area rectangle stored as client property.
161+
/// Allowes the mouse pointer to safely enter the tooltip without the risk of it disapearing.
159162
private static final String MOUSE_MOVE_IGNORED_AREA = "ToolTipSupport.mouseMoveIgnoredArea"; //NOI18N
163+
160164
private static final String MOUSE_LISTENER = "ToolTipSupport.noOpMouseListener"; //NOI18N
161165

162166
private static final Action NO_ACTION = new TextAction("tooltip-no-action") { //NOI18N
@@ -667,8 +671,7 @@ private void setStatus(int status) {
667671
if (this.status != status) {
668672
int oldStatus = this.status;
669673
this.status = status;
670-
firePropertyChange(PROP_STATUS,
671-
Integer.valueOf(oldStatus), Integer.valueOf(this.status));
674+
firePropertyChange(PROP_STATUS, oldStatus, this.status);
672675
}
673676
}
674677

@@ -799,9 +802,15 @@ private void ensureVisibility(Point toolTipPosition) {
799802
try {
800803
int[] offsets = Utilities.getSelectionOrIdentifierBlock(component, pos);
801804
if (offsets != null) {
802-
Rectangle r1 = component.modelToView(offsets[0]);
803-
Rectangle r2 = component.modelToView(offsets[1]);
804-
blockBounds = new Rectangle(r1.x, r1.y, r2.x - r1.x, r1.height);
805+
Rectangle2D r1 = component.modelToView2D(offsets[0]);
806+
Rectangle2D r2 = component.modelToView2D(offsets[1]);
807+
int margin = 6; // additional x-margin for the ignore area, shouldn't be much wider than 1-2 characters
808+
blockBounds = new Rectangle(
809+
(int) (r1.getX() - margin),
810+
(int) (r1.getY()),
811+
(int) (r2.getX() - r1.getX() + margin * 2),
812+
(int) (r1.getHeight())
813+
);
805814
}
806815
} catch (BadLocationException ble) {}
807816
toolTip.putClientProperty(MOUSE_MOVE_IGNORED_AREA, computeMouseMoveIgnoredArea(
@@ -975,8 +984,7 @@ private Point getLastMouseEventPoint() {
975984
if (parent instanceof JLayeredPane) {
976985
parent = parent.getParent();
977986
}
978-
if (parent instanceof JViewport) {
979-
JViewport vp = (JViewport)parent;
987+
if (parent instanceof JViewport vp) {
980988
p = new Point(vp.getViewPosition().x, p.y);
981989
}
982990
}

ide/spi.debugger.ui/src/org/netbeans/modules/debugger/ui/views/ToolTipView.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public static class ExpandableTooltip extends JPanel {
116116

117117
private JButton expButton;
118118
private JButton pinButton;
119-
private JComponent textToolTip;
119+
private final JComponent textToolTip;
120120
private boolean widthCheck = true;
121121
private boolean sizeSet = false;
122122

@@ -245,6 +245,23 @@ private static JTextArea createMultiLineToolTip(String toolTipText, boolean wrap
245245
return ta;
246246
}
247247

248+
/**
249+
* Adjusts the tooltip location to position the pin button above the cursor.
250+
*
251+
* Helps to keep the mouse pointer within the mouse motion ignore area of ext.ToolTipSupport
252+
* with the purpose of preventing the tooltip from disappearing while the mouse is moving towards it.
253+
*/
254+
public int getXOffset() {
255+
double offset = 0;
256+
if (pinButton != null) {
257+
offset += pinButton.getPreferredSize().getWidth();
258+
}
259+
if (expButton != null) {
260+
offset += expButton.getPreferredSize().getWidth();
261+
}
262+
return - (int) (offset / 2 + 2);
263+
}
264+
248265
private static class TextToolTip extends JTextArea {
249266

250267
private static final String ELIPSIS = "..."; //NOI18N

ide/spi.debugger.ui/src/org/netbeans/spi/debugger/ui/ToolTipUI.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,13 @@ public final class ToolTipUI {
7272
public ToolTipSupport show(JEditorPane editorPane) {
7373
EditorUI eui = Utilities.getEditorUI(editorPane);
7474
if (eui != null) {
75-
eui.getToolTipSupport().setToolTip(et);
75+
eui.getToolTipSupport().setToolTip(
76+
et,
77+
PopupManager.ViewPortBounds,
78+
PopupManager.AbovePreferred,
79+
et.getXOffset(),
80+
0
81+
);
7682
this.editorPane = editorPane;
7783
return eui.getToolTipSupport();
7884
} else {

java/debugger.jpda.projectsui/src/org/netbeans/modules/debugger/jpda/projectsui/ToolTipAnnotation.java

Lines changed: 18 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,7 @@
2323
import com.sun.source.tree.Tree;
2424
import com.sun.source.util.SourcePositions;
2525
import com.sun.source.util.TreePath;
26-
import java.awt.Point;
27-
import java.awt.event.ActionEvent;
28-
import java.awt.event.ActionListener;
29-
import java.beans.PropertyChangeEvent;
30-
import java.beans.PropertyChangeListener;
3126
import java.io.IOException;
32-
import java.util.Arrays;
33-
import java.util.Collections;
3427
import java.util.HashSet;
3528
import java.util.List;
3629
import java.util.Set;
@@ -39,15 +32,13 @@
3932
import javax.lang.model.element.TypeElement;
4033
import javax.lang.model.type.TypeMirror;
4134
import javax.lang.model.util.Types;
42-
import javax.swing.BorderFactory;
4335
import javax.swing.JEditorPane;
4436
import javax.swing.SwingUtilities;
4537
import javax.swing.text.BadLocationException;
4638
import javax.swing.text.Element;
4739
import javax.swing.text.StyledDocument;
4840
import org.netbeans.api.debugger.DebuggerEngine;
4941
import org.netbeans.api.debugger.DebuggerManager;
50-
import org.netbeans.api.debugger.Watch;
5142
import org.netbeans.api.debugger.jpda.CallStackFrame;
5243
import org.netbeans.api.debugger.jpda.Field;
5344
import org.netbeans.api.debugger.jpda.InvalidExpressionException;
@@ -65,7 +56,6 @@
6556
import org.netbeans.api.java.source.JavaSource.Phase;
6657
import org.netbeans.api.java.source.TreeUtilities;
6758
import org.netbeans.editor.EditorUI;
68-
import org.netbeans.editor.PopupManager;
6959
import org.netbeans.editor.Utilities;
7060
import org.netbeans.editor.ext.ToolTipSupport;
7161
import org.netbeans.modules.parsing.api.ParserManager;
@@ -76,27 +66,21 @@
7666
import org.netbeans.modules.parsing.spi.Parser.Result;
7767
import org.netbeans.spi.debugger.jpda.EditorContext.Operation;
7868
import org.netbeans.spi.debugger.ui.EditorContextDispatcher;
79-
import org.netbeans.spi.debugger.ui.EditorPin;
80-
import org.netbeans.spi.debugger.ui.PinWatchUISupport;
8169
import org.netbeans.spi.debugger.ui.ToolTipUI;
8270
import org.netbeans.spi.debugger.ui.ViewFactory;
8371
import org.openide.cookies.EditorCookie;
8472
import org.openide.filesystems.FileObject;
8573
import org.openide.loaders.DataObject;
86-
import org.openide.loaders.DataObjectNotFoundException;
8774
import org.openide.text.Annotation;
8875
import org.openide.text.DataEditorSupport;
8976
import org.openide.text.Line;
9077
import org.openide.text.Line.Part;
9178
import org.openide.text.NbDocument;
92-
import org.openide.util.Exceptions;
9379
import org.openide.util.RequestProcessor;
94-
import org.openide.util.WeakListeners;
95-
9680

9781
public class ToolTipAnnotation extends Annotation implements Runnable {
9882

99-
private static final Set<String> JAVA_KEYWORDS = new HashSet<String>(Arrays.asList(new String[] {
83+
private static final Set<String> JAVA_KEYWORDS = Set.of(
10084
"abstract", "continue", "for", "new", "switch",
10185
"assert", "default", "goto", "package", "synchronized",
10286
"boolean", "do", "if", "private", /*"this",*/
@@ -106,37 +90,10 @@ public class ToolTipAnnotation extends Annotation implements Runnable {
10690
"catch", "extends", "int", "short", "try",
10791
"char", "final", "interface", "static", "void",
10892
/*"class",*/ "finally", "long", "strictfp", "volatile",
109-
"const", "float", "native", "super", "while",
110-
}));
111-
112-
private static final int MAX_STRING_LENGTH;
93+
"const", "float", "native", "super", "while"
94+
);
11395

114-
static {
115-
int maxStringLength = 100000;
116-
String javaV = System.getProperty("java.version");
117-
if (javaV.startsWith("1.8.0")) {
118-
String update = "";
119-
for (int i = "1.8.0_".length(); i < javaV.length(); i++) {
120-
char c = javaV.charAt(i);
121-
if (Character.isDigit(c)) {
122-
update += c;
123-
} else {
124-
break;
125-
}
126-
}
127-
int updateNo = 0;
128-
if (!update.isEmpty()) {
129-
try {
130-
updateNo = Integer.parseInt(update);
131-
} catch (NumberFormatException nfex) {}
132-
}
133-
if (updateNo < 60) {
134-
// Memory problem on JDK 8, fixed in update 60 (https://bugs.openjdk.java.net/browse/JDK-8072775):
135-
maxStringLength = 1000;
136-
}
137-
}
138-
MAX_STRING_LENGTH = maxStringLength;
139-
}
96+
private static final int MAX_STRING_LENGTH = 100000;
14097

14198
private Part lp;
14299
private EditorCookie ec;
@@ -266,36 +223,36 @@ public void run () {
266223
return ; // Something went wrong...
267224
}
268225
String type = v.getType ();
269-
if (v instanceof ObjectVariable) {
270-
tooltipVariable = (ObjectVariable) v;
226+
if (v instanceof ObjectVariable objectVariable) {
227+
tooltipVariable = objectVariable;
271228
try {
272229
Object jdiValue = v.getClass().getMethod("getJDIValue").invoke(v);
273230
if (jdiValue == null) {
274231
tooltipVariable = null;
275232
}
276233
} catch (Exception ex) {}
277234
if (tooltipVariable != null) {
278-
v = getFormattedValue(d, (ObjectVariable) v);
235+
v = getFormattedValue(d, objectVariable);
279236
}
280237
}
281-
if (v instanceof ObjectVariable) {
238+
if (v instanceof ObjectVariable objectVariable) {
282239
try {
283-
String toString = ((ObjectVariable) v).getToStringValue();
240+
String toString = objectVariable.getToStringValue();
284241
toolTipText = expression + " = " +
285-
(type.length () == 0 ?
242+
(type.isEmpty() ?
286243
"" :
287244
"(" + type + ") ") +
288245
toString;
289246
} catch (InvalidExpressionException ex) {
290247
toolTipText = expression + " = " +
291-
(type.length () == 0 ?
248+
(type.isEmpty() ?
292249
"" :
293250
"(" + type + ") ") +
294251
v.getValue ();
295252
}
296253
} else {
297254
toolTipText = expression + " = " +
298-
(type.length () == 0 ?
255+
(type.isEmpty() ?
299256
"" :
300257
"(" + type + ") ") +
301258
v.getValue ();
@@ -462,7 +419,7 @@ private static boolean isValidTooltipLocation(JPDADebugger debugger,
462419
final String[] className = new String[]{""};
463420
Future<Void> parsingTask;
464421
try {
465-
parsingTask = ParserManager.parseWhenScanFinished(Collections.singleton(Source.create(doc)), new UserTask() {
422+
parsingTask = ParserManager.parseWhenScanFinished(List.of(Source.create(doc)), new UserTask() {
466423
@Override
467424
public void run(ResultIterator resultIterator) throws Exception {
468425
Result res = resultIterator.getParserResult(offset);
@@ -530,7 +487,7 @@ public void run(ResultIterator resultIterator) throws Exception {
530487
isValid[0] = false;
531488
return;
532489
}
533-
if (TreeUtilities.CLASS_TREE_KINDS.contains(kind) && className[0].length() == 0) {
490+
if (TreeUtilities.CLASS_TREE_KINDS.contains(kind) && className[0].isEmpty()) {
534491
TypeElement typeElement = (TypeElement)controller.getTrees().getElement(path);
535492
className[0] = ElementUtilities.getBinaryName(typeElement);
536493
}
@@ -547,8 +504,8 @@ public void run(ResultIterator resultIterator) throws Exception {
547504
if (ElementKind.CLASS.equals(tk) ||
548505
ElementKind.ENUM.equals(tk) ||
549506
ElementKind.INTERFACE.equals(tk)) {*/
550-
if (typeElement instanceof TypeElement) {
551-
String binaryClassName = ElementUtilities.getBinaryName((TypeElement) typeElement);
507+
if (typeElement instanceof TypeElement typeElement1) {
508+
String binaryClassName = ElementUtilities.getBinaryName(typeElement1);
552509
fieldOfPtr[0] = binaryClassName;
553510
/*
554511
String currentClassName = currentFrame.getClassName();
@@ -582,7 +539,7 @@ public void run(ResultIterator resultIterator) throws Exception {
582539
return false;
583540
}
584541
if (className[0].length() > 0) {
585-
Set<String> superTypeNames = new HashSet<String>();
542+
Set<String> superTypeNames = new HashSet<>();
586543
This thisVar = currentFrame.getThisVariable();
587544
if (thisVar != null) {
588545
String fqn = thisVar.getType();
@@ -676,7 +633,7 @@ private String resolveTypeName (final int offset, Source source) {
676633
final String[] result = new String[1];
677634
result[0] = null;
678635
try {
679-
ParserManager.parse(Collections.singleton(source), new UserTask() {
636+
ParserManager.parse(List.of(source), new UserTask() {
680637
@Override
681638
public void run(ResultIterator resultIterator) throws Exception {
682639
Result res = resultIterator.getParserResult(offset);

0 commit comments

Comments
 (0)