Painting underline fixes, Goto declaration fix (also freezing)

This commit is contained in:
Jindra Petřík
2025-05-30 10:44:32 +02:00
parent 3254e192c4
commit 3c0db47094
6 changed files with 316 additions and 209 deletions

View File

@@ -435,7 +435,13 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
private Map<Openable, ABCExplorerDialog> abcExplorerDialogs = new WeakHashMap<>();
private Map<SWF, BreakpointListDialog> breakpointsListDialogs = new WeakHashMap<>();
private boolean loadingScrollPosEnabled = true;
public synchronized void setLoadingScrollPosEnabled(boolean loadingScrollPosEnabled) {
this.loadingScrollPosEnabled = loadingScrollPosEnabled;
}
public void savePins() {
pinsPanel.save();
}
@@ -6361,16 +6367,19 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
folderPreviewPanel.setSelectedItems(folderItems);
folderPreviewScrollBar.setValue(scrollValue);
}
View.execInEventDispatchLater(new Runnable() {
@Override
public void run() {
scrollPosStorage.loadScrollPos(oldItem);
}
});
if (loadingScrollPosEnabled) {
View.execInEventDispatchLater(new Runnable() {
@Override
public void run() {
scrollPosStorage.loadScrollPos(oldItem);
}
});
}
}
public void repaintTree() {
tagTree.repaint();
tagListTree.repaint();

View File

@@ -1040,7 +1040,7 @@ public class DecompiledEditorPane extends DebuggableEditorPane implements CaretL
}
@Override
public synchronized void setText(String t) {
public void setText(String t) {
super.setText(t);
setCaretPosition(0);
}
@@ -1076,7 +1076,7 @@ public class DecompiledEditorPane extends DebuggableEditorPane implements CaretL
}
@Override
public synchronized void setCaretPosition(int position) {
public void setCaretPosition(int position) {
super.setCaretPosition(position);
}
}

View File

