Browse Source

add link GestureRecognizer

lucky1213 4 years ago
parent
commit
a4712443e6

+ 2
- 22
packages/zefyr/example/lib/src/full_page.dart View File

83
           )
83
           )
84
         ],
84
         ],
85
       ),
85
       ),
86
-      body: Column(
87
-        children: <Widget>[
88
-          Container(
89
-            child: RichText(
90
-                text: TextSpan(children: [
91
-              TextSpan(text: 'Flutter is', style: TextStyle(color: Colors.black)),
92
-              WidgetSpan(
93
-                child: SizedBox(
94
-                  height: 20,
95
-                  width: 20,
96
-                    child: Image.network(
97
-                        'http://links-emoticons.oss-cn-hangzhou.aliyuncs.com/v3/e100@2x.gif'),),
98
-              ),
99
-              TextSpan(text: '123123', style: TextStyle(color: Colors.black)),
100
-            ])),
101
-          ),
102
-          Expanded(
103
-            child: ZefyrScaffold(
86
+      body: ZefyrScaffold(
104
               child: ZefyrTheme(
87
               child: ZefyrTheme(
105
                 data: ZefyrThemeData(
88
                 data: ZefyrThemeData(
106
                   // attributeTheme: AttributeTheme(
89
                   // attributeTheme: AttributeTheme(
113
                   autofocus: false,
96
                   autofocus: false,
114
                   controller: _controller,
97
                   controller: _controller,
115
                   focusNode: _focusNode,
98
                   focusNode: _focusNode,
116
-                  mode: ZefyrMode.edit,
99
+                  mode: ZefyrMode.select,
117
                   imageDelegate: CustomImageDelegate(),
100
                   imageDelegate: CustomImageDelegate(),
118
                   linkDelegate: CustomLinkDelegate(),
101
                   linkDelegate: CustomLinkDelegate(),
119
                   keyboardAppearance: _darkTheme ? Brightness.dark : Brightness.light,
102
                   keyboardAppearance: _darkTheme ? Brightness.dark : Brightness.light,
120
                 ),
103
                 ),
121
               ),
104
               ),
122
             ),
105
             ),
123
-          ),
124
-        ],
125
-      ),
126
     );
106
     );
