From abd08e5e5358c80a0268adbdbf3c76234c6d6d1b Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Wed, 28 Feb 2024 14:51:50 +0700 Subject: [PATCH] feat: support customizing font and color before typing (#4765) * fix: bius buttons highlight status * feat: support customizing font family before typing * feat: support customizing text color before typing --- .../mobile_toolbar_v3/_bius_items.dart | 2 +- .../mobile_toolbar_v3/_color_item.dart | 38 +++++++- .../mobile_toolbar_v3/_color_list.dart | 93 ++++++++++++------- .../mobile_toolbar_v3/_font_item.dart | 58 +++++++++--- .../mobile_toolbar_v3/util.dart | 5 +- 5 files changed, 149 insertions(+), 47 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_bius_items.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_bius_items.dart index 36a023251e..2fb2cd5db3 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_bius_items.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_bius_items.dart @@ -72,7 +72,7 @@ class BIUSItems extends StatelessWidget { }, icon: icon, isSelected: editorState.isTextDecorationSelected(richTextKey) && - editorState.toggledStyle[richTextKey] != true, + editorState.toggledStyle[richTextKey] != false, iconPadding: const EdgeInsets.symmetric( vertical: 14.0, ), diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_color_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_color_item.dart index e519d520dc..417c4d5c0a 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_color_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_color_item.dart @@ -20,6 +20,8 @@ class ColorItem extends StatelessWidget { @override Widget build(BuildContext context) { final theme = ToolbarColorExtension.of(context); + final selectedBackgroundColor = _getBackgroundColor(context); + return MobileToolbarMenuItemWrapper( size: const Size(82, 52), onTap: () async { @@ -42,10 +44,11 @@ class ColorItem extends StatelessWidget { ); }, icon: FlowySvgs.m_aa_color_s, - backgroundColor: theme.toolbarMenuItemBackgroundColor, - isSelected: false, + backgroundColor: + selectedBackgroundColor ?? theme.toolbarMenuItemBackgroundColor, + selectedBackgroundColor: selectedBackgroundColor, + isSelected: selectedBackgroundColor != null, showRightArrow: true, - enable: editorState.selection?.isCollapsed == false, iconPadding: const EdgeInsets.only( top: 14.0, bottom: 14.0, @@ -53,4 +56,33 @@ class ColorItem extends StatelessWidget { ), ); } + + Color? _getBackgroundColor(BuildContext context) { + final selection = editorState.selection; + if (selection == null) { + return null; + } + String? backgroundColor = + editorState.toggledStyle[AppFlowyRichTextKeys.backgroundColor]; + if (backgroundColor == null) { + if (selection.isCollapsed && selection.startIndex != 0) { + backgroundColor = editorState.getDeltaAttributeValueInSelection( + AppFlowyRichTextKeys.backgroundColor, + selection.copyWith( + start: selection.start.copyWith( + offset: selection.startIndex - 1, + ), + ), + ); + } else { + backgroundColor = editorState.getDeltaAttributeValueInSelection( + AppFlowyRichTextKeys.backgroundColor, + ); + } + } + if (backgroundColor != null && int.tryParse(backgroundColor) != null) { + return Color(int.parse(backgroundColor)); + } + return null; + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_color_list.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_color_list.dart index 202ab3a786..9ca8902143 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_color_list.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_color_list.dart @@ -63,16 +63,9 @@ class _TextColorAndBackgroundColorState extends State<_TextColorAndBackgroundColor> { @override Widget build(BuildContext context) { - final String? selectedTextColor = - widget.editorState.getDeltaAttributeValueInSelection( - AppFlowyRichTextKeys.textColor, - widget.selection, - ); + final String? selectedTextColor = _getColor(AppFlowyRichTextKeys.textColor); final String? selectedBackgroundColor = - widget.editorState.getDeltaAttributeValueInSelection( - AppFlowyRichTextKeys.backgroundColor, - widget.selection, - ); + _getColor(AppFlowyRichTextKeys.backgroundColor); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -90,17 +83,25 @@ class _TextColorAndBackgroundColorState selectedColor: selectedTextColor?.tryToColor(), onSelectedColor: (textColor) async { final hex = textColor.alpha == 0 ? null : textColor.toHex(); - await widget.editorState.formatDelta( - widget.selection, - { - AppFlowyRichTextKeys.textColor: hex, - }, - selectionExtraInfo: { - selectionExtraInfoDisableFloatingToolbar: true, - selectionExtraInfoDisableMobileToolbarKey: true, - selectionExtraInfoDoNotAttachTextService: true, - }, - ); + final selection = widget.selection; + if (selection.isCollapsed) { + widget.editorState.updateToggledStyle( + AppFlowyRichTextKeys.textColor, + hex ?? '', + ); + } else { + await widget.editorState.formatDelta( + widget.selection, + { + AppFlowyRichTextKeys.textColor: hex, + }, + selectionExtraInfo: { + selectionExtraInfoDisableFloatingToolbar: true, + selectionExtraInfoDisableMobileToolbarKey: true, + selectionExtraInfoDoNotAttachTextService: true, + }, + ); + } setState(() {}); }, ), @@ -119,23 +120,53 @@ class _TextColorAndBackgroundColorState onSelectedColor: (backgroundColor) async { final hex = backgroundColor.alpha == 0 ? null : backgroundColor.toHex(); - await widget.editorState.formatDelta( - widget.selection, - { - AppFlowyRichTextKeys.backgroundColor: hex, - }, - selectionExtraInfo: { - selectionExtraInfoDisableFloatingToolbar: true, - selectionExtraInfoDisableMobileToolbarKey: true, - selectionExtraInfoDoNotAttachTextService: true, - }, - ); + final selection = widget.selection; + if (selection.isCollapsed) { + widget.editorState.updateToggledStyle( + AppFlowyRichTextKeys.backgroundColor, + hex ?? '', + ); + } else { + await widget.editorState.formatDelta( + widget.selection, + { + AppFlowyRichTextKeys.backgroundColor: hex, + }, + selectionExtraInfo: { + selectionExtraInfoDisableFloatingToolbar: true, + selectionExtraInfoDisableMobileToolbarKey: true, + selectionExtraInfoDoNotAttachTextService: true, + }, + ); + } setState(() {}); }, ), ], ); } + + String? _getColor(String key) { + final selection = widget.selection; + String? color = widget.editorState.toggledStyle[key]; + if (color == null) { + if (selection.isCollapsed && selection.startIndex != 0) { + color = widget.editorState.getDeltaAttributeValueInSelection( + key, + selection.copyWith( + start: selection.start.copyWith( + offset: selection.startIndex - 1, + ), + ), + ); + } else { + color = widget.editorState.getDeltaAttributeValueInSelection( + key, + ); + } + } + return color; + } } class _BackgroundColors extends StatelessWidget { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_font_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_font_item.dart index 02b2063ef1..cc5914f53b 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_font_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_font_item.dart @@ -1,13 +1,12 @@ import 'dart:async'; -import 'package:flutter/material.dart'; - import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_toolbar_theme.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/util/google_font_family_extension.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -23,15 +22,16 @@ class FontFamilyItem extends StatelessWidget { @override Widget build(BuildContext context) { final theme = ToolbarColorExtension.of(context); - final fontFamily = editorState.getDeltaAttributeValueInSelection( - AppFlowyRichTextKeys.fontFamily, - ); + final fontFamily = _getCurrentSelectedFontFamilyName(); final systemFonFamily = context.read().state.fontFamily; return MobileToolbarMenuItemWrapper( size: const Size(144, 52), onTap: () async { final selection = editorState.selection; + if (selection == null) { + return; + } // disable the floating toolbar unawaited( editorState.updateSelectionWithReason( @@ -46,12 +46,17 @@ class FontFamilyItem extends StatelessWidget { final newFont = await context .read() .push(FontPickerScreen.routeName); - if (newFont != null && newFont != fontFamily) { - await editorState.formatDelta(selection, { - AppFlowyRichTextKeys.fontFamily: - GoogleFonts.getFont(newFont).fontFamily, - }); + + // if the selection is not collapsed, apply the font to the selection. + if (newFont != null && !selection.isCollapsed) { + if (newFont != fontFamily) { + await editorState.formatDelta(selection, { + AppFlowyRichTextKeys.fontFamily: + GoogleFonts.getFont(newFont).fontFamily, + }); + } } + // wait for the font picker screen to be dismissed. Future.delayed(const Duration(milliseconds: 250), () { // highlight the selected text again. @@ -62,13 +67,20 @@ class FontFamilyItem extends StatelessWidget { selectionExtraInfoDisableMobileToolbarKey: false, }, ); + // if the selection is collapsed, save the font for the next typing. + if (newFont != null && selection.isCollapsed) { + editorState.updateToggledStyle( + AppFlowyRichTextKeys.fontFamily, + GoogleFonts.getFont(newFont).fontFamily, + ); + } }); }, text: (fontFamily ?? systemFonFamily).parseFontFamilyName(), fontFamily: fontFamily ?? systemFonFamily, backgroundColor: theme.toolbarMenuItemBackgroundColor, isSelected: false, - enable: editorState.selection?.isCollapsed == false, + enable: true, showRightArrow: true, iconPadding: const EdgeInsets.only( top: 14.0, @@ -81,4 +93,28 @@ class FontFamilyItem extends StatelessWidget { ), ); } + + String? _getCurrentSelectedFontFamilyName() { + final toggleFontFamily = + editorState.toggledStyle[AppFlowyRichTextKeys.fontFamily]; + if (toggleFontFamily is String && toggleFontFamily.isNotEmpty) { + return toggleFontFamily; + } + final selection = editorState.selection; + if (selection != null && + selection.isCollapsed && + selection.startIndex != 0) { + return editorState.getDeltaAttributeValueInSelection( + AppFlowyRichTextKeys.fontFamily, + selection.copyWith( + start: selection.start.copyWith( + offset: selection.startIndex - 1, + ), + ), + ); + } + return editorState.getDeltaAttributeValueInSelection( + AppFlowyRichTextKeys.fontFamily, + ); + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/util.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/util.dart index 8b723cc917..4c3f1da550 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/util.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/util.dart @@ -11,6 +11,7 @@ class MobileToolbarMenuItemWrapper extends StatelessWidget { this.icon, this.text, this.backgroundColor, + this.selectedBackgroundColor, this.enable, this.fontFamily, required this.isSelected, @@ -40,6 +41,7 @@ class MobileToolbarMenuItemWrapper extends StatelessWidget { final bool showDownArrow; final bool showRightArrow; final Color? backgroundColor; + final Color? selectedBackgroundColor; final EdgeInsets textPadding; @override @@ -90,7 +92,8 @@ class MobileToolbarMenuItemWrapper extends StatelessWidget { alignment: text != null ? Alignment.centerLeft : Alignment.center, decoration: BoxDecoration( color: isSelected - ? theme.toolbarMenuItemSelectedBackgroundColor + ? (selectedBackgroundColor ?? + theme.toolbarMenuItemSelectedBackgroundColor) : backgroundColor, borderRadius: BorderRadius.only( topLeft: enableTopLeftRadius ? radius : Radius.zero,