use of org.reactfx.Subscription in project RichTextFX by FXMisc.
the class JavaKeywords method start.
@Override
public void start(Stage primaryStage) {
CodeArea codeArea = new CodeArea();
// add line numbers to the left of area
codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
// recompute the syntax highlighting 500 ms after user stops editing area
Subscription cleanupWhenNoLongerNeedIt = codeArea.multiPlainChanges().successionEnds(Duration.ofMillis(500)).subscribe(ignore -> codeArea.setStyleSpans(0, computeHighlighting(codeArea.getText())));
// when no longer need syntax highlighting and wish to clean up memory leaks
// run: `cleanupWhenNoLongerNeedIt.unsubscribe();`
codeArea.replaceText(0, 0, sampleCode);
Scene scene = new Scene(new StackPane(new VirtualizedScrollPane<>(codeArea)), 600, 400);
scene.getStylesheets().add(JavaKeywordsAsync.class.getResource("java-keywords.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.setTitle("Java Keywords Demo");
primaryStage.show();
}
use of org.reactfx.Subscription in project RichTextFX by FXMisc.
the class JavaKeywordsAsync method start.
@Override
public void start(Stage primaryStage) {
executor = Executors.newSingleThreadExecutor();
codeArea = new CodeArea();
codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
Subscription cleanupWhenDone = codeArea.multiPlainChanges().successionEnds(Duration.ofMillis(500)).supplyTask(this::computeHighlightingAsync).awaitLatest(codeArea.multiPlainChanges()).filterMap(t -> {
if (t.isSuccess()) {
return Optional.of(t.get());
} else {
t.getFailure().printStackTrace();
return Optional.empty();
}
}).subscribe(this::applyHighlighting);
// call when no longer need it: `cleanupWhenFinished.unsubscribe();`
codeArea.replaceText(0, 0, sampleCode);
Scene scene = new Scene(new StackPane(new VirtualizedScrollPane<>(codeArea)), 600, 400);
scene.getStylesheets().add(JavaKeywordsAsync.class.getResource("java-keywords.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.setTitle("Java Keywords Async Demo");
primaryStage.show();
}
use of org.reactfx.Subscription in project RichTextFX by FXMisc.
the class SpellChecking method start.
@Override
public void start(Stage primaryStage) {
StyleClassedTextArea textArea = new StyleClassedTextArea();
textArea.setWrapText(true);
Subscription cleanupWhenFinished = textArea.multiPlainChanges().successionEnds(Duration.ofMillis(500)).subscribe(change -> {
textArea.setStyleSpans(0, computeHighlighting(textArea.getText()));
});
// load the dictionary
try (InputStream input = getClass().getResourceAsStream("spellchecking.dict");
BufferedReader br = new BufferedReader(new InputStreamReader(input))) {
String line;
while ((line = br.readLine()) != null) {
dictionary.add(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// load the sample document
InputStream input2 = getClass().getResourceAsStream("spellchecking.txt");
try (java.util.Scanner s = new java.util.Scanner(input2)) {
String document = s.useDelimiter("\\A").hasNext() ? s.next() : "";
textArea.replaceText(0, 0, document);
}
Scene scene = new Scene(new StackPane(new VirtualizedScrollPane<>(textArea)), 600, 400);
scene.getStylesheets().add(getClass().getResource("spellchecking.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.setTitle("Spell Checking Demo");
primaryStage.show();
}
use of org.reactfx.Subscription in project RichTextFX by FXMisc.
the class GenericStyledArea method createCell.
/* ********************************************************************** *
* *
* Private methods *
* *
* ********************************************************************** */
private Cell<Paragraph<PS, SEG, S>, ParagraphBox<PS, SEG, S>> createCell(Paragraph<PS, SEG, S> paragraph, BiConsumer<TextFlow, PS> applyParagraphStyle, Function<StyledSegment<SEG, S>, Node> nodeFactory) {
ParagraphBox<PS, SEG, S> box = new ParagraphBox<>(paragraph, applyParagraphStyle, nodeFactory);
box.highlightTextFillProperty().bind(highlightTextFill);
box.wrapTextProperty().bind(wrapTextProperty());
box.graphicFactoryProperty().bind(paragraphGraphicFactoryProperty());
box.graphicOffset.bind(virtualFlow.breadthOffsetProperty());
EventStream<Integer> boxIndexValues = box.indexProperty().values().filter(i -> i != -1);
Subscription firstParPseudoClass = boxIndexValues.subscribe(idx -> box.pseudoClassStateChanged(FIRST_PAR, idx == 0));
Subscription lastParPseudoClass = EventStreams.combine(boxIndexValues, getParagraphs().sizeProperty().values()).subscribe(in -> in.exec((i, n) -> box.pseudoClassStateChanged(LAST_PAR, i == n - 1)));
// set up caret
Function<CaretNode, Subscription> subscribeToCaret = caret -> {
EventStream<Integer> caretIndexStream = EventStreams.nonNullValuesOf(caret.paragraphIndexProperty());
// a new event stream needs to be created for each caret added, so that it will immediately
// fire the box's current index value as an event, thereby running the code in the subscribe block
// Reusing boxIndexValues will not fire its most recent event, leading to a caret not being added
// Thus, we'll call the new event stream "fresh" box index values
EventStream<Integer> freshBoxIndexValues = box.indexProperty().values().filter(i -> i != -1);
return EventStreams.combine(caretIndexStream, freshBoxIndexValues).subscribe(t -> {
int caretParagraphIndex = t.get1();
int boxIndex = t.get2();
if (caretParagraphIndex == boxIndex) {
box.caretsProperty().add(caret);
} else {
box.caretsProperty().remove(caret);
}
});
};
Subscription caretSubscription = caretSet.addSubscriber(subscribeToCaret);
// TODO: how should 'hasCaret' be handled now?
Subscription hasCaretPseudoClass = EventStreams.combine(boxIndexValues, Val.wrap(currentParagraphProperty()).values()).map(t -> t.get1().equals(t.get2())).subscribe(value -> box.pseudoClassStateChanged(HAS_CARET, value));
Function<Selection<PS, SEG, S>, Subscription> subscribeToSelection = selection -> {
EventStream<Integer> startParagraphValues = EventStreams.nonNullValuesOf(selection.startParagraphIndexProperty());
EventStream<Integer> endParagraphValues = EventStreams.nonNullValuesOf(selection.endParagraphIndexProperty());
// see comment in caret section about why a new box index EventStream is needed
EventStream<Integer> freshBoxIndexValues = box.indexProperty().values().filter(i -> i != -1);
return EventStreams.combine(startParagraphValues, endParagraphValues, freshBoxIndexValues).subscribe(t -> {
int startPar = t.get1();
int endPar = t.get2();
int boxIndex = t.get3();
if (startPar <= boxIndex && boxIndex <= endPar) {
// So that we don't add multiple paths for the same selection,
// which leads to not removing the additional paths when selection is removed,
// this is a `Map#putIfAbsent(Key, Value)` implementation that creates the path lazily
SelectionPath p = box.selectionsProperty().get(selection);
if (p == null) {
// create & configure path
Val<IndexRange> range = Val.create(() -> boxIndex != -1 ? getParagraphSelection(selection, boxIndex) : EMPTY_RANGE, selection.rangeProperty());
SelectionPath path = new SelectionPath(range);
selection.configureSelectionPath(path);
box.selectionsProperty().put(selection, path);
}
} else {
box.selectionsProperty().remove(selection);
}
});
};
Subscription selectionSubscription = selectionSet.addSubscriber(subscribeToSelection);
return new Cell<Paragraph<PS, SEG, S>, ParagraphBox<PS, SEG, S>>() {
@Override
public ParagraphBox<PS, SEG, S> getNode() {
return box;
}
@Override
public void updateIndex(int index) {
box.setIndex(index);
}
@Override
public void dispose() {
box.highlightTextFillProperty().unbind();
box.wrapTextProperty().unbind();
box.graphicFactoryProperty().unbind();
box.graphicOffset.unbind();
firstParPseudoClass.unsubscribe();
lastParPseudoClass.unsubscribe();
caretSubscription.unsubscribe();
hasCaretPseudoClass.unsubscribe();
selectionSubscription.unsubscribe();
}
};
}
use of org.reactfx.Subscription in project RichTextFX by FXMisc.
the class SubscribeableContentsObsSetTest method adding_new_subscriber_when_list_has_contents_does_not_fire_invalidation_event.
@Test
public void adding_new_subscriber_when_list_has_contents_does_not_fire_invalidation_event() {
SubscribeableContentsObsSet<Integer> contentSet = new SubscribeableContentsObsSet<>();
contentSet.add(1);
contentSet.add(2);
contentSet.add(3);
// when a change occurs add the additions/removals in another list
SimpleBooleanProperty changeWasFired = new SimpleBooleanProperty(false);
Subscription removeInvalidationListener = contentSet.addInvalidationListener(change -> changeWasFired.set(true));
// when property is set to a new value, store the new value in storageList
contentSet.addSubscriber(ignore -> Subscription.EMPTY);
assertFalse(changeWasFired.get());
// cleanup
removeInvalidationListener.unsubscribe();
}
Aggregations