diff --git a/lib/src/models/documents/nodes/line.dart b/lib/src/models/documents/nodes/line.dart index be3eb7b0..df9374a2 100644 --- a/lib/src/models/documents/nodes/line.dart +++ b/lib/src/models/documents/nodes/line.dart @@ -372,7 +372,7 @@ base class Line extends QuillContainer { final data = queryChild(offset, true); var node = data.node as Leaf?; if (node != null) { - result = result.mergeAll(node.style); + result = node.style; var pos = node.length - data.offset; while (!node!.isLast && pos < local) { node = node.next as Leaf; @@ -380,7 +380,6 @@ base class Line extends QuillContainer { pos += node.length; } } - result = result.mergeAll(style); if (parent is Block) { final block = parent as Block; @@ -390,7 +389,6 @@ base class Line extends QuillContainer { final remaining = len - local; if (remaining > 0 && nextLine != null) { final rest = nextLine!.collectStyle(0, remaining); - result = result.mergeAll(rest); handle(rest); } diff --git a/lib/src/models/rules/insert.dart b/lib/src/models/rules/insert.dart index 73fd044e..9b0a3ea7 100644 --- a/lib/src/models/rules/insert.dart +++ b/lib/src/models/rules/insert.dart @@ -564,8 +564,12 @@ class PreserveInlineStylesRule extends InsertRule { if (prev == null || prev.data is! String) return null; if ((prev.data as String).endsWith('\n')) { - if (prev.attributes?.containsKey(Attribute.list.key) == true) { - return null; + if (prev.attributes != null) { + for (final key in prev.attributes!.keys) { + if (Attribute.blockKeys.contains(key)) { + return null; + } + } } prev = itr .next(); // at the start of a line, apply the style for the current line and not the style for the preceding line diff --git a/lib/src/widgets/editor/editor.dart b/lib/src/widgets/editor/editor.dart index 6a1d58e0..a5635967 100644 --- a/lib/src/widgets/editor/editor.dart +++ b/lib/src/widgets/editor/editor.dart @@ -172,6 +172,7 @@ class QuillEditorState extends State @override void initState() { super.initState(); + widget.configurations.controller.editorFocusNode ??= widget.focusNode; _editorKey = configurations.editorKey ?? GlobalKey(); _selectionGestureDetectorBuilder = _QuillEditorSelectionGestureDetectorBuilder( diff --git a/lib/src/widgets/quill/quill_controller.dart b/lib/src/widgets/quill/quill_controller.dart index c8e6a7c3..7e71f201 100644 --- a/lib/src/widgets/quill/quill_controller.dart +++ b/lib/src/widgets/quill/quill_controller.dart @@ -25,14 +25,17 @@ class QuillController extends ChangeNotifier { this.onSelectionCompleted, this.onSelectionChanged, this.readOnly = false, + this.editorFocusNode, }) : _document = document, _selection = selection; factory QuillController.basic( {QuillControllerConfigurations configurations = - const QuillControllerConfigurations()}) { + const QuillControllerConfigurations(), + FocusNode? editorFocusNode}) { return QuillController( configurations: configurations, + editorFocusNode: editorFocusNode, document: Document(), selection: const TextSelection.collapsed(offset: 0), ); @@ -485,6 +488,9 @@ class QuillController extends ChangeNotifier { List get pasteStyleAndEmbed => _pasteStyleAndEmbed; bool readOnly; + /// Used to give focus to the editor following a toolbar action + FocusNode? editorFocusNode; + ImageUrl? _copiedImageUrl; ImageUrl? get copiedImageUrl => _copiedImageUrl; diff --git a/lib/src/widgets/toolbar/base_button/base_value_button.dart b/lib/src/widgets/toolbar/base_button/base_value_button.dart index edc53923..6fc0acb8 100644 --- a/lib/src/widgets/toolbar/base_button/base_value_button.dart +++ b/lib/src/widgets/toolbar/base_button/base_value_button.dart @@ -4,10 +4,10 @@ import '../../../../flutter_quill.dart'; /// The [T] is the options for the button /// The [E] is the extra options for the button -abstract class QuillToolbarBaseValueButton< +abstract class QuillToolbarBaseButton< T extends QuillToolbarBaseButtonOptions, E extends QuillToolbarBaseButtonExtraOptions> extends StatefulWidget { - const QuillToolbarBaseValueButton( + const QuillToolbarBaseButton( {required this.controller, required this.options, super.key}); final T options; @@ -16,16 +16,46 @@ abstract class QuillToolbarBaseValueButton< } /// The [W] is the widget that creates this State -/// The [V] is the type of the currentValue -abstract class QuillToolbarBaseValueButtonState< - W extends QuillToolbarBaseValueButton, +abstract class QuillToolbarCommonButtonState< + W extends QuillToolbarBaseButton, T extends QuillToolbarBaseButtonOptions, - E extends QuillToolbarBaseButtonExtraOptions, - V> extends State { + E extends QuillToolbarBaseButtonExtraOptions> extends State { T get options => widget.options; QuillController get controller => widget.controller; + QuillToolbarBaseButtonOptions? get baseButtonExtraOptions => + context.quillToolbarBaseButtonOptions; + + String get defaultTooltip; + + String get tooltip => + options.tooltip ?? baseButtonExtraOptions?.tooltip ?? defaultTooltip; + + double get iconSize => + options.iconSize ?? baseButtonExtraOptions?.iconSize ?? kDefaultIconSize; + + double get iconButtonFactor => + options.iconButtonFactor ?? + baseButtonExtraOptions?.iconButtonFactor ?? + kDefaultIconButtonFactor; + + QuillIconTheme? get iconTheme => + options.iconTheme ?? baseButtonExtraOptions?.iconTheme; + + VoidCallback? get afterButtonPressed => + options.afterButtonPressed ?? + baseButtonExtraOptions?.afterButtonPressed ?? + () => controller.editorFocusNode?.requestFocus(); +} + +/// The [W] is the widget that creates this State +/// The [V] is the type of the currentValue +abstract class QuillToolbarBaseButtonState< + W extends QuillToolbarBaseButton, + T extends QuillToolbarBaseButtonOptions, + E extends QuillToolbarBaseButtonExtraOptions, + V> extends QuillToolbarCommonButtonState { V? _currentValue; V get currentValue => _currentValue!; set currentValue(V value) => _currentValue = value; @@ -72,46 +102,13 @@ abstract class QuillToolbarBaseValueButtonState< /// Extra listeners allow a subclass to listen to an external event that can affect its currentValue void addExtraListener() {} void removeExtraListener(covariant W oldWidget) {} - - String get defaultTooltip; - - String get tooltip { - return options.tooltip ?? - context.quillToolbarBaseButtonOptions?.tooltip ?? - defaultTooltip; - } - - double get iconSize { - final baseFontSize = baseButtonExtraOptions?.iconSize; - final iconSize = options.iconSize; - return iconSize ?? baseFontSize ?? kDefaultIconSize; - } - - double get iconButtonFactor { - final baseIconFactor = baseButtonExtraOptions?.iconButtonFactor; - final iconButtonFactor = options.iconButtonFactor; - return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor; - } - - QuillIconTheme? get iconTheme { - return options.iconTheme ?? baseButtonExtraOptions?.iconTheme; - } - - QuillToolbarBaseButtonOptions? get baseButtonExtraOptions { - return context.quillToolbarBaseButtonOptions; - } - - VoidCallback? get afterButtonPressed { - return options.afterButtonPressed ?? - baseButtonExtraOptions?.afterButtonPressed; - } } -typedef QuillToolbarToggleStyleBaseButton = QuillToolbarBaseValueButton< +typedef QuillToolbarToggleStyleBaseButton = QuillToolbarBaseButton< QuillToolbarToggleStyleButtonOptions, QuillToolbarToggleStyleButtonExtraOptions>; typedef QuillToolbarToggleStyleBaseButtonState< W extends QuillToolbarToggleStyleBaseButton> - = QuillToolbarBaseValueButtonState; diff --git a/lib/src/widgets/toolbar/base_button/stateless_base_button.dart b/lib/src/widgets/toolbar/base_button/stateless_base_button.dart index cd6cc5ed..10039383 100644 --- a/lib/src/widgets/toolbar/base_button/stateless_base_button.dart +++ b/lib/src/widgets/toolbar/base_button/stateless_base_button.dart @@ -33,7 +33,8 @@ abstract class QuillToolbarBaseButton extends StatelessWidget { VoidCallback? afterButtonPressed(BuildContext context) { return options?.afterButtonPressed ?? - baseButtonExtraOptions(context)?.afterButtonPressed; + baseButtonExtraOptions(context)?.afterButtonPressed ?? + () => controller.editorFocusNode?.requestFocus(); } QuillIconTheme? iconTheme(BuildContext context) { @@ -53,11 +54,11 @@ abstract class QuillToolbarBaseButton extends StatelessWidget { String tooltip(BuildContext context) { return options?.tooltip ?? baseButtonExtraOptions(context)?.tooltip ?? - getDefaultIconSize(context); + getDefaultTooltip(context); } abstract final IconData Function(BuildContext context) getDefaultIconData; - abstract final String Function(BuildContext context) getDefaultIconSize; + abstract final String Function(BuildContext context) getDefaultTooltip; Widget buildButton(BuildContext context); Widget? buildCustomChildBuilder( diff --git a/lib/src/widgets/toolbar/buttons/clear_format_button.dart b/lib/src/widgets/toolbar/buttons/clear_format_button.dart index 4a28dcf1..1c7de128 100644 --- a/lib/src/widgets/toolbar/buttons/clear_format_button.dart +++ b/lib/src/widgets/toolbar/buttons/clear_format_button.dart @@ -59,6 +59,6 @@ class QuillToolbarClearFormatButton extends QuillToolbarBaseButton { (context) => Icons.format_clear; @override - String Function(BuildContext context) get getDefaultIconSize => + String Function(BuildContext context) get getDefaultTooltip => (context) => context.loc.clearFormat; } diff --git a/lib/src/widgets/toolbar/buttons/color/color_button.dart b/lib/src/widgets/toolbar/buttons/color/color_button.dart index 77fa424c..5f553160 100644 --- a/lib/src/widgets/toolbar/buttons/color/color_button.dart +++ b/lib/src/widgets/toolbar/buttons/color/color_button.dart @@ -5,39 +5,47 @@ import '../../../../l10n/extensions/localizations.dart'; import '../../../../l10n/widgets/localizations.dart'; import '../../../../models/documents/attribute.dart'; import '../../../../models/documents/style.dart'; -import '../../../../models/themes/quill_icon_theme.dart'; import '../../../../utils/color.dart'; -import '../../../quill/quill_controller.dart'; +import '../../base_button/base_value_button.dart'; import '../../base_toolbar.dart'; import 'color_dialog.dart'; +typedef QuillToolbarColorBaseButton = QuillToolbarBaseButton< + QuillToolbarColorButtonOptions, QuillToolbarColorButtonExtraOptions>; + +typedef QuillToolbarColorBaseButtonState + = QuillToolbarCommonButtonState; + /// Controls color styles. /// /// When pressed, this button displays overlay toolbar with /// buttons for each color. -class QuillToolbarColorButton extends StatefulWidget { +class QuillToolbarColorButton extends QuillToolbarColorBaseButton { const QuillToolbarColorButton({ - required this.controller, + required super.controller, required this.isBackground, - this.options = const QuillToolbarColorButtonOptions(), + super.options = const QuillToolbarColorButtonOptions(), super.key, }); /// Is this background color button or font color final bool isBackground; - final QuillController controller; - final QuillToolbarColorButtonOptions options; @override QuillToolbarColorButtonState createState() => QuillToolbarColorButtonState(); } -class QuillToolbarColorButtonState extends State { +class QuillToolbarColorButtonState extends QuillToolbarColorBaseButtonState { late bool _isToggledColor; late bool _isToggledBackground; late bool _isWhite; late bool _isWhiteBackground; + @override + String get defaultTooltip => + widget.isBackground ? context.loc.backgroundColor : context.loc.fontColor; + Style get _selectionStyle => widget.controller.getSelectionStyle(); void _didChangeEditingValue() { @@ -95,53 +103,12 @@ class QuillToolbarColorButtonState extends State { super.dispose(); } - QuillToolbarColorButtonOptions get options { - return widget.options; - } - - QuillController get controller { - return widget.controller; - } - - double get iconSize { - final baseFontSize = baseButtonExtraOptions?.iconSize; - final iconSize = options.iconSize; - return iconSize ?? baseFontSize ?? kDefaultIconSize; - } - - double get iconButtonFactor { - final baseIconFactor = baseButtonExtraOptions?.iconButtonFactor; - final iconButtonFactor = options.iconButtonFactor; - return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor; - } - - VoidCallback? get afterButtonPressed { - return options.afterButtonPressed ?? - baseButtonExtraOptions?.afterButtonPressed; - } - - QuillIconTheme? get iconTheme { - return options.iconTheme ?? baseButtonExtraOptions?.iconTheme; - } - - QuillToolbarBaseButtonOptions? get baseButtonExtraOptions { - return context.quillToolbarBaseButtonOptions; - } - IconData get iconData { return options.iconData ?? baseButtonExtraOptions?.iconData ?? (widget.isBackground ? Icons.format_color_fill : Icons.color_lens); } - String get tooltip { - return options.tooltip ?? - baseButtonExtraOptions?.tooltip ?? - (widget.isBackground - ? context.loc.backgroundColor - : context.loc.fontColor); - } - @override Widget build(BuildContext context) { final iconColor = _isToggledColor && !widget.isBackground && !_isWhite diff --git a/lib/src/widgets/toolbar/buttons/font_family_button.dart b/lib/src/widgets/toolbar/buttons/font_family_button.dart index 10cf8712..3ed3e0d5 100644 --- a/lib/src/widgets/toolbar/buttons/font_family_button.dart +++ b/lib/src/widgets/toolbar/buttons/font_family_button.dart @@ -7,7 +7,7 @@ import '../../../models/documents/attribute.dart'; import '../base_button/base_value_button.dart'; import '../base_toolbar.dart'; -class QuillToolbarFontFamilyButton extends QuillToolbarBaseValueButton< +class QuillToolbarFontFamilyButton extends QuillToolbarBaseButton< QuillToolbarFontFamilyButtonOptions, QuillToolbarFontFamilyButtonExtraOptions> { QuillToolbarFontFamilyButton({ @@ -28,12 +28,11 @@ class QuillToolbarFontFamilyButton extends QuillToolbarBaseValueButton< QuillToolbarFontFamilyButtonState(); } -class QuillToolbarFontFamilyButtonState - extends QuillToolbarBaseValueButtonState< - QuillToolbarFontFamilyButton, - QuillToolbarFontFamilyButtonOptions, - QuillToolbarFontFamilyButtonExtraOptions, - String> { +class QuillToolbarFontFamilyButtonState extends QuillToolbarBaseButtonState< + QuillToolbarFontFamilyButton, + QuillToolbarFontFamilyButtonOptions, + QuillToolbarFontFamilyButtonExtraOptions, + String> { @override String get currentStateValue { final attribute = diff --git a/lib/src/widgets/toolbar/buttons/font_size_button.dart b/lib/src/widgets/toolbar/buttons/font_size_button.dart index 71a6bfcc..6a70a531 100644 --- a/lib/src/widgets/toolbar/buttons/font_size_button.dart +++ b/lib/src/widgets/toolbar/buttons/font_size_button.dart @@ -9,7 +9,7 @@ import '../../../utils/font.dart'; import '../base_button/base_value_button.dart'; import '../base_toolbar.dart'; -class QuillToolbarFontSizeButton extends QuillToolbarBaseValueButton< +class QuillToolbarFontSizeButton extends QuillToolbarBaseButton< QuillToolbarFontSizeButtonOptions, QuillToolbarFontSizeButtonExtraOptions> { QuillToolbarFontSizeButton({ required super.controller, @@ -28,7 +28,7 @@ class QuillToolbarFontSizeButton extends QuillToolbarBaseValueButton< QuillToolbarFontSizeButtonState(); } -class QuillToolbarFontSizeButtonState extends QuillToolbarBaseValueButtonState< +class QuillToolbarFontSizeButtonState extends QuillToolbarBaseButtonState< QuillToolbarFontSizeButton, QuillToolbarFontSizeButtonOptions, QuillToolbarFontSizeButtonExtraOptions, diff --git a/lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_buttons.dart b/lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_buttons.dart index e04a1f22..53162b7b 100644 --- a/lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_buttons.dart +++ b/lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_buttons.dart @@ -5,29 +5,40 @@ import '../../../../extensions/quill_configurations_ext.dart'; import '../../../../l10n/extensions/localizations.dart'; import '../../../../models/documents/attribute.dart'; import '../../../../models/documents/style.dart'; -import '../../../../models/themes/quill_icon_theme.dart'; -import '../../../quill/quill_controller.dart'; +import '../../base_button/base_value_button.dart'; import '../../base_toolbar.dart'; -class QuillToolbarSelectHeaderStyleButtons extends StatefulWidget { +typedef QuillToolbarSelectHeaderStyleBaseButtons = QuillToolbarBaseButton< + QuillToolbarSelectHeaderStyleButtonsOptions, + QuillToolbarSelectHeaderStyleButtonsExtraOptions>; + +typedef QuillToolbarSelectHeaderStyleBaseButtonsState< + W extends QuillToolbarSelectHeaderStyleBaseButtons> + = QuillToolbarCommonButtonState< + W, + QuillToolbarSelectHeaderStyleButtonsOptions, + QuillToolbarSelectHeaderStyleButtonsExtraOptions>; + +class QuillToolbarSelectHeaderStyleButtons + extends QuillToolbarSelectHeaderStyleBaseButtons { const QuillToolbarSelectHeaderStyleButtons({ - required this.controller, - this.options = const QuillToolbarSelectHeaderStyleButtonsOptions(), + required super.controller, + super.options = const QuillToolbarSelectHeaderStyleButtonsOptions(), super.key, }); - final QuillController controller; - final QuillToolbarSelectHeaderStyleButtonsOptions options; - @override QuillToolbarSelectHeaderStyleButtonsState createState() => QuillToolbarSelectHeaderStyleButtonsState(); } class QuillToolbarSelectHeaderStyleButtonsState - extends State { + extends QuillToolbarSelectHeaderStyleBaseButtonsState { Attribute? _selectedAttribute; + @override + String get defaultTooltip => context.loc.headerStyle; + Style get _selectionStyle => controller.getSelectionStyle(); final _valueToText = { @@ -46,45 +57,6 @@ class QuillToolbarSelectHeaderStyleButtonsState controller.addListener(_didChangeEditingValue); } - QuillToolbarSelectHeaderStyleButtonsOptions get options { - return widget.options; - } - - QuillController get controller { - return widget.controller; - } - - double get iconSize { - final baseFontSize = baseButtonExtraOptions?.iconSize; - final iconSize = options.iconSize; - return iconSize ?? baseFontSize ?? kDefaultIconSize; - } - - double get iconButtonFactor { - final baseIconFactor = baseButtonExtraOptions?.iconButtonFactor; - final iconButtonFactor = options.iconButtonFactor; - return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor; - } - - VoidCallback? get afterButtonPressed { - return options.afterButtonPressed ?? - baseButtonExtraOptions?.afterButtonPressed; - } - - QuillIconTheme? get iconTheme { - return options.iconTheme ?? baseButtonExtraOptions?.iconTheme; - } - - QuillToolbarBaseButtonOptions? get baseButtonExtraOptions { - return context.quillToolbarBaseButtonOptions; - } - - String get tooltip { - return options.tooltip ?? - baseButtonExtraOptions?.tooltip ?? - context.loc.headerStyle; - } - Axis get axis { return options.axis ?? context.quillSimpleToolbarConfigurations?.axis ?? @@ -99,7 +71,7 @@ class QuillToolbarSelectHeaderStyleButtonsState afterButtonPressed?.call(); } - List get _attrbuites { + List get _attributes { return options.attributes ?? const [ Attribute.header, @@ -112,7 +84,7 @@ class QuillToolbarSelectHeaderStyleButtonsState @override Widget build(BuildContext context) { assert( - _attrbuites.every( + _attributes.every( (element) => _valueToText.keys.contains(element), ), 'All attributes must be one of them: header, h1, h2 or h3', @@ -126,7 +98,7 @@ class QuillToolbarSelectHeaderStyleButtonsState final childBuilder = options.childBuilder ?? baseButtonExtraOptions?.childBuilder; - final children = _attrbuites.map((attribute) { + final children = _attributes.map((attribute) { if (childBuilder != null) { return childBuilder( options, @@ -149,7 +121,7 @@ class QuillToolbarSelectHeaderStyleButtonsState icon: Text( _valueToText[attribute] ?? (throw ArgumentError.notNull( - 'attrbuite', + 'attribute', )), style: style.copyWith( color: isSelected diff --git a/lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_dropdown_button.dart b/lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_dropdown_button.dart index 11990200..7a84ba5d 100644 --- a/lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_dropdown_button.dart +++ b/lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_dropdown_button.dart @@ -3,27 +3,38 @@ import 'package:flutter/material.dart'; import '../../../../../translations.dart'; import '../../../../extensions/quill_configurations_ext.dart'; import '../../../../models/documents/attribute.dart'; -import '../../../../models/themes/quill_icon_theme.dart'; -import '../../../quill/quill_controller.dart'; +import '../../base_button/base_value_button.dart'; import '../../base_toolbar.dart'; -class QuillToolbarSelectHeaderStyleDropdownButton extends StatefulWidget { +typedef QuillToolbarSelectHeaderStyleDropdownBaseButton + = QuillToolbarBaseButton; + +typedef QuillToolbarSelectHeaderStyleDropdownBaseButtonsState< + W extends QuillToolbarSelectHeaderStyleDropdownButton> + = QuillToolbarCommonButtonState< + W, + QuillToolbarSelectHeaderStyleDropdownButtonOptions, + QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions>; + +class QuillToolbarSelectHeaderStyleDropdownButton + extends QuillToolbarSelectHeaderStyleDropdownBaseButton { const QuillToolbarSelectHeaderStyleDropdownButton({ - required this.controller, - this.options = const QuillToolbarSelectHeaderStyleDropdownButtonOptions(), + required super.controller, + super.options = const QuillToolbarSelectHeaderStyleDropdownButtonOptions(), super.key, }); - final QuillController controller; - final QuillToolbarSelectHeaderStyleDropdownButtonOptions options; - @override - State createState() => + QuillToolbarSelectHeaderStyleDropdownBaseButtonsState createState() => _QuillToolbarSelectHeaderStyleDropdownButtonState(); } class _QuillToolbarSelectHeaderStyleDropdownButtonState - extends State { + extends QuillToolbarSelectHeaderStyleDropdownBaseButtonsState { + @override + String get defaultTooltip => context.loc.headerStyle; + Attribute _selectedItem = Attribute.header; final _menuController = MenuController(); @@ -89,24 +100,6 @@ class _QuillToolbarSelectHeaderStyleDropdownButtonState return label; } - double get iconSize { - final baseFontSize = context.quillToolbarBaseButtonOptions?.iconSize; - final iconSize = widget.options.iconSize; - return iconSize ?? baseFontSize ?? kDefaultIconSize; - } - - double get iconButtonFactor { - final baseIconFactor = - context.quillToolbarBaseButtonOptions?.iconButtonFactor; - final iconButtonFactor = widget.options.iconButtonFactor; - return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor; - } - - QuillIconTheme? get iconTheme { - return widget.options.iconTheme ?? - context.quillToolbarBaseButtonOptions?.iconTheme; - } - List> get headerAttributes { return widget.options.attributes ?? [ @@ -117,21 +110,6 @@ class _QuillToolbarSelectHeaderStyleDropdownButtonState ]; } - String get tooltip { - return widget.options.tooltip ?? - context.quillToolbarBaseButtonOptions?.tooltip ?? - context.loc.headerStyle; - } - - QuillToolbarBaseButtonOptions? get baseButtonExtraOptions { - return context.quillToolbarBaseButtonOptions; - } - - VoidCallback? get afterButtonPressed { - return widget.options.afterButtonPressed ?? - baseButtonExtraOptions?.afterButtonPressed; - } - void _onPressed(Attribute e) { setState(() => _selectedItem = e); widget.controller.formatSelection(_selectedItem); diff --git a/lib/src/widgets/toolbar/buttons/history_button.dart b/lib/src/widgets/toolbar/buttons/history_button.dart index 9751a696..09284a84 100644 --- a/lib/src/widgets/toolbar/buttons/history_button.dart +++ b/lib/src/widgets/toolbar/buttons/history_button.dart @@ -1,15 +1,21 @@ import 'package:flutter/material.dart'; -import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; -import '../../quill/quill_controller.dart'; +import '../base_button/base_value_button.dart'; import '../base_toolbar.dart'; -class QuillToolbarHistoryButton extends StatefulWidget { +typedef QuillToolbarHistoryBaseButton = QuillToolbarBaseButton< + QuillToolbarHistoryButtonOptions, QuillToolbarHistoryButtonExtraOptions>; + +typedef QuillToolbarHistoryBaseButtonState + = QuillToolbarCommonButtonState; + +class QuillToolbarHistoryButton extends QuillToolbarHistoryBaseButton { const QuillToolbarHistoryButton({ - required this.controller, + required super.controller, required this.isUndo, - this.options = const QuillToolbarHistoryButtonOptions(), + super.options = const QuillToolbarHistoryButtonOptions(), super.key, }); @@ -17,25 +23,18 @@ class QuillToolbarHistoryButton extends StatefulWidget { /// otherwise it will be redo final bool isUndo; - final QuillToolbarHistoryButtonOptions options; - final QuillController controller; - @override QuillToolbarHistoryButtonState createState() => QuillToolbarHistoryButtonState(); } -class QuillToolbarHistoryButtonState extends State { - late ThemeData theme; +class QuillToolbarHistoryButtonState + extends QuillToolbarHistoryBaseButtonState { var _canPressed = false; - QuillToolbarHistoryButtonOptions get options { - return widget.options; - } - - QuillController get controller { - return widget.controller; - } + @override + String get defaultTooltip => + widget.isUndo ? context.loc.undo : context.loc.redo; @override void initState() { @@ -54,25 +53,11 @@ class QuillToolbarHistoryButtonState extends State { @override Widget build(BuildContext context) { - final baseButtonConfigurations = context.quillToolbarBaseButtonOptions; - final tooltip = options.tooltip ?? - baseButtonConfigurations?.tooltip ?? - (widget.isUndo ? context.loc.undo : context.loc.redo); final iconData = options.iconData ?? - baseButtonConfigurations?.iconData ?? + baseButtonExtraOptions?.iconData ?? (widget.isUndo ? Icons.undo_outlined : Icons.redo_outlined); final childBuilder = - options.childBuilder ?? baseButtonConfigurations?.childBuilder; - final iconSize = options.iconSize ?? - baseButtonConfigurations?.iconSize ?? - kDefaultIconSize; - final iconButtonFactor = options.iconButtonFactor ?? - baseButtonConfigurations?.iconButtonFactor ?? - kDefaultIconButtonFactor; - final iconTheme = options.iconTheme ?? baseButtonConfigurations?.iconTheme; - - final afterButtonPressed = options.afterButtonPressed ?? - baseButtonConfigurations?.afterButtonPressed; + options.childBuilder ?? baseButtonExtraOptions?.childBuilder; if (childBuilder != null) { return childBuilder( @@ -89,7 +74,6 @@ class QuillToolbarHistoryButtonState extends State { ); } - theme = Theme.of(context); return QuillToolbarIconButton( tooltip: tooltip, icon: Icon( @@ -122,13 +106,13 @@ class QuillToolbarHistoryButtonState extends State { if (controller.hasUndo) { controller.undo(); } - // _updateCanPressed(); // We are already listeneting for the changes + // _updateCanPressed(); // We are already listening for the changes return; } if (controller.hasRedo) { controller.redo(); - // _updateCanPressed(); // We are already listeneting for the changes + // _updateCanPressed(); // We are already listening for the changes } } } diff --git a/lib/src/widgets/toolbar/buttons/indent_button.dart b/lib/src/widgets/toolbar/buttons/indent_button.dart index 2b2453a4..a750e7fc 100644 --- a/lib/src/widgets/toolbar/buttons/indent_button.dart +++ b/lib/src/widgets/toolbar/buttons/indent_button.dart @@ -1,63 +1,37 @@ import 'package:flutter/material.dart'; -import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; import '../../../models/config/toolbar/simple_toolbar_configurations.dart'; -import '../../../models/themes/quill_icon_theme.dart'; -import '../../quill/quill_controller.dart'; -import '../base_toolbar.dart' - show QuillToolbarBaseButtonOptions, QuillToolbarIconButton; +import '../base_button/base_value_button.dart'; +import '../base_toolbar.dart' show QuillToolbarIconButton; -class QuillToolbarIndentButton extends StatefulWidget { +typedef QuillToolbarIndentBaseButton = QuillToolbarBaseButton< + QuillToolbarIndentButtonOptions, QuillToolbarIndentButtonExtraOptions>; + +typedef QuillToolbarIndentBaseButtonState + = QuillToolbarCommonButtonState; + +class QuillToolbarIndentButton extends QuillToolbarIndentBaseButton { const QuillToolbarIndentButton({ - required this.controller, + required super.controller, required this.isIncrease, - this.options = const QuillToolbarIndentButtonOptions(), + super.options = const QuillToolbarIndentButtonOptions(), super.key, }); - final QuillController controller; final bool isIncrease; - final QuillToolbarIndentButtonOptions options; @override QuillToolbarIndentButtonState createState() => QuillToolbarIndentButtonState(); } -class QuillToolbarIndentButtonState extends State { - QuillToolbarIndentButtonOptions get options { - return widget.options; - } - - QuillController get controller { - return widget.controller; - } - - double get iconSize { - final baseFontSize = baseButtonExtraOptions?.iconSize; - final iconSize = options.iconSize; - return iconSize ?? baseFontSize ?? kDefaultIconSize; - } - - double get iconButtonFactor { - final baseIconFactor = baseButtonExtraOptions?.iconButtonFactor; - final iconButtonFactor = options.iconButtonFactor; - return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor; - } - - VoidCallback? get afterButtonPressed { - return options.afterButtonPressed ?? - baseButtonExtraOptions?.afterButtonPressed; - } - - QuillIconTheme? get iconTheme { - return options.iconTheme ?? baseButtonExtraOptions?.iconTheme; - } - - QuillToolbarBaseButtonOptions? get baseButtonExtraOptions { - return context.quillToolbarBaseButtonOptions; - } +class QuillToolbarIndentButtonState extends QuillToolbarIndentBaseButtonState { + @override + String get defaultTooltip => widget.isIncrease + ? context.loc.increaseIndent + : context.loc.decreaseIndent; IconData get iconData { return options.iconData ?? @@ -67,14 +41,6 @@ class QuillToolbarIndentButtonState extends State { : Icons.format_indent_decrease); } - String get tooltip { - return options.tooltip ?? - baseButtonExtraOptions?.tooltip ?? - (widget.isIncrease - ? context.loc.increaseIndent - : context.loc.decreaseIndent); - } - void _sharedOnPressed() { widget.controller.indentSelection(widget.isIncrease); } diff --git a/lib/src/widgets/toolbar/buttons/link_style2_button.dart b/lib/src/widgets/toolbar/buttons/link_style2_button.dart index ff54f117..b6178cdd 100644 --- a/lib/src/widgets/toolbar/buttons/link_style2_button.dart +++ b/lib/src/widgets/toolbar/buttons/link_style2_button.dart @@ -12,15 +12,25 @@ import '../../../models/themes/quill_dialog_theme.dart'; import '../../../models/themes/quill_icon_theme.dart'; import '../../others/link.dart'; import '../../quill/quill_controller.dart'; +import '../base_button/base_value_button.dart'; import '../base_toolbar.dart'; +typedef QuillToolbarLinkStyleBaseButton2 = QuillToolbarBaseButton< + QuillToolbarLinkStyleButton2Options, + QuillToolbarLinkStyleButton2ExtraOptions>; + +typedef QuillToolbarLinkStyleBaseButton2State< + W extends QuillToolbarLinkStyleBaseButton2> + = QuillToolbarCommonButtonState; + /// Alternative version of [QuillToolbarLinkStyleButton]. This widget has more /// customization /// and uses dialog similar to one which is used on [http://quilljs.com]. -class QuillToolbarLinkStyleButton2 extends StatefulWidget { +class QuillToolbarLinkStyleButton2 extends QuillToolbarLinkStyleBaseButton2 { QuillToolbarLinkStyleButton2({ - required this.controller, - this.options = const QuillToolbarLinkStyleButton2Options(), + required super.controller, + super.options = const QuillToolbarLinkStyleButton2Options(), super.key, }) : assert(options.addLinkLabel == null || (options.addLinkLabel?.isNotEmpty ?? true)), @@ -30,9 +40,6 @@ class QuillToolbarLinkStyleButton2 extends StatefulWidget { assert(options.validationMessage == null || (options.validationMessage?.isNotEmpty ?? true)); - final QuillController controller; - final QuillToolbarLinkStyleButton2Options options; - @override State createState() => _QuillToolbarLinkStyleButton2State(); diff --git a/lib/src/widgets/toolbar/buttons/link_style_button.dart b/lib/src/widgets/toolbar/buttons/link_style_button.dart index dfa22817..cb8f24b8 100644 --- a/lib/src/widgets/toolbar/buttons/link_style_button.dart +++ b/lib/src/widgets/toolbar/buttons/link_style_button.dart @@ -7,28 +7,36 @@ import '../../../models/documents/attribute.dart'; import '../../../models/rules/insert.dart'; import '../../../models/structs/link_dialog_action.dart'; import '../../../models/themes/quill_dialog_theme.dart'; -import '../../../models/themes/quill_icon_theme.dart'; import '../../others/link.dart'; -import '../../quill/quill_controller.dart'; +import '../base_button/base_value_button.dart'; import '../base_toolbar.dart'; -class QuillToolbarLinkStyleButton extends StatefulWidget { +typedef QuillToolbarLinkStyleBaseButton = QuillToolbarBaseButton< + QuillToolbarLinkStyleButtonOptions, + QuillToolbarLinkStyleButtonExtraOptions>; + +typedef QuillToolbarLinkStyleBaseButtonState< + W extends QuillToolbarLinkStyleBaseButton> + = QuillToolbarCommonButtonState; + +class QuillToolbarLinkStyleButton extends QuillToolbarLinkStyleBaseButton { const QuillToolbarLinkStyleButton({ - required this.controller, - this.options = const QuillToolbarLinkStyleButtonOptions(), + required super.controller, + super.options = const QuillToolbarLinkStyleButtonOptions(), super.key, }); - final QuillController controller; - final QuillToolbarLinkStyleButtonOptions options; - @override QuillToolbarLinkStyleButtonState createState() => QuillToolbarLinkStyleButtonState(); } class QuillToolbarLinkStyleButtonState - extends State { + extends QuillToolbarLinkStyleBaseButtonState { + @override + String get defaultTooltip => context.loc.insertURL; + void _didChangeSelection() { setState(() {}); } @@ -54,45 +62,6 @@ class QuillToolbarLinkStyleButtonState controller.removeListener(_didChangeSelection); } - QuillController get controller { - return widget.controller; - } - - QuillToolbarLinkStyleButtonOptions get options { - return widget.options; - } - - double get iconSize { - final baseFontSize = baseButtonExtraOptions?.iconSize; - final iconSize = options.iconSize; - return iconSize ?? baseFontSize ?? kDefaultIconSize; - } - - double get iconButtonFactor { - final baseIconFactor = baseButtonExtraOptions?.iconButtonFactor; - final iconButtonFactor = options.iconButtonFactor; - return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor; - } - - VoidCallback? get afterButtonPressed { - return options.afterButtonPressed ?? - baseButtonExtraOptions?.afterButtonPressed; - } - - QuillIconTheme? get iconTheme { - return options.iconTheme ?? baseButtonExtraOptions?.iconTheme; - } - - QuillToolbarBaseButtonOptions? get baseButtonExtraOptions { - return context.quillToolbarBaseButtonOptions; - } - - String get tooltip { - return options.tooltip ?? - baseButtonExtraOptions?.tooltip ?? - context.loc.insertURL; - } - IconData get iconData { return options.iconData ?? baseButtonExtraOptions?.iconData ?? Icons.link; } diff --git a/lib/src/widgets/toolbar/buttons/toggle_check_list_button.dart b/lib/src/widgets/toolbar/buttons/toggle_check_list_button.dart index 00a46db3..2bf6825b 100644 --- a/lib/src/widgets/toolbar/buttons/toggle_check_list_button.dart +++ b/lib/src/widgets/toolbar/buttons/toggle_check_list_button.dart @@ -7,7 +7,7 @@ import '../../../utils/widgets.dart'; import '../base_button/base_value_button.dart'; import '../base_toolbar.dart'; -class QuillToolbarToggleCheckListButton extends QuillToolbarBaseValueButton< +class QuillToolbarToggleCheckListButton extends QuillToolbarBaseButton< QuillToolbarToggleCheckListButtonOptions, QuillToolbarToggleCheckListButtonExtraOptions> { const QuillToolbarToggleCheckListButton({ @@ -22,7 +22,7 @@ class QuillToolbarToggleCheckListButton extends QuillToolbarBaseValueButton< } class QuillToolbarToggleCheckListButtonState - extends QuillToolbarBaseValueButtonState< + extends QuillToolbarBaseButtonState< QuillToolbarToggleCheckListButton, QuillToolbarToggleCheckListButtonOptions, QuillToolbarToggleCheckListButtonExtraOptions, diff --git a/test/widgets/controller_test.dart b/test/widgets/controller_test.dart index 10133064..d162a543 100644 --- a/test/widgets/controller_test.dart +++ b/test/widgets/controller_test.dart @@ -355,5 +355,61 @@ void main() { expect(controller.document.length, 6, reason: 'Cut not permitted on readOnly document'); }); + + test('blockSelectionStyles', () { + Style select(int start, int end) { + controller.updateSelection( + TextSelection(baseOffset: start, extentOffset: end), + ChangeSource.local); + return controller.getSelectionStyle(); + } + + Attribute fromKey(String key) => switch (key) { + 'header' => Attribute.h1, + 'list' => Attribute.ol, + 'align' => Attribute.centerAlignment, + 'code-block' => Attribute.codeBlock, + 'blockquote' => Attribute.blockQuote, + 'indent' => Attribute.indentL2, + 'direction' => Attribute.rtl, + String() => throw UnimplementedError(key) + }; + + for (final blockKey in Attribute.blockKeys) { + final blockAttribute = fromKey(blockKey); + controller + ..clear() + ..replaceText(0, 0, 'line 1\nLine 2\nLine 3', null) + ..formatText(0, 0, blockAttribute) // first 2 lines + ..formatText( + 4, 6, Attribute.bold) // spans end of line 1 and start of line 2 + ..formatText(7, 0, blockAttribute); + + expect(select(2, 5), const Style().put(blockAttribute), + reason: 'line 1 block, plain and bold'); + expect( + select(5, 6), const Style().put(Attribute.bold).put(blockAttribute), + reason: 'line 1 block, bold'); + expect( + select(4, 8), const Style().put(Attribute.bold).put(blockAttribute), + reason: 'spans line1 and 2, selection is all bold'); + expect(select(4, 11), const Style().put(blockAttribute), + reason: 'selection expands into non-bold text'); + expect(select(2, 11), const Style().put(blockAttribute), + reason: + 'selection starts in non-bold text extends into plain on next line'); + expect(select(2, 8), const Style().put(blockAttribute), + reason: + 'selection starts in non-bold text, extends into bold on next line'); + + expect( + select(7, 8), const Style().put(Attribute.bold).put(blockAttribute), + reason: 'line 2 block, bold'); + expect(select(7, 11), const Style().put(blockAttribute), + reason: 'line 2 block, selection extends into plain text'); + expect(select(4, 16), const Style(), + reason: 'line 1 extends into line3 which is not block'); + } + }); }); }