Skip to content

Commit 7c253dd

Browse files
Merge pull request #463 from JordanMartinez/removeSTAModel
Remove middleman: ESD is the model now, not STAModel
2 parents 7bf8f4c + ec94238 commit 7c253dd

8 files changed

Lines changed: 395 additions & 811 deletions

File tree

richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java

Lines changed: 261 additions & 84 deletions
Large diffs are not rendered by default.

richtextfx/src/main/java/org/fxmisc/richtext/StyledTextAreaBehavior.java

Lines changed: 50 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import javafx.scene.input.MouseButton;
2121
import javafx.scene.input.MouseEvent;
2222

23-
import org.fxmisc.richtext.model.StyledTextAreaModel;
2423
import org.fxmisc.richtext.model.NavigationActions.SelectionPolicy;
2524
import org.fxmisc.richtext.model.TwoDimensional.Position;
2625
import org.fxmisc.wellbehaved.event.EventPattern;
@@ -64,13 +63,13 @@ class StyledTextAreaBehavior {
6463
anyOf(keyPressed(PASTE), keyPressed(V, SHORTCUT_DOWN), keyPressed(INSERT, SHIFT_DOWN)),
6564
(b, e) -> b.view.paste()),
6665
// tab & newline
67-
consume(keyPressed(ENTER), (b, e) -> b.model.replaceSelection("\n")),
68-
consume(keyPressed(TAB), (b, e) -> b.model.replaceSelection("\t")),
66+
consume(keyPressed(ENTER), (b, e) -> b.view.replaceSelection("\n")),
67+
consume(keyPressed(TAB), (b, e) -> b.view.replaceSelection("\t")),
6968
// undo/redo
70-
consume(keyPressed(Z, SHORTCUT_DOWN), (b, e) -> b.model.undo()),
69+
consume(keyPressed(Z, SHORTCUT_DOWN), (b, e) -> b.view.undo()),
7170
consume(
7271
anyOf(keyPressed(Y, SHORTCUT_DOWN), keyPressed(Z, SHORTCUT_DOWN, SHIFT_DOWN)),
73-
(b, e) -> b.model.redo())
72+
(b, e) -> b.view.redo())
7473
);
7574
InputMapTemplate<StyledTextAreaBehavior, KeyEvent> edits = when(b -> b.view.isEditable(), editsBase);
7675

