I just want to implement some code folding between brackets or carets like other java IDEs: eclipse, netbeans, intellij using codearea control of richtextfx library. Please give me some code hints? thank you.
-
3That could be difficult to implement. Probably out of scope for a StackOverflow answer. I’d suggest starting with an existing code editor that already implemented the functionality you want. I don’t know which one. But there are some Swing based and many html based code editors that are usable from JavaFX. – jewelsea Nov 11 '22 at 06:05
-
3As far as I can tell [monaco](https://stackoverflow.com/questions/69779045/stuck-on-javafx-graphics-does-not-export-com-sun-javafx-sg-prism-to-unnamed-modu/69781310#69781310) supports [fokding](https://github.com/microsoft/monaco-editor/issues/178), though I have never tried it with that feature. – jewelsea Nov 11 '22 at 06:14
1 Answers
The trick is to implement your own line number factory that adds a control to the line number label (e.g. the graphic of a Label) that indicates a spot where the lines can be folded, or that they are already folded.
When that graphic is clicked you would call: codeArea.foldParagraphs(startLine, endLine);
to fold the lines, or codeArea.unfoldParagraphs(line);
to unfold the lines.
How you determine if a line is foldable and how many lines to fold is out of scope for a StackOverflow answer. It can be a simple as checking if the line ("paragraph") ends with a '{'
and then scan ahead to the first '}'
. But that will fail in some cases, like a "}" that is part of a string or character literal or something like that.
Here is a skeleton of what the code might look like:
public class FoldingLineNumberFactory implements IntFunction<Node> {
private IntFunction<Node> defaultFactory;
private CodeArea editor;
public FoldingLineNumberFactory(CodeArea editor) {
this.editor = editor;
defaultFactory = LineNumberFactory.get(editor);
}
@Override
public Node apply(int lineNum) {
Node n = defaultFactory.apply(lineNum);
if (n instanceof Label lab) {
lab.setContentDisplay(ContentDisplay.RIGHT);
lab.setGraphic(graphic(lineNum));
}
return label;
}
private Node graphic(int line) {
// Folding support should be factored out into another class
if (beginsFoldableRegion(line)) {
boolean folded = isLineFolded(line);
var g = getFoldableGraphic(folded);
g.setUserData(line);
g.addEventFilter(MouseEvent.MOUSE_CLICKED, this::mouseClicked);
g.setCursor(Cursor.DEFAULT);
return g;
}
return nullGraphic(); // takes the same space, but is invisible
}
private void mouseClicked(MouseEvent e) {
if (e.getSource() instanceof Node n) {
if (n.getUserData() instanceof Integer line) {
if (isFoldedAt(line)) {
unfold(e);
} else {
fold(e);
}
e.consume();
}
}
}
private void fold(int line) {
// TODO: record somewhere that this line is folded
int lastLine = endLineForRegionStartingAt(line);
editor.foldParagraphs(line, lastLine);
}
private void unfold(int line) {
// TODO: record that this line isn't folded anymore
editor.unfoldParagraphs(line);
if (editor.getParagraphGraphic(line) instanceof Label lab) {
lab.setGraphic(graphic(line));
} else {
System.out.println("paragraph graphic not a Label");
}
}
protected boolean isLineFolded(int line) {
return false; // You need to implement this
}
protected boolean beginsFoldableRegion(int line) {
return false; // You need to implement this
}
private int endLineForRegionStartingAt(int line) {
// TODO: implement this
return -1;
}
private Node getFoldableGraphic(boolean folded) {
if (folded) {
return getFoldGraphic(); // TODO
} else {
return getUnfoldGraphic(); // TODO
}
}
private Node nullGraphic() {
var r = new Rectangle(10, 10, Color.TRANSPARENT); // just guessing at size
// setting stroke changes final size
r.setStroke(Color.TRANSPARENT);
return r;
}
How you fill in the rest depends on how you determine what is foldable, and what you want the folding control to look like.

- 3,890
- 2
- 23
- 31