From 71a8f757663340927a3e6b630e2b8f5da338462d Mon Sep 17 00:00:00 2001 From: appflowy <86001920+appflowy@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:43:15 +0800 Subject: [PATCH] Extract the block style widgets from text_block.dart to style_widgets folder (#435) --- .../widgets/style_widgets/bullet_point.dart | 22 +++ lib/src/widgets/style_widgets/checkbox.dart | 39 ++++ .../widgets/style_widgets/number_point.dart | 108 +++++++++++ .../widgets/style_widgets/style_widgets.dart | 3 + lib/src/widgets/text_block.dart | 174 +----------------- 5 files changed, 178 insertions(+), 168 deletions(-) create mode 100644 lib/src/widgets/style_widgets/bullet_point.dart create mode 100644 lib/src/widgets/style_widgets/checkbox.dart create mode 100644 lib/src/widgets/style_widgets/number_point.dart create mode 100644 lib/src/widgets/style_widgets/style_widgets.dart diff --git a/lib/src/widgets/style_widgets/bullet_point.dart b/lib/src/widgets/style_widgets/bullet_point.dart new file mode 100644 index 00000000..ee33c93a --- /dev/null +++ b/lib/src/widgets/style_widgets/bullet_point.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class QuillBulletPoint extends StatelessWidget { + const QuillBulletPoint({ + required this.style, + required this.width, + Key? key, + }) : super(key: key); + + final TextStyle style; + final double width; + + @override + Widget build(BuildContext context) { + return Container( + alignment: AlignmentDirectional.topEnd, + width: width, + padding: const EdgeInsetsDirectional.only(end: 13), + child: Text('•', style: style), + ); + } +} diff --git a/lib/src/widgets/style_widgets/checkbox.dart b/lib/src/widgets/style_widgets/checkbox.dart new file mode 100644 index 00000000..ee2eb816 --- /dev/null +++ b/lib/src/widgets/style_widgets/checkbox.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +class QuillCheckbox extends StatelessWidget { + const QuillCheckbox({ + 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 int? offset; + final Function(int, bool)? onTap; + + 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: width, + padding: const EdgeInsetsDirectional.only(end: 13), + child: GestureDetector( + onLongPress: () => _onCheckboxClicked(!isChecked), + child: Checkbox( + value: isChecked, + onChanged: _onCheckboxClicked, + ), + ), + ); + } +} diff --git a/lib/src/widgets/style_widgets/number_point.dart b/lib/src/widgets/style_widgets/number_point.dart new file mode 100644 index 00000000..6debf548 --- /dev/null +++ b/lib/src/widgets/style_widgets/number_point.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import '/models/documents/attribute.dart'; +import '/widgets/text_block.dart'; + + +class QuillNumberPoint extends StatelessWidget { + const QuillNumberPoint({ + required this.index, + required this.indentLevelCounts, + required this.count, + required this.style, + required this.width, + required this.attrs, + this.withDot = true, + this.padding = 0.0, + Key? key, + }) : super(key: key); + + final int index; + final Map indentLevelCounts; + final int count; + final TextStyle style; + final double width; + final Map attrs; + final bool withDot; + final double padding; + + @override + Widget build(BuildContext context) { + var s = index.toString(); + int? level = 0; + if (!attrs.containsKey(Attribute.indent.key) && + !indentLevelCounts.containsKey(1)) { + indentLevelCounts.clear(); + return Container( + alignment: AlignmentDirectional.topEnd, + width: width, + padding: EdgeInsetsDirectional.only(end: padding), + child: Text(withDot ? '$s.' : s, style: style), + ); + } + if (attrs.containsKey(Attribute.indent.key)) { + level = attrs[Attribute.indent.key]!.value; + } else { + // first level but is back from previous indent level + // supposed to be "2." + indentLevelCounts[0] = 1; + } + if (indentLevelCounts.containsKey(level! + 1)) { + // last visited level is done, going up + indentLevelCounts.remove(level + 1); + } + final count = (indentLevelCounts[level] ?? 0) + 1; + indentLevelCounts[level] = count; + + s = count.toString(); + if (level % 3 == 1) { + // a. b. c. d. e. ... + s = _toExcelSheetColumnTitle(count); + } else if (level % 3 == 2) { + // i. ii. iii. ... + s = _intToRoman(count); + } + // level % 3 == 0 goes back to 1. 2. 3. + + return Container( + alignment: AlignmentDirectional.topEnd, + width: width, + padding: EdgeInsetsDirectional.only(end: padding), + child: Text(withDot ? '$s.' : s, style: style), + ); + } + + String _toExcelSheetColumnTitle(int n) { + final result = StringBuffer(); + while (n > 0) { + n--; + result.write(String.fromCharCode((n % 26).floor() + 97)); + n = (n / 26).floor(); + } + + return result.toString().split('').reversed.join(); + } + + String _intToRoman(int input) { + var num = input; + + if (num < 0) { + return ''; + } else if (num == 0) { + return 'nulla'; + } + + final builder = StringBuffer(); + for (var a = 0; a < arabianRomanNumbers.length; a++) { + final times = (num / arabianRomanNumbers[a]) + .truncate(); // equals 1 only when arabianRomanNumbers[a] = num + // executes n times where n is the number of times you have to add + // the current roman number value to reach current num. + builder.write(romanNumbers[a] * times); + num -= times * + arabianRomanNumbers[ + a]; // subtract previous roman number value from num + } + + return builder.toString().toLowerCase(); + } +} diff --git a/lib/src/widgets/style_widgets/style_widgets.dart b/lib/src/widgets/style_widgets/style_widgets.dart new file mode 100644 index 00000000..0f0dd0a5 --- /dev/null +++ b/lib/src/widgets/style_widgets/style_widgets.dart @@ -0,0 +1,3 @@ +export 'bullet_point.dart'; +export 'checkbox.dart'; +export 'number_point.dart'; \ No newline at end of file diff --git a/lib/src/widgets/text_block.dart b/lib/src/widgets/text_block.dart index e46cba0d..132ff145 100644 --- a/lib/src/widgets/text_block.dart +++ b/lib/src/widgets/text_block.dart @@ -6,6 +6,7 @@ import 'package:tuple/tuple.dart'; import '../models/documents/attribute.dart'; import '../models/documents/nodes/block.dart'; import '../models/documents/nodes/line.dart'; +import '../widgets/style_widgets/style_widgets.dart'; import 'box.dart'; import 'cursor.dart'; import 'default_styles.dart'; @@ -148,7 +149,7 @@ class EditableTextBlock extends StatelessWidget { final defaultStyles = QuillStyles.getStyles(context, false); final attrs = line.style.attributes; if (attrs[Attribute.list.key] == Attribute.ol) { - return _NumberPoint( + return QuillNumberPoint( index: index, indentLevelCounts: indentLevelCounts, count: count, @@ -160,7 +161,7 @@ class EditableTextBlock extends StatelessWidget { } if (attrs[Attribute.list.key] == Attribute.ul) { - return _BulletPoint( + return QuillBulletPoint( style: defaultStyles!.leading!.style.copyWith(fontWeight: FontWeight.bold), width: 32, @@ -168,7 +169,7 @@ class EditableTextBlock extends StatelessWidget { } if (attrs[Attribute.list.key] == Attribute.checked) { - return _Checkbox( + return QuillCheckbox( key: UniqueKey(), style: defaultStyles!.leading!.style, width: 32, @@ -179,7 +180,7 @@ class EditableTextBlock extends StatelessWidget { } if (attrs[Attribute.list.key] == Attribute.unchecked) { - return _Checkbox( + return QuillCheckbox( key: UniqueKey(), style: defaultStyles!.leading!.style, width: 32, @@ -189,7 +190,7 @@ class EditableTextBlock extends StatelessWidget { } if (attrs.containsKey(Attribute.codeBlock.key)) { - return _NumberPoint( + return QuillNumberPoint( index: index, indentLevelCounts: indentLevelCounts, count: count, @@ -607,166 +608,3 @@ class _EditableBlock extends MultiChildRenderObjectWidget { ..contentPadding = _contentPadding; } } - -class _NumberPoint extends StatelessWidget { - const _NumberPoint({ - required this.index, - required this.indentLevelCounts, - required this.count, - required this.style, - required this.width, - required this.attrs, - this.withDot = true, - this.padding = 0.0, - Key? key, - }) : super(key: key); - - final int index; - final Map indentLevelCounts; - final int count; - final TextStyle style; - final double width; - final Map attrs; - final bool withDot; - final double padding; - - @override - Widget build(BuildContext context) { - var s = index.toString(); - int? level = 0; - if (!attrs.containsKey(Attribute.indent.key) && - !indentLevelCounts.containsKey(1)) { - indentLevelCounts.clear(); - return Container( - alignment: AlignmentDirectional.topEnd, - width: width, - padding: EdgeInsetsDirectional.only(end: padding), - child: Text(withDot ? '$s.' : s, style: style), - ); - } - if (attrs.containsKey(Attribute.indent.key)) { - level = attrs[Attribute.indent.key]!.value; - } else { - // first level but is back from previous indent level - // supposed to be "2." - indentLevelCounts[0] = 1; - } - if (indentLevelCounts.containsKey(level! + 1)) { - // last visited level is done, going up - indentLevelCounts.remove(level + 1); - } - final count = (indentLevelCounts[level] ?? 0) + 1; - indentLevelCounts[level] = count; - - s = count.toString(); - if (level % 3 == 1) { - // a. b. c. d. e. ... - s = _toExcelSheetColumnTitle(count); - } else if (level % 3 == 2) { - // i. ii. iii. ... - s = _intToRoman(count); - } - // level % 3 == 0 goes back to 1. 2. 3. - - return Container( - alignment: AlignmentDirectional.topEnd, - width: width, - padding: EdgeInsetsDirectional.only(end: padding), - child: Text(withDot ? '$s.' : s, style: style), - ); - } - - String _toExcelSheetColumnTitle(int n) { - final result = StringBuffer(); - while (n > 0) { - n--; - result.write(String.fromCharCode((n % 26).floor() + 97)); - n = (n / 26).floor(); - } - - return result.toString().split('').reversed.join(); - } - - String _intToRoman(int input) { - var num = input; - - if (num < 0) { - return ''; - } else if (num == 0) { - return 'nulla'; - } - - final builder = StringBuffer(); - for (var a = 0; a < arabianRomanNumbers.length; a++) { - final times = (num / arabianRomanNumbers[a]) - .truncate(); // equals 1 only when arabianRomanNumbers[a] = num - // executes n times where n is the number of times you have to add - // the current roman number value to reach current num. - builder.write(romanNumbers[a] * times); - num -= times * - arabianRomanNumbers[ - a]; // subtract previous roman number value from num - } - - return builder.toString().toLowerCase(); - } -} - -class _BulletPoint extends StatelessWidget { - const _BulletPoint({ - required this.style, - required this.width, - Key? key, - }) : super(key: key); - - final TextStyle style; - final double width; - - @override - Widget build(BuildContext context) { - return Container( - alignment: AlignmentDirectional.topEnd, - width: width, - padding: const EdgeInsetsDirectional.only(end: 13), - child: Text('•', style: style), - ); - } -} - -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 int? offset; - final Function(int, bool)? onTap; - - 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: width, - padding: const EdgeInsetsDirectional.only(end: 13), - child: GestureDetector( - onLongPress: () => _onCheckboxClicked(!isChecked), - child: Checkbox( - value: isChecked, - onChanged: _onCheckboxClicked, - ), - ), - ); - } -}