@@ -111,8 +110,8 @@ class StyledTextAreaBehavior {
111110
keyPressed(LEFT, SHORTCUT_DOWN),
112111
keyPressed(KP_LEFT, SHORTCUT_DOWN)
113112
), (b, e) -> b.skipToPrevWord(SelectionPolicy.CLEAR)),
114-
consume(keyPressed(HOME, SHORTCUT_DOWN), (b, e) -> b.model.start(SelectionPolicy.CLEAR)),
115-
consume(keyPressed(END, SHORTCUT_DOWN), (b, e) -> b.model.end(SelectionPolicy.CLEAR)),
113+
consume(keyPressed(HOME, SHORTCUT_DOWN), (b, e) -> b.view.start(SelectionPolicy.CLEAR)),
114+
consume(keyPressed(END, SHORTCUT_DOWN), (b, e) -> b.view.end(SelectionPolicy.CLEAR)),
116115
// selection
117116
consume(
118117
anyOf(
@@ -126,8 +125,8 @@ class StyledTextAreaBehavior {
126125
), StyledTextAreaBehavior::selectLeft),
127126
consume(keyPressed(HOME, SHIFT_DOWN), (b, e) -> b.view.lineStart(selPolicy)),
128127
consume(keyPressed(END, SHIFT_DOWN), (b, e) -> b.view.lineEnd(selPolicy)),
129-
consume(keyPressed(HOME, SHIFT_DOWN, SHORTCUT_DOWN), (b, e) -> b.model.start(selPolicy)),
130-
consume(keyPressed(END, SHIFT_DOWN, SHORTCUT_DOWN), (b, e) -> b.model.end(selPolicy)),
128+
consume(keyPressed(HOME, SHIFT_DOWN, SHORTCUT_DOWN), (b, e) -> b.view.start(selPolicy)),
129+
consume(keyPressed(END, SHIFT_DOWN, SHORTCUT_DOWN), (b, e) -> b.view.end(selPolicy)),
131130
consume(
132131
anyOf(
133132
keyPressed(RIGHT, SHIFT_DOWN, SHORTCUT_DOWN),
@@ -138,7 +137,7 @@ class StyledTextAreaBehavior {
138137
keyPressed(LEFT, SHIFT_DOWN, SHORTCUT_DOWN),
139138
keyPressed(KP_LEFT, SHIFT_DOWN, SHORTCUT_DOWN)
140139
), (b, e) -> b.skipToPrevWord(selPolicy)),
141-
consume(keyPressed(A, SHORTCUT_DOWN), (b, e) -> b.model.selectAll())
140+
consume(keyPressed(A, SHORTCUT_DOWN), (b, e) -> b.view.selectAll())
142141
);
143142

144143
InputMapTemplate<StyledTextAreaBehavior, KeyEvent> copyAction = consume(
@@ -214,8 +213,6 @@ private enum DragState {
214213

215214
private final GenericStyledArea<?, ?, ?> view;
216215

217-
private final StyledTextAreaModel<?, ?, ?> model;
218-
219216
/**
220217
* Indicates whether selection is being dragged by the user.
221218
*/
@@ -229,7 +226,6 @@ private enum DragState {
229226

230227
StyledTextAreaBehavior(GenericStyledArea<?, ?, ?> area) {
231228
this.view = area;
232-
this.model = area.getModel();
233229

234230
InputMapTemplate.installFallback(EVENT_TEMPLATE, this, b -> b.view);
235231

@@ -266,7 +262,7 @@ private void keyTyped(KeyEvent event) {
266262
return;
267263
}
268264

269-
model.replaceSelection(text);
265+
view.replaceSelection(text);
270266
}
271267

272268
private static boolean isLegal(String text) {
@@ -280,71 +276,71 @@ private static boolean isLegal(String text) {
280276
}
281277

282278
private void deleteBackward(KeyEvent ignore) {
283-
IndexRange selection = model.getSelection();
279+
IndexRange selection = view.getSelection();
284280
if(selection.getLength() == 0) {
285-
model.deletePreviousChar();
281+
view.deletePreviousChar();
286282
} else {
287-
model.replaceSelection("");
283+
view.replaceSelection("");
288284
}
289285
}
290286

291287
private void deleteForward(KeyEvent ignore) {
292-
IndexRange selection = model.getSelection();
288+
IndexRange selection = view.getSelection();
293289
if(selection.getLength() == 0) {
294-
model.deleteNextChar();
290+
view.deleteNextChar();
295291
} else {
296-
model.replaceSelection("");
292+
view.replaceSelection("");
297293
}
298294
}
299295

300296
private void left(KeyEvent ignore) {
301-
IndexRange sel = model.getSelection();
297+
IndexRange sel = view.getSelection();
302298
if(sel.getLength() == 0) {
303-
model.previousChar(SelectionPolicy.CLEAR);
299+
view.previousChar(SelectionPolicy.CLEAR);
304300
} else {
305-
model.moveTo(sel.getStart(), SelectionPolicy.CLEAR);
301+
view.moveTo(sel.getStart(), SelectionPolicy.CLEAR);
306302
}
307303
}
308304

309305
private void right(KeyEvent ignore) {
310-
IndexRange sel = model.getSelection();
306+
IndexRange sel = view.getSelection();
311307
if(sel.getLength() == 0) {
312-
model.nextChar(SelectionPolicy.CLEAR);
308+
view.nextChar(SelectionPolicy.CLEAR);
313309
} else {
314-
model.moveTo(sel.getEnd(), SelectionPolicy.CLEAR);
310+
view.moveTo(sel.getEnd(), SelectionPolicy.CLEAR);
315311
}
316312
}
317313

318314
private void selectLeft(KeyEvent ignore) {
319-
model.previousChar(SelectionPolicy.ADJUST);
315+
view.previousChar(SelectionPolicy.ADJUST);
320316
}
321317

322318
private void selectRight(KeyEvent ignore) {
323-
model.nextChar(SelectionPolicy.ADJUST);
319+
view.nextChar(SelectionPolicy.ADJUST);
324320
}
325321

326322
private void selectWord() {
327-
model.wordBreaksBackwards(1, SelectionPolicy.CLEAR);
328-
model.wordBreaksForwards(1, SelectionPolicy.ADJUST);
323+
view.wordBreaksBackwards(1, SelectionPolicy.CLEAR);
324+
view.wordBreaksForwards(1, SelectionPolicy.ADJUST);
329325
}
330326

331327
private void deletePrevWord(KeyEvent ignore) {
332-
int end = model.getCaretPosition();
328+
int end = view.getCaretPosition();
333329

334330
if (end > 0) {
335-
model.wordBreaksBackwards(2, SelectionPolicy.CLEAR);
336-
int start = model.getCaretPosition();
337-
model.replaceText(start, end, "");
331+
view.wordBreaksBackwards(2, SelectionPolicy.CLEAR);
332+
int start = view.getCaretPosition();
333+
view.replaceText(start, end, "");
338334
}
339335
}
340336

341337
private void deleteNextWord(KeyEvent ignore) {
342-
int start = model.getCaretPosition();
338+
int start = view.getCaretPosition();
343339

344-
if (start < model.getLength()) {
345-
model.wordBreaksForwards(2, SelectionPolicy.CLEAR);
346-
int end = model.getCaretPosition();
347-
model.replaceText(start, end, "");
340+
if (start < view.getLength()) {
341+
view.wordBreaksForwards(2, SelectionPolicy.CLEAR);
342+
int end = view.getCaretPosition();
343+
view.replaceText(start, end, "");
348344
}
349345
}
350346

@@ -356,7 +352,7 @@ private void downLines(SelectionPolicy selectionPolicy, int nLines) {
356352
CharacterHit hit = view.hit(view.getTargetCaretOffset(), targetLine);
357353

358354
// update model
359-
model.moveTo(hit.getInsertionIndex(), selectionPolicy);
355+
view.moveTo(hit.getInsertionIndex(), selectionPolicy);
360356
}
361357
}
362358

@@ -369,23 +365,23 @@ private void nextLine(SelectionPolicy selectionPolicy) {
369365
}
370366

371367
private void skipToPrevWord(SelectionPolicy selectionPolicy) {
372-
int caretPos = model.getCaretPosition();
368+
int caretPos = view.getCaretPosition();
373369

374370
// if (0 == caretPos), do nothing as can't move to the left anyway
375371
if (1 <= caretPos ) {
376-
boolean prevCharIsWhiteSpace = isWhitespace(model.getText(caretPos - 1, caretPos).charAt(0));
377-
model.wordBreaksBackwards(prevCharIsWhiteSpace ? 2 : 1, selectionPolicy);
372+
boolean prevCharIsWhiteSpace = isWhitespace(view.getText(caretPos - 1, caretPos).charAt(0));
373+
view.wordBreaksBackwards(prevCharIsWhiteSpace ? 2 : 1, selectionPolicy);
378374
}
379375
}
380376

381377
private void skipToNextWord(SelectionPolicy selectionPolicy) {
382-
int caretPos = model.getCaretPosition();
383-
int length = model.getLength();
378+
int caretPos = view.getCaretPosition();
379+
int length = view.getLength();
384380

385381
// if (caretPos == length), do nothing as can't move to the right anyway
386382
if (caretPos <= length - 1) {
387-
boolean nextCharIsWhiteSpace = isWhitespace(model.getText(caretPos, caretPos + 1).charAt(0));
388-
model.wordBreaksForwards(nextCharIsWhiteSpace ? 2 : 1, selectionPolicy);
383+
boolean nextCharIsWhiteSpace = isWhitespace(view.getText(caretPos, caretPos + 1).charAt(0));
384+
view.wordBreaksForwards(nextCharIsWhiteSpace ? 2 : 1, selectionPolicy);
389385
}
390386
}
391387

@@ -420,14 +416,14 @@ private void mousePressed(MouseEvent e) {
420416
if(e.isShiftDown()) {
421417
// On Mac always extend selection,
422418
// switching anchor and caret if necessary.
423-
model.moveTo(
419+
view.moveTo(
424420
hit.getInsertionIndex(),
425421
isMac ? SelectionPolicy.EXTEND : SelectionPolicy.ADJUST);
426422
} else {
427423
switch (e.getClickCount()) {
428424
case 1: firstLeftPress(hit); break;
429425
case 2: selectWord(); break;
430-
case 3: model.selectParagraph(); break;
426+
case 3: view.selectParagraph(); break;
431427
default: // do nothing
432428
}
433429
}
@@ -438,7 +434,7 @@ private void mousePressed(MouseEvent e) {
438434

439435
private void firstLeftPress(CharacterHit hit) {
440436
view.clearTargetCaretOffset();
441-
IndexRange selection = model.getSelection();
437+
IndexRange selection = view.getSelection();
442438
if(view.isEditable() &&
443439
selection.getLength() != 0 &&
444440
hit.getCharacterIndex().isPresent() &&
@@ -448,7 +444,7 @@ private void firstLeftPress(CharacterHit hit) {
448444
dragSelection = DragState.POTENTIAL_DRAG;
449445
} else {
450446
dragSelection = DragState.NO_DRAG;
451-
model.moveTo(hit.getInsertionIndex(), SelectionPolicy.CLEAR);
447+
view.moveTo(hit.getInsertionIndex(), SelectionPolicy.CLEAR);
452448
}
453449
}
454450

@@ -486,9 +482,9 @@ private void dragTo(Point2D p) {
486482

487483
if(dragSelection == DragState.DRAG ||
488484
dragSelection == DragState.POTENTIAL_DRAG) { // MOUSE_DRAGGED may arrive even before DRAG_DETECTED
489-
model.positionCaret(hit.getInsertionIndex());
485+
view.positionCaret(hit.getInsertionIndex());
490486
} else {
491-
model.moveTo(hit.getInsertionIndex(), SelectionPolicy.ADJUST);
487+
view.moveTo(hit.getInsertionIndex(), SelectionPolicy.ADJUST);
492488
}
493489
}
494490

@@ -505,7 +501,7 @@ private void mouseReleased(MouseEvent e) {
505501
case POTENTIAL_DRAG:
506502
// drag didn't happen, position caret
507503
CharacterHit hit = view.hit(e.getX(), e.getY());
508-
model.moveTo(hit.getInsertionIndex(), SelectionPolicy.CLEAR);
504+
view.moveTo(hit.getInsertionIndex(), SelectionPolicy.CLEAR);
509505
break;
510506
case DRAG:
511507
// only handle drags if mouse was released inside of view

richtextfx/src/main/java/org/fxmisc/richtext/model/EditableStyledDocument.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
import org.reactfx.EventStream;
77
import org.reactfx.SuspendableNo;
8+
import org.reactfx.collection.LiveList;
89
import org.reactfx.value.Val;
910

1011
/**
1112
* Content model for {@link org.fxmisc.richtext.GenericStyledArea}. Implements edit operations
12-
* on styled text, but not worrying about additional aspects such as
13-
* caret or selection, which are handled by {@link StyledTextAreaModel}.
13+
* on styled text, but not worrying about view aspects.
1414
*/
1515
public interface EditableStyledDocument<PS, SEG, S> extends StyledDocument<PS, SEG, S> {
1616

@@ -30,7 +30,7 @@ public interface EditableStyledDocument<PS, SEG, S> extends StyledDocument<PS, S
3030
Val<Integer> lengthProperty();
3131

3232
@Override
33-
ObservableList<Paragraph<PS, SEG, S>> getParagraphs();
33+
LiveList<Paragraph<PS, SEG, S>> getParagraphs();
3434

3535
/**
3636
* Read-only snapshot of the current state of this document.

richtextfx/src/main/java/org/fxmisc/richtext/model/GenericEditableStyledDocumentBase.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,22 @@
66
import java.util.Collections;
77
import java.util.List;
88

9+
import javafx.collections.ObservableList;
910
import org.reactfx.EventSource;
1011
import org.reactfx.EventStream;
1112
import org.reactfx.Subscription;
13+
import org.reactfx.Suspendable;
14+
import org.reactfx.SuspendableEventStream;
1215
import org.reactfx.SuspendableNo;
1316
import org.reactfx.collection.LiveList;
1417
import org.reactfx.collection.LiveListBase;
1518
import org.reactfx.collection.MaterializedListModification;
1619
import org.reactfx.collection.QuasiListModification;
20+
import org.reactfx.collection.SuspendableList;
1721
import org.reactfx.collection.UnmodifiableByDefaultLiveList;
1822
import org.reactfx.util.BiIndex;
1923
import org.reactfx.util.Lists;
24+
import org.reactfx.value.SuspendableVal;
2025
import org.reactfx.value.Val;
2126

2227
/**
@@ -51,24 +56,26 @@ protected Subscription observeInputs() {
5156

5257
private ReadOnlyStyledDocument<PS, SEG, S> doc;
5358

54-
private final EventSource<RichTextChange<PS, SEG, S>> richChanges = new EventSource<>();
59+
private final EventSource<RichTextChange<PS, SEG, S>> internalRichChanges = new EventSource<>();
60+
private final SuspendableEventStream<RichTextChange<PS, SEG, S>> richChanges = internalRichChanges.pausable();
5561
@Override public EventStream<RichTextChange<PS, SEG, S>> richChanges() { return richChanges; }
5662

57-
private final Val<String> text = Val.create(() -> doc.getText(), richChanges);
63+
private final Val<String> internalText = Val.create(() -> doc.getText(), internalRichChanges);
64+
private final SuspendableVal<String> text = internalText.suspendable();
5865
@Override public String getText() { return text.getValue(); }
5966
@Override public Val<String> textProperty() { return text; }
6067

6168

62-
private final Val<Integer> length = Val.create(() -> doc.length(), richChanges);
69+
private final Val<Integer> internalLength = Val.create(() -> doc.length(), internalRichChanges);
70+
private final SuspendableVal<Integer> length = internalLength.suspendable();
6371
@Override public int getLength() { return length.getValue(); }
6472
@Override public Val<Integer> lengthProperty() { return length; }
6573
@Override public int length() { return length.getValue(); }
6674

6775
private final EventSource<MaterializedListModification<Paragraph<PS, SEG, S>>> parChanges =
6876
new EventSource<>();
6977

70-
private final LiveList<Paragraph<PS, SEG, S>> paragraphs = new ParagraphList();
71-
78+
private final SuspendableList<Paragraph<PS, SEG, S>> paragraphs = new ParagraphList().suspendable();
7279
@Override
7380
public LiveList<Paragraph<PS, SEG, S>> getParagraphs() {
7481
return paragraphs;
@@ -85,6 +92,17 @@ public ReadOnlyStyledDocument<PS, SEG, S> snapshot() {
8592

8693
GenericEditableStyledDocumentBase(Paragraph<PS, SEG, S> initialParagraph/*, SegmentOps<SEG, S> segmentOps*/) {
8794
this.doc = new ReadOnlyStyledDocument<>(Collections.singletonList(initialParagraph));
95+
96+
final Suspendable omniSuspendable = Suspendable.combine(
97+
text,
98+
length,
99+
100+
// add streams after properties, to be released before them
101+
richChanges,
102+
103+
// paragraphs to be released first
104+
paragraphs);
105+
omniSuspendable.suspendWhen(beingUpdated);
88106
}
89107

90108
/**
@@ -202,7 +220,7 @@ private void update(
202220
MaterializedListModification<Paragraph<PS, SEG, S>> parChange) {
203221
this.doc = newValue;
204222
beingUpdated.suspendWhile(() -> {
205-
richChanges.push(change);
223+
internalRichChanges.push(change);
206224
parChanges.push(parChange);
207225
});
208226
}

0 commit comments

Comments
 (0)