|
6 | 6 |
|
7 | 7 | package org.fxmisc.richtext.demo.richtext; |
8 | 8 |
|
9 | | -import java.io.DataInputStream; |
10 | | -import java.io.DataOutputStream; |
11 | | -import java.io.IOException; |
12 | | -import java.nio.charset.MalformedInputException; |
13 | | -import java.util.Objects; |
14 | | -import java.util.Optional; |
15 | 9 | import java.util.function.Function; |
16 | 10 |
|
17 | 11 | import javafx.application.Application; |
|
33 | 27 | import javafx.scene.text.Font; |
34 | 28 | import javafx.stage.Stage; |
35 | 29 |
|
36 | | -import org.fxmisc.richtext.Codec; |
37 | 30 | import org.fxmisc.richtext.InlineStyleTextArea; |
38 | 31 | import org.fxmisc.richtext.Paragraph; |
39 | | -import org.fxmisc.richtext.StyleSpan; |
40 | 32 | import org.fxmisc.richtext.StyleSpans; |
41 | 33 | import org.reactfx.SuspendableNo; |
42 | 34 |
|
43 | 35 | public class RichText extends Application { |
44 | 36 |
|
45 | | - private static class StyleInfo { |
46 | | - |
47 | | - public static final StyleInfo EMPTY = new StyleInfo(); |
48 | | - |
49 | | - public static final Codec<StyleInfo> CODEC = new Codec<StyleInfo>() { |
50 | | - |
51 | | - private final Codec<Color> COLOR_CODEC = new Codec<Color>() { |
52 | | - |
53 | | - @Override |
54 | | - public String getName() { |
55 | | - return "color"; |
56 | | - } |
57 | | - |
58 | | - @Override |
59 | | - public void encode(DataOutputStream os, Color c) |
60 | | - throws IOException { |
61 | | - os.writeDouble(c.getRed()); |
62 | | - os.writeDouble(c.getGreen()); |
63 | | - os.writeDouble(c.getBlue()); |
64 | | - os.writeDouble(c.getOpacity()); |
65 | | - } |
66 | | - |
67 | | - @Override |
68 | | - public Color decode(DataInputStream is) throws IOException { |
69 | | - return Color.color( |
70 | | - is.readDouble(), |
71 | | - is.readDouble(), |
72 | | - is.readDouble(), |
73 | | - is.readDouble()); |
74 | | - } |
75 | | - |
76 | | - }; |
77 | | - |
78 | | - @Override |
79 | | - public String getName() { |
80 | | - return "style-info"; |
81 | | - } |
82 | | - |
83 | | - @Override |
84 | | - public void encode(DataOutputStream os, StyleInfo s) |
85 | | - throws IOException { |
86 | | - os.writeByte(encodeBoldItalicUnderlineStrikethrough(s)); |
87 | | - os.writeInt(encodeOptionalUint(s.fontSize)); |
88 | | - encodeOptional(os, s.fontFamily, Codec.STRING_CODEC); |
89 | | - encodeOptional(os, s.textColor, COLOR_CODEC); |
90 | | - encodeOptional(os, s.backgroundColor, COLOR_CODEC); |
91 | | - } |
92 | | - |
93 | | - @Override |
94 | | - public StyleInfo decode(DataInputStream is) throws IOException { |
95 | | - byte bius = is.readByte(); |
96 | | - Optional<Integer> fontSize = decodeOptionalUint(is.readInt()); |
97 | | - Optional<String> fontFamily = decodeOptional(is, Codec.STRING_CODEC); |
98 | | - Optional<Color> textColor = decodeOptional(is, COLOR_CODEC); |
99 | | - Optional<Color> bgrColor = decodeOptional(is, COLOR_CODEC); |
100 | | - return new StyleInfo( |
101 | | - bold(bius), italic(bius), underline(bius), strikethrough(bius), |
102 | | - fontSize, fontFamily, textColor, bgrColor); |
103 | | - } |
104 | | - |
105 | | - private int encodeBoldItalicUnderlineStrikethrough(StyleInfo s) { |
106 | | - return encodeOptionalBoolean(s.bold) << 6 | |
107 | | - encodeOptionalBoolean(s.italic) << 4 | |
108 | | - encodeOptionalBoolean(s.underline) << 2 | |
109 | | - encodeOptionalBoolean(s.strikethrough); |
110 | | - } |
111 | | - |
112 | | - private Optional<Boolean> bold(byte bius) throws IOException { |
113 | | - return decodeOptionalBoolean((bius >> 6) & 3); |
114 | | - } |
115 | | - |
116 | | - private Optional<Boolean> italic(byte bius) throws IOException { |
117 | | - return decodeOptionalBoolean((bius >> 4) & 3); |
118 | | - } |
119 | | - |
120 | | - private Optional<Boolean> underline(byte bius) throws IOException { |
121 | | - return decodeOptionalBoolean((bius >> 2) & 3); |
122 | | - } |
123 | | - |
124 | | - private Optional<Boolean> strikethrough(byte bius) throws IOException { |
125 | | - return decodeOptionalBoolean((bius >> 0) & 3); |
126 | | - } |
127 | | - |
128 | | - private int encodeOptionalBoolean(Optional<Boolean> ob) { |
129 | | - return ob.map(b -> 2 + (b ? 1 : 0)).orElse(0); |
130 | | - } |
131 | | - |
132 | | - private Optional<Boolean> decodeOptionalBoolean(int i) throws IOException { |
133 | | - switch(i) { |
134 | | - case 0: return Optional.empty(); |
135 | | - case 2: return Optional.of(false); |
136 | | - case 3: return Optional.of(true); |
137 | | - } |
138 | | - throw new MalformedInputException(0); |
139 | | - } |
140 | | - |
141 | | - private int encodeOptionalUint(Optional<Integer> oi) { |
142 | | - return oi.orElse(-1); |
143 | | - } |
144 | | - |
145 | | - private Optional<Integer> decodeOptionalUint(int i) { |
146 | | - return (i < 0) ? Optional.empty() : Optional.of(i); |
147 | | - } |
148 | | - |
149 | | - private <T> void encodeOptional(DataOutputStream os, Optional<T> ot, Codec<T> codec) throws IOException { |
150 | | - if(ot.isPresent()) { |
151 | | - os.writeBoolean(true); |
152 | | - codec.encode(os, ot.get()); |
153 | | - } else { |
154 | | - os.writeBoolean(false); |
155 | | - } |
156 | | - } |
157 | | - |
158 | | - private <T> Optional<T> decodeOptional(DataInputStream is, Codec<T> codec) throws IOException { |
159 | | - return is.readBoolean() |
160 | | - ? Optional.of(codec.decode(is)) |
161 | | - : Optional.empty(); |
162 | | - } |
163 | | - }; |
164 | | - |
165 | | - public static StyleInfo fontSize(int fontSize) { return EMPTY.updateFontSize(fontSize); } |
166 | | - public static StyleInfo fontFamily(String family) { return EMPTY.updateFontFamily(family); } |
167 | | - public static StyleInfo textColor(Color color) { return EMPTY.updateTextColor(color); } |
168 | | - public static StyleInfo backgroundColor(Color color) { return EMPTY.updateBackgroundColor(color); } |
169 | | - |
170 | | - private static String cssColor(Color color) { |
171 | | - int red = (int) (color.getRed() * 255); |
172 | | - int green = (int) (color.getGreen() * 255); |
173 | | - int blue = (int) (color.getBlue() * 255); |
174 | | - return "rgb(" + red + ", " + green + ", " + blue + ")"; |
175 | | - } |
176 | | - |
177 | | - final Optional<Boolean> bold; |
178 | | - final Optional<Boolean> italic; |
179 | | - final Optional<Boolean> underline; |
180 | | - final Optional<Boolean> strikethrough; |
181 | | - final Optional<Integer> fontSize; |
182 | | - final Optional<String> fontFamily; |
183 | | - final Optional<Color> textColor; |
184 | | - final Optional<Color> backgroundColor; |
185 | | - |
186 | | - public StyleInfo() { |
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 | | - ); |
197 | | - } |
198 | | - |
199 | | - public StyleInfo( |
200 | | - Optional<Boolean> bold, |
201 | | - Optional<Boolean> italic, |
202 | | - Optional<Boolean> underline, |
203 | | - Optional<Boolean> strikethrough, |
204 | | - Optional<Integer> fontSize, |
205 | | - Optional<String> fontFamily, |
206 | | - Optional<Color> textColor, |
207 | | - Optional<Color> backgroundColor) { |
208 | | - this.bold = bold; |
209 | | - this.italic = italic; |
210 | | - this.underline = underline; |
211 | | - this.strikethrough = strikethrough; |
212 | | - this.fontSize = fontSize; |
213 | | - this.fontFamily = fontFamily; |
214 | | - this.textColor = textColor; |
215 | | - this.backgroundColor = backgroundColor; |
216 | | - } |
217 | | - |
218 | | - @Override |
219 | | - public int hashCode() { |
220 | | - return Objects.hash( |
221 | | - bold, italic, underline, strikethrough, |
222 | | - fontSize, fontFamily, textColor, backgroundColor); |
223 | | - } |
224 | | - |
225 | | - @Override |
226 | | - public boolean equals(Object other) { |
227 | | - if(other instanceof StyleInfo) { |
228 | | - StyleInfo that = (StyleInfo) other; |
229 | | - return Objects.equals(this.bold, that.bold) && |
230 | | - Objects.equals(this.italic, that.italic) && |
231 | | - Objects.equals(this.underline, that.underline) && |
232 | | - Objects.equals(this.strikethrough, that.strikethrough) && |
233 | | - Objects.equals(this.fontSize, that.fontSize) && |
234 | | - Objects.equals(this.fontFamily, that.fontFamily) && |
235 | | - Objects.equals(this.textColor, that.textColor) && |
236 | | - Objects.equals(this.backgroundColor, that.backgroundColor); |
237 | | - } else { |
238 | | - return false; |
239 | | - } |
240 | | - } |
241 | | - |
242 | | - public String toCss() { |
243 | | - StringBuilder sb = new StringBuilder(); |
244 | | - |
245 | | - if(bold.isPresent()) { |
246 | | - if(bold.get()) { |
247 | | - sb.append("-fx-font-weight: bold;"); |
248 | | - } else { |
249 | | - sb.append("-fx-font-weight: normal;"); |
250 | | - } |
251 | | - } |
252 | | - |
253 | | - if(italic.isPresent()) { |
254 | | - if(italic.get()) { |
255 | | - sb.append("-fx-font-style: italic;"); |
256 | | - } else { |
257 | | - sb.append("-fx-font-style: normal;"); |
258 | | - } |
259 | | - } |
260 | | - |
261 | | - if(underline.isPresent()) { |
262 | | - if(underline.get()) { |
263 | | - sb.append("-fx-underline: true;"); |
264 | | - } else { |
265 | | - sb.append("-fx-underline: false;"); |
266 | | - } |
267 | | - } |
268 | | - |
269 | | - if(strikethrough.isPresent()) { |
270 | | - if(strikethrough.get()) { |
271 | | - sb.append("-fx-strikethrough: true;"); |
272 | | - } else { |
273 | | - sb.append("-fx-strikethrough: false;"); |
274 | | - } |
275 | | - } |
276 | | - |
277 | | - if(fontSize.isPresent()) { |
278 | | - sb.append("-fx-font-size: " + fontSize.get() + "pt;"); |
279 | | - } |
280 | | - |
281 | | - if(fontFamily.isPresent()) { |
282 | | - sb.append("-fx-font-family: " + fontFamily.get() + ";"); |
283 | | - } |
284 | | - |
285 | | - if(textColor.isPresent()) { |
286 | | - Color color = textColor.get(); |
287 | | - sb.append("-fx-fill: " + cssColor(color) + ";"); |
288 | | - } |
289 | | - |
290 | | - if(backgroundColor.isPresent()) { |
291 | | - Color color = backgroundColor.get(); |
292 | | - sb.append("-fx-background-fill: " + cssColor(color) + ";"); |
293 | | - } |
294 | | - |
295 | | - return sb.toString(); |
296 | | - } |
297 | | - |
298 | | - public StyleInfo updateWith(StyleInfo mixin) { |
299 | | - return new StyleInfo( |
300 | | - mixin.bold.isPresent() ? mixin.bold : bold, |
301 | | - mixin.italic.isPresent() ? mixin.italic : italic, |
302 | | - mixin.underline.isPresent() ? mixin.underline : underline, |
303 | | - mixin.strikethrough.isPresent() ? mixin.strikethrough : strikethrough, |
304 | | - mixin.fontSize.isPresent() ? mixin.fontSize : fontSize, |
305 | | - mixin.fontFamily.isPresent() ? mixin.fontFamily : fontFamily, |
306 | | - mixin.textColor.isPresent() ? mixin.textColor : textColor, |
307 | | - mixin.backgroundColor.isPresent() ? mixin.backgroundColor : backgroundColor); |
308 | | - } |
309 | | - |
310 | | - public StyleInfo updateBold(boolean bold) { |
311 | | - return new StyleInfo(Optional.of(bold), italic, underline, strikethrough, fontSize, fontFamily, textColor, backgroundColor); |
312 | | - } |
313 | | - |
314 | | - public StyleInfo updateItalic(boolean italic) { |
315 | | - return new StyleInfo(bold, Optional.of(italic), underline, strikethrough, fontSize, fontFamily, textColor, backgroundColor); |
316 | | - } |
317 | | - |
318 | | - public StyleInfo updateUnderline(boolean underline) { |
319 | | - return new StyleInfo(bold, italic, Optional.of(underline), strikethrough, fontSize, fontFamily, textColor, backgroundColor); |
320 | | - } |
321 | | - |
322 | | - public StyleInfo updateStrikethrough(boolean strikethrough) { |
323 | | - return new StyleInfo(bold, italic, underline, Optional.of(strikethrough), fontSize, fontFamily, textColor, backgroundColor); |
324 | | - } |
325 | | - |
326 | | - public StyleInfo updateFontSize(int fontSize) { |
327 | | - return new StyleInfo(bold, italic, underline, strikethrough, Optional.of(fontSize), fontFamily, textColor, backgroundColor); |
328 | | - } |
329 | | - |
330 | | - public StyleInfo updateFontFamily(String fontFamily) { |
331 | | - return new StyleInfo(bold, italic, underline, strikethrough, fontSize, Optional.of(fontFamily), textColor, backgroundColor); |
332 | | - } |
333 | | - |
334 | | - public StyleInfo updateTextColor(Color textColor) { |
335 | | - return new StyleInfo(bold, italic, underline, strikethrough, fontSize, fontFamily, Optional.of(textColor), backgroundColor); |
336 | | - } |
337 | | - |
338 | | - public StyleInfo updateBackgroundColor(Color backgroundColor) { |
339 | | - return new StyleInfo(bold, italic, underline, strikethrough, fontSize, fontFamily, textColor, Optional.of(backgroundColor)); |
340 | | - } |
341 | | - } |
342 | | - |
343 | 37 | public static void main(String[] args) { |
344 | 38 | launch(args); |
345 | 39 | } |
|
0 commit comments