diff --git a/lib/widgets/editor.dart b/lib/widgets/editor.dart index d15a8789..f6dd8e40 100644 --- a/lib/widgets/editor.dart +++ b/lib/widgets/editor.dart @@ -393,8 +393,6 @@ class _QuillEditorSelectionGestureDetectorBuilder final segmentResult = line.queryChild(result.offset, false); if (segmentResult.node == null) { if (line.length == 1) { - // tapping when no text yet on this line - _flipListCheckbox(pos, line, segmentResult); getEditor()!.widget.controller.updateSelection( TextSelection.collapsed(offset: pos.offset), ChangeSource.LOCAL); return true; @@ -434,37 +432,9 @@ class _QuillEditorSelectionGestureDetectorBuilder ), ); } - return false; - } - if (_flipListCheckbox(pos, line, segmentResult)) { - return true; } - return false; - } - bool _flipListCheckbox( - TextPosition pos, Line line, container_node.ChildQuery segmentResult) { - if (getEditor()!.widget.readOnly || - !line.style.containsKey(Attribute.list.key) || - segmentResult.offset != 0) { - return false; - } - // segmentResult.offset == 0 means tap at the beginning of the TextLine - final String? listVal = line.style.attributes[Attribute.list.key]!.value; - if (listVal == Attribute.unchecked.value) { - getEditor()! - .widget - .controller - .formatText(pos.offset, 0, Attribute.checked); - } else if (listVal == Attribute.checked.value) { - getEditor()! - .widget - .controller - .formatText(pos.offset, 0, Attribute.unchecked); - } - getEditor()!.widget.controller.updateSelection( - TextSelection.collapsed(offset: pos.offset), ChangeSource.LOCAL); - return true; + return false; } Future _launchUrl(String url) async { diff --git a/lib/widgets/raw_editor.dart b/lib/widgets/raw_editor.dart index 35e3aa68..f93eabe2 100644 --- a/lib/widgets/raw_editor.dart +++ b/lib/widgets/raw_editor.dart @@ -579,6 +579,18 @@ class RawEditorState extends EditorState } } + /// Updates the checkbox positioned at [offset] in document + /// by changing its attribute according to [value]. + void _handleCheckboxTap(int offset, bool value) { + if (!widget.readOnly) { + if (value) { + widget.controller.formatText(offset, 0, Attribute.checked); + } else { + widget.controller.formatText(offset, 0, Attribute.unchecked); + } + } + } + List _buildChildren(Document doc, BuildContext context) { final result = []; final indentLevelCounts = {}; @@ -589,21 +601,23 @@ class RawEditorState extends EditorState } else if (node is Block) { final attrs = node.style.attributes; final editableTextBlock = EditableTextBlock( - node, - _textDirection, - widget.scrollBottomInset, - _getVerticalSpacingForBlock(node, _styles), - widget.controller.selection, - widget.selectionColor, - _styles, - widget.enableInteractiveSelection, - _hasFocus, - attrs.containsKey(Attribute.codeBlock.key) - ? const EdgeInsets.all(16) - : null, - widget.embedBuilder, - _cursorCont, - indentLevelCounts); + node, + _textDirection, + widget.scrollBottomInset, + _getVerticalSpacingForBlock(node, _styles), + widget.controller.selection, + widget.selectionColor, + _styles, + widget.enableInteractiveSelection, + _hasFocus, + attrs.containsKey(Attribute.codeBlock.key) + ? const EdgeInsets.all(16) + : null, + widget.embedBuilder, + _cursorCont, + indentLevelCounts, + _handleCheckboxTap, + ); result.add(editableTextBlock); } else { throw StateError('Unreachable.'); diff --git a/lib/widgets/text_block.dart b/lib/widgets/text_block.dart index 309b9cf8..f533a160 100644 --- a/lib/widgets/text_block.dart +++ b/lib/widgets/text_block.dart @@ -61,6 +61,7 @@ class EditableTextBlock extends StatelessWidget { this.embedBuilder, this.cursorCont, this.indentLevelCounts, + this.onCheckboxTap, ); final Block block; @@ -76,6 +77,7 @@ class EditableTextBlock extends StatelessWidget { final EmbedBuilder embedBuilder; final CursorCont cursorCont; final Map indentLevelCounts; + final Function(int, bool) onCheckboxTap; @override Widget build(BuildContext context) { @@ -161,12 +163,23 @@ class EditableTextBlock extends StatelessWidget { if (attrs[Attribute.list.key] == Attribute.checked) { return _Checkbox( - style: defaultStyles!.leading!.style, width: 32, isChecked: true); + key: UniqueKey(), + style: defaultStyles!.leading!.style, + width: 32, + isChecked: true, + offset: block.offset + line.offset, + onTap: onCheckboxTap, + ); } if (attrs[Attribute.list.key] == Attribute.unchecked) { return _Checkbox( - style: defaultStyles!.leading!.style, width: 32, isChecked: false); + key: UniqueKey(), + style: defaultStyles!.leading!.style, + width: 32, + offset: block.offset + line.offset, + onTap: onCheckboxTap, + ); } if (attrs.containsKey(Attribute.codeBlock.key)) { @@ -685,46 +698,39 @@ class _BulletPoint extends StatelessWidget { } } -class _Checkbox extends StatefulWidget { - const _Checkbox({Key? key, this.style, this.width, this.isChecked}) - : super(key: key); - +class _Checkbox extends StatelessWidget { + const _Checkbox({ + Key? key, + this.style, + this.width, + this.isChecked = false, + this.offset, + this.onTap, + }) : super(key: key); final TextStyle? style; final double? width; - final bool? isChecked; + final bool isChecked; + final int? offset; + final Function(int, bool)? onTap; - @override - __CheckboxState createState() => __CheckboxState(); -} - -class __CheckboxState extends State<_Checkbox> { - bool? isChecked; - - void _onCheckboxClicked(bool? newValue) => setState(() { - isChecked = newValue; - - if (isChecked!) { - // check list - } else { - // uncheck list - } - }); - - @override - void initState() { - super.initState(); - isChecked = widget.isChecked; + void _onCheckboxClicked(bool? newValue) { + if (onTap != null && newValue != null && offset != null) { + onTap!(offset!, newValue); + } } @override Widget build(BuildContext context) { return Container( alignment: AlignmentDirectional.topEnd, - width: widget.width, + width: width, padding: const EdgeInsetsDirectional.only(end: 13), - child: Checkbox( - value: widget.isChecked, - onChanged: _onCheckboxClicked, + child: GestureDetector( + onLongPress: () => _onCheckboxClicked(!isChecked), + child: Checkbox( + value: isChecked, + onChanged: _onCheckboxClicked, + ), ), ); }