diff --git a/CHANGELOG.md b/CHANGELOG.md index 00fb97789..fd5a486bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [7.7.0] + +- **Breaking change**: We have mirgrated more buttons in the toolbar configurations, you can do change them in the `QuillProvider` +- Important bug fixes + ## [7.6.1] - Bug fixes diff --git a/lib/src/models/config/toolbar/buttons/base.dart b/lib/src/models/config/toolbar/buttons/base.dart index d683b5a07..931b6fa39 100644 --- a/lib/src/models/config/toolbar/buttons/base.dart +++ b/lib/src/models/config/toolbar/buttons/base.dart @@ -1,11 +1,35 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:equatable/equatable.dart'; import 'package:flutter/foundation.dart' show VoidCallback, immutable; -import 'package:flutter/widgets.dart' show IconData, Widget; +import 'package:flutter/widgets.dart' show BuildContext, IconData, Widget; import '../../../../../flutter_quill.dart' show QuillController, QuillProvider; import '../../../themes/quill_icon_theme.dart' show QuillIconTheme; import '../../quill_configurations.dart' show kDefaultIconSize; +@immutable +class QuillToolbarBaseButtonExtraOptions extends Equatable { + const QuillToolbarBaseButtonExtraOptions({ + required this.controller, + required this.context, + required this.onPressed, + }); + + /// if you need the not null controller for some usage in the [childBuilder] + /// then please use this instead of the one in the [options] + final QuillController controller; + + /// if the child builder you must use this when the widget tapped or pressed + /// in order to do what it expected to do + final VoidCallback? onPressed; + + final BuildContext context; + @override + List get props => [ + controller, + ]; +} + /// The [T] is the options for the button, usually should refresnce itself /// it's used in [childBuilder] so the developer can custmize this when using it /// The [I] is extra options for the button, usually for it's state @@ -41,10 +65,13 @@ class QuillToolbarBaseButtonOptions extends Equatable { final QuillIconTheme? iconTheme; /// If you want to dispaly a differnet widget based using a builder - final Widget Function(T options, I extraOptions)? childBuilder; + final QuillToolbarButtonOptionsChildBuilder childBuilder; /// By default it will be from the one in [QuillProvider] /// To override it you must pass not null controller + /// if you wish to use the controller in the [childBuilder], please use the + /// one from the extraOptions since it will be not null and will be the one + /// which will be used from the quill toolbar final QuillController? controller; @override @@ -57,4 +84,29 @@ class QuillToolbarBaseButtonOptions extends Equatable { childBuilder, controller, ]; + + // QuillToolbarBaseButtonOptions copyWith({ + // IconData? iconData, + // double? globalIconSize, + // VoidCallback? afterButtonPressed, + // String? tooltip, + // QuillIconTheme? iconTheme, + // Widget Function(T options, I extraOptions)? childBuilder, + // QuillController? controller, + // }) { + // return QuillToolbarBaseButtonOptions( + // iconData: iconData ?? this.iconData, + // globalIconSize: globalIconSize ?? this.globalIconSize, + // afterButtonPressed: afterButtonPressed ?? this.afterButtonPressed, + // tooltip: tooltip ?? this.tooltip, + // iconTheme: iconTheme ?? this.iconTheme, + // childBuilder: childBuilder ?? this.childBuilder, + // controller: controller ?? this.controller, + // ); + // } } + +typedef QuillToolbarButtonOptionsChildBuilder = Widget Function( + T options, + I extraOptions, +)?; diff --git a/lib/src/models/config/toolbar/buttons/font_family.dart b/lib/src/models/config/toolbar/buttons/font_family.dart index 92bb5395f..6b79685fe 100644 --- a/lib/src/models/config/toolbar/buttons/font_family.dart +++ b/lib/src/models/config/toolbar/buttons/font_family.dart @@ -3,19 +3,25 @@ import 'package:flutter/material.dart' show Colors, PopupMenuEntry; import 'package:flutter/widgets.dart' show Color, - ValueChanged, + EdgeInsets, EdgeInsetsGeometry, + IconData, + TextOverflow, TextStyle, - EdgeInsets, - TextOverflow; + ValueChanged, + VoidCallback; import '../../../../../flutter_quill.dart'; @immutable -class QuillToolbarFontFamilyButtonExtraOptions { +class QuillToolbarFontFamilyButtonExtraOptions + extends QuillToolbarBaseButtonExtraOptions { const QuillToolbarFontFamilyButtonExtraOptions({ required this.defaultDisplayText, required this.currentValue, + required super.controller, + required super.context, + required super.onPressed, }); final String defaultDisplayText; final String currentValue; @@ -76,4 +82,59 @@ class QuillToolbarFontFamilyButtonOptions extends QuillToolbarBaseButtonOptions< /// By default will use [globalIconSize] final double? iconSize; + + QuillToolbarFontFamilyButtonOptions copyWith({ + Color? fillColor, + double? hoverElevation, + double? highlightElevation, + List>? items, + Map? rawItemsMap, + ValueChanged? onSelected, + Attribute? attribute, + EdgeInsetsGeometry? padding, + TextStyle? style, + double? width, + String? initialValue, + TextOverflow? labelOverflow, + bool? renderFontFamilies, + bool? overrideTooltipByFontFamily, + double? itemHeight, + EdgeInsets? itemPadding, + Color? defaultItemColor, + double? iconSize, + // Add properties to override inherited properties + QuillController? controller, + IconData? iconData, + VoidCallback? afterButtonPressed, + String? tooltip, + QuillIconTheme? iconTheme, + }) { + return QuillToolbarFontFamilyButtonOptions( + attribute: attribute ?? this.attribute, + rawItemsMap: rawItemsMap ?? this.rawItemsMap, + controller: controller ?? this.controller, + iconData: iconData ?? this.iconData, + afterButtonPressed: afterButtonPressed ?? this.afterButtonPressed, + tooltip: tooltip ?? this.tooltip, + iconTheme: iconTheme ?? this.iconTheme, + onSelected: onSelected ?? this.onSelected, + padding: padding ?? this.padding, + style: style ?? this.style, + width: width ?? this.width, + initialValue: initialValue ?? this.initialValue, + labelOverflow: labelOverflow ?? this.labelOverflow, + renderFontFamilies: renderFontFamilies ?? this.renderFontFamilies, + overrideTooltipByFontFamily: + overrideTooltipByFontFamily ?? this.overrideTooltipByFontFamily, + itemHeight: itemHeight ?? this.itemHeight, + itemPadding: itemPadding ?? this.itemPadding, + defaultItemColor: defaultItemColor ?? this.defaultItemColor, + iconSize: iconSize ?? this.iconSize, + fillColor: fillColor ?? this.fillColor, + hoverElevation: hoverElevation ?? this.hoverElevation, + highlightElevation: highlightElevation ?? this.highlightElevation, + // ignore: deprecated_member_use_from_same_package + items: items ?? this.items, + ); + } } diff --git a/lib/src/models/config/toolbar/buttons/font_size.dart b/lib/src/models/config/toolbar/buttons/font_size.dart index 693e55fbb..e9d4d5e7e 100644 --- a/lib/src/models/config/toolbar/buttons/font_size.dart +++ b/lib/src/models/config/toolbar/buttons/font_size.dart @@ -1,22 +1,33 @@ +import 'dart:ui'; + import 'package:flutter/foundation.dart' show immutable; import 'package:flutter/material.dart' show Colors, PopupMenuEntry, ValueChanged; import 'package:flutter/widgets.dart' - show - Color, - EdgeInsetsGeometry, - TextStyle, - VoidCallback, - TextOverflow, - EdgeInsets; + show Color, EdgeInsets, EdgeInsetsGeometry, TextOverflow, TextStyle; import '../../../../widgets/controller.dart'; import '../../../documents/attribute.dart'; import '../../../themes/quill_icon_theme.dart'; import '../../quill_configurations.dart'; +class QuillToolbarFontSizeButtonExtraOptions + extends QuillToolbarBaseButtonExtraOptions { + const QuillToolbarFontSizeButtonExtraOptions({ + required super.controller, + required this.currentValue, + required this.defaultDisplayText, + required super.context, + required super.onPressed, + }); + + final String currentValue; + final String defaultDisplayText; +} + @immutable -class QuillToolbarFontSizeButtonOptions extends QuillToolbarBaseButtonOptions { +class QuillToolbarFontSizeButtonOptions extends QuillToolbarBaseButtonOptions< + QuillToolbarFontSizeButtonOptions, QuillToolbarFontSizeButtonExtraOptions> { const QuillToolbarFontSizeButtonOptions({ this.iconSize, this.fillColor, @@ -25,11 +36,11 @@ class QuillToolbarFontSizeButtonOptions extends QuillToolbarBaseButtonOptions { this.items, this.rawItemsMap, this.onSelected, - this.iconTheme, + super.iconTheme, this.attribute = Attribute.size, - this.controller, - this.afterButtonPressed, - this.tooltip, + super.controller, + super.afterButtonPressed, + super.tooltip, this.padding, this.style, this.width, @@ -38,6 +49,7 @@ class QuillToolbarFontSizeButtonOptions extends QuillToolbarBaseButtonOptions { this.itemHeight, this.itemPadding, this.defaultItemColor = Colors.red, + super.childBuilder, }); final double? iconSize; @@ -46,17 +58,12 @@ class QuillToolbarFontSizeButtonOptions extends QuillToolbarBaseButtonOptions { final double highlightElevation; @Deprecated('It is not required because of `rawItemsMap`') final List>? items; + + /// By default it will be [fontSizesValues] from [QuillToolbarConfigurations] + /// You can override this if you want final Map? rawItemsMap; final ValueChanged? onSelected; - @override - final QuillIconTheme? iconTheme; final Attribute attribute; - @override - final QuillController? controller; - @override - final VoidCallback? afterButtonPressed; - @override - final String? tooltip; final EdgeInsetsGeometry? padding; final TextStyle? style; final double? width; @@ -65,4 +72,51 @@ class QuillToolbarFontSizeButtonOptions extends QuillToolbarBaseButtonOptions { final double? itemHeight; final EdgeInsets? itemPadding; final Color? defaultItemColor; + + QuillToolbarFontSizeButtonOptions copyWith({ + double? iconSize, + Color? fillColor, + double? hoverElevation, + double? highlightElevation, + List>? items, + Map? rawItemsMap, + ValueChanged? onSelected, + Attribute? attribute, + EdgeInsetsGeometry? padding, + TextStyle? style, + double? width, + String? initialValue, + TextOverflow? labelOverflow, + double? itemHeight, + EdgeInsets? itemPadding, + Color? defaultItemColor, + VoidCallback? afterButtonPressed, + String? tooltip, + QuillIconTheme? iconTheme, + QuillController? controller, + }) { + return QuillToolbarFontSizeButtonOptions( + iconSize: iconSize ?? this.iconSize, + fillColor: fillColor ?? this.fillColor, + hoverElevation: hoverElevation ?? this.hoverElevation, + highlightElevation: highlightElevation ?? this.highlightElevation, + // ignore: deprecated_member_use_from_same_package + items: items ?? this.items, + rawItemsMap: rawItemsMap ?? this.rawItemsMap, + onSelected: onSelected ?? this.onSelected, + attribute: attribute ?? this.attribute, + padding: padding ?? this.padding, + style: style ?? this.style, + width: width ?? this.width, + initialValue: initialValue ?? this.initialValue, + labelOverflow: labelOverflow ?? this.labelOverflow, + itemHeight: itemHeight ?? this.itemHeight, + itemPadding: itemPadding ?? this.itemPadding, + defaultItemColor: defaultItemColor ?? this.defaultItemColor, + tooltip: tooltip ?? super.tooltip, + iconTheme: iconTheme ?? super.iconTheme, + afterButtonPressed: afterButtonPressed ?? super.afterButtonPressed, + controller: controller ?? super.controller, + ); + } } diff --git a/lib/src/models/config/toolbar/buttons/history.dart b/lib/src/models/config/toolbar/buttons/history.dart index 06076fcd5..eeceeda56 100644 --- a/lib/src/models/config/toolbar/buttons/history.dart +++ b/lib/src/models/config/toolbar/buttons/history.dart @@ -1,24 +1,24 @@ -import 'package:flutter/foundation.dart' show VoidCallback, immutable; +import 'package:flutter/foundation.dart' show immutable; import '../../../../../flutter_quill.dart'; @immutable -class HistoryButtonExtraOptions { - const HistoryButtonExtraOptions({ - required this.onPressed, +class QuillToolbarHistoryButtonExtraOptions + extends QuillToolbarBaseButtonExtraOptions { + const QuillToolbarHistoryButtonExtraOptions({ required this.canPressed, + required super.controller, + required super.context, + required super.onPressed, }); - /// When the button pressed - final VoidCallback onPressed; - /// If it can redo or undo final bool canPressed; } @immutable class QuillToolbarHistoryButtonOptions extends QuillToolbarBaseButtonOptions< - QuillToolbarHistoryButtonOptions, HistoryButtonExtraOptions> { + QuillToolbarHistoryButtonOptions, QuillToolbarHistoryButtonExtraOptions> { const QuillToolbarHistoryButtonOptions({ required this.isUndo, super.iconData, diff --git a/lib/src/models/config/toolbar/buttons/toggle_style.dart b/lib/src/models/config/toolbar/buttons/toggle_style.dart index 4f68693a4..68df179cd 100644 --- a/lib/src/models/config/toolbar/buttons/toggle_style.dart +++ b/lib/src/models/config/toolbar/buttons/toggle_style.dart @@ -1,15 +1,32 @@ import 'package:flutter/foundation.dart' show immutable; -import 'package:flutter/widgets.dart' show IconData; +import 'package:flutter/widgets.dart' show Color; import 'base.dart'; +class QuillToolbarToggleStyleButtonExtraOptions + extends QuillToolbarBaseButtonExtraOptions { + const QuillToolbarToggleStyleButtonExtraOptions({ + required super.controller, + required super.context, + required super.onPressed, + }); +} + @immutable class QuillToolbarToggleStyleButtonOptions - extends QuillToolbarBaseButtonOptions { + extends QuillToolbarBaseButtonOptions { const QuillToolbarToggleStyleButtonOptions({ - required this.iconData, + super.iconData, + this.iconSize, + this.fillColor, + super.tooltip, + super.afterButtonPressed, + super.iconTheme, + super.childBuilder, + super.controller, }); - @override - final IconData iconData; + final double? iconSize; + final Color? fillColor; } diff --git a/lib/src/models/config/toolbar/configurations.dart b/lib/src/models/config/toolbar/configurations.dart index b28b437db..3a8141437 100644 --- a/lib/src/models/config/toolbar/configurations.dart +++ b/lib/src/models/config/toolbar/configurations.dart @@ -3,8 +3,9 @@ import 'package:flutter/foundation.dart' show immutable; import 'buttons/base.dart'; import 'buttons/font_family.dart'; -// import 'buttons/font_size.dart'; +import 'buttons/font_size.dart'; import 'buttons/history.dart'; +import 'buttons/toggle_style.dart'; export './buttons/base.dart'; export './buttons/font_family.dart'; @@ -31,6 +32,7 @@ class QuillToolbarConfigurations extends Equatable { this.buttonOptions = const QuillToolbarButtonOptions(), this.multiRowsDisplay = true, this.fontFamilyValues, + this.fontSizesValues, /// By default it will calculated based on the [baseOptions] iconSize /// You can change it but the the change only apply if @@ -55,7 +57,7 @@ class QuillToolbarConfigurations extends Equatable { final QuillToolbarButtonOptions buttonOptions; final bool multiRowsDisplay; - /// By default will be final + /// By default it will be /// ``` /// { /// 'Sans Serif': 'sans-serif', @@ -71,11 +73,23 @@ class QuillToolbarConfigurations extends Equatable { /// ``` final Map? fontFamilyValues; + /// By default it will be + /// ``` + /// { + /// 'Small'.i18n: 'small', + /// 'Large'.i18n: 'large', + /// 'Huge'.i18n: 'huge', + /// 'Clear'.i18n: '0' + /// } + /// ``` + final Map? fontSizesValues; + @override List get props => [ buttonOptions, multiRowsDisplay, fontFamilyValues, + fontSizesValues, toolbarSize, ]; } @@ -92,7 +106,20 @@ class QuillToolbarButtonOptions extends Equatable { isUndo: false, ), this.fontFamily = const QuillToolbarFontFamilyButtonOptions(), - // this.fontSize = const QuillToolbarFontSizeButtonOptions(), + this.fontSize = const QuillToolbarFontSizeButtonOptions(), + this.bold = const QuillToolbarToggleStyleButtonOptions(), + this.subscript = const QuillToolbarToggleStyleButtonOptions(), + this.superscript = const QuillToolbarToggleStyleButtonOptions(), + this.italic = const QuillToolbarToggleStyleButtonOptions(), + this.small = const QuillToolbarToggleStyleButtonOptions(), + this.underLine = const QuillToolbarToggleStyleButtonOptions(), + this.strikeThrough = const QuillToolbarToggleStyleButtonOptions(), + this.inlineCode = const QuillToolbarToggleStyleButtonOptions(), + this.direction = const QuillToolbarToggleStyleButtonOptions(), + this.listNumbers = const QuillToolbarToggleStyleButtonOptions(), + this.listBullets = const QuillToolbarToggleStyleButtonOptions(), + this.codeBlock = const QuillToolbarToggleStyleButtonOptions(), + this.quote = const QuillToolbarToggleStyleButtonOptions(), }); /// The base configurations for all the buttons which will apply to all @@ -102,7 +129,20 @@ class QuillToolbarButtonOptions extends Equatable { final QuillToolbarHistoryButtonOptions undoHistory; final QuillToolbarHistoryButtonOptions redoHistory; final QuillToolbarFontFamilyButtonOptions fontFamily; - // final QuillToolbarFontSizeButtonOptions fontSize; + final QuillToolbarFontSizeButtonOptions fontSize; + final QuillToolbarToggleStyleButtonOptions bold; + final QuillToolbarToggleStyleButtonOptions subscript; + final QuillToolbarToggleStyleButtonOptions superscript; + final QuillToolbarToggleStyleButtonOptions italic; + final QuillToolbarToggleStyleButtonOptions small; + final QuillToolbarToggleStyleButtonOptions underLine; + final QuillToolbarToggleStyleButtonOptions strikeThrough; + final QuillToolbarToggleStyleButtonOptions inlineCode; + final QuillToolbarToggleStyleButtonOptions direction; + final QuillToolbarToggleStyleButtonOptions listNumbers; + final QuillToolbarToggleStyleButtonOptions listBullets; + final QuillToolbarToggleStyleButtonOptions codeBlock; + final QuillToolbarToggleStyleButtonOptions quote; @override List get props => [ diff --git a/lib/src/models/documents/attribute.dart b/lib/src/models/documents/attribute.dart index d0dd2cfce..91e6c89e4 100644 --- a/lib/src/models/documents/attribute.dart +++ b/lib/src/models/documents/attribute.dart @@ -1,5 +1,6 @@ import 'dart:collection'; +import 'package:equatable/equatable.dart'; import 'package:quiver/core.dart'; enum AttributeScope { @@ -9,8 +10,12 @@ enum AttributeScope { IGNORE, // attributes that can be ignored } -class Attribute { - const Attribute(this.key, this.scope, this.value); +class Attribute extends Equatable { + const Attribute( + this.key, + this.scope, + this.value, + ); /// Unique key of this attribute. final String key; @@ -102,7 +107,7 @@ class Attribute { static final ScriptAttribute script = ScriptAttribute(null); - // TODO: You might want to mark those as key (mobileWidthKey) + // TODO: You might want to mark those as key (like mobileWidthKey) // because it was not very clear to a developer that is new to this project static const String mobileWidth = 'mobileWidth'; @@ -253,6 +258,7 @@ class Attribute { return Attribute(origin.key, origin.scope, value); } + // This might not needed anymore because of equatable @override bool operator ==(Object other) { if (identical(this, other)) return true; @@ -263,6 +269,7 @@ class Attribute { value == typedOther.value; } + // This might not needed anymore because of equatable @override int get hashCode => hash3(key, scope, value); @@ -270,6 +277,9 @@ class Attribute { String toString() { return 'Attribute{key: $key, scope: $scope, value: $value}'; } + + @override + List get props => [key, scope, value]; } class BoldAttribute extends Attribute { diff --git a/lib/src/utils/extensions/quill_controller.dart b/lib/src/utils/extensions/quill_controller.dart index 0495daa2b..6fa3c73ba 100644 --- a/lib/src/utils/extensions/quill_controller.dart +++ b/lib/src/utils/extensions/quill_controller.dart @@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart' show BuildContext; import '../../../flutter_quill.dart' show QuillController, QuillProvider; import 'build_context.dart'; -extension QuillControllerExt on QuillController? { +extension QuillControllerNullableExt on QuillController? { /// Simple logic to use the current passed controller if not null /// if null then we will have to use the default one from [QuillProvider] /// using the [context] diff --git a/lib/src/utils/widgets.dart b/lib/src/utils/widgets.dart index c27777f0e..f7126616e 100644 --- a/lib/src/utils/widgets.dart +++ b/lib/src/utils/widgets.dart @@ -6,16 +6,20 @@ typedef WidgetWrapper = Widget Function(Widget child); abstract class UtilityWidgets { /// Conditionally wraps the [child] with [Tooltip] widget if [message] /// is not null and not empty. - static Widget maybeTooltip({required Widget child, String? message}) => - (message ?? '').isNotEmpty - ? Tooltip(message: message!, child: child) + static Widget maybeTooltip({ + required Widget child, + String? message, + }) => + (message?.isNotEmpty ?? false) + ? Tooltip(message: message, child: child) : child; /// Conditionally wraps the [child] with [wrapper] widget if [enabled] /// is true. - static Widget maybeWidget( - {required WidgetWrapper wrapper, - required Widget child, - bool enabled = false}) => + static Widget maybeWidget({ + required WidgetWrapper wrapper, + required Widget child, + bool enabled = false, + }) => enabled ? wrapper(child) : child; } diff --git a/lib/src/widgets/toolbar/buttons/arrow_indicated_list.dart b/lib/src/widgets/toolbar/buttons/arrow_indicated_list.dart index 84f198548..146fcc954 100644 --- a/lib/src/widgets/toolbar/buttons/arrow_indicated_list.dart +++ b/lib/src/widgets/toolbar/buttons/arrow_indicated_list.dart @@ -6,8 +6,8 @@ import 'package:flutter/material.dart'; /// /// The arrow indicators are automatically hidden if the list is not /// scrollable in the direction of the respective arrow. -class ArrowIndicatedButtonList extends StatefulWidget { - const ArrowIndicatedButtonList({ +class QuillToolbarArrowIndicatedButtonList extends StatefulWidget { + const QuillToolbarArrowIndicatedButtonList({ required this.axis, required this.buttons, Key? key, @@ -17,11 +17,12 @@ class ArrowIndicatedButtonList extends StatefulWidget { final List buttons; @override - _ArrowIndicatedButtonListState createState() => - _ArrowIndicatedButtonListState(); + _QuillToolbarArrowIndicatedButtonListState createState() => + _QuillToolbarArrowIndicatedButtonListState(); } -class _ArrowIndicatedButtonListState extends State +class _QuillToolbarArrowIndicatedButtonListState + extends State with WidgetsBindingObserver { final ScrollController _controller = ScrollController(); bool _showBackwardArrow = false; diff --git a/lib/src/widgets/toolbar/buttons/clear_format.dart b/lib/src/widgets/toolbar/buttons/clear_format.dart index 7a1d53979..56a4af798 100644 --- a/lib/src/widgets/toolbar/buttons/clear_format.dart +++ b/lib/src/widgets/toolbar/buttons/clear_format.dart @@ -5,8 +5,8 @@ import '../../../models/themes/quill_icon_theme.dart'; import '../../controller.dart'; import '../toolbar.dart'; -class ClearFormatButton extends StatefulWidget { - const ClearFormatButton({ +class QuillToolbarClearFormatButton extends StatefulWidget { + const QuillToolbarClearFormatButton({ required this.icon, required this.controller, this.iconSize = kDefaultIconSize, @@ -26,10 +26,12 @@ class ClearFormatButton extends StatefulWidget { final String? tooltip; @override - _ClearFormatButtonState createState() => _ClearFormatButtonState(); + _QuillToolbarClearFormatButtonState createState() => + _QuillToolbarClearFormatButtonState(); } -class _ClearFormatButtonState extends State { +class _QuillToolbarClearFormatButtonState + extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -37,7 +39,7 @@ class _ClearFormatButtonState extends State { widget.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color; final fillColor = widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor; - return QuillIconButton( + return QuillToolbarIconButton( tooltip: widget.tooltip, highlightElevation: 0, hoverElevation: 0, diff --git a/lib/src/widgets/toolbar/buttons/color.dart b/lib/src/widgets/toolbar/buttons/color.dart index 8733c2096..e7d5e6b71 100644 --- a/lib/src/widgets/toolbar/buttons/color.dart +++ b/lib/src/widgets/toolbar/buttons/color.dart @@ -13,8 +13,8 @@ import '../toolbar.dart'; /// /// When pressed, this button displays overlay toolbar with /// buttons for each color. -class ColorButton extends StatefulWidget { - const ColorButton({ +class QuillToolbarColorButton extends StatefulWidget { + const QuillToolbarColorButton({ required this.icon, required this.controller, required this.background, @@ -36,10 +36,11 @@ class ColorButton extends StatefulWidget { final Color dialogBarrierColor; @override - _ColorButtonState createState() => _ColorButtonState(); + _QuillToolbarColorButtonState createState() => + _QuillToolbarColorButtonState(); } -class _ColorButtonState extends State { +class _QuillToolbarColorButtonState extends State { late bool _isToggledColor; late bool _isToggledBackground; late bool _isWhite; @@ -81,7 +82,7 @@ class _ColorButtonState extends State { } @override - void didUpdateWidget(covariant ColorButton oldWidget) { + void didUpdateWidget(covariant QuillToolbarColorButton oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.controller != widget.controller) { oldWidget.controller.removeListener(_didChangeEditingValue); @@ -122,7 +123,7 @@ class _ColorButtonState extends State { ? stringToColor('#ffffff') : (widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor); - return QuillIconButton( + return QuillToolbarIconButton( tooltip: widget.tooltip, highlightElevation: 0, hoverElevation: 0, diff --git a/lib/src/widgets/toolbar/buttons/custom_button.dart b/lib/src/widgets/toolbar/buttons/custom_button.dart index 99924f8b0..b958ed0e7 100644 --- a/lib/src/widgets/toolbar/buttons/custom_button.dart +++ b/lib/src/widgets/toolbar/buttons/custom_button.dart @@ -28,7 +28,7 @@ class CustomButton extends StatelessWidget { final theme = Theme.of(context); final iconColor = iconTheme?.iconUnselectedColor ?? theme.iconTheme.color; - return QuillIconButton( + return QuillToolbarIconButton( highlightElevation: 0, hoverElevation: 0, size: iconSize * kIconButtonFactor, diff --git a/lib/src/widgets/toolbar/buttons/font_family.dart b/lib/src/widgets/toolbar/buttons/font_family.dart index e23288682..5ec16f6d8 100644 --- a/lib/src/widgets/toolbar/buttons/font_family.dart +++ b/lib/src/widgets/toolbar/buttons/font_family.dart @@ -4,14 +4,15 @@ import '../../../../extensions.dart'; import '../../../models/config/toolbar/buttons/font_family.dart'; import '../../../models/documents/attribute.dart'; import '../../../models/documents/style.dart'; +import '../../../models/themes/quill_icon_theme.dart'; import '../../../translations/toolbar.i18n.dart'; import '../../../utils/extensions/build_context.dart'; -import '../../../utils/extensions/quill_controller.dart'; import '../../controller.dart'; class QuillToolbarFontFamilyButton extends StatefulWidget { QuillToolbarFontFamilyButton({ required this.options, + required this.controller, super.key, }) : assert(options.rawItemsMap?.isNotEmpty ?? (true)), assert( @@ -20,6 +21,10 @@ class QuillToolbarFontFamilyButton extends StatefulWidget { final QuillToolbarFontFamilyButtonOptions options; + /// Since we can't get the state from the instace of the widget for comparing + /// in [didUpdateWidget] then we will have to store reference here + final QuillController controller; + @override _QuillToolbarFontFamilyButtonState createState() => _QuillToolbarFontFamilyButtonState(); @@ -33,16 +38,12 @@ class _QuillToolbarFontFamilyButtonState return widget.options; } - /// Since t's not safe to call anything related to the context in dispose + /// Since it's not safe to call anything related to the context in dispose /// then we will save a reference to the [controller] /// and update it in [didChangeDependencies] /// and use it in dispose method late QuillController _controller; - QuillController get controller { - return options.controller.notNull(context); - } - Style get _selectionStyle => controller.getSelectionStyle(); @override @@ -82,7 +83,7 @@ class _QuillToolbarFontFamilyButtonState @override void didUpdateWidget(covariant QuillToolbarFontFamilyButton oldWidget) { super.didUpdateWidget(oldWidget); - if (controller == controller) { + if (oldWidget.controller == controller) { return; } controller @@ -102,6 +103,7 @@ class _QuillToolbarFontFamilyButtonState Map get rawItemsMap { final rawItemsMap = options.rawItemsMap ?? + context.requireQuillToolbarConfigurations.fontFamilyValues ?? { 'Sans Serif': 'sans-serif', 'Serif': 'serif', @@ -125,15 +127,36 @@ class _QuillToolbarFontFamilyButtonState return null; } + QuillController get controller { + return options.controller ?? widget.controller; + } + double get iconSize { + final baseFontSize = + context.requireQuillToolbarBaseButtonOptions.globalIconSize; final iconSize = options.iconSize; - return iconSize ?? 40; - // final baseFontSize = - // context.requireQuillToolbarBaseButtonOptions.globalIconSize; - // if (baseFontSize != iconSize) { - // return 40; - // } - // return iconSize ?? baseFontSize; + return iconSize ?? baseFontSize; + } + + VoidCallback? get afterButtonPressed { + return options.afterButtonPressed ?? + context.requireQuillToolbarBaseButtonOptions.afterButtonPressed; + } + + QuillIconTheme? get iconTheme { + return options.iconTheme ?? + context.requireQuillToolbarBaseButtonOptions.iconTheme; + } + + String get tooltip { + return options.tooltip ?? + context.requireQuillToolbarBaseButtonOptions.tooltip ?? + 'Font family'.i18n; + } + + void _onPressed() { + _showMenu(); + options.afterButtonPressed?.call(); } @override @@ -144,10 +167,19 @@ class _QuillToolbarFontFamilyButtonState options.childBuilder ?? baseButtonConfigurations.childBuilder; if (childBuilder != null) { return childBuilder( - options, + options.copyWith( + iconSize: iconSize, + rawItemsMap: rawItemsMap, + iconTheme: iconTheme, + tooltip: tooltip, + afterButtonPressed: afterButtonPressed, + ), QuillToolbarFontFamilyButtonExtraOptions( currentValue: _currentValue, defaultDisplayText: _defaultDisplayText, + controller: controller, + context: context, + onPressed: _onPressed, ), ); } @@ -157,10 +189,9 @@ class _QuillToolbarFontFamilyButtonState width: options.width, ), child: UtilityWidgets.maybeWidget( - enabled: (options.tooltip ?? '').isNotEmpty || - options.overrideTooltipByFontFamily, + enabled: tooltip.isNotEmpty || options.overrideTooltipByFontFamily, wrapper: (child) { - var effectiveTooltip = options.tooltip ?? ''; + var effectiveTooltip = tooltip; if (options.overrideTooltipByFontFamily) { effectiveTooltip = effectiveTooltip.isNotEmpty ? '$effectiveTooltip: $_currentValue' @@ -171,17 +202,13 @@ class _QuillToolbarFontFamilyButtonState child: RawMaterialButton( visualDensity: VisualDensity.compact, shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(options.iconTheme?.borderRadius ?? 2), + borderRadius: BorderRadius.circular(iconTheme?.borderRadius ?? 2), ), fillColor: options.fillColor, elevation: 0, hoverElevation: options.hoverElevation, highlightElevation: options.hoverElevation, - onPressed: () { - _showMenu(); - options.afterButtonPressed?.call(); - }, + onPressed: _onPressed, child: _buildContent(context), ), ), @@ -266,8 +293,8 @@ class _QuillToolbarFontFamilyButtonState style: options.style ?? TextStyle( fontSize: iconSize / 1.15, - color: options.iconTheme?.iconUnselectedColor ?? - theme.iconTheme.color, + color: + iconTheme?.iconUnselectedColor ?? theme.iconTheme.color, ), ), ), @@ -275,8 +302,7 @@ class _QuillToolbarFontFamilyButtonState Icon( Icons.arrow_drop_down, size: iconSize / 1.15, - color: - options.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color, + color: iconTheme?.iconUnselectedColor ?? theme.iconTheme.color, ) ], ), diff --git a/lib/src/widgets/toolbar/buttons/font_size.dart b/lib/src/widgets/toolbar/buttons/font_size.dart new file mode 100644 index 000000000..d4622d550 --- /dev/null +++ b/lib/src/widgets/toolbar/buttons/font_size.dart @@ -0,0 +1,282 @@ +import 'package:flutter/material.dart'; + +import '../../../../extensions.dart'; +import '../../../../flutter_quill.dart'; +import '../../../translations/toolbar.i18n.dart'; +import '../../../utils/extensions/build_context.dart'; +import '../../../utils/font.dart'; + +class QuillToolbarFontSizeButton extends StatefulWidget { + QuillToolbarFontSizeButton({ + required this.options, + required this.controller, + super.key, + }) : assert(options.rawItemsMap?.isNotEmpty ?? true), + assert(options.initialValue == null || + (options.initialValue?.isNotEmpty ?? true)); + + final QuillToolbarFontSizeButtonOptions options; + + /// Since we can't get the state from the instace of the widget for comparing + /// in [didUpdateWidget] then we will have to store reference here + final QuillController controller; + + @override + _QuillToolbarFontSizeButtonState createState() => + _QuillToolbarFontSizeButtonState(); +} + +class _QuillToolbarFontSizeButtonState + extends State { + String _currentValue = ''; + + QuillToolbarFontSizeButtonOptions get options { + return widget.options; + } + + /// Since it's not safe to call anything related to the context in dispose + /// then we will save a reference to the [controller] + /// and update it in [didChangeDependencies] + /// and use it in dispose method + late QuillController _controller; + + Map get rawItemsMap { + final fontSizes = options.rawItemsMap ?? + context.requireQuillToolbarConfigurations.fontSizesValues ?? + { + 'Small'.i18n: 'small', + 'Large'.i18n: 'large', + 'Huge'.i18n: 'huge', + 'Clear'.i18n: '0' + }; + return fontSizes; + } + + String get _defaultDisplayText { + return options.initialValue ?? 'Size'.i18n; + } + + Style get _selectionStyle => controller.getSelectionStyle(); + + @override + void initState() { + super.initState(); + + _initState(); + } + + Future _initState() async { + if (isFlutterTest()) { + return; + } + await Future.delayed(Duration.zero); + setState(() { + _currentValue = _defaultDisplayText; + }); + controller.addListener(_didChangeEditingValue); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _controller = controller; + } + + @override + void dispose() { + _controller.removeListener(_didChangeEditingValue); + super.dispose(); + } + + @override + void didUpdateWidget(covariant QuillToolbarFontSizeButton oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.controller == controller) { + return; + } + controller + ..removeListener(_didChangeEditingValue) + ..addListener(_didChangeEditingValue); + } + + void _didChangeEditingValue() { + final attribute = _selectionStyle.attributes[options.attribute.key]; + if (attribute == null) { + setState(() => _currentValue = _defaultDisplayText); + return; + } + final keyName = _getKeyName(attribute.value); + setState(() => _currentValue = keyName ?? _defaultDisplayText); + } + + String? _getKeyName(dynamic value) { + for (final entry in rawItemsMap.entries) { + if (getFontSize(entry.value) == getFontSize(value)) { + return entry.key; + } + } + return null; + } + + QuillController get controller { + return options.controller ?? widget.controller; + } + + double get iconSize { + final baseFontSize = + context.requireQuillToolbarBaseButtonOptions.globalIconSize; + final iconSize = options.iconSize; + return iconSize ?? baseFontSize; + } + + VoidCallback? get afterButtonPressed { + return options.afterButtonPressed ?? + context.requireQuillToolbarBaseButtonOptions.afterButtonPressed; + } + + QuillIconTheme? get iconTheme { + return options.iconTheme ?? + context.requireQuillToolbarBaseButtonOptions.iconTheme; + } + + String get tooltip { + return options.tooltip ?? + context.requireQuillToolbarBaseButtonOptions.tooltip ?? + 'Font size'.i18n; + } + + void _onPressed() { + _showMenu(); + afterButtonPressed?.call(); + } + + @override + Widget build(BuildContext context) { + final baseButtonConfigurations = + context.requireQuillToolbarBaseButtonOptions; + final childBuilder = + options.childBuilder ?? baseButtonConfigurations.childBuilder; + if (childBuilder != null) { + return childBuilder( + options.copyWith( + tooltip: tooltip, + iconSize: iconSize, + iconTheme: iconTheme, + afterButtonPressed: afterButtonPressed, + controller: controller, + ), + QuillToolbarFontSizeButtonExtraOptions( + controller: controller, + currentValue: _currentValue, + defaultDisplayText: _defaultDisplayText, + context: context, + onPressed: _onPressed, + ), + ); + } + return ConstrainedBox( + constraints: BoxConstraints.tightFor( + height: iconSize * 1.81, + width: options.width, + ), + child: UtilityWidgets.maybeTooltip( + message: tooltip, + child: RawMaterialButton( + visualDensity: VisualDensity.compact, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(iconTheme?.borderRadius ?? 2), + ), + fillColor: options.fillColor, + elevation: 0, + hoverElevation: options.hoverElevation, + highlightElevation: options.hoverElevation, + onPressed: _onPressed, + child: _buildContent(context), + ), + ), + ); + } + + Future _showMenu() async { + final popupMenuTheme = PopupMenuTheme.of(context); + final button = context.findRenderObject() as RenderBox; + final overlay = Overlay.of(context).context.findRenderObject() as RenderBox; + final position = RelativeRect.fromRect( + Rect.fromPoints( + button.localToGlobal(Offset.zero, ancestor: overlay), + button.localToGlobal(button.size.bottomLeft(Offset.zero), + ancestor: overlay), + ), + Offset.zero & overlay.size, + ); + final newValue = await showMenu( + context: context, + elevation: 4, + items: [ + for (final MapEntry fontSize in rawItemsMap.entries) + PopupMenuItem( + key: ValueKey(fontSize.key), + value: fontSize.value, + height: options.itemHeight ?? kMinInteractiveDimension, + padding: options.itemPadding, + child: Text( + fontSize.key.toString(), + style: TextStyle( + color: fontSize.value == '0' ? options.defaultItemColor : null, + ), + ), + ), + ], + position: position, + shape: popupMenuTheme.shape, + color: popupMenuTheme.color, + ); + if (!mounted) return; + if (newValue == null) { + return; + } + final keyName = _getKeyName(newValue); + setState(() { + _currentValue = keyName ?? _defaultDisplayText; + if (keyName != null) { + controller.formatSelection(Attribute.fromKeyValue( + 'size', newValue == '0' ? null : getFontSize(newValue))); + options.onSelected?.call(newValue); + } + }); + } + + Widget _buildContent(BuildContext context) { + final theme = Theme.of(context); + final hasFinalWidth = options.width != null; + return Padding( + padding: options.padding ?? const EdgeInsets.fromLTRB(10, 0, 0, 0), + child: Row( + mainAxisSize: !hasFinalWidth ? MainAxisSize.min : MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + UtilityWidgets.maybeWidget( + enabled: hasFinalWidth, + wrapper: (child) => Expanded(child: child), + child: Text( + _currentValue, + overflow: options.labelOverflow, + style: options.style ?? + TextStyle( + fontSize: iconSize / 1.15, + color: + iconTheme?.iconUnselectedColor ?? theme.iconTheme.color, + ), + ), + ), + const SizedBox(width: 3), + Icon( + Icons.arrow_drop_down, + size: iconSize / 1.15, + color: iconTheme?.iconUnselectedColor ?? theme.iconTheme.color, + ) + ], + ), + ); + } +} diff --git a/lib/src/widgets/toolbar/buttons/history.dart b/lib/src/widgets/toolbar/buttons/history.dart index 603d10d86..817d9410a 100644 --- a/lib/src/widgets/toolbar/buttons/history.dart +++ b/lib/src/widgets/toolbar/buttons/history.dart @@ -86,16 +86,18 @@ class _QuillToolbarHistoryButtonState extends State { iconTheme: iconTheme, tooltip: tooltip, ), - HistoryButtonExtraOptions( + QuillToolbarHistoryButtonExtraOptions( onPressed: () { _updateHistory(); afterButtonPressed?.call(); }, canPressed: _canPressed, + controller: controller, + context: context, ), ); } - return QuillIconButton( + return QuillToolbarIconButton( tooltip: tooltip, highlightElevation: 0, hoverElevation: 0, diff --git a/lib/src/widgets/toolbar/buttons/indent.dart b/lib/src/widgets/toolbar/buttons/indent.dart index 2297431af..835c568e9 100644 --- a/lib/src/widgets/toolbar/buttons/indent.dart +++ b/lib/src/widgets/toolbar/buttons/indent.dart @@ -4,8 +4,8 @@ import '../../../models/themes/quill_icon_theme.dart'; import '../../controller.dart'; import '../toolbar.dart'; -class IndentButton extends StatefulWidget { - const IndentButton({ +class QuillToolbarIndentButton extends StatefulWidget { + const QuillToolbarIndentButton({ required this.icon, required this.controller, required this.isIncrease, @@ -26,10 +26,11 @@ class IndentButton extends StatefulWidget { final String? tooltip; @override - _IndentButtonState createState() => _IndentButtonState(); + _QuillToolbarIndentButtonState createState() => + _QuillToolbarIndentButtonState(); } -class _IndentButtonState extends State { +class _QuillToolbarIndentButtonState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -38,7 +39,7 @@ class _IndentButtonState extends State { widget.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color; final iconFillColor = widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor; - return QuillIconButton( + return QuillToolbarIconButton( tooltip: widget.tooltip, highlightElevation: 0, hoverElevation: 0, diff --git a/lib/src/widgets/toolbar/buttons/link_style.dart b/lib/src/widgets/toolbar/buttons/link_style.dart index bca23b58e..277f20d38 100644 --- a/lib/src/widgets/toolbar/buttons/link_style.dart +++ b/lib/src/widgets/toolbar/buttons/link_style.dart @@ -10,8 +10,8 @@ import '../../controller.dart'; import '../../link.dart'; import '../toolbar.dart'; -class LinkStyleButton extends StatefulWidget { - const LinkStyleButton({ +class QuillToolbarLinkStyleButton extends StatefulWidget { + const QuillToolbarLinkStyleButton({ required this.controller, this.iconSize = kDefaultIconSize, this.icon, @@ -37,10 +37,12 @@ class LinkStyleButton extends StatefulWidget { final Color dialogBarrierColor; @override - _LinkStyleButtonState createState() => _LinkStyleButtonState(); + _QuillToolbarLinkStyleButtonState createState() => + _QuillToolbarLinkStyleButtonState(); } -class _LinkStyleButtonState extends State { +class _QuillToolbarLinkStyleButtonState + extends State { void _didChangeSelection() { setState(() {}); } @@ -52,7 +54,7 @@ class _LinkStyleButtonState extends State { } @override - void didUpdateWidget(covariant LinkStyleButton oldWidget) { + void didUpdateWidget(covariant QuillToolbarLinkStyleButton oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.controller != widget.controller) { oldWidget.controller.removeListener(_didChangeSelection); @@ -71,7 +73,7 @@ class _LinkStyleButtonState extends State { final theme = Theme.of(context); final isToggled = _getLinkAttributeValue() != null; final pressedHandler = () => _openLinkDialog(context); - return QuillIconButton( + return QuillToolbarIconButton( tooltip: widget.tooltip, highlightElevation: 0, hoverElevation: 0, diff --git a/lib/src/widgets/toolbar/buttons/link_style2.dart b/lib/src/widgets/toolbar/buttons/link_style2.dart index 2e21941ad..0fea44ce7 100644 --- a/lib/src/widgets/toolbar/buttons/link_style2.dart +++ b/lib/src/widgets/toolbar/buttons/link_style2.dart @@ -12,10 +12,11 @@ import '../../controller.dart'; import '../../link.dart'; import '../toolbar.dart'; -/// Alternative version of [LinkStyleButton]. This widget has more customization +/// Alternative version of [QuillToolbarLinkStyleButton]. This widget has more +/// customization /// and uses dialog similar to one which is used on [http://quilljs.com]. -class LinkStyleButton2 extends StatefulWidget { - const LinkStyleButton2({ +class QuillToolbarLinkStyleButton2 extends StatefulWidget { + const QuillToolbarLinkStyleButton2({ required this.controller, this.icon, this.iconSize = kDefaultIconSize, @@ -71,10 +72,12 @@ class LinkStyleButton2 extends StatefulWidget { final Color dialogBarrierColor; @override - State createState() => _LinkStyleButton2State(); + State createState() => + _QuillToolbarLinkStyleButton2State(); } -class _LinkStyleButton2State extends State { +class _QuillToolbarLinkStyleButton2State + extends State { @override void dispose() { super.dispose(); @@ -88,7 +91,7 @@ class _LinkStyleButton2State extends State { } @override - void didUpdateWidget(covariant LinkStyleButton2 oldWidget) { + void didUpdateWidget(covariant QuillToolbarLinkStyleButton2 oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.controller != widget.controller) { oldWidget.controller.removeListener(_didChangeSelection); @@ -100,7 +103,7 @@ class _LinkStyleButton2State extends State { Widget build(BuildContext context) { final theme = Theme.of(context); final isToggled = _getLinkAttributeValue() != null; - return QuillIconButton( + return QuillToolbarIconButton( tooltip: widget.tooltip, highlightElevation: 0, hoverElevation: 0, diff --git a/lib/src/widgets/toolbar/buttons/quill_font_size.dart b/lib/src/widgets/toolbar/buttons/quill_font_size.dart deleted file mode 100644 index 5230005b2..000000000 --- a/lib/src/widgets/toolbar/buttons/quill_font_size.dart +++ /dev/null @@ -1,225 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../../models/documents/attribute.dart'; -import '../../../models/documents/style.dart'; -import '../../../models/themes/quill_icon_theme.dart'; -import '../../../translations/toolbar.i18n.dart'; -import '../../../utils/font.dart'; -import '../../../utils/widgets.dart'; -import '../../controller.dart'; - -class QuillToolbarFontSizeButton extends StatefulWidget { - const QuillToolbarFontSizeButton({ - required this.rawItemsMap, - required this.attribute, - required this.controller, - this.onSelected, - @Deprecated('It is not required because of `rawItemsMap`') this.items, - this.iconSize = 40, - this.fillColor, - this.hoverElevation = 1, - this.highlightElevation = 1, - this.iconTheme, - this.afterButtonPressed, - this.tooltip, - this.padding, - this.style, - this.width, - this.initialValue, - this.labelOverflow = TextOverflow.visible, - this.itemHeight, - this.itemPadding, - this.defaultItemColor = Colors.red, - Key? key, - }) : assert(rawItemsMap.length > 0), - assert(initialValue == null || initialValue.length > 0), - super(key: key); - - final double iconSize; - final Color? fillColor; - final double hoverElevation; - final double highlightElevation; - @Deprecated('It is not required because of `rawItemsMap`') - final List>? items; - final Map rawItemsMap; - final ValueChanged? onSelected; - final QuillIconTheme? iconTheme; - final Attribute attribute; - final QuillController controller; - final VoidCallback? afterButtonPressed; - final String? tooltip; - final EdgeInsetsGeometry? padding; - final TextStyle? style; - final double? width; - final String? initialValue; - final TextOverflow labelOverflow; - final double? itemHeight; - final EdgeInsets? itemPadding; - final Color? defaultItemColor; - - @override - _QuillToolbarFontSizeButtonState createState() => - _QuillToolbarFontSizeButtonState(); -} - -class _QuillToolbarFontSizeButtonState - extends State { - late String _defaultDisplayText; - late String _currentValue; - Style get _selectionStyle => widget.controller.getSelectionStyle(); - - @override - void initState() { - super.initState(); - _currentValue = _defaultDisplayText = widget.initialValue ?? 'Size'.i18n; - widget.controller.addListener(_didChangeEditingValue); - } - - @override - void dispose() { - widget.controller.removeListener(_didChangeEditingValue); - super.dispose(); - } - - @override - void didUpdateWidget(covariant QuillToolbarFontSizeButton oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.controller != widget.controller) { - oldWidget.controller.removeListener(_didChangeEditingValue); - widget.controller.addListener(_didChangeEditingValue); - } - } - - void _didChangeEditingValue() { - final attribute = _selectionStyle.attributes[widget.attribute.key]; - if (attribute == null) { - setState(() => _currentValue = _defaultDisplayText); - return; - } - final keyName = _getKeyName(attribute.value); - setState(() => _currentValue = keyName ?? _defaultDisplayText); - } - - String? _getKeyName(dynamic value) { - for (final entry in widget.rawItemsMap.entries) { - if (getFontSize(entry.value) == getFontSize(value)) { - return entry.key; - } - } - return null; - } - - @override - Widget build(BuildContext context) { - return ConstrainedBox( - constraints: BoxConstraints.tightFor( - height: widget.iconSize * 1.81, - width: widget.width, - ), - child: UtilityWidgets.maybeTooltip( - message: widget.tooltip, - child: RawMaterialButton( - visualDensity: VisualDensity.compact, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(widget.iconTheme?.borderRadius ?? 2), - ), - fillColor: widget.fillColor, - elevation: 0, - hoverElevation: widget.hoverElevation, - highlightElevation: widget.hoverElevation, - onPressed: () { - _showMenu(); - widget.afterButtonPressed?.call(); - }, - child: _buildContent(context), - ), - ), - ); - } - - Future _showMenu() async { - final popupMenuTheme = PopupMenuTheme.of(context); - final button = context.findRenderObject() as RenderBox; - final overlay = Overlay.of(context).context.findRenderObject() as RenderBox; - final position = RelativeRect.fromRect( - Rect.fromPoints( - button.localToGlobal(Offset.zero, ancestor: overlay), - button.localToGlobal(button.size.bottomLeft(Offset.zero), - ancestor: overlay), - ), - Offset.zero & overlay.size, - ); - final newValue = await showMenu( - context: context, - elevation: 4, - items: [ - for (final MapEntry fontSize - in widget.rawItemsMap.entries) - PopupMenuItem( - key: ValueKey(fontSize.key), - value: fontSize.value, - height: widget.itemHeight ?? kMinInteractiveDimension, - padding: widget.itemPadding, - child: Text( - fontSize.key.toString(), - style: TextStyle( - color: fontSize.value == '0' ? widget.defaultItemColor : null, - ), - ), - ), - ], - position: position, - shape: popupMenuTheme.shape, - color: popupMenuTheme.color, - ); - if (!mounted) return; - if (newValue == null) { - return; - } - final keyName = _getKeyName(newValue); - setState(() { - _currentValue = keyName ?? _defaultDisplayText; - if (keyName != null) { - widget.controller.formatSelection(Attribute.fromKeyValue( - 'size', newValue == '0' ? null : getFontSize(newValue))); - widget.onSelected?.call(newValue); - } - }); - } - - Widget _buildContent(BuildContext context) { - final theme = Theme.of(context); - final hasFinalWidth = widget.width != null; - return Padding( - padding: widget.padding ?? const EdgeInsets.fromLTRB(10, 0, 0, 0), - child: Row( - mainAxisSize: !hasFinalWidth ? MainAxisSize.min : MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - UtilityWidgets.maybeWidget( - enabled: hasFinalWidth, - wrapper: (child) => Expanded(child: child), - child: Text( - _currentValue, - overflow: widget.labelOverflow, - style: widget.style ?? - TextStyle( - fontSize: widget.iconSize / 1.15, - color: widget.iconTheme?.iconUnselectedColor ?? - theme.iconTheme.color, - ), - ), - ), - const SizedBox(width: 3), - Icon( - Icons.arrow_drop_down, - size: widget.iconSize / 1.15, - color: - widget.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color, - ) - ], - ), - ); - } -} diff --git a/lib/src/widgets/toolbar/buttons/quill_icon.dart b/lib/src/widgets/toolbar/buttons/quill_icon.dart index c07f740ad..0ac6e8f97 100644 --- a/lib/src/widgets/toolbar/buttons/quill_icon.dart +++ b/lib/src/widgets/toolbar/buttons/quill_icon.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import '../../../utils/widgets.dart'; -class QuillIconButton extends StatelessWidget { - const QuillIconButton({ +class QuillToolbarIconButton extends StatelessWidget { + const QuillToolbarIconButton({ required this.onPressed, this.afterPressed, this.icon, diff --git a/lib/src/widgets/toolbar/buttons/search.dart b/lib/src/widgets/toolbar/buttons/search.dart index 6245de78d..3e8fa7920 100644 --- a/lib/src/widgets/toolbar/buttons/search.dart +++ b/lib/src/widgets/toolbar/buttons/search.dart @@ -6,8 +6,8 @@ import '../../controller.dart'; import '../search_dialog.dart'; import '../toolbar.dart'; -class SearchButton extends StatelessWidget { - const SearchButton({ +class QuillToolbarSearchButton extends StatelessWidget { + const QuillToolbarSearchButton({ required this.icon, required this.controller, this.iconSize = kDefaultIconSize, @@ -40,7 +40,7 @@ class SearchButton extends StatelessWidget { final iconFillColor = iconTheme?.iconUnselectedFillColor ?? (fillColor ?? theme.canvasColor); - return QuillIconButton( + return QuillToolbarIconButton( tooltip: tooltip, icon: Icon(icon, size: iconSize, color: iconColor), highlightElevation: 0, diff --git a/lib/src/widgets/toolbar/buttons/select_alignment.dart b/lib/src/widgets/toolbar/buttons/select_alignment.dart index 6957ce66b..a14a6603b 100644 --- a/lib/src/widgets/toolbar/buttons/select_alignment.dart +++ b/lib/src/widgets/toolbar/buttons/select_alignment.dart @@ -9,8 +9,8 @@ import '../../controller.dart'; import '../enum.dart'; import '../toolbar.dart'; -class SelectAlignmentButton extends StatefulWidget { - const SelectAlignmentButton({ +class QuillToolbarSelectAlignmentButton extends StatefulWidget { + const QuillToolbarSelectAlignmentButton({ required this.controller, this.iconSize = kDefaultIconSize, this.iconTheme, @@ -37,10 +37,12 @@ class SelectAlignmentButton extends StatefulWidget { final EdgeInsetsGeometry? padding; @override - _SelectAlignmentButtonState createState() => _SelectAlignmentButtonState(); + _QuillToolbarSelectAlignmentButtonState createState() => + _QuillToolbarSelectAlignmentButtonState(); } -class _SelectAlignmentButtonState extends State { +class _QuillToolbarSelectAlignmentButtonState + extends State { Attribute? _value; Style get _selectionStyle => widget.controller.getSelectionStyle(); @@ -164,7 +166,7 @@ class _SelectAlignmentButtonState extends State { } @override - void didUpdateWidget(covariant SelectAlignmentButton oldWidget) { + void didUpdateWidget(covariant QuillToolbarSelectAlignmentButton oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.controller != widget.controller) { oldWidget.controller.removeListener(_didChangeEditingValue); diff --git a/lib/src/widgets/toolbar/buttons/select_header_style.dart b/lib/src/widgets/toolbar/buttons/select_header_style.dart index cde2cb3f8..0803e99ed 100644 --- a/lib/src/widgets/toolbar/buttons/select_header_style.dart +++ b/lib/src/widgets/toolbar/buttons/select_header_style.dart @@ -8,8 +8,8 @@ import '../../../utils/widgets.dart'; import '../../controller.dart'; import '../toolbar.dart'; -class SelectHeaderStyleButton extends StatefulWidget { - const SelectHeaderStyleButton({ +class QuillToolbarSelectHeaderStyleButton extends StatefulWidget { + const QuillToolbarSelectHeaderStyleButton({ required this.controller, this.axis = Axis.horizontal, this.iconSize = kDefaultIconSize, @@ -34,11 +34,12 @@ class SelectHeaderStyleButton extends StatefulWidget { final String? tooltip; @override - _SelectHeaderStyleButtonState createState() => - _SelectHeaderStyleButtonState(); + _QuillToolbarSelectHeaderStyleButtonState createState() => + _QuillToolbarSelectHeaderStyleButtonState(); } -class _SelectHeaderStyleButtonState extends State { +class _QuillToolbarSelectHeaderStyleButtonState + extends State { Attribute? _selectedAttribute; Style get _selectionStyle => widget.controller.getSelectionStyle(); @@ -148,7 +149,8 @@ class _SelectHeaderStyleButtonState extends State { } @override - void didUpdateWidget(covariant SelectHeaderStyleButton oldWidget) { + void didUpdateWidget( + covariant QuillToolbarSelectHeaderStyleButton oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.controller != widget.controller) { oldWidget.controller.removeListener(_didChangeEditingValue); diff --git a/lib/src/widgets/toolbar/buttons/toggle_check_list.dart b/lib/src/widgets/toolbar/buttons/toggle_check_list.dart index fa5c14525..d431c8b11 100644 --- a/lib/src/widgets/toolbar/buttons/toggle_check_list.dart +++ b/lib/src/widgets/toolbar/buttons/toggle_check_list.dart @@ -7,8 +7,8 @@ import '../../../utils/widgets.dart'; import '../../controller.dart'; import '../toolbar.dart'; -class ToggleCheckListButton extends StatefulWidget { - const ToggleCheckListButton({ +class QuillToolbarToggleCheckListButton extends StatefulWidget { + const QuillToolbarToggleCheckListButton({ required this.icon, required this.controller, required this.attribute, @@ -37,10 +37,12 @@ class ToggleCheckListButton extends StatefulWidget { final String? tooltip; @override - _ToggleCheckListButtonState createState() => _ToggleCheckListButtonState(); + _QuillToolbarToggleCheckListButtonState createState() => + _QuillToolbarToggleCheckListButtonState(); } -class _ToggleCheckListButtonState extends State { +class _QuillToolbarToggleCheckListButtonState + extends State { bool? _isToggled; Style get _selectionStyle => widget.controller.getSelectionStyle(); @@ -77,7 +79,7 @@ class _ToggleCheckListButtonState extends State { } @override - void didUpdateWidget(covariant ToggleCheckListButton oldWidget) { + void didUpdateWidget(covariant QuillToolbarToggleCheckListButton oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.controller != widget.controller) { oldWidget.controller.removeListener(_didChangeEditingValue); diff --git a/lib/src/widgets/toolbar/buttons/toggle_style.dart b/lib/src/widgets/toolbar/buttons/toggle_style.dart index e85ac6d1a..b5ca2131e 100644 --- a/lib/src/widgets/toolbar/buttons/toggle_style.dart +++ b/lib/src/widgets/toolbar/buttons/toggle_style.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; +import '../../../../translations.dart'; import '../../../models/documents/attribute.dart'; import '../../../models/documents/style.dart'; import '../../../models/themes/quill_icon_theme.dart'; +import '../../../utils/extensions/build_context.dart'; import '../../../utils/widgets.dart'; import '../../controller.dart'; import '../toolbar.dart'; @@ -19,84 +21,234 @@ typedef ToggleStyleButtonBuilder = Widget Function( QuillIconTheme? iconTheme, ]); -class ToggleStyleButton extends StatefulWidget { - const ToggleStyleButton({ - required this.attribute, - required this.icon, +class QuillToolbarToggleStyleButton extends StatefulWidget { + const QuillToolbarToggleStyleButton({ + required this.options, required this.controller, - this.iconSize = kDefaultIconSize, - this.fillColor, - this.childBuilder = defaultToggleStyleButtonBuilder, - this.iconTheme, - this.afterButtonPressed, - this.tooltip, - Key? key, - }) : super(key: key); + required this.attribute, + // required this.icon, + // required this.controller, + // this.iconSize = kDefaultIconSize, + // this.fillColor, + // this.childBuilder = defaultToggleStyleButtonBuilder, + // this.iconTheme, + // this.afterButtonPressed, + // this.tooltip, + super.key, + }); final Attribute attribute; - final IconData icon; - final double iconSize; + // final IconData icon; + // final double iconSize; - final Color? fillColor; + // final Color? fillColor; - final QuillController controller; + // final QuillController controller; - final ToggleStyleButtonBuilder childBuilder; + // final ToggleStyleButtonBuilder childBuilder; - ///Specify an icon theme for the icons in the toolbar - final QuillIconTheme? iconTheme; + // ///Specify an icon theme for the icons in the toolbar + // final QuillIconTheme? iconTheme; - final VoidCallback? afterButtonPressed; - final String? tooltip; + // final VoidCallback? afterButtonPressed; + // final String? tooltip; + final QuillToolbarToggleStyleButtonOptions options; + + /// Since we can't get the state from the instace of the widget for comparing + /// in [didUpdateWidget] then we will have to store reference here + final QuillController controller; @override - _ToggleStyleButtonState createState() => _ToggleStyleButtonState(); + _QuillToolbarToggleStyleButtonState createState() => + _QuillToolbarToggleStyleButtonState(); } -class _ToggleStyleButtonState extends State { +class _QuillToolbarToggleStyleButtonState + extends State { + /// Since it's not safe to call anything related to the context in dispose + /// then we will save a reference to the [controller] + /// and update it in [didChangeDependencies] + /// and use it in dispose method + late QuillController _controller; + bool? _isToggled; - Style get _selectionStyle => widget.controller.getSelectionStyle(); + Style get _selectionStyle => controller.getSelectionStyle(); + + QuillToolbarToggleStyleButtonOptions get options { + return widget.options; + } @override void initState() { super.initState(); _isToggled = _getIsToggled(_selectionStyle.attributes); - widget.controller.addListener(_didChangeEditingValue); + controller.addListener(_didChangeEditingValue); + } + + QuillController get controller { + return options.controller ?? widget.controller; + } + + double get iconSize { + final baseFontSize = + context.requireQuillToolbarBaseButtonOptions.globalIconSize; + final iconSize = options.iconSize; + return iconSize ?? baseFontSize; + } + + VoidCallback? get afterButtonPressed { + return options.afterButtonPressed ?? + context.requireQuillToolbarBaseButtonOptions.afterButtonPressed; + } + + QuillIconTheme? get iconTheme { + return options.iconTheme ?? + context.requireQuillToolbarBaseButtonOptions.iconTheme; + } + + String? get _defaultTooltip { + switch (widget.attribute.key) { + case 'bold': + return 'Bold'.i18n; + case 'script': + if (widget.attribute.value == ScriptAttributes.sub.value) { + return 'Subscript'.i18n; + } + return 'Superscript'.i18n; + case 'italic': + return 'Italic'.i18n; + case 'small': + return 'Small'.i18n; + case 'underline': + return 'Underline'.i18n; + case 'strike': + return 'Strike through'.i18n; + case 'code': + return 'Inline code'.i18n; + case 'rtl': + return 'Text direction'.i18n; + case 'list': + if (widget.attribute.value == 'bullet') { + return 'Bullet list'.i18n; + } + return 'Numbered list'.i18n; + case 'code-block': + return 'Code block'.i18n; + case 'blockquote': + return 'Quote'.i18n; + default: + throw ArgumentError( + 'Could not find the default tooltip for ' + '${widget.attribute.toString()}', + ); + } + } + + String? get tooltip { + return options.tooltip ?? + context.requireQuillToolbarBaseButtonOptions.tooltip ?? + _defaultTooltip; + } + + IconData get _defaultIconData { + switch (widget.attribute.key) { + case 'bold': + return Icons.format_bold; + case 'script': + if (widget.attribute.value == ScriptAttributes.sub.value) { + return Icons.subscript; + } + return Icons.superscript; + case 'italic': + return Icons.format_italic; + case 'small': + return Icons.format_size; + case 'underline': + return Icons.format_underline; + case 'strike': + return Icons.format_strikethrough; + case 'code': + return Icons.code; + case 'rtl': + return Icons.format_textdirection_r_to_l; + case 'list': + if (widget.attribute.value == 'bullet') { + return Icons.format_list_bulleted; + } + return Icons.format_list_numbered; + case 'code-block': + return Icons.code; + case 'blockquote': + return Icons.format_quote; + default: + throw ArgumentError( + 'Could not find the icon for ${widget.attribute.toString()}', + ); + } + } + + IconData get iconData { + return options.iconData ?? + context.requireQuillToolbarBaseButtonOptions.iconData ?? + _defaultIconData; + } + + void _onPressed() { + _toggleAttribute(); + options.afterButtonPressed?.call(); } @override Widget build(BuildContext context) { + final childBuilder = options.childBuilder ?? + context.requireQuillToolbarBaseButtonOptions.childBuilder; + if (childBuilder != null) { + return childBuilder( + options, + QuillToolbarToggleStyleButtonExtraOptions( + context: context, + controller: controller, + onPressed: _onPressed, + ), + ); + } return UtilityWidgets.maybeTooltip( - message: widget.tooltip, - child: widget.childBuilder( + message: tooltip, + child: defaultToggleStyleButtonBuilder( context, widget.attribute, - widget.icon, - widget.fillColor, + iconData, + options.fillColor, _isToggled, _toggleAttribute, - widget.afterButtonPressed, - widget.iconSize, - widget.iconTheme, + options.afterButtonPressed, + iconSize, + iconTheme, ), ); } @override - void didUpdateWidget(covariant ToggleStyleButton oldWidget) { + void didUpdateWidget(covariant QuillToolbarToggleStyleButton oldWidget) { super.didUpdateWidget(oldWidget); - if (oldWidget.controller != widget.controller) { + if (oldWidget.controller != controller) { oldWidget.controller.removeListener(_didChangeEditingValue); widget.controller.addListener(_didChangeEditingValue); _isToggled = _getIsToggled(_selectionStyle.attributes); } } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _controller = controller; + } + @override void dispose() { - widget.controller.removeListener(_didChangeEditingValue); + _controller.removeListener(_didChangeEditingValue); super.dispose(); } @@ -117,9 +269,9 @@ class _ToggleStyleButtonState extends State { } void _toggleAttribute() { - widget.controller.formatSelection(_isToggled! - ? Attribute.clone(widget.attribute, null) - : widget.attribute); + controller.formatSelection( + _isToggled! ? Attribute.clone(widget.attribute, null) : widget.attribute, + ); } } @@ -151,7 +303,7 @@ Widget defaultToggleStyleButtonBuilder( theme.canvasColor) //Unselected icon fill color : : (iconTheme?.disabledIconFillColor ?? (fillColor ?? theme.canvasColor)); //Disabled icon fill color - return QuillIconButton( + return QuillToolbarIconButton( highlightElevation: 0, hoverElevation: 0, size: iconSize * kIconButtonFactor, diff --git a/lib/src/widgets/toolbar/toolbar.dart b/lib/src/widgets/toolbar/toolbar.dart index c269813ad..f61d43908 100644 --- a/lib/src/widgets/toolbar/toolbar.dart +++ b/lib/src/widgets/toolbar/toolbar.dart @@ -12,11 +12,11 @@ export 'buttons/clear_format.dart'; export 'buttons/color.dart'; export 'buttons/custom_button.dart'; export 'buttons/font_family.dart'; +export 'buttons/font_size.dart'; export 'buttons/history.dart'; export 'buttons/indent.dart'; export 'buttons/link_style.dart'; export 'buttons/link_style2.dart'; -export 'buttons/quill_font_size.dart'; export 'buttons/quill_icon.dart'; export 'buttons/search.dart'; export 'buttons/select_alignment.dart'; @@ -48,7 +48,6 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { factory QuillToolbar.basic({ Axis axis = Axis.horizontal, - // double toolbarIconSize = kDefaultIconSize, double toolbarSectionSpacing = kToolbarSectionSpacing, WrapAlignment toolbarIconAlignment = WrapAlignment.center, WrapCrossAlignment toolbarIconCrossAlignment = WrapCrossAlignment.center, @@ -88,9 +87,6 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { /// The decoration to use for the toolbar. Decoration? decoration, - ///Map of font sizes in string - Map? fontSizeValues, - /// Toolbar items to display for controls of embed blocks List? embedButtons, @@ -161,20 +157,9 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { showLink || showSearchButton ]; - //default font size values - final fontSizes = fontSizeValues ?? - { - 'Small'.i18n: 'small', - 'Large'.i18n: 'large', - 'Huge'.i18n: 'huge', - 'Clear'.i18n: '0' - }; - //default button tooltips final buttonTooltips = tooltips ?? { - // ToolbarButtons.undo: 'Undo'.i18n, - // ToolbarButtons.redo: 'Redo'.i18n, ToolbarButtons.fontFamily: 'Font family'.i18n, ToolbarButtons.fontSize: 'Font size'.i18n, ToolbarButtons.bold: 'Bold'.i18n, @@ -235,99 +220,130 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { if (showFontFamily) QuillToolbarFontFamilyButton( options: toolbarConfigurations.buttonOptions.fontFamily, + controller: + toolbarConfigurations.buttonOptions.fontFamily.controller ?? + context.requireQuillController, ), if (showFontSize) QuillToolbarFontSizeButton( - iconTheme: iconTheme, - iconSize: toolbarIconSize, - tooltip: buttonTooltips[ToolbarButtons.fontSize], - attribute: Attribute.size, - controller: controller, - rawItemsMap: fontSizes, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.fontSize, + controller: + toolbarConfigurations.buttonOptions.fontFamily.controller ?? + context.requireQuillController, ), if (showBoldButton) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.bold, - icon: Icons.format_bold, - iconSize: toolbarIconSize, - tooltip: buttonTooltips[ToolbarButtons.bold], - controller: controller, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.bold, + controller: toolbarConfigurations.buttonOptions.bold.controller ?? + context.requireQuillController, + // icon: Icons.format_bold, + // iconSize: toolbarIconSize, + // tooltip: buttonTooltips[ToolbarButtons.bold], + // controller: controller, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showSubscript) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.subscript, - icon: Icons.subscript, - iconSize: toolbarIconSize, - tooltip: buttonTooltips[ToolbarButtons.subscript], - controller: controller, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.subscript, + controller: + toolbarConfigurations.buttonOptions.subscript.controller ?? + context.requireQuillController, + // icon: Icons.subscript, + // iconSize: toolbarIconSize, + // tooltip: buttonTooltips[ToolbarButtons.subscript], + // controller: controller, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showSuperscript) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.superscript, - icon: Icons.superscript, - iconSize: toolbarIconSize, - tooltip: buttonTooltips[ToolbarButtons.superscript], - controller: controller, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.superscript, + controller: + toolbarConfigurations.buttonOptions.superscript.controller ?? + context.requireQuillController, + // icon: Icons.superscript, + // iconSize: toolbarIconSize, + // tooltip: buttonTooltips[ToolbarButtons.superscript], + // controller: controller, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showItalicButton) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.italic, - icon: Icons.format_italic, - iconSize: toolbarIconSize, - tooltip: buttonTooltips[ToolbarButtons.italic], - controller: controller, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.italic, + controller: + toolbarConfigurations.buttonOptions.italic.controller ?? + context.requireQuillController, + // icon: Icons.format_italic, + // iconSize: toolbarIconSize, + // tooltip: buttonTooltips[ToolbarButtons.italic], + // controller: controller, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showSmallButton) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.small, - icon: Icons.format_size, - iconSize: toolbarIconSize, - tooltip: buttonTooltips[ToolbarButtons.small], - controller: controller, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.small, + controller: + toolbarConfigurations.buttonOptions.small.controller ?? + context.requireQuillController, + // icon: Icons.format_size, + // iconSize: toolbarIconSize, + // tooltip: buttonTooltips[ToolbarButtons.small], + // controller: controller, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showUnderLineButton) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.underline, - icon: Icons.format_underline, - iconSize: toolbarIconSize, - tooltip: buttonTooltips[ToolbarButtons.underline], - controller: controller, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.underLine, + controller: + toolbarConfigurations.buttonOptions.underLine.controller ?? + context.requireQuillController, + // icon: Icons.format_underline, + // iconSize: toolbarIconSize, + // tooltip: buttonTooltips[ToolbarButtons.underline], + // controller: controller, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showStrikeThrough) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.strikeThrough, - icon: Icons.format_strikethrough, - iconSize: toolbarIconSize, - tooltip: buttonTooltips[ToolbarButtons.strikeThrough], - controller: controller, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.strikeThrough, + controller: toolbarConfigurations + .buttonOptions.strikeThrough.controller ?? + context.requireQuillController, + // icon: Icons.format_strikethrough, + // iconSize: toolbarIconSize, + // tooltip: buttonTooltips[ToolbarButtons.strikeThrough], + // controller: controller, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showInlineCode) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.inlineCode, - icon: Icons.code, - iconSize: toolbarIconSize, - tooltip: buttonTooltips[ToolbarButtons.inlineCode], - controller: controller, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.inlineCode, + controller: + toolbarConfigurations.buttonOptions.inlineCode.controller ?? + context.requireQuillController, + // icon: Icons.code, + // iconSize: toolbarIconSize, + // tooltip: buttonTooltips[ToolbarButtons.inlineCode], + // controller: controller, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showColorButton) - ColorButton( + QuillToolbarColorButton( icon: Icons.color_lens, iconSize: toolbarIconSize, tooltip: buttonTooltips[ToolbarButtons.color], @@ -339,7 +355,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { context.requireQuillSharedConfigurations.dialogBarrierColor, ), if (showBackgroundColorButton) - ColorButton( + QuillToolbarColorButton( icon: Icons.format_color_fill, iconSize: toolbarIconSize, tooltip: buttonTooltips[ToolbarButtons.backgroundColor], @@ -351,7 +367,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { context.requireQuillSharedConfigurations.dialogBarrierColor, ), if (showClearFormat) - ClearFormatButton( + QuillToolbarClearFormatButton( icon: Icons.format_clear, iconSize: toolbarIconSize, tooltip: buttonTooltips[ToolbarButtons.clearFormat], @@ -369,13 +385,13 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { isButtonGroupShown[3] || isButtonGroupShown[4] || isButtonGroupShown[5])) - QuillDivider( + QuillToolbarDivider( axis, color: sectionDividerColor, space: sectionDividerSpace, ), if (showAlignmentButtons) - SelectAlignmentButton( + QuillToolbarSelectAlignmentButton( controller: controller, tooltips: Map.of(buttonTooltips) ..removeWhere((key, value) => ![ @@ -393,14 +409,18 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { afterButtonPressed: afterButtonPressed, ), if (showDirection) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.rtl, - tooltip: buttonTooltips[ToolbarButtons.direction], - controller: controller, - icon: Icons.format_textdirection_r_to_l, - iconSize: toolbarIconSize, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.direction, + controller: + toolbarConfigurations.buttonOptions.direction.controller ?? + context.requireQuillController, + // tooltip: buttonTooltips[ToolbarButtons.direction], + // controller: controller, + // icon: Icons.format_textdirection_r_to_l, + // iconSize: toolbarIconSize, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showDividers && isButtonGroupShown[1] && @@ -408,13 +428,13 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { isButtonGroupShown[3] || isButtonGroupShown[4] || isButtonGroupShown[5])) - QuillDivider( + QuillToolbarDivider( axis, color: sectionDividerColor, space: sectionDividerSpace, ), if (showHeaderStyle) - SelectHeaderStyleButton( + QuillToolbarSelectHeaderStyleButton( tooltip: buttonTooltips[ToolbarButtons.headerStyle], controller: controller, axis: axis, @@ -428,33 +448,41 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { (isButtonGroupShown[3] || isButtonGroupShown[4] || isButtonGroupShown[5])) - QuillDivider( + QuillToolbarDivider( axis, color: sectionDividerColor, space: sectionDividerSpace, ), if (showListNumbers) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.ol, - tooltip: buttonTooltips[ToolbarButtons.listNumbers], - controller: controller, - icon: Icons.format_list_numbered, - iconSize: toolbarIconSize, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.listNumbers, + controller: + toolbarConfigurations.buttonOptions.listNumbers.controller ?? + context.requireQuillController, + // tooltip: buttonTooltips[ToolbarButtons.listNumbers], + // controller: controller, + // icon: Icons.format_list_numbered, + // iconSize: toolbarIconSize, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showListBullets) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.ul, - tooltip: buttonTooltips[ToolbarButtons.listBullets], - controller: controller, - icon: Icons.format_list_bulleted, - iconSize: toolbarIconSize, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.listBullets, + controller: + toolbarConfigurations.buttonOptions.listBullets.controller ?? + context.requireQuillController, + // tooltip: buttonTooltips[ToolbarButtons.listBullets], + // controller: controller, + // icon: Icons.format_list_bulleted, + // iconSize: toolbarIconSize, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showListCheck) - ToggleCheckListButton( + QuillToolbarToggleCheckListButton( attribute: Attribute.unchecked, tooltip: buttonTooltips[ToolbarButtons.listChecks], controller: controller, @@ -464,32 +492,40 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { afterButtonPressed: afterButtonPressed, ), if (showCodeBlock) - ToggleStyleButton( + QuillToolbarToggleStyleButton( attribute: Attribute.codeBlock, - tooltip: buttonTooltips[ToolbarButtons.codeBlock], - controller: controller, - icon: Icons.code, - iconSize: toolbarIconSize, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + options: toolbarConfigurations.buttonOptions.codeBlock, + controller: + toolbarConfigurations.buttonOptions.codeBlock.controller ?? + context.requireQuillController, + // tooltip: buttonTooltips[ToolbarButtons.codeBlock], + // controller: controller, + // icon: Icons.code, + // iconSize: toolbarIconSize, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showDividers && isButtonGroupShown[3] && (isButtonGroupShown[4] || isButtonGroupShown[5])) - QuillDivider(axis, + QuillToolbarDivider(axis, color: sectionDividerColor, space: sectionDividerSpace), if (showQuote) - ToggleStyleButton( + QuillToolbarToggleStyleButton( + options: toolbarConfigurations.buttonOptions.quote, + controller: + toolbarConfigurations.buttonOptions.quote.controller ?? + context.requireQuillController, attribute: Attribute.blockQuote, - tooltip: buttonTooltips[ToolbarButtons.quote], - controller: controller, - icon: Icons.format_quote, - iconSize: toolbarIconSize, - iconTheme: iconTheme, - afterButtonPressed: afterButtonPressed, + // tooltip: buttonTooltips[ToolbarButtons.quote], + // controller: controller, + // icon: Icons.format_quote, + // iconSize: toolbarIconSize, + // iconTheme: iconTheme, + // afterButtonPressed: afterButtonPressed, ), if (showIndent) - IndentButton( + QuillToolbarIndentButton( icon: Icons.format_indent_increase, iconSize: toolbarIconSize, tooltip: buttonTooltips[ToolbarButtons.indentIncrease], @@ -499,7 +535,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { afterButtonPressed: afterButtonPressed, ), if (showIndent) - IndentButton( + QuillToolbarIndentButton( icon: Icons.format_indent_decrease, iconSize: toolbarIconSize, tooltip: buttonTooltips[ToolbarButtons.indentDecrease], @@ -509,10 +545,10 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { afterButtonPressed: afterButtonPressed, ), if (showDividers && isButtonGroupShown[4] && isButtonGroupShown[5]) - QuillDivider(axis, + QuillToolbarDivider(axis, color: sectionDividerColor, space: sectionDividerSpace), if (showLink) - LinkStyleButton( + QuillToolbarLinkStyleButton( tooltip: buttonTooltips[ToolbarButtons.link], controller: controller, iconSize: toolbarIconSize, @@ -525,7 +561,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { context.requireQuillSharedConfigurations.dialogBarrierColor, ), if (showSearchButton) - SearchButton( + QuillToolbarSearchButton( icon: Icons.search, iconSize: toolbarIconSize, dialogBarrierColor: @@ -538,7 +574,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { ), if (customButtons.isNotEmpty) if (showDividers) - QuillDivider( + QuillToolbarDivider( axis, color: sectionDividerColor, space: sectionDividerSpace, @@ -628,7 +664,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { height: axis == Axis.horizontal ? toolbarSize : null, width: axis == Axis.vertical ? toolbarSize : null, ), - child: ArrowIndicatedButtonList( + child: QuillToolbarArrowIndicatedButtonList( axis: axis, buttons: childrenBuilder(context), ), @@ -641,8 +677,8 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { /// /// It can be used outside of this package, for example when user does not use /// [QuillToolbar.basic] and compose toolbar's children on its own. -class QuillDivider extends StatelessWidget { - const QuillDivider( +class QuillToolbarDivider extends StatelessWidget { + const QuillToolbarDivider( this.axis, { Key? key, this.color, @@ -650,11 +686,11 @@ class QuillDivider extends StatelessWidget { }) : super(key: key); /// Provides a horizontal divider for vertical toolbar. - const QuillDivider.horizontal({Color? color, double? space}) + const QuillToolbarDivider.horizontal({Color? color, double? space}) : this(Axis.horizontal, color: color, space: space); /// Provides a horizontal divider for horizontal toolbar. - const QuillDivider.vertical({Color? color, double? space}) + const QuillToolbarDivider.vertical({Color? color, double? space}) : this(Axis.vertical, color: color, space: space); /// The axis along which the toolbar is. diff --git a/pubspec.yaml b/pubspec.yaml index f1b1a4d52..64b4af374 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill description: A rich text editor built for the modern Android, iOS, web and desktop platforms. It is the WYSIWYG editor and a Quill component for Flutter. -version: 7.6.1 +version: 7.7.0 homepage: https://1o24bbs.com/c/bulletjournal/108 repository: https://github.com/singerdmx/flutter-quill platforms: diff --git a/test/bug_fix_test.dart b/test/bug_fix_test.dart index b3c93c418..f9a8124d0 100644 --- a/test/bug_fix_test.dart +++ b/test/bug_fix_test.dart @@ -30,21 +30,21 @@ void main() { final builtinFinder = find.descendant( of: find.byType(QuillToolbarHistoryButton), - matching: find.byType(QuillIconButton), + matching: find.byType(QuillToolbarIconButton), matchRoot: true, ); expect(builtinFinder, findsOneWidget); final builtinButton = - builtinFinder.evaluate().first.widget as QuillIconButton; + builtinFinder.evaluate().first.widget as QuillToolbarIconButton; final customFinder = find.descendant( of: find.byType(QuillToolbar), matching: find.byWidgetPredicate((widget) => - widget is QuillIconButton && widget.tooltip == tooltip), + widget is QuillToolbarIconButton && widget.tooltip == tooltip), matchRoot: true); expect(customFinder, findsOneWidget); final customButton = - customFinder.evaluate().first.widget as QuillIconButton; + customFinder.evaluate().first.widget as QuillToolbarIconButton; expect(customButton.fillColor, equals(builtinButton.fillColor)); });