Skip to content
Open
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
@@ -1,10 +1,7 @@
package org.fxmisc.richtext.demo;

import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -20,13 +17,9 @@

import org.fxmisc.flowless.VirtualizedScrollPane;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.GenericStyledArea;
import org.fxmisc.richtext.LineNumberFactory;
import org.fxmisc.richtext.model.Paragraph;
import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyleSpansBuilder;
import org.reactfx.collection.ListModification;
import org.reactfx.Subscription;

public class JavaKeywordsDemo extends Application {

Expand Down Expand Up @@ -118,12 +111,7 @@ public void start(Stage primaryStage) {
// run: `cleanupWhenNoLongerNeedIt.unsubscribe();`
*/
// recompute syntax highlighting only for visible paragraph changes
// Note that this shows how it can be done but is not recommended for production where multi-
// line syntax requirements are needed, like comment blocks without a leading * on each line.
codeArea.getVisibleParagraphs().addModificationObserver
(
new VisibleParagraphStyler<>( codeArea, this::computeHighlighting )
);
codeArea.setVisibleOnlyStyler( (pNo,p) -> p.restyle(0, computeHighlighting(pNo, p.getText())) );

// auto-indent: insert previous line's indents on enter
final Pattern whiteSpace = Pattern.compile( "^\\s+" );
Expand All @@ -147,11 +135,10 @@ public void start(Stage primaryStage) {
primaryStage.show();
}

private StyleSpans<Collection<String>> computeHighlighting(String text) {
private StyleSpans<Collection<String>> computeHighlighting(Integer paragraphNo, String text) {
Matcher matcher = PATTERN.matcher(text);
int lastKwEnd = 0;
StyleSpansBuilder<Collection<String>> spansBuilder
= new StyleSpansBuilder<>();
StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
while(matcher.find()) {
String styleClass =
matcher.group("KEYWORD") != null ? "keyword" :
Expand All @@ -170,40 +157,6 @@ private StyleSpans<Collection<String>> computeHighlighting(String text) {
return spansBuilder.create();
}

private class VisibleParagraphStyler<PS, SEG, S> implements Consumer<ListModification<? extends Paragraph<PS, SEG, S>>>
{
private final GenericStyledArea<PS, SEG, S> area;
private final Function<String,StyleSpans<S>> computeStyles;
private int prevParagraph, prevTextLength;

public VisibleParagraphStyler( GenericStyledArea<PS, SEG, S> area, Function<String,StyleSpans<S>> computeStyles )
{
this.computeStyles = computeStyles;
this.area = area;
}

@Override
public void accept( ListModification<? extends Paragraph<PS, SEG, S>> lm )
{
if ( lm.getAddedSize() > 0 ) Platform.runLater( () ->
{
int paragraph = Math.min( area.firstVisibleParToAllParIndex() + lm.getFrom(), area.getParagraphs().size()-1 );
String text = area.getText( paragraph, 0, paragraph, area.getParagraphLength( paragraph ) );

if ( paragraph != prevParagraph || text.length() != prevTextLength )
{
if ( paragraph < area.getParagraphs().size()-1 )
{
int startPos = area.getAbsolutePosition( paragraph, 0 );
area.setStyleSpans( startPos, computeStyles.apply( text ) );
}
prevTextLength = text.length();
prevParagraph = paragraph;
}
});
}
}

private class DefaultContextMenu extends ContextMenu
{
private MenuItem fold, unfold, print;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -778,9 +778,9 @@ public GenericStyledArea(
// Initialize content
virtualFlow = VirtualFlow.createVertical(
getParagraphs(),
par -> {
(index, par) -> {
Cell<Paragraph<PS, SEG, S>, ParagraphBox<PS, SEG, S>> cell = createCell(
par,
index, par,
applyParagraphStyle,
nodeFactory);
nonEmptyCells.add(cell.getNode());
Expand Down Expand Up @@ -1548,6 +1548,24 @@ public final PS getParagraphStyleForInsertionAt(int pos) {
}
}

private BiFunction<Integer,Paragraph<PS, SEG, S>,Paragraph<PS, SEG, S>> visibleOnlyStyler;
/**
* This styler will only be applied to visible Paragraphs, just before being displayed.
* <p><b>Important Notes</b></p>
* <ol>
* <li> The result of this styling does NOT modify the document model.
* <li> Paragraph is immutable, so don't return the same object expecting changes.
* <li> The styler should return the result of one of Paragraph's restyle methods.
* </ol>
*/
public void setVisibleOnlyStyler(BiFunction<Integer,Paragraph<PS, SEG, S>,Paragraph<PS, SEG, S>> styler) {
visibleOnlyStyler = styler;
}

public void refreshParagraphs(int paragraphStart, int paragraphEnd) {
virtualFlow.refreshCells(paragraphStart, paragraphEnd);
}

@Override
public void replaceText(int start, int end, String text) {
StyledDocument<PS, SEG, S> doc = ReadOnlyStyledDocument.fromString(
Expand Down Expand Up @@ -1910,10 +1928,11 @@ ParagraphBox.CaretOffsetX getTargetCaretOffset() {
* ********************************************************************** */

private Cell<Paragraph<PS, SEG, S>, ParagraphBox<PS, SEG, S>> createCell(
Paragraph<PS, SEG, S> paragraph,
Integer index, Paragraph<PS, SEG, S> paragraph,
BiConsumer<TextFlow, PS> applyParagraphStyle,
Function<StyledSegment<SEG, S>, Node> nodeFactory) {

if (visibleOnlyStyler != null) paragraph = visibleOnlyStyler.apply(index, paragraph);
ParagraphBox<PS, SEG, S> box = new ParagraphBox<>(paragraph, applyParagraphStyle, nodeFactory);

box.highlightTextFillProperty().bind(highlightTextFill);
Expand Down