editor_style.dart 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import 'package:appflowy/plugins/document/presentation/editor_plugins/inline_math_equation/inline_math_equation.dart';
  2. import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart';
  3. import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
  4. import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart';
  5. import 'package:appflowy/util/google_font_family_extension.dart';
  6. import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
  7. import 'package:collection/collection.dart';
  8. import 'package:flutter/material.dart';
  9. import 'package:flutter_bloc/flutter_bloc.dart';
  10. import 'package:google_fonts/google_fonts.dart';
  11. class EditorStyleCustomizer {
  12. EditorStyleCustomizer({
  13. required this.context,
  14. required this.padding,
  15. });
  16. final BuildContext context;
  17. final EdgeInsets padding;
  18. EditorStyle style() {
  19. if (PlatformExtension.isDesktopOrWeb) {
  20. return desktop();
  21. } else if (PlatformExtension.isMobile) {
  22. return mobile();
  23. }
  24. throw UnimplementedError();
  25. }
  26. EditorStyle desktop() {
  27. final theme = Theme.of(context);
  28. final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
  29. final fontFamily = context.read<DocumentAppearanceCubit>().state.fontFamily;
  30. final defaultTextDirection =
  31. context.read<DocumentAppearanceCubit>().state.defaultTextDirection;
  32. return EditorStyle.desktop(
  33. padding: padding,
  34. cursorColor: theme.colorScheme.primary,
  35. defaultTextDirection: defaultTextDirection,
  36. textStyleConfiguration: TextStyleConfiguration(
  37. text: baseTextStyle(fontFamily).copyWith(
  38. fontSize: fontSize,
  39. color: theme.colorScheme.onBackground,
  40. height: 1.5,
  41. ),
  42. bold: baseTextStyle(fontFamily, fontWeight: FontWeight.bold).copyWith(
  43. fontWeight: FontWeight.w600,
  44. ),
  45. italic: baseTextStyle(fontFamily).copyWith(
  46. fontStyle: FontStyle.italic,
  47. ),
  48. underline: baseTextStyle(fontFamily).copyWith(
  49. decoration: TextDecoration.underline,
  50. ),
  51. strikethrough: baseTextStyle(fontFamily).copyWith(
  52. decoration: TextDecoration.lineThrough,
  53. ),
  54. href: baseTextStyle(fontFamily).copyWith(
  55. color: theme.colorScheme.primary,
  56. decoration: TextDecoration.underline,
  57. ),
  58. code: GoogleFonts.arefRuqaaInk(
  59. textStyle: baseTextStyle(fontFamily).copyWith(
  60. fontSize: fontSize,
  61. fontWeight: FontWeight.normal,
  62. fontStyle: FontStyle.italic,
  63. color: Colors.red,
  64. backgroundColor: theme.colorScheme.inverseSurface.withOpacity(0.8),
  65. ),
  66. ),
  67. ),
  68. textSpanDecorator: customizeAttributeDecorator,
  69. );
  70. }
  71. EditorStyle mobile() {
  72. final theme = Theme.of(context);
  73. final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
  74. final fontFamily = context.read<DocumentAppearanceCubit>().state.fontFamily;
  75. return EditorStyle.desktop(
  76. padding: padding,
  77. cursorColor: theme.colorScheme.primary,
  78. textStyleConfiguration: TextStyleConfiguration(
  79. text: baseTextStyle(fontFamily).copyWith(
  80. fontSize: fontSize,
  81. color: theme.colorScheme.onBackground,
  82. height: 1.5,
  83. ),
  84. bold: baseTextStyle(fontFamily).copyWith(
  85. fontWeight: FontWeight.w600,
  86. ),
  87. italic: baseTextStyle(fontFamily).copyWith(fontStyle: FontStyle.italic),
  88. underline: baseTextStyle(fontFamily)
  89. .copyWith(decoration: TextDecoration.underline),
  90. strikethrough: baseTextStyle(fontFamily)
  91. .copyWith(decoration: TextDecoration.lineThrough),
  92. href: baseTextStyle(fontFamily).copyWith(
  93. color: theme.colorScheme.primary,
  94. decoration: TextDecoration.underline,
  95. ),
  96. code: GoogleFonts.arefRuqaaInk(
  97. textStyle: baseTextStyle(fontFamily).copyWith(
  98. fontSize: fontSize,
  99. fontWeight: FontWeight.normal,
  100. color: Colors.red,
  101. backgroundColor: theme.colorScheme.inverseSurface,
  102. ),
  103. ),
  104. ),
  105. );
  106. }
  107. TextStyle headingStyleBuilder(int level) {
  108. final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
  109. final fontSizes = [
  110. fontSize + 16,
  111. fontSize + 12,
  112. fontSize + 8,
  113. fontSize + 4,
  114. fontSize + 2,
  115. fontSize
  116. ];
  117. return TextStyle(
  118. fontSize: fontSizes.elementAtOrNull(level - 1) ?? fontSize,
  119. fontWeight: FontWeight.bold,
  120. );
  121. }
  122. TextStyle codeBlockStyleBuilder() {
  123. final theme = Theme.of(context);
  124. final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
  125. final fontFamily = context.read<DocumentAppearanceCubit>().state.fontFamily;
  126. return baseTextStyle(fontFamily).copyWith(
  127. fontSize: fontSize,
  128. height: 1.5,
  129. color: theme.colorScheme.onBackground,
  130. );
  131. }
  132. TextStyle outlineBlockPlaceholderStyleBuilder() {
  133. final theme = Theme.of(context);
  134. final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
  135. return TextStyle(
  136. fontFamily: 'poppins',
  137. fontSize: fontSize,
  138. height: 1.5,
  139. color: theme.colorScheme.onBackground.withOpacity(0.6),
  140. );
  141. }
  142. SelectionMenuStyle selectionMenuStyleBuilder() {
  143. final theme = Theme.of(context);
  144. return SelectionMenuStyle(
  145. selectionMenuBackgroundColor: theme.cardColor,
  146. selectionMenuItemTextColor: theme.colorScheme.onBackground,
  147. selectionMenuItemIconColor: theme.colorScheme.onBackground,
  148. selectionMenuItemSelectedIconColor: theme.colorScheme.onSurface,
  149. selectionMenuItemSelectedTextColor: theme.colorScheme.onSurface,
  150. selectionMenuItemSelectedColor: theme.hoverColor,
  151. );
  152. }
  153. InlineActionsMenuStyle inlineActionsMenuStyleBuilder() {
  154. final theme = Theme.of(context);
  155. return InlineActionsMenuStyle(
  156. backgroundColor: theme.cardColor,
  157. groupTextColor: theme.colorScheme.onBackground.withOpacity(.8),
  158. menuItemTextColor: theme.colorScheme.onBackground,
  159. menuItemSelectedColor: theme.hoverColor,
  160. menuItemSelectedTextColor: theme.colorScheme.onSurface,
  161. );
  162. }
  163. FloatingToolbarStyle floatingToolbarStyleBuilder() {
  164. final theme = Theme.of(context);
  165. return FloatingToolbarStyle(
  166. backgroundColor: theme.colorScheme.onTertiary,
  167. );
  168. }
  169. TextStyle baseTextStyle(
  170. String fontFamily, {
  171. FontWeight? fontWeight,
  172. }) {
  173. try {
  174. return GoogleFonts.getFont(
  175. fontFamily,
  176. fontWeight: fontWeight,
  177. );
  178. } on Exception {
  179. return GoogleFonts.getFont('Poppins');
  180. }
  181. }
  182. InlineSpan customizeAttributeDecorator(
  183. BuildContext context,
  184. Node node,
  185. int index,
  186. TextInsert text,
  187. TextSpan textSpan,
  188. ) {
  189. final attributes = text.attributes;
  190. if (attributes == null) {
  191. return textSpan;
  192. }
  193. // try to refresh font here.
  194. if (attributes.fontFamily != null) {
  195. try {
  196. GoogleFonts.getFont(attributes.fontFamily!.parseFontFamilyName());
  197. } catch (e) {
  198. // ignore
  199. }
  200. }
  201. // Inline Mentions (Page Reference, Date, Reminder, etc.)
  202. final mention =
  203. attributes[MentionBlockKeys.mention] as Map<String, dynamic>?;
  204. if (mention != null) {
  205. final type = mention[MentionBlockKeys.type];
  206. return WidgetSpan(
  207. alignment: PlaceholderAlignment.middle,
  208. child: MentionBlock(
  209. key: ValueKey(
  210. switch (type) {
  211. MentionType.page => mention[MentionBlockKeys.pageId],
  212. MentionType.date ||
  213. MentionType.reminder =>
  214. mention[MentionBlockKeys.date],
  215. _ => MentionBlockKeys.mention,
  216. },
  217. ),
  218. node: node,
  219. index: index,
  220. mention: mention,
  221. ),
  222. );
  223. }
  224. // customize the inline math equation block
  225. final formula = attributes[InlineMathEquationKeys.formula] as String?;
  226. if (formula != null) {
  227. return WidgetSpan(
  228. alignment: PlaceholderAlignment.middle,
  229. child: InlineMathEquation(
  230. node: node,
  231. index: index,
  232. formula: formula,
  233. textStyle: style().textStyleConfiguration.text,
  234. ),
  235. );
  236. }
  237. return defaultTextSpanDecoratorForAttribute(
  238. context,
  239. node,
  240. index,
  241. text,
  242. textSpan,
  243. );
  244. }
  245. }