JBR-2234 Support CSS setting overflow-wrap:anywhere in JEditorPane

port from JBR 11 to JBR 15 (cherry picked from commits b6583d0a71, 6003abc15f)

cherry picked from commit 93ad4f06dd
This commit is contained in:
Dmitry Batrak
2020-03-31 15:36:04 +03:00
committed by alexey.ushakov@jetbrains.com
parent c624938627
commit 60d8918c3d
5 changed files with 106 additions and 33 deletions

View File

@@ -1607,7 +1607,12 @@ public abstract class AbstractDocument implements Document, Serializable {
/**
* Document property that indicates if a character has been inserted
* into the document that is more than one byte long. GlyphView uses
* this to determine if it should use BreakIterator.
* this to determine if it should use BreakIterator. This property should
* not be publicly exposed, since it is used for implementation convenience
* only. As a side effect, copies of this property may be in its subclasses
* that live in different packages (e.g. HTMLDocument as of now), so those
* copies should also be taken care of when this property needs to be
* modified.
*/
static final Object MultiByteProperty = "multiByte";

View File

@@ -770,38 +770,8 @@ public class GlyphView extends View implements TabableView, Cloneable {
private int getBreakSpot(int p0, int p1) {
if (breakSpots == null) {
// Re-calculate breakpoints for the whole view
int start = getStartOffset();
int end = getEndOffset();
int[] bs = new int[end + 1 - start];
int ix = 0;
// Breaker should work on the parent element because there may be
// a valid breakpoint at the end edge of the view (space, etc.)
Element parent = getElement().getParentElement();
int pstart = (parent == null ? start : parent.getStartOffset());
int pend = (parent == null ? end : parent.getEndOffset());
Segment s = getText(pstart, pend);
s.first();
BreakIterator breaker = getBreaker();
breaker.setText(s);
// Backward search should start from end+1 unless there's NO end+1
int startFrom = end + (pend > end ? 1 : 0);
for (;;) {
startFrom = breaker.preceding(s.offset + (startFrom - pstart))
+ (pstart - s.offset);
if (startFrom > start) {
// The break spot is within the view
bs[ix++] = startFrom;
} else {
break;
}
}
SegmentCache.releaseSharedSegment(s);
breakSpots = new int[ix];
System.arraycopy(bs, 0, breakSpots, 0, ix);
breakSpots = calcBreakSpots(breaker);
}
int breakSpot = BreakIterator.DONE;
@@ -817,6 +787,48 @@ public class GlyphView extends View implements TabableView, Cloneable {
return breakSpot;
}
/**
* Collects all possible breaking positions in the element,
* generated by the provided breaker.
*
* @param breaker provider of break positions
* @return possible break positions in the element
*/
protected int[] calcBreakSpots(BreakIterator breaker) {
int start = getStartOffset();
int end = getEndOffset();
int[] bs = new int[end + 1 - start];
int ix = 0;
// Breaker should work on the parent element because there may be
// a valid breakpoint at the end edge of the view (space, etc.)
Element parent = getElement().getParentElement();
int pstart = (parent == null ? start : parent.getStartOffset());
int pend = (parent == null ? end : parent.getEndOffset());
Segment s = getText(pstart, pend);
s.first();
breaker.setText(s);
// Backward search should start from end+1 unless there's NO end+1
int startFrom = end + (pend > end ? 1 : 0);
for (;;) {
startFrom = breaker.preceding(s.offset + (startFrom - pstart))
+ (pstart - s.offset);
if (startFrom > start) {
// The break spot is within the view
bs[ix++] = startFrom;
} else {
break;
}
}
SegmentCache.releaseSharedSegment(s);
int[] result = new int[ix];
System.arraycopy(bs, 0, result, 0, ix);
return result;
}
/**
* Return break iterator appropriate for the current document.
*

View File

@@ -95,6 +95,7 @@ import javax.swing.text.View;
* <li>list-style-image
* <li>list-style-type
* <li>list-style-position
* <li>overflow-wrap
* </ul>
* The following are modeled, but currently not rendered.
* <ul><li>font-variant
@@ -492,6 +493,12 @@ public class CSS implements Serializable {
public static final Attribute MARGIN_TOP =
new Attribute("margin-top", "0", false);
/**
* CSS attribute "overflow-wrap".
*/
public static final Attribute OVERFLOW_WRAP =
new Attribute("overflow-wrap", "normal", true);
/**
* CSS attribute "padding".
*/
@@ -595,7 +602,8 @@ public class CSS implements Serializable {
PADDING_TOP, TEXT_ALIGN, TEXT_DECORATION, TEXT_INDENT, TEXT_TRANSFORM,
VERTICAL_ALIGN, WORD_SPACING, WHITE_SPACE, WIDTH,
BORDER_SPACING, CAPTION_SIDE,
MARGIN_LEFT_LTR, MARGIN_LEFT_RTL, MARGIN_RIGHT_LTR, MARGIN_RIGHT_RTL
MARGIN_LEFT_LTR, MARGIN_LEFT_RTL, MARGIN_RIGHT_LTR, MARGIN_RIGHT_RTL,
OVERFLOW_WRAP
};
private static final Attribute[] ALL_MARGINS =

View File

@@ -1868,6 +1868,13 @@ public class HTMLDocument extends DefaultStyledDocument {
*/
private static final String I18NProperty = "i18n";
/**
* Multi-byte property key.
*
* @see AbstractDocument#MultiByteProperty
*/
static final String MultiByteProperty = "multiByte";
static {
contentAttributeSet = new SimpleAttributeSet();
((MutableAttributeSet)contentAttributeSet).

View File

@@ -26,6 +26,7 @@ package javax.swing.text.html;
import java.awt.*;
import java.text.BreakIterator;
import java.util.Locale;
import javax.swing.event.DocumentEvent;
import javax.swing.text.*;
@@ -61,6 +62,7 @@ public class InlineView extends LabelView {
* @see View#insertUpdate
*/
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
wrapAnywhereMinimumSpan = -1;
super.insertUpdate(e, a, f);
}
@@ -77,6 +79,7 @@ public class InlineView extends LabelView {
* @see View#removeUpdate
*/
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
wrapAnywhereMinimumSpan = -1;
super.removeUpdate(e, a, f);
}
@@ -90,6 +93,7 @@ public class InlineView extends LabelView {
* @see View#changedUpdate
*/
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
wrapAnywhereMinimumSpan = -1;
super.changedUpdate(e, a, f);
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
@@ -206,6 +210,8 @@ public class InlineView extends LabelView {
nowrap = false;
}
wrapAnywhere = "anywhere".equals(a.getAttribute(CSS.Attribute.OVERFLOW_WRAP));
HTMLDocument doc = (HTMLDocument)getDocument();
// fetches background color from stylesheet if specified
Color bg = doc.getBackground(a);
@@ -224,6 +230,41 @@ public class InlineView extends LabelView {
return doc.getStyleSheet();
}
@Override
public float getMinimumSpan(int axis) {
if (axis == View.X_AXIS && wrapAnywhere) {
if (wrapAnywhereMinimumSpan < 0) {
wrapAnywhereMinimumSpan = 0;
int startOffset = getStartOffset();
int endOffset = getEndOffset();
Document doc = getDocument();
if ((doc != null) && Boolean.TRUE.equals(doc.getProperty(HTMLDocument.MultiByteProperty))) {
Container c = getContainer();
Locale locale = (c == null ? Locale.getDefault() : c.getLocale());
BreakIterator breaker = BreakIterator.getCharacterInstance(locale);
int[] breakSpots = calcBreakSpots(breaker);
int lastBreak = endOffset;
for (int breakSpot : breakSpots) {
wrapAnywhereMinimumSpan = Math.max(wrapAnywhereMinimumSpan,
getPartialSpan(breakSpot, lastBreak));
lastBreak = breakSpot;
}
wrapAnywhereMinimumSpan = Math.max(wrapAnywhereMinimumSpan,
getPartialSpan(startOffset, lastBreak));
} else {
for (int i = startOffset ; i < endOffset; i++) {
wrapAnywhereMinimumSpan = Math.max(wrapAnywhereMinimumSpan,
getPartialSpan(i, i + 1));
}
}
}
return wrapAnywhereMinimumSpan;
}
return super.getMinimumSpan(axis);
}
private float wrapAnywhereMinimumSpan = -1;
private boolean wrapAnywhere;
private boolean nowrap;
private AttributeSet attr;
}