|
|
|
@ -1,8 +1,118 @@ |
|
|
|
|
import 'package:flutter/foundation.dart'; |
|
|
|
|
import 'package:flutter/material.dart'; |
|
|
|
|
|
|
|
|
|
import '../../../../flutter_quill.dart'; |
|
|
|
|
import '../../../../translations.dart'; |
|
|
|
|
import '../../../utils/widgets.dart'; |
|
|
|
|
|
|
|
|
|
class QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions |
|
|
|
|
extends QuillToolbarBaseButtonExtraOptions { |
|
|
|
|
const QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions({ |
|
|
|
|
required this.currentValue, |
|
|
|
|
required super.controller, |
|
|
|
|
required super.context, |
|
|
|
|
required super.onPressed, |
|
|
|
|
}); |
|
|
|
|
final Attribute currentValue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class QuillToolbarSelectHeaderStyleDropdownButtonOptions |
|
|
|
|
extends QuillToolbarBaseButtonOptions< |
|
|
|
|
QuillToolbarSelectHeaderStyleDropdownButtonOptions, |
|
|
|
|
QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions> { |
|
|
|
|
const QuillToolbarSelectHeaderStyleDropdownButtonOptions({ |
|
|
|
|
super.controller, |
|
|
|
|
super.iconData, |
|
|
|
|
super.afterButtonPressed, |
|
|
|
|
super.tooltip, |
|
|
|
|
super.iconTheme, |
|
|
|
|
super.childBuilder, |
|
|
|
|
this.iconSize, |
|
|
|
|
this.iconButtonFactor, |
|
|
|
|
this.fillColor, |
|
|
|
|
this.hoverElevation = 1, |
|
|
|
|
this.highlightElevation = 1, |
|
|
|
|
this.rawItemsMap, |
|
|
|
|
this.onSelected, |
|
|
|
|
this.attributes, |
|
|
|
|
this.padding, |
|
|
|
|
this.style, |
|
|
|
|
this.width, |
|
|
|
|
this.initialValue, |
|
|
|
|
this.labelOverflow = TextOverflow.visible, |
|
|
|
|
this.itemHeight, |
|
|
|
|
this.itemPadding, |
|
|
|
|
this.defaultItemColor, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
final double? iconSize; |
|
|
|
|
final double? iconButtonFactor; |
|
|
|
|
final Color? fillColor; |
|
|
|
|
final double hoverElevation; |
|
|
|
|
final double highlightElevation; |
|
|
|
|
final Map<String, String>? rawItemsMap; |
|
|
|
|
final ValueChanged<String>? onSelected; |
|
|
|
|
final List<Attribute>? attributes; |
|
|
|
|
final EdgeInsetsGeometry? padding; |
|
|
|
|
final TextStyle? style; |
|
|
|
|
final double? width; |
|
|
|
|
final String? initialValue; |
|
|
|
|
final TextOverflow labelOverflow; |
|
|
|
|
final double? itemHeight; |
|
|
|
|
final EdgeInsets? itemPadding; |
|
|
|
|
final Color? defaultItemColor; |
|
|
|
|
|
|
|
|
|
QuillToolbarSelectHeaderStyleDropdownButtonOptions copyWith({ |
|
|
|
|
Color? fillColor, |
|
|
|
|
double? hoverElevation, |
|
|
|
|
double? highlightElevation, |
|
|
|
|
List<PopupMenuEntry<String>>? items, |
|
|
|
|
Map<String, String>? rawItemsMap, |
|
|
|
|
ValueChanged<String>? onSelected, |
|
|
|
|
List<Attribute>? attributes, |
|
|
|
|
EdgeInsetsGeometry? padding, |
|
|
|
|
TextStyle? style, |
|
|
|
|
double? width, |
|
|
|
|
String? initialValue, |
|
|
|
|
TextOverflow? labelOverflow, |
|
|
|
|
bool? renderFontFamilies, |
|
|
|
|
bool? overrideTooltipByFontFamily, |
|
|
|
|
double? itemHeight, |
|
|
|
|
EdgeInsets? itemPadding, |
|
|
|
|
Color? defaultItemColor, |
|
|
|
|
double? iconSize, |
|
|
|
|
double? iconButtonFactor, |
|
|
|
|
// Add properties to override inherited properties |
|
|
|
|
QuillController? controller, |
|
|
|
|
IconData? iconData, |
|
|
|
|
VoidCallback? afterButtonPressed, |
|
|
|
|
String? tooltip, |
|
|
|
|
QuillIconTheme? iconTheme, |
|
|
|
|
}) { |
|
|
|
|
return QuillToolbarSelectHeaderStyleDropdownButtonOptions( |
|
|
|
|
attributes: attributes ?? this.attributes, |
|
|
|
|
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, |
|
|
|
|
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, |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class QuillToolbarSelectHeaderStyleDropdownButton extends StatefulWidget { |
|
|
|
|
const QuillToolbarSelectHeaderStyleDropdownButton({ |
|
|
|
@ -11,8 +121,10 @@ class QuillToolbarSelectHeaderStyleDropdownButton extends StatefulWidget { |
|
|
|
|
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 QuillToolbarSelectHeaderStyleButtonsOptions options; |
|
|
|
|
final QuillToolbarSelectHeaderStyleDropdownButtonOptions options; |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
State<QuillToolbarSelectHeaderStyleDropdownButton> createState() => |
|
|
|
@ -26,13 +138,13 @@ class _QuillToolbarSelectHeaderStyleDropdownButtonState |
|
|
|
|
Style get _selectionStyle => controller.getSelectionStyle(); |
|
|
|
|
|
|
|
|
|
final _valueToText = <Attribute, String>{ |
|
|
|
|
Attribute.header: 'N', |
|
|
|
|
Attribute.header: 'Normal', |
|
|
|
|
Attribute.h1: 'H1', |
|
|
|
|
Attribute.h2: 'H2', |
|
|
|
|
Attribute.h3: 'H3', |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
QuillToolbarSelectHeaderStyleButtonsOptions get options { |
|
|
|
|
QuillToolbarSelectHeaderStyleDropdownButtonOptions get options { |
|
|
|
|
return widget.options; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -71,14 +183,6 @@ class _QuillToolbarSelectHeaderStyleDropdownButtonState |
|
|
|
|
context.loc.headerStyle; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Axis get axis { |
|
|
|
|
return options.axis ?? |
|
|
|
|
context.quillToolbarConfigurations?.axis ?? |
|
|
|
|
context.quillBaseToolbarConfigurations?.axis ?? |
|
|
|
|
(throw ArgumentError( |
|
|
|
|
'There is no default value for the Axis of the toolbar')); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
List<Attribute> get _attrbuites { |
|
|
|
|
return options.attributes ?? |
|
|
|
|
const [ |
|
|
|
@ -109,9 +213,7 @@ class _QuillToolbarSelectHeaderStyleDropdownButtonState |
|
|
|
|
@override |
|
|
|
|
void initState() { |
|
|
|
|
super.initState(); |
|
|
|
|
setState(() { |
|
|
|
|
_selectedAttribute = _getHeaderValue(); |
|
|
|
|
}); |
|
|
|
|
_selectedAttribute = _getHeaderValue(); |
|
|
|
|
controller.addListener(_didChangeEditingValue); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -124,76 +226,45 @@ class _QuillToolbarSelectHeaderStyleDropdownButtonState |
|
|
|
|
'All attributes must be one of them: header, h1, h2 or h3', |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
final style = TextStyle( |
|
|
|
|
fontWeight: FontWeight.w600, |
|
|
|
|
fontSize: iconSize * 0.7, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
final baseButtonConfigurations = |
|
|
|
|
context.requireQuillToolbarBaseButtonOptions; |
|
|
|
|
final childBuilder = |
|
|
|
|
options.childBuilder ?? baseButtonExtraOptions.childBuilder; |
|
|
|
|
|
|
|
|
|
for (final attribute in _attrbuites) { |
|
|
|
|
if (childBuilder != null) { |
|
|
|
|
return childBuilder( |
|
|
|
|
QuillToolbarSelectHeaderStyleButtonsOptions( |
|
|
|
|
afterButtonPressed: afterButtonPressed, |
|
|
|
|
attributes: _attrbuites, |
|
|
|
|
axis: axis, |
|
|
|
|
iconSize: iconSize, |
|
|
|
|
iconButtonFactor: iconButtonFactor, |
|
|
|
|
iconTheme: iconTheme, |
|
|
|
|
tooltip: tooltip, |
|
|
|
|
), |
|
|
|
|
QuillToolbarSelectHeaderStyleButtonExtraOptions( |
|
|
|
|
controller: controller, |
|
|
|
|
context: context, |
|
|
|
|
onPressed: () => _sharedOnPressed(attribute), |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
options.childBuilder ?? baseButtonConfigurations.childBuilder; |
|
|
|
|
if (childBuilder != null) { |
|
|
|
|
return childBuilder( |
|
|
|
|
options.copyWith( |
|
|
|
|
iconSize: iconSize, |
|
|
|
|
iconTheme: iconTheme, |
|
|
|
|
tooltip: tooltip, |
|
|
|
|
afterButtonPressed: afterButtonPressed, |
|
|
|
|
), |
|
|
|
|
QuillToolbarSelectHeaderStyleDropdownButtonExtraOptions( |
|
|
|
|
currentValue: _selectedAttribute!, |
|
|
|
|
controller: controller, |
|
|
|
|
context: context, |
|
|
|
|
onPressed: _onPressed, |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final theme = Theme.of(context); |
|
|
|
|
|
|
|
|
|
return ConstrainedBox( |
|
|
|
|
constraints: BoxConstraints.tightFor( |
|
|
|
|
width: iconSize * iconButtonFactor, |
|
|
|
|
height: iconSize * iconButtonFactor, |
|
|
|
|
height: iconSize * 1.81, |
|
|
|
|
width: options.width, |
|
|
|
|
), |
|
|
|
|
child: Tooltip( |
|
|
|
|
child: UtilityWidgets.maybeTooltip( |
|
|
|
|
message: tooltip, |
|
|
|
|
child: DropdownButtonHideUnderline( |
|
|
|
|
child: DropdownButton<Attribute>( |
|
|
|
|
value: _selectedAttribute, |
|
|
|
|
items: _valueToText.entries |
|
|
|
|
.map((header) => DropdownMenuItem( |
|
|
|
|
value: header.key, |
|
|
|
|
child: Text( |
|
|
|
|
header.value, |
|
|
|
|
style: style, |
|
|
|
|
), |
|
|
|
|
)) |
|
|
|
|
.toList(), |
|
|
|
|
selectedItemBuilder: (context) => |
|
|
|
|
_valueToText.entries.map((header) { |
|
|
|
|
final isSelected = _selectedAttribute == header.key; |
|
|
|
|
return Text( |
|
|
|
|
header.value, |
|
|
|
|
style: style.copyWith( |
|
|
|
|
color: isSelected |
|
|
|
|
? (iconTheme?.iconSelectedColor ?? |
|
|
|
|
theme.primaryIconTheme.color) |
|
|
|
|
: (iconTheme?.iconUnselectedColor ?? |
|
|
|
|
theme.iconTheme.color), |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
}).toList(), |
|
|
|
|
elevation: 0, |
|
|
|
|
child: RawMaterialButton( |
|
|
|
|
visualDensity: VisualDensity.compact, |
|
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
|
borderRadius: BorderRadius.circular(iconTheme?.borderRadius ?? 2), |
|
|
|
|
padding: |
|
|
|
|
const EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0), |
|
|
|
|
onChanged: (attribute) => _sharedOnPressed(attribute!), |
|
|
|
|
), |
|
|
|
|
fillColor: options.fillColor, |
|
|
|
|
elevation: 0, |
|
|
|
|
hoverElevation: options.hoverElevation, |
|
|
|
|
highlightElevation: options.hoverElevation, |
|
|
|
|
onPressed: _onPressed, |
|
|
|
|
child: _buildContent(context), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
@ -215,9 +286,85 @@ class _QuillToolbarSelectHeaderStyleDropdownButtonState |
|
|
|
|
return _selectionStyle.attributes[Attribute.header.key] ?? Attribute.header; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void _sharedOnPressed(Attribute attribute) { |
|
|
|
|
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( |
|
|
|
|
_selectedAttribute!.key, |
|
|
|
|
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, |
|
|
|
|
) |
|
|
|
|
], |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void _onPressed() { |
|
|
|
|
_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( |
|
|
|
|
color: header.value == 'N' ? options.defaultItemColor : null, |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
], |
|
|
|
|
position: position, |
|
|
|
|
shape: popupMenuTheme.shape, |
|
|
|
|
color: popupMenuTheme.color, |
|
|
|
|
); |
|
|
|
|
if (newValue == null) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final attribute0 = |
|
|
|
|
_selectedAttribute == attribute ? Attribute.header : attribute; |
|
|
|
|
_selectedAttribute == newValue ? Attribute.header : newValue; |
|
|
|
|
controller.formatSelection(attribute0); |
|
|
|
|
afterButtonPressed?.call(); |
|
|
|
|
} |
|
|
|
|