From 2bfed0a1d96f5c65c0f19eea0587b645cc5a9678 Mon Sep 17 00:00:00 2001 From: Alan Mantoux Date: Thu, 7 Dec 2023 19:06:15 +0100 Subject: [PATCH 1/3] Support for automatic mode in color selection buttons --- .../lib/src/widgets/editor_toolbar.dart | 105 +++++++++--------- .../test/widgets/editor_toolbar_test.dart | 10 +- 2 files changed, 56 insertions(+), 59 deletions(-) diff --git a/packages/fleather/lib/src/widgets/editor_toolbar.dart b/packages/fleather/lib/src/widgets/editor_toolbar.dart index 6505be5a..19c52d82 100644 --- a/packages/fleather/lib/src/widgets/editor_toolbar.dart +++ b/packages/fleather/lib/src/widgets/editor_toolbar.dart @@ -353,9 +353,9 @@ Widget defaultToggleStyleButtonBuilder( /// Signature of callbacks that return a [Color] picked from a [BuildContext]. typedef PickColor = Future Function(BuildContext); -/// Signature of callbacks the return a [Widget] from a [BuildContext] -/// and a [Color]. -typedef ColorButtonBuilder = Widget Function(BuildContext, Color); +/// Signature of callbacks the returns a [Widget] from a [BuildContext] +/// and a [Color] (`null` color to use the default color of the text - copes with dark mode). +typedef ColorButtonBuilder = Widget Function(BuildContext, Color?); /// Toolbar button which allows to apply background color style to a portion of text. /// @@ -365,14 +365,12 @@ class ColorButton extends StatefulWidget { {Key? key, required this.controller, required this.attributeKey, - required this.defaultColor, required this.builder, this.pickColor}) : super(key: key); final FleatherController controller; final ColorParchmentAttributeBuilder attributeKey; - final Color defaultColor; final ColorButtonBuilder builder; final PickColor? pickColor; @@ -383,14 +381,15 @@ class ColorButton extends StatefulWidget { class _ColorButtonState extends State { static double buttonSize = 32; - late Color _value; + late Color? _value; ParchmentStyle get _selectionStyle => widget.controller.getSelectionStyle(); void _didChangeEditingValue() { setState(() { - _value = Color(_selectionStyle.get(widget.attributeKey)?.value ?? - widget.defaultColor.value); + final selectionColor = _selectionStyle.get(widget.attributeKey); + _value = + selectionColor?.value != null ? Color(selectionColor!.value!) : null; }); } @@ -408,7 +407,7 @@ class _ColorButtonState extends State { child: Container( constraints: BoxConstraints(maxWidth: maxWidth), padding: const EdgeInsets.all(8.0), - child: _ColorPalette(defaultColor: widget.defaultColor)), + child: const _ColorPalette()), ); return Navigator.of(context).push( @@ -433,8 +432,9 @@ class _ColorButtonState extends State { @override void initState() { super.initState(); - _value = Color(_selectionStyle.get(widget.attributeKey)?.value ?? - widget.defaultColor.value); + final selectionColor = _selectionStyle.get(widget.attributeKey); + _value = + selectionColor?.value != null ? Color(selectionColor!.value!) : null; widget.controller.addListener(_didChangeEditingValue); } @@ -444,8 +444,9 @@ class _ColorButtonState extends State { if (oldWidget.controller != widget.controller) { oldWidget.controller.removeListener(_didChangeEditingValue); widget.controller.addListener(_didChangeEditingValue); - _value = Color(_selectionStyle.get(widget.attributeKey)?.value ?? - widget.defaultColor.value); + final selectionColor = _selectionStyle.get(widget.attributeKey); + _value = + selectionColor?.value != null ? Color(selectionColor!.value!) : null; } } @@ -472,8 +473,10 @@ class _ColorButtonState extends State { onPressed: () async { final selectedColor = await (widget.pickColor ?? _defaultPickColor)(context); - widget.controller.formatSelection(widget.attributeKey - .withColor(selectedColor?.value ?? widget.defaultColor.value)); + final attribute = selectedColor != null + ? widget.attributeKey.withColor(selectedColor.value) + : widget.attributeKey.unset; + widget.controller.formatSelection(attribute); }, child: Builder(builder: (context) => widget.builder(context, _value)), ), @@ -483,6 +486,7 @@ class _ColorButtonState extends State { class _ColorPalette extends StatelessWidget { static const colors = [ + null, Colors.indigo, Colors.blue, Colors.cyan, @@ -497,12 +501,11 @@ class _ColorPalette extends StatelessWidget { Colors.purple, Colors.brown, Colors.grey, - Colors.white + Colors.white, + Colors.black, ]; - const _ColorPalette({required this.defaultColor}); - - final Color defaultColor; + const _ColorPalette(); @override Widget build(BuildContext context) { @@ -511,9 +514,7 @@ class _ColorPalette extends StatelessWidget { alignment: WrapAlignment.start, runSpacing: 4, spacing: 4, - children: [defaultColor, ...colors] - .map((e) => _ColorPaletteElement(color: e)) - .toList(), + children: [...colors].map((e) => _ColorPaletteElement(color: e)).toList(), ); } } @@ -521,7 +522,7 @@ class _ColorPalette extends StatelessWidget { class _ColorPaletteElement extends StatelessWidget { const _ColorPaletteElement({required this.color}); - final Color color; + final Color? color; @override Widget build(BuildContext context) { @@ -529,18 +530,20 @@ class _ColorPaletteElement extends StatelessWidget { final isMobile = !kIsWeb && (Platform.isAndroid || Platform.isIOS); final size = isMobile ? 32.0 : 16.0; return Container( - width: size, + width: (color == null ? 4 : 1) * size + (color == null ? 3 * 4 : 0), height: size, decoration: BoxDecoration( color: color, - border: color == Colors.transparent - ? Border.all( - color: Colors.black, - strokeAlign: BorderSide.strokeAlignInside, + ), + child: RawMaterialButton( + onPressed: () => Navigator.pop(context, color), + child: color == null + ? Text( + 'Automatic', + style: Theme.of(context).textTheme.bodySmall, ) : null, ), - child: RawMaterialButton(onPressed: () => Navigator.pop(context, color)), ); } } @@ -855,28 +858,26 @@ class FleatherToolbar extends StatefulWidget implements PreferredSizeWidget { ) ], ); - Widget textColorBuilder(context, value) => Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - const Icon( - Icons.text_fields_sharp, - size: 16, - ), - Container( - width: 18, - height: 4, - decoration: BoxDecoration( - color: value, - border: value == Colors.transparent - ? Border.all( - color: - Theme.of(context).iconTheme.color ?? Colors.black) - : null, - ), - ) - ], - ); + Widget textColorBuilder(context, value) { + Color effectiveColor = + value ?? DefaultTextStyle.of(context).style.color ?? Colors.black; + return Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.text_fields_sharp, + size: 16, + ), + Container( + width: 18, + height: 4, + decoration: BoxDecoration(color: effectiveColor), + ) + ], + ); + } + return FleatherToolbar(key: key, padding: padding, children: [ ...leading, Visibility( @@ -920,7 +921,6 @@ class FleatherToolbar extends StatefulWidget implements PreferredSizeWidget { child: ColorButton( controller: controller, attributeKey: ParchmentAttribute.foregroundColor, - defaultColor: Colors.black, builder: textColorBuilder, ), ), @@ -929,7 +929,6 @@ class FleatherToolbar extends StatefulWidget implements PreferredSizeWidget { child: ColorButton( controller: controller, attributeKey: ParchmentAttribute.backgroundColor, - defaultColor: Colors.transparent, builder: backgroundColorBuilder, ), ), diff --git a/packages/fleather/test/widgets/editor_toolbar_test.dart b/packages/fleather/test/widgets/editor_toolbar_test.dart index 256c52cd..485b9f2d 100644 --- a/packages/fleather/test/widgets/editor_toolbar_test.dart +++ b/packages/fleather/test/widgets/editor_toolbar_test.dart @@ -84,13 +84,11 @@ Widget widget(FleatherController controller, {bool withBasic = false}) { ColorButton( controller: controller, attributeKey: ParchmentAttribute.backgroundColor, - defaultColor: Colors.transparent, builder: backgroundColorBuilder, ), ColorButton( controller: controller, attributeKey: ParchmentAttribute.foregroundColor, - defaultColor: Colors.black, builder: textColorBuilder, ), IndentationButton(controller: controller), @@ -325,7 +323,7 @@ void main() { matching: find.byType(RawMaterialButton)); expect( colorElement, - findsNWidgets(16), + findsNWidgets(17), ); await tester.tap(find @@ -335,7 +333,7 @@ void main() { .last); await tester.pumpAndSettle(throttleDuration); expect(controller.document.toDelta().first, - Operation.insert('Hello', {'bg': Colors.white.value})); + Operation.insert('Hello', {'bg': Colors.black.value})); }); testWidgets('Text color', (tester) async { @@ -356,7 +354,7 @@ void main() { matching: find.byType(RawMaterialButton)); expect( colorElement, - findsNWidgets(16), + findsNWidgets(17), ); await tester.tap(find @@ -366,7 +364,7 @@ void main() { .last); await tester.pumpAndSettle(throttleDuration); expect(controller.document.toDelta().first, - Operation.insert('Hello', {'fg': Colors.white.value})); + Operation.insert('Hello', {'fg': Colors.black.value})); }); } From fb8df3f82037a0f74fc9a55da4725b8cbefbeae2 Mon Sep 17 00:00:00 2001 From: Alan Mantoux Date: Thu, 7 Dec 2023 19:14:18 +0100 Subject: [PATCH 2/3] Remove useless border --- packages/fleather/lib/src/widgets/editor_toolbar.dart | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/fleather/lib/src/widgets/editor_toolbar.dart b/packages/fleather/lib/src/widgets/editor_toolbar.dart index 19c52d82..802d5e1e 100644 --- a/packages/fleather/lib/src/widgets/editor_toolbar.dart +++ b/packages/fleather/lib/src/widgets/editor_toolbar.dart @@ -847,14 +847,7 @@ class FleatherToolbar extends StatefulWidget implements PreferredSizeWidget { Container( width: 18, height: 4, - decoration: BoxDecoration( - color: value, - border: value == Colors.transparent - ? Border.all( - color: - Theme.of(context).iconTheme.color ?? Colors.black) - : null, - ), + decoration: BoxDecoration(color: value), ) ], ); From 7b58999d2e6eaac47fe0b093568cd5ac886ee8f8 Mon Sep 17 00:00:00 2001 From: Alan Mantoux Date: Thu, 7 Dec 2023 23:22:48 +0100 Subject: [PATCH 3/3] Custom label for null color in color picker --- .../lib/src/widgets/editor_toolbar.dart | 32 +++++++++++++------ .../test/widgets/editor_toolbar_test.dart | 4 ++- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/fleather/lib/src/widgets/editor_toolbar.dart b/packages/fleather/lib/src/widgets/editor_toolbar.dart index 802d5e1e..8b1edae7 100644 --- a/packages/fleather/lib/src/widgets/editor_toolbar.dart +++ b/packages/fleather/lib/src/widgets/editor_toolbar.dart @@ -350,8 +350,10 @@ Widget defaultToggleStyleButtonBuilder( ); } -/// Signature of callbacks that return a [Color] picked from a [BuildContext]. -typedef PickColor = Future Function(BuildContext); +/// Signature of callbacks that return a [Color] picked from a palette built in +/// a [BuildContext] with a [String] specifying the label of the `null` selection +/// option +typedef PickColor = Future Function(BuildContext, String); /// Signature of callbacks the returns a [Widget] from a [BuildContext] /// and a [Color] (`null` color to use the default color of the text - copes with dark mode). @@ -365,12 +367,14 @@ class ColorButton extends StatefulWidget { {Key? key, required this.controller, required this.attributeKey, + required this.nullColorLabel, required this.builder, this.pickColor}) : super(key: key); final FleatherController controller; final ColorParchmentAttributeBuilder attributeKey; + final String nullColorLabel; final ColorButtonBuilder builder; final PickColor? pickColor; @@ -393,7 +397,8 @@ class _ColorButtonState extends State { }); } - Future _defaultPickColor(BuildContext context) async { + Future _defaultPickColor( + BuildContext context, String nullColorLabel) async { // kIsWeb important here as Platform.xxx will cause a crash en web final isMobile = !kIsWeb && (Platform.isAndroid || Platform.isIOS); final maxWidth = isMobile ? 200.0 : 100.0; @@ -407,7 +412,7 @@ class _ColorButtonState extends State { child: Container( constraints: BoxConstraints(maxWidth: maxWidth), padding: const EdgeInsets.all(8.0), - child: const _ColorPalette()), + child: _ColorPalette(nullColorLabel)), ); return Navigator.of(context).push( @@ -471,8 +476,8 @@ class _ColorButtonState extends State { hoverElevation: 1, highlightElevation: 1, onPressed: () async { - final selectedColor = - await (widget.pickColor ?? _defaultPickColor)(context); + final selectedColor = await (widget.pickColor ?? _defaultPickColor)( + context, widget.nullColorLabel); final attribute = selectedColor != null ? widget.attributeKey.withColor(selectedColor.value) : widget.attributeKey.unset; @@ -505,7 +510,9 @@ class _ColorPalette extends StatelessWidget { Colors.black, ]; - const _ColorPalette(); + const _ColorPalette(this.nullColorLabel); + + final String nullColorLabel; @override Widget build(BuildContext context) { @@ -514,15 +521,18 @@ class _ColorPalette extends StatelessWidget { alignment: WrapAlignment.start, runSpacing: 4, spacing: 4, - children: [...colors].map((e) => _ColorPaletteElement(color: e)).toList(), + children: [...colors] + .map((e) => _ColorPaletteElement(e, nullColorLabel)) + .toList(), ); } } class _ColorPaletteElement extends StatelessWidget { - const _ColorPaletteElement({required this.color}); + const _ColorPaletteElement(this.color, this.nullColorLabel); final Color? color; + final String nullColorLabel; @override Widget build(BuildContext context) { @@ -539,7 +549,7 @@ class _ColorPaletteElement extends StatelessWidget { onPressed: () => Navigator.pop(context, color), child: color == null ? Text( - 'Automatic', + nullColorLabel, style: Theme.of(context).textTheme.bodySmall, ) : null, @@ -914,6 +924,7 @@ class FleatherToolbar extends StatefulWidget implements PreferredSizeWidget { child: ColorButton( controller: controller, attributeKey: ParchmentAttribute.foregroundColor, + nullColorLabel: 'Automatic', builder: textColorBuilder, ), ), @@ -922,6 +933,7 @@ class FleatherToolbar extends StatefulWidget implements PreferredSizeWidget { child: ColorButton( controller: controller, attributeKey: ParchmentAttribute.backgroundColor, + nullColorLabel: 'No color', builder: backgroundColorBuilder, ), ), diff --git a/packages/fleather/test/widgets/editor_toolbar_test.dart b/packages/fleather/test/widgets/editor_toolbar_test.dart index 485b9f2d..748e42de 100644 --- a/packages/fleather/test/widgets/editor_toolbar_test.dart +++ b/packages/fleather/test/widgets/editor_toolbar_test.dart @@ -1,5 +1,5 @@ -import 'package:flutter/material.dart'; import 'package:fleather/fleather.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:quill_delta/quill_delta.dart'; @@ -84,11 +84,13 @@ Widget widget(FleatherController controller, {bool withBasic = false}) { ColorButton( controller: controller, attributeKey: ParchmentAttribute.backgroundColor, + nullColorLabel: 'No color', builder: backgroundColorBuilder, ), ColorButton( controller: controller, attributeKey: ParchmentAttribute.foregroundColor, + nullColorLabel: 'Automatic', builder: textColorBuilder, ), IndentationButton(controller: controller),