127
     if (_darkTheme) {
107
     if (_darkTheme) {
128
       return Theme(data: ThemeData.dark(), child: result);
108
       return Theme(data: ThemeData.dark(), child: result);

+ 9
- 2
packages/zefyr/example/lib/src/images.dart View File

4
 
4
 
5
 import 'dart:io';
5
 import 'dart:io';
6
 import 'dart:typed_data';
6
 import 'dart:typed_data';
7
+import 'package:flutter/gestures.dart';
8
+import 'package:flutter/services.dart';
7
 import 'package:image/image.dart' as Im;
9
 import 'package:image/image.dart' as Im;
8
 
10
 
9
 import 'package:flutter/material.dart';
11
 import 'package:flutter/material.dart';
77
                 color: Colors.white,
79
                 color: Colors.white,
78
                 child: Column(
80
                 child: Column(
79
                   children: <Widget>[
81
                   children: <Widget>[
80
-                    TextField(controller: _controller,),
82
+                    TextField(controller: _controller, inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],),
81
                     FlatButton(
83
                     FlatButton(
82
                       onPressed: () {
84
                       onPressed: () {
83
                         Navigator.pop(buildContext, ZefyrLinkEntity(
85
                         Navigator.pop(buildContext, ZefyrLinkEntity(
84
-                          text: '测试用',
86
+                          text: '测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用测试用',
85
                           url: _controller.text,
87
                           url: _controller.text,
86
                         ));
88
                         ));
87
                       },
89
                       },
103
     );
105
     );
104
   }
106
   }
105
   
107
   
108
+  @override
109
+  GestureRecognizer provideLinkGesture(ZefyrLinkEntity value) {
110
+    // return 
111
+    return TapGestureRecognizer()..onTap = () {print('The word touched is 123123');};
112
+  }
106
 }
113
 }
107
 
114
 
108
 Widget _buildMaterialDialogTransitions(
115
 Widget _buildMaterialDialogTransitions(

+ 14
- 170
packages/zefyr/lib/src/widgets/buttons.dart View File

866
 
866
 
867
   bool hasLink(NotusStyle style) => style.contains(NotusAttribute.link);
867
   bool hasLink(NotusStyle style) => style.contains(NotusAttribute.link);
868
 
868
 
869
-  String getLink([String defaultValue]) {
869
+  String getLink([String defaultValue = '']) {
870
     final editor = ZefyrToolbar.of(context).editor;
870
     final editor = ZefyrToolbar.of(context).editor;
871
     final attrs = editor.selectionStyle;
871
     final attrs = editor.selectionStyle;
872
     if (hasLink(attrs)) {
872
     if (hasLink(attrs)) {
875
     return defaultValue;
875
     return defaultValue;
876
   }
876
   }
877
 
877
 
878
+  String getText([String defaultValue = '']) {
879
+    final editor = ZefyrToolbar.of(context).editor;
880
+    final plainTextEditingValue = editor.controller.plainTextEditingValue;
881
+    if (!editor.selection.isCollapsed) {
882
+      return plainTextEditingValue.text.substring(editor.selection.start, editor.selection.end);
883
+    }
884
+    return defaultValue;
885
+  }
886
+
878
   OverlayEntry _overlayEntry;
887
   OverlayEntry _overlayEntry;
879
 
888
 
880
   Future<void> showOverlay() async {
889
   Future<void> showOverlay() async {
881
     final editor = ZefyrToolbar.of(context).editor;
890
     final editor = ZefyrToolbar.of(context).editor;
882
     editor.closeKeyboard(true);
891
     editor.closeKeyboard(true);
883
     var _selection = editor.selection;
892
     var _selection = editor.selection;
884
-    var value = ZefyrLinkEntity();
885
-    if (hasLink(editor.selectionStyle)) {
886
-      value =
887
-          value.copyWith(url: editor.selectionStyle.value(NotusAttribute.link));
888
-    }
889
-    // 已选中
890
-    if (!editor.selection.isCollapsed) {
891
-      var text = editor.controller.document
892
-          .toPlainText()
893
-          .substring(editor.selection.start, editor.selection.end);
894
-      value = value.copyWith(text: text);
895
-    }
896
-    ;
897
-    var result = await editor.linkDelegate.fillLink(context, value);
893
+    var result = await editor.linkDelegate.fillLink(context, ZefyrLinkEntity(
894
+      text: getText(),
895
+      url: getLink(),
896
+    ));
898
 
897
 
899
     if (result != null) {
898
     if (result != null) {
900
       // toolbar.editor.updateSelection(_selection, source: ChangeSource.local);
899
       // toolbar.editor.updateSelection(_selection, source: ChangeSource.local);
997
       toolbar.markNeedsRebuild();
996
       toolbar.markNeedsRebuild();
998
     });
997
     });
999
   }
998
   }
1000
-
1001
-  Widget buildOverlay(BuildContext context) {
1002
-    final toolbar = ZefyrToolbar.of(context);
1003
-    final style = toolbar.editor.selectionStyle;
1004
-
1005
-    String value = 'Tap to edit link';
1006
-    if (style.contains(NotusAttribute.link)) {
1007
-      value = style.value(NotusAttribute.link);
1008
-    }
1009
-    final clipboardEnabled = value != 'Tap to edit link';
1010
-    final body = !isEditing
1011
-        ? _LinkView(value: value, onTap: edit)
1012
-        : _LinkInput(
1013
-            key: _inputKey,
1014
-            controller: _inputController,
1015
-            formatError: _formatError,
1016
-          );
1017
-    final items = <Widget>[Expanded(child: body)];
1018
-    if (!isEditing) {
1019
-      final unlinkHandler = hasLink(style) ? unlink : null;
1020
-      final copyHandler = clipboardEnabled ? copyToClipboard : null;
1021
-      final openHandler = hasLink(style) ? openInBrowser : null;
1022
-      final buttons = <Widget>[
1023
-        toolbar.buildButton(context, ZefyrToolbarAction.unlink,
1024
-            onPressed: unlinkHandler),
1025
-        toolbar.buildButton(context, ZefyrToolbarAction.clipboardCopy,
1026
-            onPressed: copyHandler),
1027
-        toolbar.buildButton(
1028
-          context,
1029
-          ZefyrToolbarAction.openInBrowser,
1030
-          onPressed: openHandler,
1031
-        ),
1032
-      ];
1033
-      items.addAll(buttons);
1034
-    }
1035
-    final trailingPressed = isEditing ? doneEdit : closeOverlay;
1036
-    final trailingAction =
1037
-        isEditing ? ZefyrToolbarAction.confirm : ZefyrToolbarAction.close;
1038
-
1039
-    return ZefyrToolbarScaffold(
1040
-      body: Row(children: items),
1041
-      trailing: toolbar.buildButton(
1042
-        context,
1043
-        trailingAction,
1044
-        onPressed: trailingPressed,
1045
-      ),
1046
-    );
1047
-  }
1048
 }
999
 }
1049
 
1000
 
1050
-class _LinkInput extends StatefulWidget {
1051
-  final TextEditingController controller;
1052
-  final bool formatError;
1053
-
1054
-  const _LinkInput(
1055
-      {Key key, @required this.controller, this.formatError = false})
1056
-      : super(key: key);
1057
-
1058
-  @override
1059
-  _LinkInputState createState() {
1060
-    return _LinkInputState();
1061
-  }
1062
-}
1063
-
1064
-class _LinkInputState extends State<_LinkInput> {
1065
-  final FocusNode _focusNode = FocusNode();
1066
-
1067
-  ZefyrScope _editor;
1068
-  bool _didAutoFocus = false;
1069
-
1070
-  @override
1071
-  void didChangeDependencies() {
1072
-    super.didChangeDependencies();
1073
-    if (!_didAutoFocus) {
1074
-      FocusScope.of(context).requestFocus(_focusNode);
1075
-      _didAutoFocus = true;
1076
-    }
1077
-
1078
-    final toolbar = ZefyrToolbar.of(context);
1079
-
1080
-    if (_editor != toolbar.editor) {
1081
-      _editor?.toolbarFocusNode = null;
1082
-      _editor = toolbar.editor;
1083
-      _editor.toolbarFocusNode = _focusNode;
1084
-    }
1085
-  }
1086
-
1087
-  @override
1088
-  void dispose() {
1089
-    _editor?.toolbarFocusNode = null;
1090
-    _focusNode.dispose();
1091
-    _editor = null;
1092
-    super.dispose();
1093
-  }
1094
-
1095
-  @override
1096
-  Widget build(BuildContext context) {
1097
-    final theme = Theme.of(context);
1098
-    final toolbarTheme = ZefyrTheme.of(context).toolbarTheme;
1099
-    final color =
1100
-        widget.formatError ? Colors.redAccent : toolbarTheme.iconColor;
1101
-    final style = theme.textTheme.subhead.copyWith(color: color);
1102
-    return TextField(
1103
-      style: style,
1104
-      keyboardType: TextInputType.url,
1105
-      focusNode: _focusNode,
1106
-      controller: widget.controller,
1107
-      autofocus: true,
1108
-      decoration: InputDecoration(
1109
-        hintText: 'https://',
1110
-        filled: true,
1111
-        fillColor: toolbarTheme.color,
1112
-        border: InputBorder.none,
1113
-        contentPadding: const EdgeInsets.all(10.0),
1114
-      ),
1115
-    );
1116
-  }
1117
-}
1118
-
1119
-class _LinkView extends StatelessWidget {
1120
-  const _LinkView({Key key, @required this.value, this.onTap})
1121
-      : super(key: key);
1122
-  final String value;
1123
-  final VoidCallback onTap;
1124
-
1125
-  @override
1126
-  Widget build(BuildContext context) {
1127
-    final theme = Theme.of(context);
1128
-    final toolbarTheme = ZefyrTheme.of(context).toolbarTheme;
1129
-    Widget widget = ClipRect(
1130
-      child: ListView(
1131
-        scrollDirection: Axis.horizontal,
1132
-        children: <Widget>[
1133
-          Container(
1134
-            alignment: AlignmentDirectional.centerStart,
1135
-            constraints: BoxConstraints(minHeight: ZefyrToolbar.kToolbarHeight),
1136
-            padding: const EdgeInsets.all(10.0),
1137
-            child: Text(
1138
-              value,
1139
-              maxLines: 1,
1140
-              overflow: TextOverflow.ellipsis,
1141
-              style: theme.textTheme.subhead
1142
-                  .copyWith(color: toolbarTheme.disabledIconColor),
1143
-            ),
1144
-          )
1145
-        ],
1146
-      ),
1147
-    );
1148
-    if (onTap != null) {
1149
-      widget = GestureDetector(
1150
-        child: widget,
1151
-        onTap: onTap,
1152
-      );
1153
-    }
1154
-    return widget;
1155
-  }
1156
-}

+ 23
- 6
packages/zefyr/lib/src/widgets/common.dart View File

2
 // for details. All rights reserved. Use of this source code is governed by a
2
 // for details. All rights reserved. Use of this source code is governed by a
3
 // BSD-style license that can be found in the LICENSE file.
3
 // BSD-style license that can be found in the LICENSE file.
4
 import 'package:flutter/cupertino.dart';
4
 import 'package:flutter/cupertino.dart';
5
+import 'package:flutter/gestures.dart';
5
 import 'package:flutter/material.dart';
6
 import 'package:flutter/material.dart';
6
 import 'package:flutter/rendering.dart';
7
 import 'package:flutter/rendering.dart';
7
 import 'package:flutter/widgets.dart';
8
 import 'package:flutter/widgets.dart';
10
 import 'editable_box.dart';
11
 import 'editable_box.dart';
11
 import 'horizontal_rule.dart';
12
 import 'horizontal_rule.dart';
12
 import 'image.dart';
13
 import 'image.dart';
14
+import 'link.dart';
13
 import 'rich_text.dart';
15
 import 'rich_text.dart';
14
 import 'scope.dart';
16
 import 'scope.dart';
15
 import 'theme.dart';
17
 import 'theme.dart';
54
         node: widget.node,
56
         node: widget.node,
55
         text: buildText(context),
57
         text: buildText(context),
56
       );
