Skip to content

Commit a5dbc59

Browse files
committed
Improve editor tooltip mouse ignore area computation.
Some editor tooltips are interactive. To be able to enter the tooltip with the mouse, a rectangle is computed which prevents the tooltip to hide, even if the mouse leaves the component (which would hide it instantly). This improves this mechanism slightly by - adding an extra x-margin to the ignored area - offsetting the debugger tooltip to further move the buttons into the ignored area (right above the mouse cursor) removed obsolete code in ToolTipAnnotation and minor cleanup in that class (no other changes).
1 parent b5d2a9b commit a5dbc59

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)