editor_style.dart 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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.robotoMono(
  59. textStyle: baseTextStyle(fontFamily).copyWith(
  60. fontSize: fontSize,
  61. fontWeight: FontWeight.normal,
  62. color: Colors.red,
  63. backgroundColor: theme.colorScheme.inverseSurface,
  64. ),
  65. ),
  66. ),
  67. textSpanDecorator: customizeAttributeDecorator,
  68. );
  69. }
  70. EditorStyle mobile() {
  71. final theme = Theme.of(context);
  72. final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
  73. final fontFamily = context.read<DocumentAppearanceCubit>().state.fontFamily;
  74. return EditorStyle.desktop(
  75. padding: padding,
  76. cursorColor: theme.colorScheme.primary,
  77. textStyleConfiguration: TextStyleConfiguration(
  78. text: baseTextStyle(fontFamily).copyWith(
  79. fontSize: fontSize,
  80. color: theme.colorScheme.onBackground,
  81. height: 1.5,
  82. ),
  83. bold: baseTextStyle(fontFamily).copyWith(
  84. fontWeight: FontWeight.w600,
  85. ),
  86. italic: baseTextStyle(fontFamily).copyWith(fontStyle: FontStyle.italic),
  87. underline: baseTextStyle(fontFamily)
  88. .copyWith(decoration: TextDecoration.underline),
  89. strikethrough: baseTextStyle(fontFamily)
  90. .copyWith(decoration: TextDecoration.lineThrough),
  91. href: baseTextStyle(fontFamily).copyWith(
  92. color: theme.colorScheme.primary,
  93. decoration: TextDecoration.underline,
  94. ),
  95. code: GoogleFonts.robotoMono(
  96. textStyle: baseTextStyle(fontFamily).copyWith(
  97. fontSize: fontSize,
  98. fontWeight: FontWeight.normal,
  99. color: Colors.red,
  100. backgroundColor: theme.colorScheme.inverseSurface,
  101. ),
  102. ),
  103. ),
  104. );
  105. }
  106. TextStyle headingStyleBuilder(int level) {
  107. final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
  108. final fontSizes = [
  109. fontSize + 16,
  110. fontSize + 12,
  111. fontSize + 8,
  112. fontSize + 4,
  113. fontSize + 2,
  114. fontSize
  115. ];
  116. return TextStyle(
  117. fontSize: fontSizes.elementAtOrNull(level - 1) ?? fontSize,
  118. fontWeight: FontWeight.bold,
  119. );
  120. }
  121. TextStyle codeBlockStyleBuilder() {
  122. final theme = Theme.of(context);
  123. final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
  124. final fontFamily = context.read<DocumentAppearanceCubit>().state.fontFamily;
  125. return baseTextStyle(fontFamily).copyWith(
  126. fontSize: fontSize,
  127. height: 1.5,
  128. color: theme.colorScheme.onBackground,
  129. );
  130. }
  131. TextStyle outlineBlockPlaceholderStyleBuilder() {
  132. final theme = Theme.of(context);
  133. final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
  134. return TextStyle(
  135. fontFamily: 'poppins',
  136. fontSize: fontSize,
  137. height: 1.5,
  138. color: theme.colorScheme.onBackground.withOpacity(0.6),
  139. );
  140. }
  141. SelectionMenuStyle selectionMenuStyleBuilder() {
  142. final theme = Theme.of(context);
  143. return SelectionMenuStyle(
  144. selectionMenuBackgroundColor: theme.cardColor,
  145. selectionMenuItemTextColor: theme.colorScheme.onBackground,
  146. selectionMenuItemIconColor: theme.colorScheme.onBackground,
  147. selectionMenuItemSelectedIconColor: theme.colorScheme.onSurface,
  148. selectionMenuItemSelectedTextColor: theme.colorScheme.onSurface,
  149. selectionMenuItemSelectedColor: theme.hoverColor,
  150. );
  151. }
  152. InlineActionsMenuStyle inlineActionsMenuStyleBuilder() {
  153. final theme = Theme.of(context);
  154. return InlineActionsMenuStyle(
  155. backgroundColor: theme.cardColor,
  156. groupTextColor: theme.colorScheme.onBackground.withOpacity(.8),
  157. menuItemTextColor: theme.colorScheme.onBackground,
  158. menuItemSelectedColor: theme.hoverColor,
  159. menuItemSelectedTextColor: theme.colorScheme.onSurface,
  160. );
  161. }
  162. FloatingToolbarStyle floatingToolbarStyleBuilder() {
  163. final theme = Theme.of(context);
  164. return FloatingToolbarStyle(
  165. backgroundColor: theme.colorScheme.onTertiary,
  166. );
  167. }
  168. TextStyle baseTextStyle(
  169. String fontFamily, {
  170. FontWeight? fontWeight,
  171. }) {
  172. try {
  173. return GoogleFonts.getFont(
  174. fontFamily,
  175. fontWeight: fontWeight,
  176. );
  177. } on Exception {
  178. return GoogleFonts.getFont('Poppins');
  179. }
  180. }
  181. InlineSpan customizeAttributeDecorator(
  182. BuildContext context,
  183. Node node,
  184. int index,
  185. TextInsert text,
  186. TextSpan textSpan,
  187. ) {
  188. final attributes = text.attributes;
  189. if (attributes == null) {
  190. return textSpan;
  191. }
  192. // try to refresh font here.
  193. if (attributes.fontFamily != null) {
  194. try {
  195. GoogleFonts.getFont(attributes.fontFamily!.parseFontFamilyName());
  196. } catch (e) {
  197. // ignore
  198. }
  199. }
  200. // Inline Mentions (Page Reference, Date, Reminder, etc.)
  201. final mention =
  202. attributes[MentionBlockKeys.mention] as Map<String, dynamic>?;
  203. if (mention != null) {
  204. final type = mention[MentionBlockKeys.type];
  205. return WidgetSpan(
  206. alignment: PlaceholderAlignment.middle,
  207. child: MentionBlock(
  208. key: ValueKey(
  209. switch (type) {
  210. MentionType.page => mention[MentionBlockKeys.pageId],
  211. MentionType.date ||
  212. MentionType.reminder =>
  213. mention[MentionBlockKeys.date],
  214. _ => MentionBlockKeys.mention,
  215. },
  216. ),
  217. node: node,
  218. index: index,
  219. mention: mention,
  220. ),
  221. );
  222. }
  223. // customize the inline math equation block
  224. final formula = attributes[InlineMathEquationKeys.formula] as String?;
  225. if (formula != null) {
  226. return WidgetSpan(
  227. alignment: PlaceholderAlignment.middle,
  228. child: InlineMathEquation(
  229. node: node,
  230. index: index,
  231. formula: formula,
  232. textStyle: style().textStyleConfiguration.text,
  233. ),
  234. );
  235. }
  236. return defaultTextSpanDecoratorForAttribute(
  237. context,
  238. node,
  239. index,
  240. text,
  241. textSpan,
  242. );
  243. }
  244. }