58
       );
59
+      // content = RichText(
60
+      //   text: TextSpan(
61
+      //     text: '123',
62
+      //   )
63
+      // );
57
     }
64
     }
58
 
65
 
59
     if (scope.isEditable) {
66
     if (scope.isEditable) {
119
 
126
 
120
   TextSpan buildText(BuildContext context) {
127
   TextSpan buildText(BuildContext context) {
121
     final theme = ZefyrTheme.of(context);
128
     final theme = ZefyrTheme.of(context);
122
-    final List<TextSpan> children = widget.node.children
129
+    final children = widget.node.children
123
         .map((node) => _segmentToTextSpan(node, theme))
130
         .map((node) => _segmentToTextSpan(node, theme))
124
         .toList(growable: false);
131
         .toList(growable: false);
125
     return TextSpan(style: widget.style, children: children);
132
     return TextSpan(style: widget.style, children: children);
126
   }
133
   }
127
 
134
 
128
-  TextSpan _segmentToTextSpan(Node node, ZefyrThemeData theme) {
135
+  InlineSpan _segmentToTextSpan(Node node, ZefyrThemeData theme) {
136
+    final _linkDelegate = ZefyrScope.of(context).linkDelegate;
129
     final TextNode segment = node;
137
     final TextNode segment = node;
130
     final attrs = segment.style;
138
     final attrs = segment.style;
131
-
139
+    GestureRecognizer recognizer = TapGestureRecognizer();
140
+    if (attrs.contains(NotusAttribute.link)) {
141
+      if (_linkDelegate != null) {
142
+        recognizer = ZefyrScope.of(context).linkDelegate.provideLinkGesture(ZefyrLinkEntity(
143
+          text: segment.value,
144
+          url: attrs.value(NotusAttribute.link),
145
+        ));
146
+      }
147
+    }
132
     return TextSpan(
148
     return TextSpan(
133
       text: segment.value,
149
       text: segment.value,
150
+      recognizer: TapGestureRecognizer()..onTap = () {
151
+        print('object');
152
+      },
134
       style: _getTextStyle(attrs, theme),
153
       style: _getTextStyle(attrs, theme),
135
     );
154
     );
136
   }
155
   }
155
       result = result.merge(theme.attributeTheme.link);
174
       result = result.merge(theme.attributeTheme.link);
156
     }
175
     }
