From fce1458b834fe296c9f1ab64b65291b072115712 Mon Sep 17 00:00:00 2001 From: Aleksei <130981115+MacDeveloper1@users.noreply.github.com> Date: Tue, 19 Dec 2023 12:52:02 +0100 Subject: [PATCH] Resore original button PR #1575 --- ..._style_dropdown_button_configurations.dart | 82 ++-- .../select_header_style_dropdown_button.dart | 351 +++++++++++------- 2 files changed, 273 insertions(+), 160 deletions(-) diff --git a/lib/src/models/config/toolbar/buttons/select_header_style_dropdown_button_configurations.dart b/lib/src/models/config/toolbar/buttons/select_header_style_dropdown_button_configurations.dart index 8aafcc78..c7740d58 100644 --- a/lib/src/models/config/toolbar/buttons/select_header_style_dropdown_button_configurations.dart +++ b/lib/src/models/config/toolbar/buttons/select_header_style_dropdown_button_configurations.dart @@ -1,9 +1,9 @@ -import 'package:flutter/widgets.dart' - show IconData, TextStyle, ValueChanged, VoidCallback; +import 'package:flutter/material.dart'; -import '../../../../widgets/toolbar/base_toolbar.dart'; +import '../../../../widgets/quill/quill_controller.dart'; import '../../../documents/attribute.dart'; import '../../../themes/quill_icon_theme.dart'; +import '../../quill_configurations.dart'; class QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions extends QuillToolbarBaseButtonExtraOptions { @@ -21,46 +21,68 @@ class QuillToolbarSelectHeaderStyleDropdownButtonOptions QuillToolbarSelectHeaderStyleDropdownButtonOptions, QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions> { const QuillToolbarSelectHeaderStyleDropdownButtonOptions({ + super.iconData, super.afterButtonPressed, - super.childBuilder, - super.iconTheme, super.tooltip, + super.iconTheme, + super.childBuilder, this.iconSize, this.iconButtonFactor, - this.textStyle, - super.iconData, + this.fillColor, + this.hoverElevation = 0, + this.highlightElevation = 0, + this.onSelected, this.attributes, + this.padding, + this.style, + this.width, + this.labelOverflow = TextOverflow.visible, + this.itemHeight, + this.itemPadding, + this.defaultItemColor, + this.renderItemTextStyle = false, }); - /// By default we will the toolbar axis from [QuillSimpleToolbarConfigurations] final double? iconSize; final double? iconButtonFactor; - final TextStyle? textStyle; - - /// Header attributes, defaults to: - /// ```dart - /// [ - /// Attribute.h1, - /// Attribute.h2, - /// Attribute.h3, - /// Attribute.h4, - /// Attribute.h5, - /// Attribute.h6, - /// Attribute.header, - /// ] - /// ``` - final List>? attributes; + final Color? fillColor; + final double hoverElevation; + final double highlightElevation; + final ValueChanged? onSelected; + final List? attributes; + final EdgeInsetsGeometry? padding; + final TextStyle? style; + final double? width; + final TextOverflow labelOverflow; + final double? itemHeight; + final EdgeInsets? itemPadding; + final Color? defaultItemColor; + final bool renderItemTextStyle; QuillToolbarSelectHeaderStyleDropdownButtonOptions copyWith({ + Color? fillColor, + double? hoverElevation, + double? highlightElevation, + List>? items, ValueChanged? onSelected, - List>? attributes, + List? attributes, + EdgeInsetsGeometry? padding, TextStyle? style, + double? width, + TextOverflow? labelOverflow, + bool? renderFontFamilies, + bool? overrideTooltipByFontFamily, + double? itemHeight, + EdgeInsets? itemPadding, + Color? defaultItemColor, double? iconSize, double? iconButtonFactor, + QuillController? controller, IconData? iconData, VoidCallback? afterButtonPressed, String? tooltip, QuillIconTheme? iconTheme, + bool? renderItemTextStyle, }) { return QuillToolbarSelectHeaderStyleDropdownButtonOptions( attributes: attributes ?? this.attributes, @@ -68,8 +90,20 @@ class QuillToolbarSelectHeaderStyleDropdownButtonOptions 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, + labelOverflow: labelOverflow ?? this.labelOverflow, + itemHeight: itemHeight ?? this.itemHeight, + itemPadding: itemPadding ?? this.itemPadding, + defaultItemColor: defaultItemColor ?? this.defaultItemColor, iconSize: iconSize ?? this.iconSize, iconButtonFactor: iconButtonFactor ?? this.iconButtonFactor, + fillColor: fillColor ?? this.fillColor, + hoverElevation: hoverElevation ?? this.hoverElevation, + highlightElevation: highlightElevation ?? this.highlightElevation, + renderItemTextStyle: renderItemTextStyle ?? this.renderItemTextStyle, ); } } 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 fdfad12e..9a7c7290 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 @@ -1,19 +1,25 @@ import 'package:flutter/material.dart'; +import '../../../../../extensions.dart'; import '../../../../../translations.dart'; import '../../../../extensions/quill_configurations_ext.dart'; +import '../../../../models/config/toolbar/base_button_configurations.dart'; +import '../../../../models/config/toolbar/buttons/select_header_style_dropdown_button_configurations.dart'; import '../../../../models/documents/attribute.dart'; +import '../../../../models/documents/style.dart'; import '../../../../models/themes/quill_icon_theme.dart'; +import '../../../others/default_styles.dart'; import '../../../quill/quill_controller.dart'; -import '../../base_toolbar.dart'; class QuillToolbarSelectHeaderStyleDropdownButton extends StatefulWidget { const QuillToolbarSelectHeaderStyleDropdownButton({ required this.controller, - this.options = const QuillToolbarSelectHeaderStyleDropdownButtonOptions(), + required this.options, super.key, }); + /// 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; final QuillToolbarSelectHeaderStyleDropdownButtonOptions options; @@ -24,190 +30,263 @@ class QuillToolbarSelectHeaderStyleDropdownButton extends StatefulWidget { class _QuillToolbarSelectHeaderStyleDropdownButtonState extends State { - Attribute _selectedItem = Attribute.header; + Attribute? _selectedAttribute; - final _menuController = MenuController(); - @override - void initState() { - super.initState(); - widget.controller.addListener(_didChangeEditingValue); - } + Style get _selectionStyle => controller.getSelectionStyle(); - @override - void dispose() { - widget.controller.removeListener(_didChangeEditingValue); - super.dispose(); - } + late final _valueToText = { + Attribute.h1: context.loc.heading1, + Attribute.h2: context.loc.heading2, + Attribute.h3: context.loc.heading3, + Attribute.h4: context.loc.heading4, + Attribute.h5: context.loc.heading5, + Attribute.h6: context.loc.heading6, + Attribute.header: context.loc.normal, + }; - @override - void didUpdateWidget( - covariant QuillToolbarSelectHeaderStyleDropdownButton oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.controller == widget.controller) { - return; - } - widget.controller - ..removeListener(_didChangeEditingValue) - ..addListener(_didChangeEditingValue); - } + Map? _headerTextStyles; - void _didChangeEditingValue() { - final newSelectedItem = _getHeaderValue(); - if (newSelectedItem == _selectedItem) { - return; - } - setState(() { - _selectedItem = newSelectedItem; - }); + QuillToolbarSelectHeaderStyleDropdownButtonOptions get options { + return widget.options; } - Attribute _getHeaderValue() { - final attr = widget.controller.toolbarButtonToggler[Attribute.header.key]; - if (attr != null) { - // checkbox tapping causes controller.selection to go to offset 0 - widget.controller.toolbarButtonToggler.remove(Attribute.header.key); - return attr; - } - return widget.controller - .getSelectionStyle() - .attributes[Attribute.header.key] ?? - Attribute.header; - } - - String _label(Attribute value) { - final label = switch (value) { - Attribute.h1 => context.loc.heading1, - Attribute.h2 => context.loc.heading2, - Attribute.h3 => context.loc.heading3, - Attribute.h4 => context.loc.heading4, - Attribute.h5 => context.loc.heading5, - Attribute.h6 => context.loc.heading6, - Attribute.header => context.loc.normal, - Attribute() => throw ArgumentError(), - }; - return label; + QuillController get controller { + return widget.controller; } double get iconSize { - final baseFontSize = context.quillToolbarBaseButtonOptions?.globalIconSize; - final iconSize = widget.options.iconSize; - return iconSize ?? baseFontSize ?? kDefaultIconSize; + final baseFontSize = baseButtonExtraOptions.globalIconSize; + final iconSize = options.iconSize; + return iconSize ?? baseFontSize; } double get iconButtonFactor { - final baseIconFactor = - context.quillToolbarBaseButtonOptions?.globalIconButtonFactor; - final iconButtonFactor = widget.options.iconButtonFactor; - return iconButtonFactor ?? baseIconFactor ?? kIconButtonFactor; + final baseIconFactor = baseButtonExtraOptions.globalIconButtonFactor; + final iconButtonFactor = options.iconButtonFactor; + return iconButtonFactor ?? baseIconFactor; } - QuillIconTheme? get iconTheme { - return widget.options.iconTheme ?? - context.quillToolbarBaseButtonOptions?.iconTheme; + VoidCallback? get afterButtonPressed { + return options.afterButtonPressed ?? + baseButtonExtraOptions.afterButtonPressed; } - List> get headerAttributes { - return widget.options.attributes ?? - [ - Attribute.h1, - Attribute.h2, - Attribute.h3, - Attribute.h4, - Attribute.h5, - Attribute.h6, - Attribute.header, - ]; + QuillIconTheme? get iconTheme { + return options.iconTheme ?? baseButtonExtraOptions.iconTheme; } QuillToolbarBaseButtonOptions get baseButtonExtraOptions { return context.requireQuillToolbarBaseButtonOptions; } - VoidCallback? get afterButtonPressed { - return widget.options.afterButtonPressed ?? - baseButtonExtraOptions.afterButtonPressed; + String get tooltip { + return options.tooltip ?? + baseButtonExtraOptions.tooltip ?? + context.loc.headerStyle; } - void _onPressed(Attribute e) { - setState(() => _selectedItem = e); - widget.controller.formatSelection(_selectedItem); + List get _attrbuites { + return options.attributes ?? _valueToText.keys.toList(); + } + + @override + void dispose() { + controller.removeListener(_didChangeEditingValue); + super.dispose(); + } + + @override + void didUpdateWidget( + covariant QuillToolbarSelectHeaderStyleDropdownButton oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.controller != controller) { + oldWidget.controller.removeListener(_didChangeEditingValue); + controller.addListener(_didChangeEditingValue); + _selectedAttribute = _getHeaderValue(); + } + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (_headerTextStyles == null) { + final defaultStyles = DefaultStyles.getInstance(context); + _headerTextStyles = { + Attribute.h1: defaultStyles.h1!.style, + Attribute.h2: defaultStyles.h2!.style, + Attribute.h3: defaultStyles.h3!.style, + Attribute.h4: defaultStyles.h4!.style, + Attribute.h5: defaultStyles.h5!.style, + Attribute.h6: defaultStyles.h6!.style, + Attribute.header: + widget.options.style ?? defaultStyles.paragraph!.style, + }; + } + } + + @override + void initState() { + super.initState(); + controller.addListener(_didChangeEditingValue); + _selectedAttribute = _getHeaderValue(); } @override Widget build(BuildContext context) { + assert(_attrbuites.every((element) => _valueToText.keys.contains(element))); + final baseButtonConfigurations = context.requireQuillToolbarBaseButtonOptions; final childBuilder = - widget.options.childBuilder ?? baseButtonConfigurations.childBuilder; + options.childBuilder ?? baseButtonConfigurations.childBuilder; if (childBuilder != null) { return childBuilder( - widget.options.copyWith( + options.copyWith( iconSize: iconSize, iconTheme: iconTheme, + tooltip: tooltip, afterButtonPressed: afterButtonPressed, ), QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions( - currentValue: _selectedItem, + currentValue: _selectedAttribute!, + controller: controller, context: context, - controller: widget.controller, - onPressed: () { - throw UnimplementedError('Not implemented yet.'); - }, + onPressed: _onPressed, ), ); } - return MenuAnchor( - controller: _menuController, - menuChildren: headerAttributes - .map( - (e) => MenuItemButton( - onPressed: () { - _onPressed(e); - }, - child: Text(_label(e)), + 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), + ), + ), + ); + } + + void _didChangeEditingValue() { + setState(() { + _selectedAttribute = _getHeaderValue(); + }); + } + + Attribute _getHeaderValue() { + final attr = controller.toolbarButtonToggler[Attribute.header.key]; + if (attr != null) { + // checkbox tapping causes controller.selection to go to offset 0 + controller.toolbarButtonToggler.remove(Attribute.header.key); + return attr; + } + return _selectionStyle.attributes[Attribute.header.key] ?? Attribute.header; + } + + 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( + _valueToText[_selectedAttribute]!, + 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, ) - .toList(), - child: Builder( - builder: (context) { - final isMaterial3 = Theme.of(context).useMaterial3; - final child = Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - _label(_selectedItem), - style: widget.options.textStyle ?? - TextStyle( - fontSize: iconSize / 1.15, - ), - ), - Icon( - Icons.arrow_drop_down, - size: iconSize * iconButtonFactor, - ), - ], - ); - if (!isMaterial3) { - return RawMaterialButton( - onPressed: _onDropdownButtonPressed, - child: child, - ); - } - return IconButton( - onPressed: _onDropdownButtonPressed, - icon: child, - ); - }, + ], ), ); } - void _onDropdownButtonPressed() { - if (_menuController.isOpen) { - _menuController.close(); + void _onPressed() { + _showMenu(); + options.afterButtonPressed?.call(); + } + + 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 header in _valueToText.entries) + PopupMenuItem( + key: ValueKey(header.value), + value: header.key, + height: options.itemHeight ?? kMinInteractiveDimension, + padding: options.itemPadding, + child: Text( + header.value, + style: TextStyle( + fontSize: options.renderItemTextStyle + ? _headerStyle(header.key).fontSize ?? + DefaultTextStyle.of(context).style.fontSize ?? + 14 + : null, + color: header.key == _selectedAttribute + ? options.defaultItemColor + : null, + ), + ), + ), + ], + position: position, + shape: popupMenuTheme.shape, + color: popupMenuTheme.color, + ); + if (newValue == null) { return; } - _menuController.open(); + + final attribute0 = + _selectedAttribute == newValue ? Attribute.header : newValue; + controller.formatSelection(attribute0); + afterButtonPressed?.call(); + } + + TextStyle _headerStyle(Attribute attribute) { + assert(_headerTextStyles!.containsKey(attribute)); + return _headerTextStyles![attribute]!; } }