1
1
import 'dart:convert' ;
2
+ import 'dart:math' ;
2
3
3
4
import 'package:chewie/chewie.dart' ;
4
5
import 'package:chewie_audio/chewie_audio.dart' ;
@@ -19,14 +20,19 @@ import 'package:html/dom.dart' as dom;
19
20
/// A [ReplacedElement] may use its children nodes to determine relevant information
20
21
/// (e.g. <video>'s <source> tags), but the children nodes will not be saved as [children] .
21
22
abstract class ReplacedElement extends StyledElement {
22
- ReplacedElement ({
23
- String name,
24
- Style style,
25
- dom.Element node,
26
- }) : super (name: name, children: null , style: style, node: node);
23
+ PlaceholderAlignment alignment;
24
+
25
+ ReplacedElement (
26
+ {String name,
27
+ Style style,
28
+ dom.Element node,
29
+ this .alignment = PlaceholderAlignment .aboveBaseline})
30
+ : super (name: name, children: null , style: style, node: node);
27
31
28
32
static List <String > parseMediaSources (List <dom.Element > elements) {
29
- return elements.where ((element) => element.localName == 'source' ).map ((element) {
33
+ return elements
34
+ .where ((element) => element.localName == 'source' )
35
+ .map ((element) {
30
36
return element.attributes['src' ];
31
37
}).toList ();
32
38
}
@@ -68,7 +74,8 @@ class ImageContentElement extends ReplacedElement {
68
74
69
75
@override
70
76
Widget toWidget (RenderContext context) {
71
- if (src == null ) return Text (alt ?? "" , style: context.style.generateTextStyle ());
77
+ if (src == null )
78
+ return Text (alt ?? "" , style: context.style.generateTextStyle ());
72
79
if (src.startsWith ("data:image" ) && src.contains ("base64," )) {
73
80
return Image .memory (base64.decode (src.split ("base64," )[1 ].trim ()));
74
81
} else {
@@ -110,7 +117,9 @@ class IframeContentElement extends ReplacedElement {
110
117
child: WebView (
111
118
initialUrl: src,
112
119
javascriptMode: JavascriptMode .unrestricted,
113
- gestureRecognizers: {Factory (() => PlatformViewVerticalGestureRecognizer ())},
120
+ gestureRecognizers: {
121
+ Factory (() => PlatformViewVerticalGestureRecognizer ())
122
+ },
114
123
),
115
124
);
116
125
}
@@ -189,7 +198,9 @@ class VideoContentElement extends ReplacedElement {
189
198
videoPlayerController: VideoPlayerController .network (
190
199
src.first ?? "" ,
191
200
),
192
- placeholder: poster != null ? Image .network (poster) : Container (color: Colors .black),
201
+ placeholder: poster != null
202
+ ? Image .network (poster)
203
+ : Container (color: Colors .black),
193
204
autoPlay: autoplay,
194
205
looping: loop,
195
206
showControls: showControls,
@@ -229,6 +240,55 @@ class EmptyContentElement extends ReplacedElement {
229
240
Widget toWidget (_) => null ;
230
241
}
231
242
243
+ class RubyElement extends ReplacedElement {
244
+ dom.Element element;
245
+
246
+ RubyElement ({@required this .element, String name = "ruby" })
247
+ : super (name: name, alignment: PlaceholderAlignment .middle);
248
+
249
+ @override
250
+ Widget toWidget (RenderContext context) {
251
+ dom.Node textNode = null ;
252
+ List <Widget > widgets = List <Widget >();
253
+ final rubySize = max (9.0 , context.style.fontSize / 2 );
254
+ final rubyYPos = rubySize + 2 ;
255
+ element.nodes.forEach ((c) {
256
+ if (c.nodeType == dom.Node .TEXT_NODE ) {
257
+ textNode = c;
258
+ }
259
+ if (c is dom.Element ) {
260
+ if (c.localName == "rt" && textNode != null ) {
261
+ final widget = Stack (
262
+ alignment: Alignment .center,
263
+ children: < Widget > [
264
+ Container (
265
+ alignment: Alignment .bottomCenter,
266
+ child: Center (
267
+ child: Transform (
268
+ transform:
269
+ Matrix4 .translationValues (0 , - (rubyYPos), 0 ),
270
+ child: Text (c.innerHtml,
271
+ style: context.style
272
+ .generateTextStyle ()
273
+ .copyWith (fontSize: rubySize))))),
274
+ Container (
275
+ child: Text (textNode.text.trim (),
276
+ style: context.style.generateTextStyle ())),
277
+ ],
278
+ );
279
+ widgets.add (widget);
280
+ }
281
+ }
282
+ });
283
+ return Row (
284
+ crossAxisAlignment: CrossAxisAlignment .end,
285
+ textBaseline: TextBaseline .alphabetic,
286
+ mainAxisSize: MainAxisSize .min,
287
+ children: widgets,
288
+ );
289
+ }
290
+ }
291
+
232
292
ReplacedElement parseReplacedElement (dom.Element element) {
233
293
switch (element.localName) {
234
294
case "audio" :
@@ -287,6 +347,10 @@ ReplacedElement parseReplacedElement(dom.Element element) {
287
347
width: double .tryParse (element.attributes['width' ] ?? "" ),
288
348
height: double .tryParse (element.attributes['height' ] ?? "" ),
289
349
);
350
+ case "ruby" :
351
+ return RubyElement (
352
+ element: element,
353
+ );
290
354
default :
291
355
return EmptyContentElement (name: element.localName);
292
356
}
0 commit comments