diff --git a/.github/ISSUE_TEMPLATE/issue-template.md b/.github/ISSUE_TEMPLATE/issue-template.md new file mode 100644 index 00000000..a908ed53 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue-template.md @@ -0,0 +1,16 @@ +--- +name: Issue template +about: Common things to fill +title: "[Web] or [Mobile] or [Desktop]" +labels: '' +assignees: '' + +--- + +My issue is about [Web] +My issue is about [Mobile] +My issue is about [Desktop] + +I have tried running `example` directory successfully before creating an issue here AND it is NOT a stupid question. + +Please note that we are using stable channel. If you are using beta or master channel, those are not supported. diff --git a/CHANGELOG.md b/CHANGELOG.md index bee97937..3550e11a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## [1.1.7] +* Fix text selection in read-only mode. + ## [1.1.6] * Remove universal_html dependency. diff --git a/example/lib/widgets/demo_scaffold.dart b/example/lib/widgets/demo_scaffold.dart index 1533da28..52d44e4f 100644 --- a/example/lib/widgets/demo_scaffold.dart +++ b/example/lib/widgets/demo_scaffold.dart @@ -19,12 +19,12 @@ class DemoScaffold extends StatefulWidget { final bool showToolbar; const DemoScaffold({ - Key? key, required this.documentFilename, required this.builder, this.actions, this.showToolbar = true, this.floatingActionButton, + Key? key, }) : super(key: key); @override @@ -59,14 +59,14 @@ class _DemoScaffoldState extends State { final doc = Document.fromJson(jsonDecode(result)); setState(() { _controller = QuillController( - document: doc, selection: TextSelection.collapsed(offset: 0)); + document: doc, selection: const TextSelection.collapsed(offset: 0)); _loading = false; }); } catch (error) { final doc = Document()..insert(0, 'Empty asset'); setState(() { _controller = QuillController( - document: doc, selection: TextSelection.collapsed(offset: 0)); + document: doc, selection: const TextSelection.collapsed(offset: 0)); _loading = false; }); } @@ -97,7 +97,7 @@ class _DemoScaffoldState extends State { ), floatingActionButton: widget.floatingActionButton, body: _loading - ? Center(child: Text('Loading...')) + ? const Center(child: Text('Loading...')) : widget.builder(context, _controller), ); } diff --git a/lib/widgets/default_styles.dart b/lib/widgets/default_styles.dart index 4f26d9d3..9490bebd 100644 --- a/lib/widgets/default_styles.dart +++ b/lib/widgets/default_styles.dart @@ -59,6 +59,7 @@ class DefaultStyles { final DefaultTextBlockStyle? code; final DefaultTextBlockStyle? indent; final DefaultTextBlockStyle? align; + final DefaultTextBlockStyle? leading; DefaultStyles( {this.h1, @@ -77,6 +78,7 @@ class DefaultStyles { this.code, this.indent, this.align, + this.leading, this.sizeSmall, this.sizeLarge, this.sizeHuge}); @@ -183,6 +185,8 @@ class DefaultStyles { baseStyle, baseSpacing, const Tuple2(0.0, 6.0), null), align: DefaultTextBlockStyle( baseStyle, const Tuple2(0.0, 0.0), const Tuple2(0.0, 0.0), null), + leading: DefaultTextBlockStyle( + baseStyle, const Tuple2(0.0, 0.0), const Tuple2(0.0, 0.0), null), sizeSmall: const TextStyle(fontSize: 10.0), sizeLarge: const TextStyle(fontSize: 18.0), sizeHuge: const TextStyle(fontSize: 22.0)); @@ -206,6 +210,7 @@ class DefaultStyles { code: other.code ?? code, indent: other.indent ?? indent, align: other.align ?? align, + leading: other.leading ?? leading, sizeSmall: other.sizeSmall ?? sizeSmall, sizeLarge: other.sizeLarge ?? sizeLarge, sizeHuge: other.sizeHuge ?? sizeHuge); diff --git a/lib/widgets/editor.dart b/lib/widgets/editor.dart index 9991c2a6..ccb2eb38 100644 --- a/lib/widgets/editor.dart +++ b/lib/widgets/editor.dart @@ -258,10 +258,10 @@ class _QuillEditorState extends State widget.placeholder, widget.onLaunchUrl, ToolbarOptions( - copy: true, + copy: widget.enableInteractiveSelection, cut: widget.enableInteractiveSelection, paste: widget.enableInteractiveSelection, - selectAll: true, + selectAll: widget.enableInteractiveSelection, ), theme.platform == TargetPlatform.iOS || theme.platform == TargetPlatform.android, diff --git a/lib/widgets/raw_editor.dart b/lib/widgets/raw_editor.dart index 130af8b4..6a24f39c 100644 --- a/lib/widgets/raw_editor.dart +++ b/lib/widgets/raw_editor.dart @@ -371,6 +371,10 @@ class RawEditorState extends EditorState _textInputConnection != null && _textInputConnection!.attached; void openConnectionIfNeeded() { + if (!shouldCreateInputConnection) { + return; + } + if (!hasConnection) { _lastKnownRemoteTextEditingValue = textEditingValue; _textInputConnection = TextInput.attach( @@ -388,11 +392,8 @@ class RawEditorState extends EditorState _textInputConnection!.setEditingState(_lastKnownRemoteTextEditingValue!); // _sentRemoteValues.add(_lastKnownRemoteTextEditingValue); } + _textInputConnection!.show(); - if (widget.readOnly) { - // temporary hack to dismiss keyboard - SystemChannels.textInput.invokeMethod('TextInput.hide'); - } } void closeConnectionIfNeeded() { @@ -897,6 +898,12 @@ class RawEditorState extends EditorState _onChangeTextEditingValue(); } else { requestKeyboard(); + if (mounted) { + setState(() { + // Use widget.controller.value in build() + // Trigger build and updateChildren + }); + } } } @@ -912,11 +919,12 @@ class RawEditorState extends EditorState SchedulerBinding.instance!.addPostFrameCallback( (Duration _) => _updateOrDisposeSelectionOverlayIfNeeded()); - if (!mounted) return; - setState(() { - // Use widget.controller.value in build() - // Trigger build and updateChildren - }); + if (mounted) { + setState(() { + // Use widget.controller.value in build() + // Trigger build and updateChildren + }); + } } void _updateOrDisposeSelectionOverlayIfNeeded() { @@ -1108,6 +1116,7 @@ class RawEditorState extends EditorState return false; } + _selectionOverlay!.update(textEditingValue); _selectionOverlay!.showToolbar(); return true; } diff --git a/lib/widgets/text_block.dart b/lib/widgets/text_block.dart index 7ea5b278..85912431 100644 --- a/lib/widgets/text_block.dart +++ b/lib/widgets/text_block.dart @@ -141,7 +141,7 @@ class EditableTextBlock extends StatelessWidget { index: index, indentLevelCounts: indentLevelCounts, count: count, - style: defaultStyles!.paragraph!.style, + style: defaultStyles!.leading!.style, attrs: attrs, width: 32.0, padding: 8.0, @@ -150,20 +150,20 @@ class EditableTextBlock extends StatelessWidget { if (attrs[Attribute.list.key] == Attribute.ul) { return _BulletPoint( - style: defaultStyles!.paragraph!.style - .copyWith(fontWeight: FontWeight.bold), + style: + defaultStyles!.leading!.style.copyWith(fontWeight: FontWeight.bold), width: 32, ); } if (attrs[Attribute.list.key] == Attribute.checked) { return _Checkbox( - style: defaultStyles!.paragraph!.style, width: 32, isChecked: true); + style: defaultStyles!.leading!.style, width: 32, isChecked: true); } if (attrs[Attribute.list.key] == Attribute.unchecked) { return _Checkbox( - style: defaultStyles!.paragraph!.style, width: 32, isChecked: false); + style: defaultStyles!.leading!.style, width: 32, isChecked: false); } if (attrs.containsKey(Attribute.codeBlock.key)) { diff --git a/lib/widgets/toolbar.dart b/lib/widgets/toolbar.dart index d3440cfa..6b4b4147 100644 --- a/lib/widgets/toolbar.dart +++ b/lib/widgets/toolbar.dart @@ -430,56 +430,62 @@ class _SelectHeaderStyleButtonState extends State { Widget _selectHeadingStyleButtonBuilder(BuildContext context, Attribute? value, ValueChanged onSelected) { - final style = const TextStyle(fontSize: 13); - final Map _valueToText = { - Attribute.header: 'Normal text', - Attribute.h1: 'Heading 1', - Attribute.h2: 'Heading 2', - Attribute.h3: 'Heading 3', + Attribute.header: 'N', + Attribute.h1: 'H1', + Attribute.h2: 'H2', + Attribute.h3: 'H3', }; - return QuillDropdownButton( - highlightElevation: 0, - hoverElevation: 0, - height: iconSize * 1.77, - fillColor: Theme.of(context).canvasColor, - initialValue: value, - items: [ - PopupMenuItem( - value: Attribute.header, - height: iconSize * 1.77, - child: Text(_valueToText[Attribute.header]!, style: style), - ), - PopupMenuItem( - value: Attribute.h1, - height: iconSize * 1.77, - child: Text(_valueToText[Attribute.h1]!, style: style), - ), - PopupMenuItem( - value: Attribute.h2, - height: iconSize * 1.77, - child: Text(_valueToText[Attribute.h2]!, style: style), - ), - PopupMenuItem( - value: Attribute.h3, - height: iconSize * 1.77, - child: Text(_valueToText[Attribute.h3]!, style: style), - ), - ], - onSelected: onSelected, - child: Text( - !kIsWeb - ? _valueToText[value!]! - : _valueToText[value!.key == 'header' - ? Attribute.header - : (value.key == 'h1') - ? Attribute.h1 - : (value.key == 'h2') - ? Attribute.h2 - : Attribute.h3]!, - style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600), - ), + List _valueAttribute = [ + Attribute.header, + Attribute.h1, + Attribute.h2, + Attribute.h3 + ]; + List _valueString = ['N', 'H1', 'H2', 'H3']; + + final theme = Theme.of(context); + final style = TextStyle( + fontWeight: FontWeight.w600, + fontSize: iconSize * 0.7, + ); + + return Row( + mainAxisSize: MainAxisSize.min, + children: List.generate(4, (index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0), + child: ConstrainedBox( + constraints: BoxConstraints.tightFor( + width: iconSize * 1.77, + height: iconSize * 1.77, + ), + child: RawMaterialButton( + hoverElevation: 0, + highlightElevation: 0, + elevation: 0.0, + visualDensity: VisualDensity.compact, + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)), + fillColor: _valueToText[value] == _valueString[index] + ? theme.toggleableActiveColor + : theme.canvasColor, + onPressed: () { + onSelected(_valueAttribute[index]); + }, + child: Text( + _valueString[index], + style: style.copyWith( + color: _valueToText[value] == _valueString[index] + ? theme.primaryIconTheme.color + : theme.iconTheme.color, + ), + ), + ), + ), + ); + }), ); } @@ -1027,11 +1033,11 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { Visibility( visible: showHeaderStyle, child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), + indent: 12, endIndent: 12, color: Colors.grey.shade400)), Visibility( visible: showHeaderStyle, child: SelectHeaderStyleButton(controller: controller)), - VerticalDivider(indent: 16, endIndent: 16, color: Colors.grey.shade400), + VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400), Visibility( visible: showListNumbers, child: ToggleStyleButton( @@ -1070,7 +1076,7 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { !showListCheck && !showCodeBlock, child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), + indent: 12, endIndent: 12, color: Colors.grey.shade400)), Visibility( visible: showQuote, child: ToggleStyleButton( @@ -1098,7 +1104,7 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { Visibility( visible: showQuote, child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), + indent: 12, endIndent: 12, color: Colors.grey.shade400)), Visibility( visible: showLink, child: LinkStyleButton(controller: controller)), Visibility( diff --git a/pubspec.yaml b/pubspec.yaml index 040d3d33..504f96d1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill description: A rich text editor supporting mobile and web (Demo App @ bulletjournal.us) -version: 1.1.6 +version: 1.1.7 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill