From 5dfc4708732a2e04548a138c94687983f4c58ac8 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Fri, 22 Sep 2023 13:53:14 +0800 Subject: [PATCH 1/5] chore: add more space between font item and font menu --- .../editor_plugins/font/customize_font_toolbar_item.dart | 1 + .../widgets/settings_appearance/font_family_setting.dart | 3 +++ .../settings_appearance/theme_setting_entry_template.dart | 5 ++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/font/customize_font_toolbar_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/font/customize_font_toolbar_item.dart index bf89adbd67..76326f2704 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/font/customize_font_toolbar_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/font/customize_font_toolbar_item.dart @@ -16,6 +16,7 @@ final customizeFontToolbarItem = ToolbarItem( cursor: SystemMouseCursors.click, child: FontFamilyDropDown( currentFontFamily: '', + offset: const Offset(0, 12), popoverController: popoverController, onOpen: () => keepEditorFocusNotifier.value += 1, onClose: () => keepEditorFocusNotifier.value -= 1, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart index 25bffbcf26..3b13963d7f 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart @@ -60,6 +60,7 @@ class FontFamilyDropDown extends StatefulWidget { this.onFontFamilyChanged, this.child, this.popoverController, + this.offset, }); final String currentFontFamily; @@ -68,6 +69,7 @@ class FontFamilyDropDown extends StatefulWidget { final void Function(String fontFamily)? onFontFamilyChanged; final Widget? child; final PopoverController? popoverController; + final Offset? offset; @override State createState() => _FontFamilyDropDownState(); @@ -87,6 +89,7 @@ class _FontFamilyDropDownState extends State { query.value = ''; widget.onClose?.call(); }, + offset: widget.offset, child: widget.child, popupBuilder: (_) { widget.onOpen?.call(); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/theme_setting_entry_template.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/theme_setting_entry_template.dart index 368f551d1d..d86f985c18 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/theme_setting_entry_template.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/theme_setting_entry_template.dart @@ -74,6 +74,7 @@ class ThemeValueDropDown extends StatefulWidget { this.onClose, this.child, this.popoverController, + this.offset, }); final String currentValue; @@ -82,6 +83,7 @@ class ThemeValueDropDown extends StatefulWidget { final void Function()? onClose; final Widget? child; final PopoverController? popoverController; + final Offset? offset; @override State createState() => _ThemeValueDropDownState(); @@ -93,13 +95,14 @@ class _ThemeValueDropDownState extends State { return AppFlowyPopover( key: widget.popoverKey, controller: widget.popoverController, - direction: PopoverDirection.bottomWithRightAligned, + direction: PopoverDirection.bottomWithCenterAligned, popupBuilder: widget.popupBuilder, constraints: const BoxConstraints( minWidth: 80, maxWidth: 160, maxHeight: 400, ), + offset: widget.offset, onClose: widget.onClose, child: widget.child ?? FlowyTextButton( From 57b78ee3c61ca3151dd03ed07868c974fe5a64bb Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Fri, 22 Sep 2023 15:49:11 +0800 Subject: [PATCH 2/5] feat: add reset font button in toolbar --- .../document/presentation/editor_page.dart | 3 ++ .../font/customize_font_toolbar_item.dart | 4 ++ .../font_family_setting.dart | 44 +++++++++++++++++++ frontend/resources/translations/en.json | 3 ++ 4 files changed, 54 insertions(+) diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index b3b6e1350e..f86d82657a 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -137,6 +137,9 @@ class _AppFlowyEditorPageState extends State { convertibleBlockTypes.add(ToggleListBlockKeys.type); slashMenuItems = _customSlashMenuItems(); effectiveScrollController = widget.scrollController ?? ScrollController(); + + // keep the previous font style when typing new text. + AppFlowyRichTextKeys.supportSliced.add(AppFlowyRichTextKeys.fontFamily); } @override diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/font/customize_font_toolbar_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/font/customize_font_toolbar_item.dart index 76326f2704..fec3156f02 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/font/customize_font_toolbar_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/font/customize_font_toolbar_item.dart @@ -20,6 +20,7 @@ final customizeFontToolbarItem = ToolbarItem( popoverController: popoverController, onOpen: () => keepEditorFocusNotifier.value += 1, onClose: () => keepEditorFocusNotifier.value -= 1, + showResetButton: true, onFontFamilyChanged: (fontFamily) async { await popoverController.close(); try { @@ -30,6 +31,9 @@ final customizeFontToolbarItem = ToolbarItem( Log.error('Failed to set font family: $e'); } }, + onResetFont: () async => await editorState.formatDelta(selection, { + AppFlowyRichTextKeys.fontFamily: null, + }), child: const Padding( padding: EdgeInsets.symmetric(horizontal: 4.0), child: FlowySvg( diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart index 3b13963d7f..5a981f5d99 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart @@ -61,6 +61,8 @@ class FontFamilyDropDown extends StatefulWidget { this.child, this.popoverController, this.offset, + this.showResetButton = false, + this.onResetFont, }); final String currentFontFamily; @@ -70,6 +72,8 @@ class FontFamilyDropDown extends StatefulWidget { final Widget? child; final PopoverController? popoverController; final Offset? offset; + final bool showResetButton; + final VoidCallback? onResetFont; @override State createState() => _FontFamilyDropDownState(); @@ -96,6 +100,13 @@ class _FontFamilyDropDownState extends State { return CustomScrollView( shrinkWrap: true, slivers: [ + if (widget.showResetButton) + SliverPersistentHeader( + delegate: _ResetFontButton( + onPressed: widget.onResetFont, + ), + pinned: true, + ), SliverPadding( padding: const EdgeInsets.only(right: 8), sliver: SliverToBoxAdapter( @@ -191,3 +202,36 @@ class _FontFamilyDropDownState extends State { ); } } + +class _ResetFontButton extends SliverPersistentHeaderDelegate { + _ResetFontButton({ + this.onPressed, + }); + + final VoidCallback? onPressed; + + @override + Widget build( + BuildContext context, + double shrinkOffset, + bool overlapsContent, + ) { + return Padding( + padding: const EdgeInsets.only(right: 8, bottom: 8.0), + child: FlowyTextButton( + LocaleKeys.document_toolbar_resetToDefaultFont.tr(), + onPressed: onPressed, + ), + ); + } + + @override + double get maxExtent => 35; + + @override + double get minExtent => 35; + + @override + bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => + true; +} diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 0e525bc45f..0ed26d8261 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -647,6 +647,9 @@ "label": "Link to page", "tooltip": "Click to open page" } + }, + "toolbar": { + "resetToDefaultFont": "Reset to default" } }, "board": { From f762a564b5a91276d8a666383390ff2dc4c21105 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Fri, 22 Sep 2023 15:59:21 +0800 Subject: [PATCH 3/5] feat: only show text direction toolbar item when RTL is enabled --- .../document/presentation/editor_page.dart | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index f86d82657a..51401758ef 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -76,7 +76,6 @@ class _AppFlowyEditorPageState extends State { buildTextColorItem(), buildHighlightColorItem(), customizeFontToolbarItem, - ...textDirectionItems, ]; late final List slashMenuItems; @@ -151,11 +150,32 @@ class _AppFlowyEditorPageState extends State { super.dispose(); } + @override + void reassemble() { + super.reassemble(); + } + @override Widget build(BuildContext context) { final (bool autoFocus, Selection? selection) = _computeAutoFocusParameters(); + final isRTL = + context.read().state.layoutDirection == + LayoutDirection.rtlLayout; + final layoutDirection = isRTL ? TextDirection.rtl : TextDirection.ltr; + + // only show the rtl item when the layout direction is ltr. + for (final item in textDirectionItems) { + if (isRTL) { + if (toolbarItems.every((element) => element.id != item.id)) { + toolbarItems.add(item); + } + } else { + toolbarItems.removeWhere((element) => element.id == item.id); + } + } + final editorScrollController = EditorScrollController( editorState: widget.editorState, shrinkWrap: widget.shrinkWrap, @@ -181,12 +201,6 @@ class _AppFlowyEditorPageState extends State { footer: const VSpace(200), ); - final layoutDirection = - context.read().state.layoutDirection == - LayoutDirection.rtlLayout - ? TextDirection.rtl - : TextDirection.ltr; - return Center( child: FloatingToolbar( style: styleCustomizer.floatingToolbarStyleBuilder(), From f2f3506b29dcd58437e5ff4548d72d29cf933fa5 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Fri, 22 Sep 2023 16:17:17 +0800 Subject: [PATCH 4/5] fix: unable to change RTL of heading block --- .../lib/plugins/document/presentation/editor_page.dart | 5 ----- frontend/appflowy_flutter/pubspec.lock | 6 +++--- frontend/appflowy_flutter/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index 51401758ef..95e30ff5d9 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -150,11 +150,6 @@ class _AppFlowyEditorPageState extends State { super.dispose(); } - @override - void reassemble() { - super.reassemble(); - } - @override Widget build(BuildContext context) { final (bool autoFocus, Selection? selection) = diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 753244119d..90c8a3d81a 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -54,11 +54,11 @@ packages: dependency: "direct main" description: path: "." - ref: a0ff609 - resolved-ref: a0ff609cb1ac53e5d167489f43452074860dd80e + ref: "8e618465258b3de0ce5253c4fa97bacb24884e8c" + resolved-ref: "8e618465258b3de0ce5253c4fa97bacb24884e8c" url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git - version: "1.4.0" + version: "1.4.1" appflowy_popover: dependency: "direct main" description: diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 8ee6eae68a..0bf5680026 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -47,7 +47,7 @@ dependencies: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: a0ff609 + ref: 8e618465258b3de0ce5253c4fa97bacb24884e8c appflowy_popover: path: packages/appflowy_popover From 89face4f02b3f309bd78497809294d7170173c25 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Sat, 23 Sep 2023 10:55:05 +0800 Subject: [PATCH 5/5] test: add integration test for ltr/rtl mode --- .../document/document_test_runner.dart | 2 + .../document_text_direction_test.dart | 58 +++++++++++++++++++ .../integration_test/util/settings.dart | 33 +++++++++++ .../document/presentation/editor_page.dart | 23 ++++---- .../direction_setting.dart | 1 + 5 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 frontend/appflowy_flutter/integration_test/document/document_text_direction_test.dart diff --git a/frontend/appflowy_flutter/integration_test/document/document_test_runner.dart b/frontend/appflowy_flutter/integration_test/document/document_test_runner.dart index 7f517678a9..a29f8df49d 100644 --- a/frontend/appflowy_flutter/integration_test/document/document_test_runner.dart +++ b/frontend/appflowy_flutter/integration_test/document/document_test_runner.dart @@ -5,6 +5,7 @@ import 'document_codeblock_paste_test.dart' as document_codeblock_paste_test; import 'document_copy_and_paste_test.dart' as document_copy_and_paste_test; import 'document_create_and_delete_test.dart' as document_create_and_delete_test; +import 'document_text_direction_test.dart' as document_text_direction_test; import 'document_with_cover_image_test.dart' as document_with_cover_image_test; import 'document_with_database_test.dart' as document_with_database_test; import 'document_with_inline_math_equation_test.dart' @@ -29,4 +30,5 @@ void startTesting() { document_copy_and_paste_test.main(); document_codeblock_paste_test.main(); document_alignment_test.main(); + document_text_direction_test.main(); } diff --git a/frontend/appflowy_flutter/integration_test/document/document_text_direction_test.dart b/frontend/appflowy_flutter/integration_test/document/document_text_direction_test.dart new file mode 100644 index 0000000000..5499143881 --- /dev/null +++ b/frontend/appflowy_flutter/integration_test/document/document_text_direction_test.dart @@ -0,0 +1,58 @@ +import 'package:appflowy/workspace/application/appearance.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import '../util/util.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('text direction', () { + testWidgets( + '''no text direction items will be displayed in the default/LTR mode,and three text direction items will be displayed in the RTL mode.''', + (tester) async { + // combine the two tests into one to avoid the time-consuming process of initializing the app + await tester.initializeAppFlowy(); + await tester.tapGoButton(); + + final selection = Selection.single( + path: [0], + startOffset: 0, + endOffset: 1, + ); + // click the first line of the readme + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.updateSelection(selection); + await tester.pumpAndSettle(); + + // because this icons are defined in the appflowy_editor package, we can't fetch the icons by SVG data. [textDirectionItems] + final textDirectionIconNames = [ + 'toolbar/text_direction_auto', + 'toolbar/text_direction_left', + 'toolbar/text_direction_right', + ]; + // no text direction items in default/LTR mode + var button = find.byWidgetPredicate( + (widget) => + widget is SVGIconItemWidget && + textDirectionIconNames.contains(widget.iconName), + ); + expect(button, findsNothing); + + // switch to the RTL mode + await tester.switchLayoutDirectionMode(LayoutDirection.rtlLayout); + + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.updateSelection(selection); + await tester.pumpAndSettle(); + + button = find.byWidgetPredicate( + (widget) => + widget is SVGIconItemWidget && + textDirectionIconNames.contains(widget.iconName), + ); + expect(button, findsNWidgets(3)); + }); + }); +} diff --git a/frontend/appflowy_flutter/integration_test/util/settings.dart b/frontend/appflowy_flutter/integration_test/util/settings.dart index 1c6e4e919f..1fd885de0a 100644 --- a/frontend/appflowy_flutter/integration_test/util/settings.dart +++ b/frontend/appflowy_flutter/integration_test/util/settings.dart @@ -1,9 +1,11 @@ import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/workspace/application/appearance.dart'; import 'package:appflowy/workspace/application/settings/prelude.dart'; import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'base.dart'; @@ -72,4 +74,35 @@ extension AppFlowySettings on WidgetTester { await testTextInput.receiveAction(TextInputAction.done); await pumpAndSettle(); } + + // go to settings page and switch the layout direction + Future switchLayoutDirectionMode( + LayoutDirection layoutDirection, + ) async { + await openSettings(); + await openSettingsPage(SettingsPage.appearance); + + final button = find.byKey(const ValueKey('layout_direction_option_button')); + expect(button, findsOneWidget); + await tapButton(button); + + switch (layoutDirection) { + case LayoutDirection.ltrLayout: + final ltrButton = find.text( + LocaleKeys.settings_appearance_layoutDirection_ltr.tr(), + ); + await tapButton(ltrButton); + break; + case LayoutDirection.rtlLayout: + final rtlButton = find.text( + LocaleKeys.settings_appearance_layoutDirection_rtl.tr(), + ); + await tapButton(rtlButton); + break; + } + + // tap anywhere to close the settings page + await tapAt(Offset.zero); + await pumpAndSettle(); + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index 95e30ff5d9..a49263a8bf 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -160,16 +160,7 @@ class _AppFlowyEditorPageState extends State { LayoutDirection.rtlLayout; final layoutDirection = isRTL ? TextDirection.rtl : TextDirection.ltr; - // only show the rtl item when the layout direction is ltr. - for (final item in textDirectionItems) { - if (isRTL) { - if (toolbarItems.every((element) => element.id != item.id)) { - toolbarItems.add(item); - } - } else { - toolbarItems.removeWhere((element) => element.id == item.id); - } - } + _setRTLToolbarItems(isRTL); final editorScrollController = EditorScrollController( editorState: widget.editorState, @@ -475,4 +466,16 @@ class _AppFlowyEditorPageState extends State { customizeShortcuts, ); } + + void _setRTLToolbarItems(bool isRTL) { + final textDirectionItemIds = textDirectionItems.map((e) => e.id); + // clear all the text direction items + toolbarItems.removeWhere( + (item) => textDirectionItemIds.contains(item.id), + ); + // only show the rtl item when the layout direction is ltr. + if (isRTL) { + toolbarItems.addAll(textDirectionItems); + } + } } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/direction_setting.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/direction_setting.dart index 36017493f8..d96e5e338f 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/direction_setting.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/direction_setting.dart @@ -25,6 +25,7 @@ class LayoutDirectionSetting extends StatelessWidget { hint: LocaleKeys.settings_appearance_layoutDirection_hint.tr(), trailing: [ ThemeValueDropDown( + key: const ValueKey('layout_direction_option_button'), currentValue: _layoutDirectionLabelText(currentLayoutDirection), popupBuilder: (context) => Column( mainAxisSize: MainAxisSize.min,