Resore original button PR #1575

pull/1621/head
Aleksei 1 year ago
parent 2c5e02a862
commit fce1458b83
  1. 82
      lib/src/models/config/toolbar/buttons/select_header_style_dropdown_button_configurations.dart
  2. 333
      lib/src/widgets/toolbar/buttons/hearder_style/select_header_style_dropdown_button.dart

@ -1,9 +1,9 @@
import 'package:flutter/widgets.dart' import 'package:flutter/material.dart';
show IconData, TextStyle, ValueChanged, VoidCallback;
import '../../../../widgets/toolbar/base_toolbar.dart'; import '../../../../widgets/quill/quill_controller.dart';
import '../../../documents/attribute.dart'; import '../../../documents/attribute.dart';
import '../../../themes/quill_icon_theme.dart'; import '../../../themes/quill_icon_theme.dart';
import '../../quill_configurations.dart';
class QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions class QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions { extends QuillToolbarBaseButtonExtraOptions {
@ -21,46 +21,68 @@ class QuillToolbarSelectHeaderStyleDropdownButtonOptions
QuillToolbarSelectHeaderStyleDropdownButtonOptions, QuillToolbarSelectHeaderStyleDropdownButtonOptions,
QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions> { QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions> {
const QuillToolbarSelectHeaderStyleDropdownButtonOptions({ const QuillToolbarSelectHeaderStyleDropdownButtonOptions({
super.iconData,
super.afterButtonPressed, super.afterButtonPressed,
super.childBuilder,
super.iconTheme,
super.tooltip, super.tooltip,
super.iconTheme,
super.childBuilder,
this.iconSize, this.iconSize,
this.iconButtonFactor, this.iconButtonFactor,
this.textStyle, this.fillColor,
super.iconData, this.hoverElevation = 0,
this.highlightElevation = 0,
this.onSelected,
this.attributes, 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? iconSize;
final double? iconButtonFactor; final double? iconButtonFactor;
final TextStyle? textStyle; final Color? fillColor;
final double hoverElevation;
/// Header attributes, defaults to: final double highlightElevation;
/// ```dart final ValueChanged<String>? onSelected;
/// [ final List<Attribute>? attributes;
/// Attribute.h1, final EdgeInsetsGeometry? padding;
/// Attribute.h2, final TextStyle? style;
/// Attribute.h3, final double? width;
/// Attribute.h4, final TextOverflow labelOverflow;
/// Attribute.h5, final double? itemHeight;
/// Attribute.h6, final EdgeInsets? itemPadding;
/// Attribute.header, final Color? defaultItemColor;
/// ] final bool renderItemTextStyle;
/// ```
final List<Attribute<int>>? attributes;
QuillToolbarSelectHeaderStyleDropdownButtonOptions copyWith({ QuillToolbarSelectHeaderStyleDropdownButtonOptions copyWith({
Color? fillColor,
double? hoverElevation,
double? highlightElevation,
List<PopupMenuEntry<String>>? items,
ValueChanged<String>? onSelected, ValueChanged<String>? onSelected,
List<Attribute<int>>? attributes, List<Attribute>? attributes,
EdgeInsetsGeometry? padding,
TextStyle? style, TextStyle? style,
double? width,
TextOverflow? labelOverflow,
bool? renderFontFamilies,
bool? overrideTooltipByFontFamily,
double? itemHeight,
EdgeInsets? itemPadding,
Color? defaultItemColor,
double? iconSize, double? iconSize,
double? iconButtonFactor, double? iconButtonFactor,
QuillController? controller,
IconData? iconData, IconData? iconData,
VoidCallback? afterButtonPressed, VoidCallback? afterButtonPressed,
String? tooltip, String? tooltip,
QuillIconTheme? iconTheme, QuillIconTheme? iconTheme,
bool? renderItemTextStyle,
}) { }) {
return QuillToolbarSelectHeaderStyleDropdownButtonOptions( return QuillToolbarSelectHeaderStyleDropdownButtonOptions(
attributes: attributes ?? this.attributes, attributes: attributes ?? this.attributes,
@ -68,8 +90,20 @@ class QuillToolbarSelectHeaderStyleDropdownButtonOptions
afterButtonPressed: afterButtonPressed ?? this.afterButtonPressed, afterButtonPressed: afterButtonPressed ?? this.afterButtonPressed,
tooltip: tooltip ?? this.tooltip, tooltip: tooltip ?? this.tooltip,
iconTheme: iconTheme ?? this.iconTheme, 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, iconSize: iconSize ?? this.iconSize,
iconButtonFactor: iconButtonFactor ?? this.iconButtonFactor, iconButtonFactor: iconButtonFactor ?? this.iconButtonFactor,
fillColor: fillColor ?? this.fillColor,
hoverElevation: hoverElevation ?? this.hoverElevation,
highlightElevation: highlightElevation ?? this.highlightElevation,
renderItemTextStyle: renderItemTextStyle ?? this.renderItemTextStyle,
); );
} }
} }

@ -1,19 +1,25 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../../../extensions.dart';
import '../../../../../translations.dart'; import '../../../../../translations.dart';
import '../../../../extensions/quill_configurations_ext.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/attribute.dart';
import '../../../../models/documents/style.dart';
import '../../../../models/themes/quill_icon_theme.dart'; import '../../../../models/themes/quill_icon_theme.dart';
import '../../../others/default_styles.dart';
import '../../../quill/quill_controller.dart'; import '../../../quill/quill_controller.dart';
import '../../base_toolbar.dart';
class QuillToolbarSelectHeaderStyleDropdownButton extends StatefulWidget { class QuillToolbarSelectHeaderStyleDropdownButton extends StatefulWidget {
const QuillToolbarSelectHeaderStyleDropdownButton({ const QuillToolbarSelectHeaderStyleDropdownButton({
required this.controller, required this.controller,
this.options = const QuillToolbarSelectHeaderStyleDropdownButtonOptions(), required this.options,
super.key, 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 QuillController controller;
final QuillToolbarSelectHeaderStyleDropdownButtonOptions options; final QuillToolbarSelectHeaderStyleDropdownButtonOptions options;
@ -24,190 +30,263 @@ class QuillToolbarSelectHeaderStyleDropdownButton extends StatefulWidget {
class _QuillToolbarSelectHeaderStyleDropdownButtonState class _QuillToolbarSelectHeaderStyleDropdownButtonState
extends State<QuillToolbarSelectHeaderStyleDropdownButton> { extends State<QuillToolbarSelectHeaderStyleDropdownButton> {
Attribute<dynamic> _selectedItem = Attribute.header; Attribute? _selectedAttribute;
final _menuController = MenuController(); Style get _selectionStyle => controller.getSelectionStyle();
@override
void initState() {
super.initState();
widget.controller.addListener(_didChangeEditingValue);
}
@override late final _valueToText = <Attribute, String>{
void dispose() { Attribute.h1: context.loc.heading1,
widget.controller.removeListener(_didChangeEditingValue); Attribute.h2: context.loc.heading2,
super.dispose(); 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 Map<Attribute, TextStyle>? _headerTextStyles;
void didUpdateWidget(
covariant QuillToolbarSelectHeaderStyleDropdownButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.controller == widget.controller) {
return;
}
widget.controller
..removeListener(_didChangeEditingValue)
..addListener(_didChangeEditingValue);
}
void _didChangeEditingValue() { QuillToolbarSelectHeaderStyleDropdownButtonOptions get options {
final newSelectedItem = _getHeaderValue(); return widget.options;
if (newSelectedItem == _selectedItem) {
return;
}
setState(() {
_selectedItem = newSelectedItem;
});
} }
Attribute<dynamic> _getHeaderValue() { QuillController get controller {
final attr = widget.controller.toolbarButtonToggler[Attribute.header.key]; return widget.controller;
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<dynamic> 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<dynamic>() => throw ArgumentError(),
};
return label;
} }
double get iconSize { double get iconSize {
final baseFontSize = context.quillToolbarBaseButtonOptions?.globalIconSize; final baseFontSize = baseButtonExtraOptions.globalIconSize;
final iconSize = widget.options.iconSize; final iconSize = options.iconSize;
return iconSize ?? baseFontSize ?? kDefaultIconSize; return iconSize ?? baseFontSize;
} }
double get iconButtonFactor { double get iconButtonFactor {
final baseIconFactor = final baseIconFactor = baseButtonExtraOptions.globalIconButtonFactor;
context.quillToolbarBaseButtonOptions?.globalIconButtonFactor; final iconButtonFactor = options.iconButtonFactor;
final iconButtonFactor = widget.options.iconButtonFactor; return iconButtonFactor ?? baseIconFactor;
return iconButtonFactor ?? baseIconFactor ?? kIconButtonFactor;
} }
QuillIconTheme? get iconTheme { VoidCallback? get afterButtonPressed {
return widget.options.iconTheme ?? return options.afterButtonPressed ??
context.quillToolbarBaseButtonOptions?.iconTheme; baseButtonExtraOptions.afterButtonPressed;
} }
List<Attribute<int?>> get headerAttributes { QuillIconTheme? get iconTheme {
return widget.options.attributes ?? return options.iconTheme ?? baseButtonExtraOptions.iconTheme;
[
Attribute.h1,
Attribute.h2,
Attribute.h3,
Attribute.h4,
Attribute.h5,
Attribute.h6,
Attribute.header,
];
} }
QuillToolbarBaseButtonOptions get baseButtonExtraOptions { QuillToolbarBaseButtonOptions get baseButtonExtraOptions {
return context.requireQuillToolbarBaseButtonOptions; return context.requireQuillToolbarBaseButtonOptions;
} }
VoidCallback? get afterButtonPressed { String get tooltip {
return widget.options.afterButtonPressed ?? return options.tooltip ??
baseButtonExtraOptions.afterButtonPressed; baseButtonExtraOptions.tooltip ??
context.loc.headerStyle;
} }
void _onPressed(Attribute<int?> e) { List<Attribute> get _attrbuites {
setState(() => _selectedItem = e); return options.attributes ?? _valueToText.keys.toList();
widget.controller.formatSelection(_selectedItem); }
@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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(_attrbuites.every((element) => _valueToText.keys.contains(element)));
final baseButtonConfigurations = final baseButtonConfigurations =
context.requireQuillToolbarBaseButtonOptions; context.requireQuillToolbarBaseButtonOptions;
final childBuilder = final childBuilder =
widget.options.childBuilder ?? baseButtonConfigurations.childBuilder; options.childBuilder ?? baseButtonConfigurations.childBuilder;
if (childBuilder != null) { if (childBuilder != null) {
return childBuilder( return childBuilder(
widget.options.copyWith( options.copyWith(
iconSize: iconSize, iconSize: iconSize,
iconTheme: iconTheme, iconTheme: iconTheme,
tooltip: tooltip,
afterButtonPressed: afterButtonPressed, afterButtonPressed: afterButtonPressed,
), ),
QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions( QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions(
currentValue: _selectedItem, currentValue: _selectedAttribute!,
controller: controller,
context: context, context: context,
controller: widget.controller, onPressed: _onPressed,
onPressed: () {
throw UnimplementedError('Not implemented yet.');
},
), ),
); );
} }
return MenuAnchor( return ConstrainedBox(
controller: _menuController, constraints: BoxConstraints.tightFor(
menuChildren: headerAttributes height: iconSize * 1.81,
.map( width: options.width,
(e) => MenuItemButton(
onPressed: () {
_onPressed(e);
},
child: Text(_label(e)),
), ),
) child: UtilityWidgets.maybeTooltip(
.toList(), message: tooltip,
child: Builder( child: RawMaterialButton(
builder: (context) { visualDensity: VisualDensity.compact,
final isMaterial3 = Theme.of(context).useMaterial3; shape: RoundedRectangleBorder(
final child = Row( borderRadius: BorderRadius.circular(iconTheme?.borderRadius ?? 2),
mainAxisSize: MainAxisSize.min, ),
fillColor: options.fillColor,
elevation: 0,
hoverElevation: options.hoverElevation,
highlightElevation: options.hoverElevation,
onPressed: _onPressed,
child: _buildContent(context),
),
),
);
}
void _didChangeEditingValue() {
setState(() {
_selectedAttribute = _getHeaderValue();
});
}
Attribute<dynamic> _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: [ children: [
Text( UtilityWidgets.maybeWidget(
_label(_selectedItem), enabled: hasFinalWidth,
style: widget.options.textStyle ?? wrapper: (child) => Expanded(child: child),
child: Text(
_valueToText[_selectedAttribute]!,
overflow: options.labelOverflow,
style: options.style ??
TextStyle( TextStyle(
fontSize: iconSize / 1.15, fontSize: iconSize / 1.15,
color:
iconTheme?.iconUnselectedColor ?? theme.iconTheme.color,
), ),
), ),
),
const SizedBox(width: 3),
Icon( Icon(
Icons.arrow_drop_down, Icons.arrow_drop_down,
size: iconSize * iconButtonFactor, size: iconSize / 1.15,
), color: iconTheme?.iconUnselectedColor ?? theme.iconTheme.color,
)
], ],
); ),
if (!isMaterial3) {
return RawMaterialButton(
onPressed: _onDropdownButtonPressed,
child: child,
); );
} }
return IconButton(
onPressed: _onDropdownButtonPressed, void _onPressed() {
icon: child, _showMenu();
options.afterButtonPressed?.call();
}
Future<void> _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<Attribute>(
context: context,
elevation: 4,
items: [
for (final header in _valueToText.entries)
PopupMenuItem<Attribute>(
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;
} }
void _onDropdownButtonPressed() { final attribute0 =
if (_menuController.isOpen) { _selectedAttribute == newValue ? Attribute.header : newValue;
_menuController.close(); controller.formatSelection(attribute0);
return; afterButtonPressed?.call();
} }
_menuController.open();
TextStyle _headerStyle(Attribute attribute) {
assert(_headerTextStyles!.containsKey(attribute));
return _headerTextStyles![attribute]!;
} }
} }

Loading…
Cancel
Save