From 071825f95b4dfe4e36e919e78537be0c997751d0 Mon Sep 17 00:00:00 2001 From: Amir Panahandeh Date: Mon, 4 Mar 2024 22:37:40 +0330 Subject: [PATCH] Add superscript and subscript --- .../fleather/lib/src/widgets/controller.dart | 1 + .../lib/src/widgets/editor_toolbar.dart | 22 ++++++++ .../fleather/lib/src/widgets/text_line.dart | 6 ++ packages/fleather/lib/src/widgets/theme.dart | 16 ++++++ ...editor_input_client_mixin_deltas_test.dart | 55 ++++++++++--------- .../lib/src/document/attributes.dart | 25 +++++++++ 6 files changed, 99 insertions(+), 26 deletions(-) diff --git a/packages/fleather/lib/src/widgets/controller.dart b/packages/fleather/lib/src/widgets/controller.dart index 478ecc7e..fbf44ecb 100644 --- a/packages/fleather/lib/src/widgets/controller.dart +++ b/packages/fleather/lib/src/widgets/controller.dart @@ -19,6 +19,7 @@ List _toggleableStyleKeys = [ ParchmentAttribute.inlineCode.key, ParchmentAttribute.backgroundColor.key, ParchmentAttribute.foregroundColor.key, + ParchmentAttribute.script.key, ]; class FleatherController extends ChangeNotifier { diff --git a/packages/fleather/lib/src/widgets/editor_toolbar.dart b/packages/fleather/lib/src/widgets/editor_toolbar.dart index 79f177c2..16e26eb1 100644 --- a/packages/fleather/lib/src/widgets/editor_toolbar.dart +++ b/packages/fleather/lib/src/widgets/editor_toolbar.dart @@ -843,6 +843,7 @@ class FleatherToolbar extends StatefulWidget implements PreferredSizeWidget { bool hideBackgroundColor = false, bool hideForegroundColor = false, bool hideInlineCode = false, + bool hideScript = false, bool hideHeadingStyle = false, bool hideIndentation = false, bool hideListNumbers = false, @@ -970,6 +971,27 @@ class FleatherToolbar extends StatefulWidget implements PreferredSizeWidget { /// ################################################################ + Visibility( + visible: !hideScript, + child: ToggleStyleButton( + attribute: ParchmentAttribute.superscript, + icon: Icons.superscript, + controller: controller, + )), + Visibility( + visible: !hideScript, + child: ToggleStyleButton( + attribute: ParchmentAttribute.subscript, + icon: Icons.subscript, + controller: controller, + )), + Visibility( + visible: !hideScript, + child: VerticalDivider( + indent: 16, endIndent: 16, color: Colors.grey.shade400)), + + /// ################################################################ + Visibility( visible: !hideDirection, child: ToggleStyleButton( diff --git a/packages/fleather/lib/src/widgets/text_line.dart b/packages/fleather/lib/src/widgets/text_line.dart index 566120b6..4520ce93 100644 --- a/packages/fleather/lib/src/widgets/text_line.dart +++ b/packages/fleather/lib/src/widgets/text_line.dart @@ -313,6 +313,12 @@ class _TextLineState extends State { result = result.copyWith(color: Color(foregroundColor.value!)); } } + if (nodeStyle.containsSame(ParchmentAttribute.superscript)) { + result = _mergeTextStyleWithDecoration(result, theme.superscript); + } else if (nodeStyle.containsSame(ParchmentAttribute.subscript)) { + result = _mergeTextStyleWithDecoration(result, theme.subscript); + } + return result; } diff --git a/packages/fleather/lib/src/widgets/theme.dart b/packages/fleather/lib/src/widgets/theme.dart index 51f39126..9c150b7f 100644 --- a/packages/fleather/lib/src/widgets/theme.dart +++ b/packages/fleather/lib/src/widgets/theme.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:parchment/parchment.dart'; @@ -74,6 +76,12 @@ class FleatherThemeData { /// Style of links in text. final TextStyle link; + /// Style of superscript text. + final TextStyle superscript; + + /// Style of subscript text. + final TextStyle subscript; + /// Default style theme for regular paragraphs of text. final TextBlockTheme paragraph; // spacing: top: 6, bottom: 10 @@ -121,6 +129,8 @@ class FleatherThemeData { required this.lists, required this.quote, required this.code, + required this.superscript, + required this.subscript, }); factory FleatherThemeData.fallback(BuildContext context) { @@ -262,6 +272,8 @@ class FleatherThemeData { borderRadius: BorderRadius.circular(2), ), ), + superscript: const TextStyle(fontFeatures: [FontFeature.superscripts()]), + subscript: const TextStyle(fontFeatures: [FontFeature.subscripts()]), ); } @@ -271,6 +283,8 @@ class FleatherThemeData { TextStyle? underline, TextStyle? strikethrough, TextStyle? link, + TextStyle? superscript, + TextStyle? subscript, InlineCodeThemeData? inlineCode, TextBlockTheme? paragraph, TextBlockTheme? heading1, @@ -300,6 +314,8 @@ class FleatherThemeData { lists: lists ?? this.lists, quote: quote ?? this.quote, code: code ?? this.code, + superscript: superscript ?? this.superscript, + subscript: subscript ?? this.subscript, ); } diff --git a/packages/fleather/test/widgets/editor_input_client_mixin_deltas_test.dart b/packages/fleather/test/widgets/editor_input_client_mixin_deltas_test.dart index 155733a1..6240a313 100644 --- a/packages/fleather/test/widgets/editor_input_client_mixin_deltas_test.dart +++ b/packages/fleather/test/widgets/editor_input_client_mixin_deltas_test.dart @@ -106,32 +106,35 @@ void main() { when(() => editorState.textEditingValue) .thenReturn(initialTextEditingValue); when(() => editorState.themeData).thenReturn(FleatherThemeData( - bold: const TextStyle(), - italic: const TextStyle(), - underline: const TextStyle(), - strikethrough: const TextStyle(), - inlineCode: InlineCodeThemeData(style: const TextStyle()), - link: const TextStyle(), - paragraph: TextBlockTheme( - style: const TextStyle(), spacing: const VerticalSpacing()), - heading1: TextBlockTheme( - style: const TextStyle(), spacing: const VerticalSpacing()), - heading2: TextBlockTheme( - style: const TextStyle(), spacing: const VerticalSpacing()), - heading3: TextBlockTheme( - style: const TextStyle(), spacing: const VerticalSpacing()), - heading4: TextBlockTheme( - style: const TextStyle(), spacing: const VerticalSpacing()), - heading5: TextBlockTheme( - style: const TextStyle(), spacing: const VerticalSpacing()), - heading6: TextBlockTheme( - style: const TextStyle(), spacing: const VerticalSpacing()), - lists: TextBlockTheme( - style: const TextStyle(), spacing: const VerticalSpacing()), - quote: TextBlockTheme( - style: const TextStyle(), spacing: const VerticalSpacing()), - code: TextBlockTheme( - style: const TextStyle(), spacing: const VerticalSpacing()))); + bold: const TextStyle(), + italic: const TextStyle(), + underline: const TextStyle(), + strikethrough: const TextStyle(), + inlineCode: InlineCodeThemeData(style: const TextStyle()), + link: const TextStyle(), + paragraph: TextBlockTheme( + style: const TextStyle(), spacing: const VerticalSpacing()), + heading1: TextBlockTheme( + style: const TextStyle(), spacing: const VerticalSpacing()), + heading2: TextBlockTheme( + style: const TextStyle(), spacing: const VerticalSpacing()), + heading3: TextBlockTheme( + style: const TextStyle(), spacing: const VerticalSpacing()), + heading4: TextBlockTheme( + style: const TextStyle(), spacing: const VerticalSpacing()), + heading5: TextBlockTheme( + style: const TextStyle(), spacing: const VerticalSpacing()), + heading6: TextBlockTheme( + style: const TextStyle(), spacing: const VerticalSpacing()), + lists: TextBlockTheme( + style: const TextStyle(), spacing: const VerticalSpacing()), + quote: TextBlockTheme( + style: const TextStyle(), spacing: const VerticalSpacing()), + code: TextBlockTheme( + style: const TextStyle(), spacing: const VerticalSpacing()), + superscript: const TextStyle(), + subscript: const TextStyle(), + )); when(() => rawEditor.controller).thenReturn(controller); when(() => rawEditor.readOnly).thenReturn(false); when(() => rawEditor.keyboardAppearance).thenReturn(Brightness.light); diff --git a/packages/parchment/lib/src/document/attributes.dart b/packages/parchment/lib/src/document/attributes.dart index 2e9cc650..421042ba 100644 --- a/packages/parchment/lib/src/document/attributes.dart +++ b/packages/parchment/lib/src/document/attributes.dart @@ -99,6 +99,7 @@ class ParchmentAttribute implements ParchmentAttributeBuilder { ParchmentAttribute.direction.key: ParchmentAttribute.direction, ParchmentAttribute.alignment.key: ParchmentAttribute.alignment, ParchmentAttribute.indent.key: ParchmentAttribute.indent, + ParchmentAttribute.script.key: ParchmentAttribute.script, }; // Inline attributes @@ -128,6 +129,16 @@ class ParchmentAttribute implements ParchmentAttributeBuilder { // ignore: const_eval_throws_exception static const link = LinkAttributeBuilder._(); + /// Script style attribute. + // ignore: const_eval_throws_exception + static const script = _ScriptAttributeBuilder._(); + + /// Alias for [ParchmentAttribute.script.superscript]. + static ParchmentAttribute get superscript => script.superscript; + + /// Alias for [ParchmentAttribute.script.subscript]. + static ParchmentAttribute get subscript => script.subscript; + // Line attributes /// Heading style attribute. @@ -433,6 +444,20 @@ class _InlineCodeAttribute extends ParchmentAttribute { : super._('c', ParchmentAttributeScope.inline, true); } +/// Builder for super/sub script style attributes. +class _ScriptAttributeBuilder extends ParchmentAttributeBuilder { + const _ScriptAttributeBuilder._() + : super._('script', ParchmentAttributeScope.inline); + + /// Creates a subscript attribute. + ParchmentAttribute get superscript => + ParchmentAttribute._(key, scope, 'super'); + + /// Creates a superscript attribute. + ParchmentAttribute get subscript => + ParchmentAttribute._(key, scope, 'sub'); +} + /// Builder for color-based style attributes. /// /// Useful in scenarios when a color attribute value is not known upfront.