Fixed #2021, #2000 Caret position in editors when using tabs and / or unicode

This commit is contained in:
Jindra Petřík
2023-11-06 16:45:45 +01:00
parent b4e4268b58
commit 8a2f49b4b9
3 changed files with 151 additions and 43 deletions

Binary file not shown.

View File

@@ -15,12 +15,14 @@ package jsyntaxpane;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
@@ -31,6 +33,9 @@ import javax.swing.text.Element;
import javax.swing.text.PlainView;
import javax.swing.text.Position;
import javax.swing.text.Segment;
import javax.swing.text.TabExpander;
import javax.swing.text.Utilities;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import jsyntaxpane.util.Configuration;
@@ -194,8 +199,13 @@ public class SyntaxView extends PlainView {
int p0 = line.getStartOffset();
Segment s = new Segment();
doc.getText(p0, pos - p0, s);
int xOffs = UniTools.getTabbedTextWidth(s, metrics, tabBase, this,p0);
int xOffs = UniTools.getTabbedTextWidth(getContainer().getGraphics(), s, tabBase, this,p0);
//System.err.println("calling Utilities.getTabbedTextWidth from normal: x = " + tabBase + ", startOffset = "+(p0));
/*int xOffs2 = Utilities.getTabbedTextWidth(s, metrics, tabBase, this,p0);
System.err.println("result = " + xOffs2);
System.err.println("UniTools.getTabbedTextWidth = " +xOffs);
System.err.println("Utilities.getTabbedTextWidth = " +xOffs2);
*/
// fill in the results and return
lineArea.x += xOffs;
lineArea.width = 1;
@@ -256,7 +266,7 @@ public class SyntaxView extends PlainView {
Segment s = new Segment();
doc.getText(p0, p1 - p0, s);
int tabBase = alloc.x;
int offs = p0 + UniTools.getTabbedTextOffset(s, metrics,
int offs = p0 + UniTools.getTabbedTextOffset(getContainer().getGraphics(), s, metrics,
tabBase, x, this, p0);
//SegmentCache.releaseSharedSegment(s);
return offs;
@@ -267,4 +277,7 @@ public class SyntaxView extends PlainView {
}
}
}
}

View File