157
     if (style.contains(NotusAttribute.highlight)) {
176
     if (style.contains(NotusAttribute.highlight)) {
158
-      result = result.merge(TextStyle(
159
-        backgroundColor: Color(0xFFFFAF64).withOpacity(0.5),
160
-      ));
177
+      result = result.merge(theme.attributeTheme.highlight);
161
     }
178
     }
162
     if (style.contains(NotusAttribute.color)) {
179
     if (style.contains(NotusAttribute.color)) {
163
       final hexStringToColor = (String hex) {
180
       final hexStringToColor = (String hex) {

+ 3
- 0
packages/zefyr/lib/src/widgets/link.dart View File

1
+import 'package:flutter/gestures.dart';
1
 import 'package:flutter/material.dart';
2
 import 'package:flutter/material.dart';
2
 import 'package:meta/meta.dart';
3
 import 'package:meta/meta.dart';
3
 
4
 
24
 abstract class ZefyrLinkDelegate {
25
 abstract class ZefyrLinkDelegate {
25
 
26
 
26
   Future<ZefyrLinkEntity> fillLink(BuildContext context, ZefyrLinkEntity value);
27
   Future<ZefyrLinkEntity> fillLink(BuildContext context, ZefyrLinkEntity value);
28
+
29
+  GestureRecognizer provideLinkGesture(ZefyrLinkEntity value);
27
 }
30
 }

+ 12
- 0
packages/zefyr/lib/src/widgets/theme.dart View File

295
   /// Style used to render "italic" text.
295
   /// Style used to render "italic" text.
296
   final TextStyle italic;
296
   final TextStyle italic;
297
 
297
 
298
+  /// Style used to render "highlight" text.
299
+  final TextStyle highlight;
300
+
298
   /// Style used to render text containing links.
301
   /// Style used to render text containing links.
299
   final TextStyle link;
302
   final TextStyle link;
300
 
303
 
334
     this.underline,
337
     this.underline,
335
     this.deleteline,
338
     this.deleteline,
336
     this.italic,
339
     this.italic,
340
+    this.highlight,
337
     this.link,
341
     this.link,
338
     this.heading1,
342
     this.heading1,
339
     this.heading2,
343
     this.heading2,
374
         decoration: TextDecoration.underline,
378
         decoration: TextDecoration.underline,
375
         color: theme.accentColor,
379
         color: theme.accentColor,
376
       ),
380
       ),
381
+      highlight: TextStyle(
382
+        backgroundColor: Color(0xFFFFAF64).withOpacity(0.5),
383
+      ),
377
       heading1: LineTheme(
384
       heading1: LineTheme(
378
         textStyle: defaultLineTheme.textStyle.copyWith(
385
         textStyle: defaultLineTheme.textStyle.copyWith(
379
           fontSize: 24.0,
386
           fontSize: 24.0,
463
     TextStyle bold,
470
     TextStyle bold,
464
     TextStyle underline,
471
     TextStyle underline,
465
     TextStyle deleteline,
472
     TextStyle deleteline,
473
+    TextStyle highlight,
466
     TextStyle italic,
474
     TextStyle italic,
467
     TextStyle link,
475
     TextStyle link,
468
     LineTheme heading1,
476
     LineTheme heading1,
480
       bold: bold ?? this.bold,
488
       bold: bold ?? this.bold,
481
       underline: underline ?? this.underline,
489
       underline: underline ?? this.underline,
482
       deleteline: deleteline ?? this.deleteline,
490
       deleteline: deleteline ?? this.deleteline,
491
+      highlight: highlight ?? this.highlight,
483
       italic: italic ?? this.italic,
492
       italic: italic ?? this.italic,
484
       link: link ?? this.link,
493
       link: link ?? this.link,
485
       heading1: heading1 ?? this.heading1,
494
       heading1: heading1 ?? this.heading1,
503
       bold: bold?.merge(other.bold) ?? other.bold,
512
       bold: bold?.merge(other.bold) ?? other.bold,
504
       underline: underline?.merge(other.underline) ?? other.underline,
513
       underline: underline?.merge(other.underline) ?? other.underline,
505
       deleteline: underline?.merge(other.deleteline) ?? other.deleteline,
514
       deleteline: underline?.merge(other.deleteline) ?? other.deleteline,
515
+      highlight: highlight?.merge(other.highlight) ?? other.highlight,
506
       italic: italic?.merge(other.italic) ?? other.italic,
516
       italic: italic?.merge(other.italic) ?? other.italic,
507
       link: link?.merge(other.link) ?? other.link,
517
       link: link?.merge(other.link) ?? other.link,
508
       heading1: heading1?.merge(other.heading1) ?? other.heading1,
518
       heading1: heading1?.merge(other.heading1) ?? other.heading1,
525
     return (otherTheme.bold == bold) &&
535
     return (otherTheme.bold == bold) &&
526
         (otherTheme.underline == underline) &&
536
         (otherTheme.underline == underline) &&
527
         (otherTheme.deleteline == deleteline) &&
537
         (otherTheme.deleteline == deleteline) &&
538
+        (otherTheme.highlight == highlight) &&
528
         (otherTheme.italic == italic) &&
539
         (otherTheme.italic == italic) &&
529
         (otherTheme.link == link) &&
540
         (otherTheme.link == link) &&
530
         (otherTheme.heading1 == heading1) &&
541
         (otherTheme.heading1 == heading1) &&
545
       bold,
556
       bold,
546
       underline,
557
       underline,
547
       deleteline,
558
       deleteline,
559
+      highlight,
548
       italic,
560
       italic,
549
       link,
561
       link,
550
       heading1,
562
       heading1,