1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.material
18 
19 import androidx.compose.runtime.Immutable
20 import androidx.compose.runtime.staticCompositionLocalOf
21 import androidx.compose.ui.text.PlatformTextStyle
22 import androidx.compose.ui.text.TextStyle
23 import androidx.compose.ui.text.font.FontFamily
24 import androidx.compose.ui.text.font.FontWeight
25 import androidx.compose.ui.text.style.LineHeightStyle
26 import androidx.compose.ui.unit.sp
27 
28 /**
29  * [Material Design type
30  * scale](https://material.io/design/typography/the-type-system.html#type-scale)
31  *
32  * The Material Design type scale includes a range of contrasting styles that support the needs of
33  * your product and its content.
34  *
35  * The type scale is a combination of thirteen styles that are supported by the type system. It
36  * contains reusable categories of text, each with an intended application and meaning.
37  *
38  * ![Typography
39  * image](https://developer.android.com/images/reference/androidx/compose/material/typography.png)
40  *
41  * @property h1 h1 is the largest headline, reserved for short, important text or numerals. For
42  *   headlines, you can choose an expressive font, such as a display, handwritten, or script style.
43  *   These unconventional font designs have details and intricacy that help attract the eye.
44  * @property h2 h2 is the second largest headline, reserved for short, important text or numerals.
45  *   For headlines, you can choose an expressive font, such as a display, handwritten, or script
46  *   style. These unconventional font designs have details and intricacy that help attract the eye.
47  * @property h3 h3 is the third largest headline, reserved for short, important text or numerals.
48  *   For headlines, you can choose an expressive font, such as a display, handwritten, or script
49  *   style. These unconventional font designs have details and intricacy that help attract the eye.
50  * @property h4 h4 is the fourth largest headline, reserved for short, important text or numerals.
51  *   For headlines, you can choose an expressive font, such as a display, handwritten, or script
52  *   style. These unconventional font designs have details and intricacy that help attract the eye.
53  * @property h5 h5 is the fifth largest headline, reserved for short, important text or numerals.
54  *   For headlines, you can choose an expressive font, such as a display, handwritten, or script
55  *   style. These unconventional font designs have details and intricacy that help attract the eye.
56  * @property h6 h6 is the sixth largest headline, reserved for short, important text or numerals.
57  *   For headlines, you can choose an expressive font, such as a display, handwritten, or script
58  *   style. These unconventional font designs have details and intricacy that help attract the eye.
59  * @property subtitle1 subtitle1 is the largest subtitle, and is typically reserved for
60  *   medium-emphasis text that is shorter in length. Serif or sans serif typefaces work well for
61  *   subtitles.
62  * @property subtitle2 subtitle2 is the smallest subtitle, and is typically reserved for
63  *   medium-emphasis text that is shorter in length. Serif or sans serif typefaces work well for
64  *   subtitles.
65  * @property body1 body1 is the largest body, and is typically used for long-form writing as it
66  *   works well for small text sizes. For longer sections of text, a serif or sans serif typeface is
67  *   recommended.
68  * @property body2 body2 is the smallest body, and is typically used for long-form writing as it
69  *   works well for small text sizes. For longer sections of text, a serif or sans serif typeface is
70  *   recommended.
71  * @property button button text is a call to action used in different types of buttons (such as
72  *   text, outlined and contained buttons) and in tabs, dialogs, and cards. Button text is typically
73  *   sans serif, using all caps text.
74  * @property caption caption is one of the smallest font sizes. It is used sparingly to annotate
75  *   imagery or to introduce a headline.
76  * @property overline overline is one of the smallest font sizes. It is used sparingly to annotate
77  *   imagery or to introduce a headline.
78  */
79 @Immutable
80 class Typography
81 internal constructor(
82     val h1: TextStyle,
83     val h2: TextStyle,
84     val h3: TextStyle,
85     val h4: TextStyle,
86     val h5: TextStyle,
87     val h6: TextStyle,
88     val subtitle1: TextStyle,
89     val subtitle2: TextStyle,
90     val body1: TextStyle,
91     val body2: TextStyle,
92     val button: TextStyle,
93     val caption: TextStyle,
94     val overline: TextStyle
95 ) {
96     /**
97      * Constructor to create a [Typography]. For information on the types of style defined in this
98      * constructor, see the property documentation for [Typography].
99      *
100      * @param defaultFontFamily the default [FontFamily] to be used for [TextStyle]s provided in
101      *   this constructor. This default will be used if the [FontFamily] on the [TextStyle] is
102      *   `null`.
103      * @param h1 h1 is the largest headline, reserved for short, important text or numerals.
104      * @param h2 h2 is the second largest headline, reserved for short, important text or numerals.
105      * @param h3 h3 is the third largest headline, reserved for short, important text or numerals.
106      * @param h4 h4 is the fourth largest headline, reserved for short, important text or numerals.
107      * @param h5 h5 is the fifth largest headline, reserved for short, important text or numerals.
108      * @param h6 h6 is the sixth largest headline, reserved for short, important text or numerals.
109      * @param subtitle1 subtitle1 is the largest subtitle, and is typically reserved for
110      *   medium-emphasis text that is shorter in length.
111      * @param subtitle2 subtitle2 is the smallest subtitle, and is typically reserved for
112      *   medium-emphasis text that is shorter in length.
113      * @param body1 body1 is the largest body, and is typically used for long-form writing as it
114      *   works well for small text sizes.
115      * @param body2 body2 is the smallest body, and is typically used for long-form writing as it
116      *   works well for small text sizes.
117      * @param button button text is a call to action used in different types of buttons (such as
118      *   text, outlined and contained buttons) and in tabs, dialogs, and cards.
119      * @param caption caption is one of the smallest font sizes. It is used sparingly to annotate
120      *   imagery or to introduce a headline.
121      * @param overline overline is one of the smallest font sizes. It is used sparingly to annotate
122      *   imagery or to introduce a headline.
123      */
124     constructor(
125         defaultFontFamily: FontFamily = FontFamily.Default,
126         h1: TextStyle =
127             DefaultTextStyle.copy(
128                 fontWeight = FontWeight.Light,
129                 fontSize = 96.sp,
130                 lineHeight = 112.sp,
131                 letterSpacing = (-1.5).sp
132             ),
133         h2: TextStyle =
134             DefaultTextStyle.copy(
135                 fontWeight = FontWeight.Light,
136                 fontSize = 60.sp,
137                 lineHeight = 72.sp,
138                 letterSpacing = (-0.5).sp
139             ),
140         h3: TextStyle =
141             DefaultTextStyle.copy(
142                 fontWeight = FontWeight.Normal,
143                 fontSize = 48.sp,
144                 lineHeight = 56.sp,
145                 letterSpacing = 0.sp
146             ),
147         h4: TextStyle =
148             DefaultTextStyle.copy(
149                 fontWeight = FontWeight.Normal,
150                 fontSize = 34.sp,
151                 lineHeight = 36.sp,
152                 letterSpacing = 0.25.sp
153             ),
154         h5: TextStyle =
155             DefaultTextStyle.copy(
156                 fontWeight = FontWeight.Normal,
157                 fontSize = 24.sp,
158                 lineHeight = 24.sp,
159                 letterSpacing = 0.sp
160             ),
161         h6: TextStyle =
162             DefaultTextStyle.copy(
163                 fontWeight = FontWeight.Medium,
164                 fontSize = 20.sp,
165                 lineHeight = 24.sp,
166                 letterSpacing = 0.15.sp
167             ),
168         subtitle1: TextStyle =
169             DefaultTextStyle.copy(
170                 fontWeight = FontWeight.Normal,
171                 fontSize = 16.sp,
172                 lineHeight = 24.sp,
173                 letterSpacing = 0.15.sp
174             ),
175         subtitle2: TextStyle =
176             DefaultTextStyle.copy(
177                 fontWeight = FontWeight.Medium,
178                 fontSize = 14.sp,
179                 lineHeight = 24.sp,
180                 letterSpacing = 0.1.sp
181             ),
182         body1: TextStyle =
183             DefaultTextStyle.copy(
184                 fontWeight = FontWeight.Normal,
185                 fontSize = 16.sp,
186                 lineHeight = 24.sp,
187                 letterSpacing = 0.5.sp
188             ),
189         body2: TextStyle =
190             DefaultTextStyle.copy(
191                 fontWeight = FontWeight.Normal,
192                 fontSize = 14.sp,
193                 lineHeight = 20.sp,
194                 letterSpacing = 0.25.sp
195             ),
196         button: TextStyle =
197             DefaultTextStyle.copy(
198                 fontWeight = FontWeight.Medium,
199                 fontSize = 14.sp,
200                 lineHeight = 16.sp,
201                 letterSpacing = 1.25.sp
202             ),
203         caption: TextStyle =
204             DefaultTextStyle.copy(
205                 fontWeight = FontWeight.Normal,
206                 fontSize = 12.sp,
207                 lineHeight = 16.sp,
208                 letterSpacing = 0.4.sp
209             ),
210         overline: TextStyle =
211             DefaultTextStyle.copy(
212                 fontWeight = FontWeight.Normal,
213                 fontSize = 10.sp,
214                 lineHeight = 16.sp,
215                 letterSpacing = 1.5.sp
216             )
217     ) : this(
218         h1 = h1.withDefaultFontFamily(defaultFontFamily),
219         h2 = h2.withDefaultFontFamily(defaultFontFamily),
220         h3 = h3.withDefaultFontFamily(defaultFontFamily),
221         h4 = h4.withDefaultFontFamily(defaultFontFamily),
222         h5 = h5.withDefaultFontFamily(defaultFontFamily),
223         h6 = h6.withDefaultFontFamily(defaultFontFamily),
224         subtitle1 = subtitle1.withDefaultFontFamily(defaultFontFamily),
225         subtitle2 = subtitle2.withDefaultFontFamily(defaultFontFamily),
226         body1 = body1.withDefaultFontFamily(defaultFontFamily),
227         body2 = body2.withDefaultFontFamily(defaultFontFamily),
228         button = button.withDefaultFontFamily(defaultFontFamily),
229         caption = caption.withDefaultFontFamily(defaultFontFamily),
230         overline = overline.withDefaultFontFamily(defaultFontFamily)
231     )
232 
233     /** Returns a copy of this Typography, optionally overriding some of the values. */
copynull234     fun copy(
235         h1: TextStyle = this.h1,
236         h2: TextStyle = this.h2,
237         h3: TextStyle = this.h3,
238         h4: TextStyle = this.h4,
239         h5: TextStyle = this.h5,
240         h6: TextStyle = this.h6,
241         subtitle1: TextStyle = this.subtitle1,
242         subtitle2: TextStyle = this.subtitle2,
243         body1: TextStyle = this.body1,
244         body2: TextStyle = this.body2,
245         button: TextStyle = this.button,
246         caption: TextStyle = this.caption,
247         overline: TextStyle = this.overline
248     ): Typography =
249         Typography(
250             h1 = h1,
251             h2 = h2,
252             h3 = h3,
253             h4 = h4,
254             h5 = h5,
255             h6 = h6,
256             subtitle1 = subtitle1,
257             subtitle2 = subtitle2,
258             body1 = body1,
259             body2 = body2,
260             button = button,
261             caption = caption,
262             overline = overline
263         )
264 
265     override fun equals(other: Any?): Boolean {
266         if (this === other) return true
267         if (other !is Typography) return false
268 
269         if (h1 != other.h1) return false
270         if (h2 != other.h2) return false
271         if (h3 != other.h3) return false
272         if (h4 != other.h4) return false
273         if (h5 != other.h5) return false
274         if (h6 != other.h6) return false
275         if (subtitle1 != other.subtitle1) return false
276         if (subtitle2 != other.subtitle2) return false
277         if (body1 != other.body1) return false
278         if (body2 != other.body2) return false
279         if (button != other.button) return false
280         if (caption != other.caption) return false
281         if (overline != other.overline) return false
282 
283         return true
284     }
285 
hashCodenull286     override fun hashCode(): Int {
287         var result = h1.hashCode()
288         result = 31 * result + h2.hashCode()
289         result = 31 * result + h3.hashCode()
290         result = 31 * result + h4.hashCode()
291         result = 31 * result + h5.hashCode()
292         result = 31 * result + h6.hashCode()
293         result = 31 * result + subtitle1.hashCode()
294         result = 31 * result + subtitle2.hashCode()
295         result = 31 * result + body1.hashCode()
296         result = 31 * result + body2.hashCode()
297         result = 31 * result + button.hashCode()
298         result = 31 * result + caption.hashCode()
299         result = 31 * result + overline.hashCode()
300         return result
301     }
302 
toStringnull303     override fun toString(): String {
304         return "Typography(h1=$h1, h2=$h2, h3=$h3, h4=$h4, h5=$h5, h6=$h6, " +
305             "subtitle1=$subtitle1, subtitle2=$subtitle2, body1=$body1, " +
306             "body2=$body2, button=$button, caption=$caption, overline=$overline)"
307     }
308 }
309 
310 /**
311  * @return [this] if there is a [FontFamily] defined, otherwise copies [this] with [default] as the
312  *   [FontFamily].
313  */
TextStylenull314 private fun TextStyle.withDefaultFontFamily(default: FontFamily): TextStyle {
315     return if (fontFamily != null) this else copy(fontFamily = default)
316 }
317 
318 internal val DefaultLineHeightStyle =
319     LineHeightStyle(
320         alignment = LineHeightStyle.Alignment.Center,
321         trim = LineHeightStyle.Trim.None,
322     )
323 
324 internal val DefaultTextStyle =
325     TextStyle.Default.copy(
326         platformStyle = defaultPlatformTextStyle(),
327         lineHeightStyle = DefaultLineHeightStyle,
328     )
329 
330 /** Returns Default [PlatformTextStyle]. */
defaultPlatformTextStylenull331 internal expect fun defaultPlatformTextStyle(): PlatformTextStyle?
332 
333 /**
334  * This CompositionLocal holds on to the current definition of typography for this application as
335  * described by the Material spec. You can read the values in it when creating custom components
336  * that want to use Material types, as well as override the values when you want to re-style a part
337  * of your hierarchy. Material components related to text such as [Button] will use this
338  * CompositionLocal to set values with which to style children text components.
339  *
340  * To access values within this CompositionLocal, use [MaterialTheme.typography].
341  */
342 internal val LocalTypography = staticCompositionLocalOf { Typography() }
343