use of org.fxmisc.richtext.model.Paragraph in project RichTextFX by FXMisc.
the class GenericStyledArea method showCaretAtTop.
void showCaretAtTop() {
int parIdx = getCurrentParagraph();
Cell<Paragraph<PS, SEG, S>, ParagraphBox<PS, SEG, S>> cell = virtualFlow.getCell(parIdx);
Bounds caretBounds = cell.getNode().getCaretBounds(caretSelectionBind.getUnderlyingCaret());
double y = caretBounds.getMinY();
suspendVisibleParsWhile(() -> virtualFlow.showAtOffset(parIdx, -y));
}
use of org.fxmisc.richtext.model.Paragraph 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();
}
};
}
Aggregations