Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ public class Params {
/** ensures the caret is always visible when reaching the edge of screen in unwrapped mode, in pixels. */
public static final double HORIZONTAL_GUARD = 0; //10; FIX restore

/** maximum length of the text to pass to IME subsystem */
public static final int IME_MAX_TEXT_LENGTH = 4096;

/** focus background outline size */
public static final double LAYOUT_FOCUS_BORDER = 1;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,14 +324,7 @@ protected String getValidKeyTyped(KeyEvent ev) {

/** returns true if both control and model are editable */
protected boolean canEdit() {
RichTextArea control = getControl();
if (control.isEditable()) {
StyledTextModel m = control.getModel();
if (m != null) {
return m.isWritable();
}
}
return false;
return RichUtils.canEdit(getControl());
}

public boolean insertTab() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 com.sun.jfx.incubator.scene.control.richtext;

import com.sun.javafx.util.Utils;
import com.sun.jfx.incubator.scene.control.richtext.util.ListenerHelper;
import jfx.incubator.scene.control.richtext.RichTextArea;
import jfx.incubator.scene.control.richtext.TextPos;
import jfx.incubator.scene.control.richtext.skin.RichTextAreaSkin;

/**
* Manages RichTextArea Accessor.
*/
public class RichTextAreaHelper {

public interface Accessor {
public boolean getText(RichTextArea t, TextPos start, TextPos end, StringBuilder sb, int limit);
}

static {
Utils.forceInit(RichTextArea.class);
}

private static Accessor accessor;

public static void setAccessor(Accessor a) {
if (accessor != null) {
// this code might break when RTA is created outside of the fx application thread
// I am not sure what this check does really
throw new IllegalStateException();
}
accessor = a;
}

public static boolean getText(RichTextArea t, TextPos start, TextPos end, StringBuilder sb, int limit) {
return accessor.getText(t, start, end, sb, limit);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -212,6 +212,38 @@ public PathElement[] getCaretShape(Region target, int charIndex, boolean leading
return RichUtils.translatePath(target, content, p, dx, dy);
}

/**
* Returns the {@code PathElement} array for the underline shape,
* translated to the {@code target} frame of reference.
*
* @param target the Region that provides the target frame of reference
* @param start the start offset
* @param end the end offset
* @return the array of path elements translated to the target coordinates
*/
public PathElement[] getUnderlineShape(Region target, int start, int end) {
PathElement[] p;
double dx;
double dy;
if (content instanceof TextFlow f) {
dx = f.snappedLeftInset(); // TODO RTL?
dy = f.snappedTopInset();

p = f.getUnderlineShape(start, end);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
p = f.getUnderlineShape(start, end);
p = f.getUnderlineShape(start, end); // TODO new api

} else {
dx = 0.0;
dy = 0.0;
double w = getWidth();
double h = getHeight();

p = new PathElement[] {
new MoveTo(0.0, h),
new LineTo(w, h)
};
}
return RichUtils.translatePath(target, content, p, dx, dy);
}

/**
* Returns the {@code PathElement} array for the range shape,
* translated to the {@code target} frame of reference.
Expand All @@ -229,7 +261,7 @@ public PathElement[] getRangeShape(Region target, int start, int end) {
dx = f.snappedLeftInset(); // TODO RTL?
dy = f.snappedTopInset();

p = f.rangeShape(start, end);
p = f.rangeShape(start, end); // TODO new api, no null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this "TODO" really related to IME? If not, I recommend removing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code predates JDK-8357594
Additional geometry-based Text/TextFlow APIs. I am planning to migrate to the new API in a follow-up.

Created https://bugs.openjdk.org/browse/JDK-8370902

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for filing the RFE. Since you aren't otherwise modifying this method in this PR, adding a random TODO like this seems like an unrelated change. Now if you meant to also add this comment to line 232 (the call to f.getUnderlineShape(start, end) that you added above), then I could see also adding a reminder here as well.

So I recommend either adding a similar comment on line 232, or removing the unrelated TODOs.


if ((p == null) || (p.length == 0)) {
p = new PathElement[] {
Expand Down Expand Up @@ -365,7 +397,7 @@ public Integer lineEnd(int line) {
private RangeInfo getTextRange() {
if (content instanceof TextFlow f) {
int len = getTextLength();
PathElement[] pe = f.rangeShape(0, len);
PathElement[] pe = f.rangeShape(0, len); // TODO new api
if (pe.length > 0) {
double sp = f.getLineSpacing();
return RangeInfo.of(pe, sp);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

package com.sun.jfx.incubator.scene.control.richtext;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
Expand All @@ -42,6 +43,7 @@
import javafx.event.EventType;
import javafx.geometry.Insets;
import javafx.geometry.NodeOrientation;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.ScrollBar;
Expand All @@ -53,6 +55,7 @@
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.scene.shape.Shape;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.util.Duration;
Expand Down Expand Up @@ -534,7 +537,7 @@ public TextPos getTextPosLocal(double localX, double localY) {
}

/** in vflow.content coordinates */
protected CaretInfo getCaretInfo(TextPos p) {
public CaretInfo getCaretInfo(TextPos p) {
return arrangement().getCaretInfo(content, p);
}

Expand Down Expand Up @@ -1668,4 +1671,52 @@ protected void layoutCells() {
}
}
}

interface ShapeGenerator {
public PathElement[] generate(TextCell cell, int beginOffset, int endOffset);
}

private List<PathElement> getShapes(TextPos start, TextPos end, ShapeGenerator gen) {
ArrayList<PathElement> ss = new ArrayList<>(16);
int ix1 = start.index();
int ix2 = end.index();
for (int ix = ix1; ix <= ix2; ix++) {
TextCell cell = arrangement().getVisibleCell(ix);
if (cell == null) {
break;
}
int beginOffset = (ix == ix1) ? start.offset() : 0;
int endOffset = (ix == ix2) ? end.offset() : cell.getTextLength();
PathElement[] es = gen.generate(cell, beginOffset, endOffset);
for (PathElement em : es) {
ss.add(em);
}
}
return ss;
}

public List<PathElement> getRangeShape(TextPos start, TextPos end) {
return getShapes(start, end, (cell, beginOffset, endOffset) -> {
return cell.getRangeShape(content, beginOffset, endOffset);
});
}

public List<PathElement> getUnderlineShape(TextPos start, TextPos end) {
return getShapes(start, end, (cell, beginOffset, endOffset) -> {
return cell.getUnderlineShape(content, beginOffset, endOffset);
});
}

public void addImeHighlights(List<Shape> shapes, TextPos start) {
content.getChildren().addAll(shapes);
}

public void removeImHighlight(List<Shape> shapes) {
content.getChildren().removeAll(shapes);
}

public Point2D getImeLocationOnScreen(TextPos pos) {
CaretInfo ci = getCaretInfo(pos);
return content.localToScreen(ci.getMinX(), ci.getMaxY());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -35,6 +35,7 @@
import java.util.List;
import java.util.Locale;
import javax.imageio.ImageIO;
import javafx.application.ColorScheme;
import javafx.application.ConditionalFeature;
import javafx.application.Platform;
import javafx.css.CssMetaData;
Expand All @@ -47,7 +48,10 @@
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.input.InputMethodEvent;
import javafx.scene.input.InputMethodTextRun;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
Expand All @@ -61,7 +65,10 @@
import com.sun.javafx.scene.text.TextFlowHelper;
import com.sun.javafx.scene.text.TextLayout;
import com.sun.javafx.scene.text.TextLine;
import jfx.incubator.scene.control.richtext.RichTextArea;
import jfx.incubator.scene.control.richtext.TextPos;
import jfx.incubator.scene.control.richtext.model.StyleAttributeMap;
import jfx.incubator.scene.control.richtext.model.StyledTextModel;

/**
* RichTextArea specific utility methods.
Expand Down Expand Up @@ -695,4 +702,43 @@ public static StyleAttributeMap fromTextNode(Text textNode) {

return b.build();
}

/** returns true if both control and model are editable */
public static boolean canEdit(RichTextArea rta) {
if (rta.isEditable()) {
StyledTextModel m = rta.getModel();
if (m != null) {
return m.isWritable();
}
}
return false;
}

/** Returns the text positions at a positive offset relative to the 'start' position. */
public static TextPos advancePosition(TextPos start, int offset) {
return TextPos.ofLeading(start.index(), start.offset() + offset);
}

/** Returns true if the color scheme is DARK, checking first the node's scene, then platform preferences. */
public static boolean isDarkScheme(Node n) {
Scene sc = n.getScene();
if (sc != null) {
return (sc.getPreferences().getColorScheme() == ColorScheme.DARK);
}
return (Platform.getPreferences().getColorScheme() == ColorScheme.DARK);
}

/** Returns composed or committed text. */
public static String getImeText(InputMethodEvent ev) {
// it's either composed or committed but not both
if (ev.getComposed().size() > 0) {
StringBuilder sb = new StringBuilder();
for (InputMethodTextRun run : ev.getComposed()) {
sb.append(run.getText());
}
return sb.toString();
} else {
return ev.getCommitted();
}
}
}
Loading