@@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.abc.usages.multinames.MultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.TraitMultinameUsage;
import com.jpexs.decompiler.flash.gui.AppDialog;
import com.jpexs.decompiler.flash.gui.FasterScrollPane;
import com.jpexs.decompiler.flash.gui.Main;
import com.jpexs.decompiler.flash.gui.View;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import java.awt.BorderLayout;
@@ -36,6 +37,8 @@ import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JPanel;
@@ -142,9 +145,17 @@ public class UsageFrame extends AppDialog implements MouseListener {
} else {
decompiledTextArea.gotoClassHeader();
}
Timer tim = new Timer();
tim.schedule(new TimerTask() {
@Override
public void run() {
Main.getMainFrame().getPanel().setLoadingScrollPosEnabled(true);
}
}, 500);
}
};
Main.getMainFrame().getPanel().setLoadingScrollPosEnabled(false);
if (decompiledTextArea.getClassIndex() == icu.getClassIndex() && abcPanel.abc == newAbc) {
setTrait.run();
} else {

View File

@@ -21,19 +21,11 @@ import com.jpexs.decompiler.flash.gui.AppStrings;
import com.jpexs.decompiler.flash.simpleparser.SimpleParser;
import com.jpexs.helpers.Reference;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.Collections;
import java.util.HashMap;
@@ -76,8 +68,6 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan
private Token lastUnderlined = null;
private static final HighlightPainter underLinePainter = new UnderLinePainter(new Color(0, 0, 255));
private LinkHandler linkHandler = this;
private Point lastCursorPos = new Point(0, 0);
@@ -338,29 +328,7 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan
}
}
});
final LinkAdapter la = new LinkAdapter();
addMouseMotionListener(la);
addMouseListener(la);
//No standard AddKeyListener as we want to catch Ctrl globally no matter of focus
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addKeyEventPostProcessor(new KeyEventPostProcessor() {
@Override
public boolean postProcessKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
la.keyPressed(e);
}
if (e.getID() == KeyEvent.KEY_RELEASED) {
la.keyReleased(e);
}
if (e.getID() == KeyEvent.KEY_TYPED) {
la.keyTyped(e);
}
return false;
}
});
});
}
public Token tokenAtPos(Point lastPos) {
@@ -399,85 +367,7 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan
public Token getTokenUnderCursor() {
return tokenAtPos(lastCursorPos);
}
private class LinkAdapter extends MouseAdapter implements KeyListener {
private boolean ctrlDown = false;
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
ctrlDown = true;
update();
}
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
ctrlDown = false;
update();
}
}
private void update() {
if (ctrlDown) {
Token t = tokenAtPos(lastCursorPos);
if (t != lastUnderlined) {
if (t == null || lastUnderlined == null || !t.equals(lastUnderlined)) {
MyMarkers.removeMarkers(LineMarkedEditorPane.this, underLinePainter);
if (t != null && linkHandler.isLink(t)) {
lastUnderlined = t;
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
} else {
lastUnderlined = null;
}
} else {
lastUnderlined = null;
}
}
if (lastUnderlined != null) {
MyMarkers.markToken(LineMarkedEditorPane.this, lastUnderlined, underLinePainter);
} else {
setCursor(Cursor.getDefaultCursor());
}
repaint();
} else {
lastUnderlined = null;
MyMarkers.removeMarkers(LineMarkedEditorPane.this, underLinePainter);
setCursor(Cursor.getDefaultCursor());
repaint();
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (ctrlDown) {
Token t = tokenAtPos(lastCursorPos);
if (t != null && linkHandler.isLink(t)) {
linkHandler.handleLink(t);
}
}
}
@Override
public void mouseMoved(MouseEvent e) {
ctrlDown = (e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0;
lastCursorPos = e.getPoint();
update();
}
}
public void setLinkHandler(LinkHandler linkHandler) {
if (linkHandler == null) {
@@ -492,7 +382,7 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan
@Override
public HighlightPainter linkPainter() {
return underLinePainter;
return null;
}
@Override
@@ -586,78 +476,7 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan
}
}
public static class UnderLinePainter extends DefaultHighlighter.DefaultHighlightPainter {
public UnderLinePainter(Color color) {
super(color);
}
@Override
public void paint(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
try {
// --- determine locations ---
TextUI mapper = c.getUI();
Color col = getColor();
if (col == null) {
col = Color.black;
}
g.setColor(col);
for (int i = offs0; i < offs1; i++) {
Rectangle2D r = com.jpexs.decompiler.flash.gui.View.textUIModelToView(mapper, c, i, Position.Bias.Forward);
Rectangle2D r1 = com.jpexs.decompiler.flash.gui.View.textUIModelToView(mapper, c, i + 1, Position.Bias.Forward);
if (r1.getY() == r.getY()) {
g.drawLine((int) r.getX(), (int) (r.getY() + r.getHeight() - 3), (int) r1.getX(), (int) (r.getY() + r.getHeight() - 3));
}
}
} catch (BadLocationException e) {
// can't render
}
}
@Override
public Shape paintLayer(Graphics g, int offs0, int offs1,
Shape bounds, JTextComponent c, View view) {
g.setColor(c.getSelectionColor());
Rectangle r;
if (offs0 == view.getStartOffset()
&& offs1 == view.getEndOffset()) {
// Contained in view, can just use bounds.
if (bounds instanceof Rectangle) {
r = (Rectangle) bounds;
} else {
r = bounds.getBounds();
}
} else {
// Should only render part of View.
try {
// --- determine locations ---
Shape shape = view.modelToView(offs0, Position.Bias.Forward,
offs1, Position.Bias.Backward,
bounds);
r = (shape instanceof Rectangle)
? (Rectangle) shape : shape.getBounds();
} catch (BadLocationException e) {
// can't render
r = null;
}
}
if (r != null) {
r.width = Math.max(r.width, 1);
paint(g, offs0, offs1, r, c);
}
return r;
}
}
private int cut(double val) {
int ival = (int) Math.round(val);

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2025 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui.editor;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import javax.swing.plaf.TextUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.View;
/**
*
* @author JPEXS
*/
public class UnderlinePainter extends OccurencesMarker {
private final Color underlineColor;
public UnderlinePainter(Color underLineColor, Color backgroundColor) {
super(backgroundColor);
this.underlineColor = underLineColor;
}
@Override
public void paint(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
if (getColor() != null) {
super.paint(g, offs0, offs1, bounds, c);
}
try {
// --- determine locations ---
TextUI mapper = c.getUI();
Color col = underlineColor;
if (col == null) {
col = Color.black;
}
g.setColor(col);
for (int i = offs0; i < offs1; i++) {
Rectangle2D r = com.jpexs.decompiler.flash.gui.View.textUIModelToView(mapper, c, i, Position.Bias.Forward);
Rectangle2D r1 = com.jpexs.decompiler.flash.gui.View.textUIModelToView(mapper, c, i + 1, Position.Bias.Forward);
if (r1.getY() == r.getY()) {
g.drawLine((int) r.getX(), (int) (r.getY() + r.getHeight() - 3), (int) r1.getX(), (int) (r.getY() + r.getHeight() - 3));
}
}
} catch (BadLocationException e) {
// can't render
}
}
@Override
public Shape paintLayer(Graphics g, int offs0, int offs1,
Shape bounds, JTextComponent c, View view) {
g.setColor(c.getSelectionColor());
Rectangle r;
if (offs0 == view.getStartOffset()
&& offs1 == view.getEndOffset()) {
// Contained in view, can just use bounds.
if (bounds instanceof Rectangle) {
r = (Rectangle) bounds;
} else {
r = bounds.getBounds();
}
} else {
// Should only render part of View.
try {
// --- determine locations ---
Shape shape = view.modelToView(offs0, Position.Bias.Forward,
offs1, Position.Bias.Backward,
bounds);
r = (shape instanceof Rectangle)
? (Rectangle) shape : shape.getBounds();
} catch (BadLocationException e) {
// can't render
r = null;
}
}
if (r != null) {
r.width = Math.max(r.width, 1);
paint(g, offs0, offs1, r, c);
}
return r;
}
}

View File

@@ -20,6 +20,14 @@ import com.jpexs.decompiler.flash.simpleparser.SimpleParseException;
import com.jpexs.decompiler.flash.simpleparser.SimpleParser;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.beans.PropertyChangeEvent;
@@ -35,11 +43,9 @@ import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
@@ -93,6 +99,17 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
private ScrollBarUI originalScrollBarUI;
private LinkAdapter linkAdapter;
private KeyEventPostProcessor kevEventPostProcessor;
private Set<Integer> occurencesPositions = new HashSet<>();
private UnderlinePainter underLinePainter = new UnderlinePainter(new Color(0, 0, 255), null);
private UnderlinePainter underLineMarkOccurencesPainter = new UnderlinePainter(new Color(0, 0, 255), DEFAULT_COLOR);
private Token lastUnderlined;
/**
* Constructs a new Token highlighter
*/
@@ -127,6 +144,9 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
*/
public void removeMarkers() {
Markers.removeMarkers(pane, marker);
Markers.removeMarkers(pane, underLinePainter);
Markers.removeMarkers(pane, underLineMarkOccurencesPainter);
occurencesPositions.clear();
}
public void removeErrorMarkers() {
@@ -158,14 +178,14 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
private Token getNearestTokenAt(SyntaxDocument sDoc, int pos) {
Token thisToken = sDoc.getTokenAt(pos);
if (thisToken != null) {
if (thisToken.length == 1) {
Token nextToken = sDoc.getTokenAt(pos + 1);
if (nextToken != null && nextToken.start == pos && nextToken.length > 1) {
return nextToken;
}
}
return thisToken;
}
@@ -185,7 +205,7 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
void addErrorMarkers() {
highlightsPanel.setErrors(errors);
SyntaxDocument doc = ActionUtils.getSyntaxDocument(pane);
if (doc == null) {
return;
@@ -216,11 +236,13 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
Token definitionToken = getIdentifierTokenAt(sDoc, definitionPos < 0 ? -(definitionPos + 1) : definitionPos);
if (definitionToken != null) {
if (definitionPosToReferences.containsKey(definitionPos)) {
Markers.markToken(pane, definitionToken, marker);
Markers.markToken(pane, definitionToken, lastUnderlined == definitionToken ? underLineMarkOccurencesPainter : marker);
occurencesPositions.add(definitionToken.start);
for (int i : definitionPosToReferences.get(definitionPos)) {
Token referenceToken = getIdentifierTokenAt(sDoc, i);
if (referenceToken != null) {
Markers.markToken(pane, referenceToken, marker);
Markers.markToken(pane, referenceToken, lastUnderlined == referenceToken ? underLineMarkOccurencesPainter : marker);
occurencesPositions.add(referenceToken.start);
}
}
}
@@ -255,9 +277,9 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
editor.addCaretListener(this);
editor.addPropertyChangeListener(this);
editor.getDocument().addDocumentListener(this);
if (editor instanceof LineMarkedEditorPane) {
/*if (editor instanceof LineMarkedEditorPane) {
((LineMarkedEditorPane) editor).setLinkHandler(this);
}
}*/
mouseMotionAdapter = new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
@@ -282,11 +304,128 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
JPanel panel = (JPanel) SwingUtilities.getAncestorOfClass(JPanel.class, scrollPane);
panel.add(highlightsPanel, BorderLayout.EAST);
linkAdapter = new LinkAdapter();
pane.addMouseListener(linkAdapter);
pane.addMouseMotionListener(linkAdapter);
//No standard AddKeyListener as we want to catch Ctrl globally no matter of focus
kevEventPostProcessor = new KeyEventPostProcessor() {
@Override
public boolean postProcessKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
linkAdapter.keyPressed(e);
}
if (e.getID() == KeyEvent.KEY_RELEASED) {
linkAdapter.keyReleased(e);
}
if (e.getID() == KeyEvent.KEY_TYPED) {
linkAdapter.keyTyped(e);
}
return false;
}
};
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(kevEventPostProcessor);
documentUpdated();
markTokenAt(editor.getCaretPosition());
status = Status.INSTALLING;
}
private class LinkAdapter extends MouseAdapter implements KeyListener {
private boolean ctrlDown = false;
private Point lastCursorPos;
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
ctrlDown = true;
update();
}
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
ctrlDown = false;
update();
}
}
private void update() {
if (ctrlDown) {
Token t = ((LineMarkedEditorPane) pane).tokenAtPos(lastCursorPos);
if (t != lastUnderlined) {
if (t == null || lastUnderlined == null || !t.equals(lastUnderlined)) {
MyMarkers.removeMarkers(pane, underLinePainter);
MyMarkers.removeMarkers(pane, underLineMarkOccurencesPainter);
if (t != null && isLink(t)) {
lastUnderlined = t;
pane.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
} else {
lastUnderlined = null;
removeMarkers();
markTokenAt(pane.getCaretPosition());
}
} else {
lastUnderlined = null;
}
}
if (lastUnderlined != null) {
Highlighter.HighlightPainter painter = underLinePainter;
if (occurencesPositions.contains(lastUnderlined.start)) {
painter = underLineMarkOccurencesPainter;
removeMarkers();
markTokenAt(pane.getCaretPosition());
} else {
MyMarkers.markToken(pane, lastUnderlined, painter);
}
} else {
pane.setCursor(Cursor.getDefaultCursor());
}
} else {
lastUnderlined = null;
MyMarkers.removeMarkers(pane, underLinePainter);
MyMarkers.removeMarkers(pane, underLineMarkOccurencesPainter);
removeMarkers();
markTokenAt(pane.getCaretPosition());
pane.setCursor(Cursor.getDefaultCursor());
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (ctrlDown) {
Token t = ((LineMarkedEditorPane) pane).tokenAtPos(lastCursorPos);
if (t != null && isLink(t)) {
e.consume();
handleLink(t);
}
}
}
@Override
public void mouseMoved(MouseEvent e) {
ctrlDown = (e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0;
lastCursorPos = e.getPoint();
update();
}
}
private void editorMouseMoved(MouseEvent e) {
if (pane instanceof LineMarkedEditorPane) {
Token token = ((LineMarkedEditorPane) pane).getTokenUnderCursor();
@@ -313,10 +452,13 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
pane.getDocument().removeDocumentListener(this);
pane.removeCaretListener(this);
pane.removeMouseMotionListener(mouseMotionAdapter);
pane.removeMouseMotionListener(linkAdapter);
pane.removeMouseListener(linkAdapter);
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(kevEventPostProcessor);
mouseMotionAdapter = null;
if (editor instanceof LineMarkedEditorPane) {
/*if (editor instanceof LineMarkedEditorPane) {
((LineMarkedEditorPane) editor).setLinkHandler(null);
}
}*/
JScrollPane scrollPane = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, editor);
JPanel panel = (JPanel) SwingUtilities.getAncestorOfClass(JPanel.class, scrollPane);
panel.remove(highlightsPanel);
@@ -376,7 +518,7 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
} catch (SimpleParseException ex) {
definitionPosToReferences.clear();
referenceToDefinition.clear();
errors.put((int) ex.position, ex.getMessage());
errors.put((int) ex.position, ex.getMessage());
}
Timer tim = errorsTimer;
if (tim != null) {
@@ -412,14 +554,27 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
@Override
public boolean isLink(Token token) {
return referenceToDefinition.containsKey(token.start) && referenceToDefinition.get(token.start) >= 0;
boolean linkBasic = referenceToDefinition.containsKey(token.start) && referenceToDefinition.get(token.start) >= 0;
if (linkBasic) {
return true;
}
if (pane.isEditable()) {
return false;
}
return ((LineMarkedEditorPane) pane).getLinkHandler().isLink(token);
}
@Override
public void handleLink(Token token) {
Integer definition = referenceToDefinition.get(token.start);
if (definition != null) {
if (definition != null && definition >= 0) {
pane.setCaretPosition(definition);
} else if (!pane.isEditable()) {
lastUnderlined = null;
removeErrorMarkers();
removeMarkers();
pane.repaint();
((LineMarkedEditorPane) pane).getLinkHandler().handleLink(token);
}
}