Skip to content

Commit cb02062

Browse files
committed
Merge pull request #190 from jobernolte/master
Merged proposed paragraph style feature branch
2 parents e404581 + 9898fe0 commit cb02062

22 files changed

Lines changed: 877 additions & 753 deletions

richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/RichText.java

Lines changed: 77 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import javafx.application.Application;
1818
import javafx.beans.binding.Bindings;
1919
import javafx.beans.binding.BooleanBinding;
20+
import javafx.beans.property.BooleanProperty;
21+
import javafx.beans.property.SimpleBooleanProperty;
2022
import javafx.collections.FXCollections;
2123
import javafx.scene.Scene;
2224
import javafx.scene.control.Button;
@@ -33,6 +35,8 @@
3335

3436
import org.fxmisc.richtext.Codec;
3537
import org.fxmisc.richtext.InlineStyleTextArea;
38+
import org.fxmisc.richtext.Paragraph;
39+
import org.fxmisc.richtext.StyleSpan;
3640
import org.fxmisc.richtext.StyleSpans;
3741
import org.reactfx.SuspendableNo;
3842

@@ -180,14 +184,16 @@ private static String cssColor(Color color) {
180184
final Optional<Color> backgroundColor;
181185

182186
public StyleInfo() {
183-
bold = Optional.empty();
184-
italic = Optional.empty();
185-
underline = Optional.empty();
186-
strikethrough = Optional.empty();
187-
fontSize = Optional.empty();
188-
fontFamily = Optional.empty();
189-
textColor = Optional.empty();
190-
backgroundColor = Optional.empty();
187+
this(
188+
Optional.empty(),
189+
Optional.empty(),
190+
Optional.empty(),
191+
Optional.empty(),
192+
Optional.empty(),
193+
Optional.empty(),
194+
Optional.empty(),
195+
Optional.empty()
196+
);
191197
}
192198

193199
public StyleInfo(
@@ -338,31 +344,34 @@ public static void main(String[] args) {
338344
launch(args);
339345
}
340346

341-
private final InlineStyleTextArea<StyleInfo> area =
342-
new InlineStyleTextArea<StyleInfo>(
347+
private final InlineStyleTextArea<StyleInfo, StyleInfo> area =
348+
new InlineStyleTextArea<>(
343349
StyleInfo.EMPTY.updateFontSize(12).updateFontFamily("Serif").updateTextColor(Color.BLACK),
344-
style -> style.toCss());
350+
StyleInfo::toCss,
351+
StyleInfo.EMPTY,
352+
StyleInfo::toCss);
345353
{
346354
area.setWrapText(true);
347355
area.setStyleCodec(StyleInfo.CODEC);
348356
}
349357

350358
private final SuspendableNo updatingToolbar = new SuspendableNo();
359+
private final BooleanProperty applyToParagraph = new SimpleBooleanProperty(false);
351360

352361
@Override
353362
public void start(Stage primaryStage) {
354363
CheckBox wrapToggle = new CheckBox("Wrap");
355364
wrapToggle.setSelected(true);
356365
area.wrapTextProperty().bind(wrapToggle.selectedProperty());
357-
Button undoBtn = createButton("undo", () -> area.undo());
358-
Button redoBtn = createButton("redo", () -> area.redo());
359-
Button cutBtn = createButton("cut", () -> area.cut());
360-
Button copyBtn = createButton("copy", () -> area.copy());
361-
Button pasteBtn = createButton("paste", () -> area.paste());
362-
Button boldBtn = createButton("bold", () -> toggleBold());
363-
Button italicBtn = createButton("italic", () -> toggleItalic());
364-
Button underlineBtn = createButton("underline", () -> toggleUnderline());
365-
Button strikeBtn = createButton("strikethrough", () -> toggleStrikethrough());
366+
Button undoBtn = createButton("undo", area::undo);
367+
Button redoBtn = createButton("redo", area::redo);
368+
Button cutBtn = createButton("cut", area::cut);
369+
Button copyBtn = createButton("copy", area::copy);
370+
Button pasteBtn = createButton("paste", area::paste);
371+
Button boldBtn = createButton("bold", this::toggleBold);
372+
Button italicBtn = createButton("italic", this::toggleItalic);
373+
Button underlineBtn = createButton("underline", this::toggleUnderline);
374+
Button strikeBtn = createButton("strikethrough", this::toggleStrikethrough);
366375
ComboBox<Integer> sizeCombo = new ComboBox<>(FXCollections.observableArrayList(5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 48, 56, 64, 72));
367376
sizeCombo.getSelectionModel().select(Integer.valueOf(12));
368377
ComboBox<String> familyCombo = new ComboBox<>(FXCollections.observableList(Font.getFamilies()));
@@ -407,9 +416,9 @@ protected boolean computeValue() {
407416
strike = styles.styleStream().anyMatch(s -> s.strikethrough.orElse(false));
408417
int[] sizes = styles.styleStream().mapToInt(s -> s.fontSize.orElse(-1)).distinct().toArray();
409418
fontSize = sizes.length == 1 ? sizes[0] : -1;
410-
String[] families = styles.styleStream().map(s -> s.fontFamily.orElse(null)).distinct().toArray(i -> new String[i]);
419+
String[] families = styles.styleStream().map(s -> s.fontFamily.orElse(null)).distinct().toArray(String[]::new);
411420
fontFamily = families.length == 1 ? families[0] : null;
412-
Color[] colors = styles.styleStream().map(s -> s.textColor.orElse(null)).distinct().toArray(i -> new Color[i]);
421+
Color[] colors = styles.styleStream().map(s -> s.textColor.orElse(null)).distinct().toArray(Color[]::new);
413422
textColor = colors.length == 1 ? colors[0] : null;
414423
Color[] backgrounds = styles.styleStream().map(s -> s.backgroundColor.orElse(null)).distinct().toArray(i -> new Color[i]);
415424
backgroundColor = backgrounds.length == 1 ? backgrounds[0] : null;
@@ -511,19 +520,35 @@ private Button createButton(String styleClass, Runnable action) {
511520
}
512521

513522
private void toggleBold() {
514-
updateStyleInSelection(spans -> StyleInfo.EMPTY.updateBold(!spans.styleStream().allMatch(style -> style.bold.orElse(false))));
523+
if (!applyToParagraph.get()) {
524+
updateStyleInSelection(spans -> StyleInfo.EMPTY.updateBold(!spans.styleStream().allMatch(style -> style.bold.orElse(false))));
525+
} else {
526+
updateParagraphStyleInSelection(styleInfo -> styleInfo.updateBold(!styleInfo.bold.orElse(false)));
527+
}
515528
}
516529

517530
private void toggleItalic() {
518-
updateStyleInSelection(spans -> StyleInfo.EMPTY.updateItalic(!spans.styleStream().allMatch(style -> style.italic.orElse(false))));
531+
if (!applyToParagraph.get()) {
532+
updateStyleInSelection(spans -> StyleInfo.EMPTY.updateItalic(!spans.styleStream().allMatch(style -> style.italic.orElse(false))));
533+
} else {
534+
updateParagraphStyleInSelection(styleInfo -> styleInfo.updateItalic(!styleInfo.italic.orElse(false)));
535+
}
519536
}
520537

521538
private void toggleUnderline() {
522-
updateStyleInSelection(spans -> StyleInfo.EMPTY.updateUnderline(!spans.styleStream().allMatch(style -> style.underline.orElse(false))));
539+
if (!applyToParagraph.get()) {
540+
updateStyleInSelection(spans -> StyleInfo.EMPTY.updateUnderline(!spans.styleStream().allMatch(style -> style.underline.orElse(false))));
541+
} else {
542+
updateParagraphStyleInSelection(styleInfo -> styleInfo.updateUnderline(!styleInfo.underline.orElse(false)));
543+
}
523544
}
524545

525546
private void toggleStrikethrough() {
526-
updateStyleInSelection(spans -> StyleInfo.EMPTY.updateStrikethrough(!spans.styleStream().allMatch(style -> style.strikethrough.orElse(false))));
547+
if (!applyToParagraph.get()) {
548+
updateStyleInSelection(spans -> StyleInfo.EMPTY.updateStrikethrough(!spans.styleStream().allMatch(style -> style.strikethrough.orElse(false))));
549+
} else {
550+
updateParagraphStyleInSelection(styleInfo -> styleInfo.updateStrikethrough(!styleInfo.strikethrough.orElse(false)));
551+
}
527552
}
528553

529554
private void updateStyleInSelection(Function<StyleSpans<StyleInfo>, StyleInfo> mixinGetter) {
@@ -538,34 +563,55 @@ private void updateStyleInSelection(Function<StyleSpans<StyleInfo>, StyleInfo> m
538563

539564
private void updateStyleInSelection(StyleInfo mixin) {
540565
IndexRange selection = area.getSelection();
541-
if(selection.getLength() != 0) {
566+
if (selection.getLength() != 0) {
542567
StyleSpans<StyleInfo> styles = area.getStyleSpans(selection);
543568
StyleSpans<StyleInfo> newStyles = styles.mapStyles(style -> style.updateWith(mixin));
544569
area.setStyleSpans(selection.getStart(), newStyles);
545570
}
546571
}
547572

573+
private void updateParagraphStyleInSelection(Function<StyleInfo, StyleInfo> updater) {
574+
Paragraph<StyleInfo, StyleInfo> paragraph = area.getParagraph(area.getCurrentParagraph());
575+
paragraph.setParagraphStyle(updater.apply(paragraph.getParagraphStyle()));
576+
}
577+
548578
private void updateFontSize(Integer size) {
549579
if(!updatingToolbar.get()) {
550-
updateStyleInSelection(StyleInfo.fontSize(size));
580+
if (!applyToParagraph.get()) {
581+
updateStyleInSelection(StyleInfo.fontSize(size));
582+
} else {
583+
updateParagraphStyleInSelection(styleInfo -> styleInfo.updateFontSize(size));
584+
}
551585
}
552586
}
553587

554588
private void updateFontFamily(String family) {
555589
if(!updatingToolbar.get()) {
556-
updateStyleInSelection(StyleInfo.fontFamily(family));
590+
if (!applyToParagraph.get()) {
591+
updateStyleInSelection(StyleInfo.fontFamily(family));
592+
} else {
593+
updateParagraphStyleInSelection(styleInfo -> styleInfo.updateFontFamily(family));
594+
}
557595
}
558596
}
559597

560598
private void updateTextColor(Color color) {
561599
if(!updatingToolbar.get()) {
562-
updateStyleInSelection(StyleInfo.textColor(color));
600+
if (!applyToParagraph.get()) {
601+
updateStyleInSelection(StyleInfo.textColor(color));
602+
} else {
603+
updateParagraphStyleInSelection(styleInfo -> styleInfo.updateTextColor(color));
604+
}
563605
}
564606
}
565607

566608
private void updateBackgroundColor(Color color) {
567609
if(!updatingToolbar.get()) {
568-
updateStyleInSelection(StyleInfo.backgroundColor(color));
610+
if (!applyToParagraph.get()) {
611+
updateStyleInSelection(StyleInfo.backgroundColor(color));
612+
} else {
613+
updateParagraphStyleInSelection(styleInfo -> styleInfo.updateBackgroundColor(color));
614+
}
569615
}
570616
}
571617
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
/**
1818
* Clipboard actions for {@link TextEditingArea}.
1919
*/
20-
public interface ClipboardActions<S> extends EditActions<S> {
20+
public interface ClipboardActions<S, PS> extends EditActions<S, PS> {
2121

2222
Optional<Codec<S>> getStyleCodec();
2323

@@ -43,9 +43,9 @@ default void copy() {
4343
content.putString(getSelectedText());
4444

4545
getStyleCodec().ifPresent(styleCodec -> {
46-
Codec<StyledDocument<S>> codec = ReadOnlyStyledDocument.codec(styleCodec);
46+
Codec<StyledDocument<S, PS>> codec = ReadOnlyStyledDocument.codec(styleCodec);
4747
DataFormat format = dataFormat(codec.getName());
48-
StyledDocument<S> doc = subDocument(selection.getStart(), selection.getEnd());
48+
StyledDocument<S, PS> doc = subDocument(selection.getStart(), selection.getEnd());
4949
ByteArrayOutputStream os = new ByteArrayOutputStream();
5050
DataOutputStream dos = new DataOutputStream(os);
5151
try {
@@ -71,13 +71,13 @@ default void paste() {
7171

7272
if(getStyleCodec().isPresent()) {
7373
Codec<S> styleCodec = getStyleCodec().get();
74-
Codec<StyledDocument<S>> codec = ReadOnlyStyledDocument.codec(styleCodec);
74+
Codec<StyledDocument<S, PS>> codec = ReadOnlyStyledDocument.codec(styleCodec);
7575
DataFormat format = dataFormat(codec.getName());
7676
if(clipboard.hasContent(format)) {
7777
byte[] bytes = (byte[]) clipboard.getContent(format);
7878
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
7979
DataInputStream dis = new DataInputStream(is);
80-
StyledDocument<S> doc = null;
80+
StyledDocument<S, PS> doc = null;
8181
try {
8282
doc = codec.decode(dis);
8383
} catch (IOException e) {

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/**
66
* Extended edit actions for {@link TextEditingArea}.
77
*/
8-
public interface EditActions<S> extends TextEditingArea<S> {
8+
public interface EditActions<S, PS> extends TextEditingArea<S, PS> {
99

1010
/**
1111
* Appends the given text to the end of the text content.
@@ -17,7 +17,7 @@ default void appendText(String text) {
1717
/**
1818
* Appends the given rich-text content to the end of this text-editing area.
1919
*/
20-
default void append(StyledDocument<S> document) {
20+
default void append(StyledDocument<S, PS> document) {
2121
insert(getLength(), document);
2222
}
2323

@@ -37,7 +37,7 @@ default void insertText(int index, String text) {
3737
* @param index The location to insert the text.
3838
* @param document The rich-text content to insert.
3939
*/
40-
default void insert(int index, StyledDocument<S> document) {
40+
default void insert(int index, StyledDocument<S, PS> document) {
4141
replace(index, index, document);
4242
}
4343

@@ -105,7 +105,7 @@ default void replaceText(String replacement) {
105105
/**
106106
* Replaces the entire content with the given rich-text content.
107107
*/
108-
default void replace(StyledDocument<S> replacement) {
108+
default void replace(StyledDocument<S, PS> replacement) {
109109
replace(0, getLength(), replacement);
110110
}
111111

@@ -125,7 +125,7 @@ default void replaceSelection(String replacement) {
125125
* caret position. If there was a selection, then the selection is cleared
126126
* and the given replacement text is inserted.
127127
*/
128-
default void replaceSelection(StyledDocument<S> replacement) {
128+
default void replaceSelection(StyledDocument<S, PS> replacement) {
129129
replace(getSelection(), replacement);
130130
}
131131

@@ -136,7 +136,7 @@ default void moveSelectedText(int pos) {
136136
// no move, just position the caret
137137
selectRange(pos, pos);
138138
} else {
139-
StyledDocument<S> text = this.subDocument(sel.getStart(), sel.getEnd());
139+
StyledDocument<S, PS> text = this.subDocument(sel.getStart(), sel.getEnd());
140140
if(pos > sel.getEnd())
141141
pos -= sel.getLength();
142142
deleteText(sel);

0 commit comments

Comments
 (0)