@@ -4,7 +4,6 @@ import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -12,7 +11,6 @@ import javax.swing.JLabel;
import javax.swing.text.Segment;
import javax.swing.text.TabExpander;
import javax.swing.text.Utilities;
import javax.swing.text.View;
/**
*
@@ -47,34 +45,10 @@ public class UniTools {
}
}
public static int getTabbedTextOffset(Segment segment, FontMetrics metrics, int tabBase,int x,TabExpander e, int startOffset){
List<Segment> segments=new ArrayList<Segment>();
List<Boolean> unis=new ArrayList<Boolean>();
Font origFont=metrics.getFont();
getSegments(origFont, segment, segments, unis);
Graphics g=new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getGraphics();
Font uniFont = defaultUniFont.deriveFont(origFont.getStyle(),origFont.getSize2D());
int ofs=0;
int totalto = 0;
for(int i=0;i<segments.size();i++){
Segment seg=segments.get(i);
FontMetrics fm=unis.get(i)?g.getFontMetrics(uniFont):metrics;
int to = Utilities.getTabbedTextOffset(seg, fm, tabBase+ofs,x, e, startOffset);
totalto += to;
ofs+=fm.stringWidth(seg.toString());
if(to<seg.length()){
break;
}
}
return totalto;
}
private static void getSegments(Font f,Segment segment,List<Segment> segments,List<Boolean> unis){
private static void getSegments(Font f,Segment segment,List<Segment> segments,List<Boolean> unis){
int start=0;
int len=0;
boolean uni=false;
boolean uni=false;
for(int i=0;i<segment.length();i++){
boolean newuni=false;
if(!f.canDisplay(segment.charAt(i))){
@@ -96,25 +70,23 @@ public class UniTools {
unis.add(uni);
}
}
public static int getTabbedTextWidth(Segment segment,FontMetrics f,int x,TabExpander e, int startOffset){
Graphics g=new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getGraphics();
g.setFont(f.getFont());
return getTabbedTextWidth(g,segment,x,e,startOffset);
}
public static int getTabbedTextWidth(Graphics g,Segment segment,int x,TabExpander e, int startOffset){
List<Segment> segments=new ArrayList<Segment>();
List<Boolean> unis=new ArrayList<Boolean>();
List<Segment> segments=new ArrayList<>();
List<Boolean> unis=new ArrayList<>();
getSegments(g.getFont(), segment, segments, unis);
Font origFont=g.getFont();
Font origFont = g.getFont();
Font uniFont = defaultUniFont.deriveFont(origFont.getStyle(),origFont.getSize2D());
FontMetrics metrics = g.getFontMetrics(origFont);
FontMetrics uniMetrics = g.getFontMetrics(uniFont);
int ret=0;
int pos=0;
for(int i=0;i<segments.size();i++){
Segment seg=segments.get(i);
ret += Utilities.getTabbedTextWidth(seg, g.getFontMetrics(unis.get(i)?uniFont:origFont), 0, e, startOffset+pos);
pos += seg.length();
int segw = Utilities.getTabbedTextWidth(seg, unis.get(i) ? uniMetrics : metrics, x, e, startOffset+pos);
ret += segw;
x += segw;
pos += seg.length();
}
return ret;
}
@@ -156,4 +128,127 @@ public class UniTools {
}
return ret;
}
public static final int getTabbedTextOffset(Graphics g, Segment s,
FontMetrics metrics,
float x0, float x, TabExpander e,
int startOffset
) {
if (x0 >= x) {
// x before x0, return.
return 0;
}
float nextX = x0;
// s may be a shared segment, so it is copied prior to calling
// the tab expander
char[] txt = s.array;
int txtOffset = s.offset;
int txtCount = s.count;
int spaceAddon = 0 ;
int spaceAddonLeftoverEnd = -1;
int startJustifiableContent = 0 ;
int endJustifiableContent = 0;
int n = s.offset + s.count;
for (int i = s.offset; i < n; i++) {
if (txt[i] == '\t'
|| ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd)
&& (txt[i] == ' ')
&& startJustifiableContent <= i
&& i <= endJustifiableContent
)){
if (txt[i] == '\t') {
if (e != null) {
nextX = e.nextTabStop(nextX, startOffset + i - txtOffset);
} else {
nextX += getFontCharWidth(g, ' ', metrics);
}
} else if (txt[i] == ' ') {
nextX += getFontCharWidth(g, ' ', metrics);
nextX += spaceAddon;
if (i <= spaceAddonLeftoverEnd) {
nextX++;
}
}
} else {
nextX += getFontCharWidth(g, txt[i], metrics);
}
if (x < nextX) {
// found the hit position... return the appropriate side
int offset;
// the length of the string measured as a whole may differ from
// the sum of individual character lengths, for example if
// fractional metrics are enabled; and we must guard from this.
offset = i + 1 - txtOffset;
float width = getFontCharsWidth(g, txt, txtOffset, offset,
metrics);
float span = x - x0;
if (span < width) {
while (offset > 0) {
float charsWidth = getFontCharsWidth(g, txt, txtOffset,
offset - 1, metrics);
float nextWidth = offset > 1 ? charsWidth : 0;
if (span >= nextWidth) {
if (span - nextWidth < width - span) {
offset--;
}
break;
}
width = nextWidth;
offset--;
}
}
return offset;
}
}
// didn't find, return end offset
return txtCount;
}
private static float getFontCharWidth(Graphics g, char c, FontMetrics fm)
{
return getFontCharsWidth(g, new char[]{c}, 0, 1, fm);
}
private static float getFontCharsWidth(Graphics g, char[] data, int offset, int len,
FontMetrics fm)
{
if (len == 0) {
return 0;
}
Font uniFont = defaultUniFont.deriveFont(fm.getFont().getStyle(),fm.getFont().getSize2D());
FontMetrics uniMetrics = g.getFontMetrics(uniFont);
int w = 0;
int batchStart = offset;
boolean lastUni = false;
for (int i = offset; i < offset + len; i++) {
char c = data[i];
boolean uni = fm.getFont().canDisplay(c);
if (uni != lastUni && i > offset) {
if (!lastUni) {
w += uniMetrics.charsWidth(data, batchStart, i - batchStart);
} else {
w += fm.charsWidth(data, batchStart, i - batchStart);
}
batchStart = i;
}
lastUni = uni;
}
if (!lastUni) {
w += uniMetrics.charsWidth(data, batchStart, offset + len - batchStart);
} else {
w += fm.charsWidth(data, batchStart, offset + len - batchStart);
}
return w;
}
}