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

also includes JBR-4006 [JCK] javax.swing.text.html.CSS$Attribute.OVERFLOW_WRAP field breaks public API
(cherry picked from commit f20a3d8679)
and JBR-4007 [JCK] javax.swing.text.GlyphView.calcBreakSpots method breaks public API
(cherry picked from commit 1002eff4f3)
(cherry picked from commit f603f9e837)
This commit is contained in:
Dmitry Batrak
2020-03-31 15:36:04 +03:00
committed by jbrbot
parent 621577658c
commit 2f946a0dcb
6 changed files with 163 additions and 33 deletions

View File

@@ -1601,7 +1601,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

@@ -32,6 +32,8 @@ import java.util.Locale;
import javax.swing.UIManager;
import sun.swing.SwingUtilities2;
import sun.swing.text.GlyphViewAccessor;
import static sun.swing.SwingUtilities2.IMPLIED_CR;
/**
@@ -777,38 +779,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;
@@ -824,6 +796,58 @@ public class GlyphView extends View implements TabableView, Cloneable {
return breakSpot;
}
static {
GlyphViewAccessor.setAccessor(new GlyphViewAccessor() {
@Override
public int[] calcBreakSpots(GlyphView glyphView, BreakIterator breaker) {
return glyphView.calcBreakSpots(breaker);
}
});
}
// TODO: convert to protected method (removing accessor) when upstreaming the fix
/**
* 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
*/
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

@@ -97,6 +97,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
@@ -495,6 +496,14 @@ public class CSS implements Serializable {
public static final Attribute MARGIN_TOP =
new Attribute("margin-top", "0", false);
/**
* CSS attribute "overflow-wrap".
*
* TODO: make public when upstreaming the fix
*/
static final Attribute OVERFLOW_WRAP =
new Attribute("overflow-wrap", "normal", true);
/**
* CSS attribute "padding".
*/
@@ -598,7 +607,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

@@ -1894,6 +1894,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

@@ -24,8 +24,11 @@
*/
package javax.swing.text.html;
import sun.swing.text.GlyphViewAccessor;
import java.awt.*;
import java.text.BreakIterator;
import java.util.Locale;
import javax.swing.event.DocumentEvent;
import javax.swing.text.*;
@@ -61,6 +64,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 +81,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 +95,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 +212,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 +232,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 = GlyphViewAccessor.getAccessor().calcBreakSpots(this, 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;
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2021 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.swing.text;
import javax.swing.text.GlyphView;
import java.text.BreakIterator;
public abstract class GlyphViewAccessor {
private static GlyphViewAccessor accessor;
public static void setAccessor(GlyphViewAccessor a) {
accessor = a;
}
public static GlyphViewAccessor getAccessor() {
return accessor;
}
public abstract int[] calcBreakSpots(GlyphView glyphView, BreakIterator breaker);
}