zefyr

__theme.dart 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // Copyright (c) 2018, the Zefyr project authors. Please see the AUTHORS file
  2. // for details. All rights reserved. Use of this source code is governed by a
  3. // BSD-style license that can be found in the LICENSE file.
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/widgets.dart';
  6. import 'package:meta/meta.dart';
  7. /// Applies a Zefyr editor theme to descendant widgets.
  8. ///
  9. /// Describes colors and typographic styles for an editor.
  10. ///
  11. /// Descendant widgets obtain the current theme's [ZefyrThemeData] object using
  12. /// [ZefyrTheme.of].
  13. ///
  14. /// See also:
  15. ///
  16. /// * [ZefyrThemeData], which describes actual configuration of a theme.
  17. class ZefyrTheme extends InheritedWidget {
  18. final ZefyrThemeData data;
  19. /// Applies the given theme [data] to [child].
  20. ///
  21. /// The [data] and [child] arguments must not be null.
  22. ZefyrTheme({
  23. Key key,
  24. @required this.data,
  25. @required Widget child,
  26. }) : assert(data != null),
  27. assert(child != null),
  28. super(key: key, child: child);
  29. @override
  30. bool updateShouldNotify(ZefyrTheme oldWidget) {
  31. return data != oldWidget.data;
  32. }
  33. /// The data from the closest [ZefyrTheme] instance that encloses the given
  34. /// context.
  35. ///
  36. /// Returns `null` if there is no [ZefyrTheme] in the given build context
  37. /// and [nullOk] is set to `true`. If [nullOk] is set to `false` (default)
  38. /// then this method asserts.
  39. static ZefyrThemeData of(BuildContext context, {bool nullOk = false}) {
  40. final ZefyrTheme widget =
  41. context.dependOnInheritedWidgetOfExactType<ZefyrTheme>();
  42. if (widget == null && nullOk) return null;
  43. assert(widget != null,
  44. '$ZefyrTheme.of() called with a context that does not contain a ZefyrEditor.');
  45. return widget.data;
  46. }
  47. }
  48. /// Holds colors and typography styles for [ZefyrEditor].
  49. class ZefyrThemeData {
  50. final TextStyle boldStyle;
  51. final TextStyle italicStyle;
  52. final TextStyle linkStyle;
  53. final StyleTheme paragraphTheme;
  54. final HeadingTheme headingTheme;
  55. final BlockTheme blockTheme;
  56. final Color selectionColor;
  57. final Color cursorColor;
  58. /// Size of indentation for blocks.
  59. final double indentSize;
  60. final ZefyrToolbarTheme toolbarTheme;
  61. factory ZefyrThemeData.fallback(BuildContext context) {
  62. final ThemeData themeData = Theme.of(context);
  63. final defaultStyle = DefaultTextStyle.of(context);
  64. final paragraphStyle = defaultStyle.style.copyWith(
  65. fontSize: 16.0,
  66. height: 1.3,
  67. );
  68. const padding = EdgeInsets.symmetric(vertical: 8.0);
  69. final boldStyle = TextStyle(fontWeight: FontWeight.bold);
  70. final italicStyle = TextStyle(fontStyle: FontStyle.italic);
  71. final linkStyle = TextStyle(
  72. color: themeData.accentColor, decoration: TextDecoration.underline);
  73. return ZefyrThemeData(
  74. boldStyle: boldStyle,
  75. italicStyle: italicStyle,
  76. linkStyle: linkStyle,
  77. paragraphTheme: StyleTheme(textStyle: paragraphStyle, padding: padding),
  78. headingTheme: HeadingTheme.fallback(context),
  79. blockTheme: BlockTheme.fallback(context),
  80. selectionColor: themeData.textSelectionColor,
  81. cursorColor: themeData.cursorColor,
  82. indentSize: 16.0,
  83. toolbarTheme: ZefyrToolbarTheme.fallback(context),
  84. );
  85. }
  86. const ZefyrThemeData({
  87. this.boldStyle,
  88. this.italicStyle,
  89. this.linkStyle,
  90. this.paragraphTheme,
  91. this.headingTheme,
  92. this.blockTheme,
  93. this.selectionColor,
  94. this.cursorColor,
  95. this.indentSize,
  96. this.toolbarTheme,
  97. });
  98. ZefyrThemeData copyWith({
  99. TextStyle textStyle,
  100. TextStyle boldStyle,
  101. TextStyle italicStyle,
  102. TextStyle linkStyle,
  103. StyleTheme paragraphTheme,
  104. HeadingTheme headingTheme,
  105. BlockTheme blockTheme,
  106. Color selectionColor,
  107. Color cursorColor,
  108. double indentSize,
  109. ZefyrToolbarTheme toolbarTheme,
  110. }) {
  111. return ZefyrThemeData(
  112. boldStyle: boldStyle ?? this.boldStyle,
  113. italicStyle: italicStyle ?? this.italicStyle,
  114. linkStyle: linkStyle ?? this.linkStyle,
  115. paragraphTheme: paragraphTheme ?? this.paragraphTheme,
  116. headingTheme: headingTheme ?? this.headingTheme,
  117. blockTheme: blockTheme ?? this.blockTheme,
  118. selectionColor: selectionColor ?? this.selectionColor,
  119. cursorColor: cursorColor ?? this.cursorColor,
  120. indentSize: indentSize ?? this.indentSize,
  121. toolbarTheme: toolbarTheme ?? this.toolbarTheme,
  122. );
  123. }
  124. ZefyrThemeData merge(ZefyrThemeData other) {
  125. return copyWith(
  126. boldStyle: other.boldStyle,
  127. italicStyle: other.italicStyle,
  128. linkStyle: other.linkStyle,
  129. paragraphTheme: other.paragraphTheme,
  130. headingTheme: other.headingTheme,
  131. blockTheme: other.blockTheme,
  132. selectionColor: other.selectionColor,
  133. cursorColor: other.cursorColor,
  134. indentSize: other.indentSize,
  135. toolbarTheme: other.toolbarTheme,
  136. );
  137. }
  138. }
  139. /// Theme for heading-styled lines of text.
  140. class HeadingTheme {
  141. /// Style theme for level 1 headings.
  142. final StyleTheme level1;
  143. /// Style theme for level 2 headings.
  144. final StyleTheme level2;
  145. /// Style theme for level 3 headings.
  146. final StyleTheme level3;
  147. HeadingTheme({
  148. @required this.level1,
  149. @required this.level2,
  150. @required this.level3,
  151. });
  152. /// Creates fallback theme for headings.
  153. factory HeadingTheme.fallback(BuildContext context) {
  154. final defaultStyle = DefaultTextStyle.of(context);
  155. return HeadingTheme(
  156. level1: StyleTheme(
  157. textStyle: defaultStyle.style.copyWith(
  158. fontSize: 34.0,
  159. color: defaultStyle.style.color.withOpacity(0.70),
  160. height: 1.15,
  161. fontWeight: FontWeight.w300,
  162. ),
  163. padding: EdgeInsets.only(top: 16.0, bottom: 0.0),
  164. ),
  165. level2: StyleTheme(
  166. textStyle: TextStyle(
  167. fontSize: 24.0,
  168. color: defaultStyle.style.color.withOpacity(0.70),
  169. height: 1.15,
  170. fontWeight: FontWeight.normal,
  171. ),
  172. padding: EdgeInsets.only(bottom: 0.0, top: 8.0),
  173. ),
  174. level3: StyleTheme(
  175. textStyle: TextStyle(
  176. fontSize: 20.0,
  177. color: defaultStyle.style.color.withOpacity(0.70),
  178. height: 1.25,
  179. fontWeight: FontWeight.w500,
  180. ),
  181. padding: EdgeInsets.only(bottom: 0.0, top: 8.0),
  182. ),
  183. );
  184. }
  185. }
  186. /// Theme for a block of lines in a document.
  187. class BlockTheme {
  188. /// Style theme for bullet lists.
  189. final StyleTheme bulletList;
  190. /// Style theme for number lists.
  191. final StyleTheme numberList;
  192. /// Style theme for code snippets.
  193. final StyleTheme code;
  194. /// Style theme for quotes.
  195. final StyleTheme quote;
  196. BlockTheme({
  197. @required this.bulletList,
  198. @required this.numberList,
  199. @required this.quote,
  200. @required this.code,
  201. });
  202. /// Creates fallback theme for blocks.
  203. factory BlockTheme.fallback(BuildContext context) {
  204. final themeData = Theme.of(context);
  205. final defaultTextStyle = DefaultTextStyle.of(context);
  206. final padding = const EdgeInsets.symmetric(vertical: 8.0);
  207. String fontFamily;
  208. switch (themeData.platform) {
  209. case TargetPlatform.iOS:
  210. case TargetPlatform.macOS:
  211. fontFamily = 'Menlo';
  212. break;
  213. case TargetPlatform.android:
  214. case TargetPlatform.fuchsia:
  215. fontFamily = 'Roboto Mono';
  216. break;
  217. }
  218. return BlockTheme(
  219. bulletList: StyleTheme(padding: padding),
  220. numberList: StyleTheme(padding: padding),
  221. quote: StyleTheme(
  222. textStyle: TextStyle(
  223. color: defaultTextStyle.style.color.withOpacity(0.6),
  224. ),
  225. padding: padding,
  226. ),
  227. code: StyleTheme(
  228. textStyle: TextStyle(
  229. color: defaultTextStyle.style.color.withOpacity(0.8),
  230. fontFamily: fontFamily,
  231. fontSize: 14.0,
  232. height: 1.25,
  233. ),
  234. padding: padding,
  235. ),
  236. );
  237. }
  238. }
  239. /// Theme for a specific attribute style.
  240. ///
  241. /// Used in [HeadingTheme] and [BlockTheme], as well as in
  242. /// [ZefyrThemeData.paragraphTheme].
  243. class StyleTheme {
  244. /// Text style of this theme.
  245. final TextStyle textStyle;
  246. /// Padding to apply around lines of text.
  247. final EdgeInsets padding;
  248. /// Creates a new [StyleTheme].
  249. StyleTheme({
  250. this.textStyle,
  251. this.padding,
  252. });
  253. }
  254. /// Defines styles and colors for [ZefyrToolbar].
  255. class ZefyrToolbarTheme {
  256. /// The background color of toolbar.
  257. final Color backgroundColor;
  258. /// Color of buttons in toggled state.
  259. final Color primaryColor;
  260. /// Color of button icons.
  261. final Color iconColor;
  262. /// Color of button icons in disabled state.
  263. final Color disabledIconColor;
  264. /// Creates fallback theme for editor toolbars.
  265. factory ZefyrToolbarTheme.fallback(BuildContext context) {
  266. final theme = Theme.of(context);
  267. return ZefyrToolbarTheme._(
  268. backgroundColor: theme.brightness == Brightness.light ? Color(0xFFFFFFFF) : Color(0xFF282828),
  269. primaryColor: theme.brightness == Brightness.light
  270. ? Colors.grey.shade400
  271. : Colors.grey.shade900,
  272. iconColor: theme.primaryIconTheme.color,
  273. disabledIconColor: theme.disabledColor,
  274. );
  275. }
  276. ZefyrToolbarTheme._({
  277. @required this.backgroundColor,
  278. @required this.primaryColor,
  279. @required this.iconColor,
  280. @required this.disabledIconColor,
  281. });
  282. ZefyrToolbarTheme copyWith({
  283. Color color,
  284. Color toggleColor,
  285. Color iconColor,
  286. Color disabledIconColor,
  287. }) {
  288. return ZefyrToolbarTheme._(
  289. backgroundColor: color ?? this.backgroundColor,
  290. primaryColor: toggleColor ?? this.primaryColor,
  291. iconColor: iconColor ?? this.iconColor,
  292. disabledIconColor: disabledIconColor ?? this.disabledIconColor,
  293. );
  294. }
  295. }