1 /*
2  * Copyright 2023 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.foundation.text.modifiers
18 
19 import androidx.compose.foundation.text.DefaultMinLines
20 import androidx.compose.foundation.text.TextAutoSize
21 import androidx.compose.ui.geometry.Rect
22 import androidx.compose.ui.graphics.ColorProducer
23 import androidx.compose.ui.node.ModifierNodeElement
24 import androidx.compose.ui.platform.InspectorInfo
25 import androidx.compose.ui.text.AnnotatedString
26 import androidx.compose.ui.text.Placeholder
27 import androidx.compose.ui.text.TextLayoutResult
28 import androidx.compose.ui.text.TextStyle
29 import androidx.compose.ui.text.font.FontFamily
30 import androidx.compose.ui.text.style.TextOverflow
31 
32 /**
33  * Modifier element for any Text with [AnnotatedString] or [onTextLayout] parameters
34  *
35  * This is slower than [TextAnnotatedStringElement]
36  */
37 internal class TextAnnotatedStringElement(
38     private val text: AnnotatedString,
39     private val style: TextStyle,
40     private val fontFamilyResolver: FontFamily.Resolver,
41     private val onTextLayout: ((TextLayoutResult) -> Unit)? = null,
42     private val overflow: TextOverflow = TextOverflow.Clip,
43     private val softWrap: Boolean = true,
44     private val maxLines: Int = Int.MAX_VALUE,
45     private val minLines: Int = DefaultMinLines,
46     private val placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
47     private val onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
48     private val selectionController: SelectionController? = null,
49     private val color: ColorProducer? = null,
50     private val autoSize: TextAutoSize? = null,
51     private val onShowTranslation: ((TextAnnotatedStringNode.TextSubstitutionValue) -> Unit)? = null
52 ) : ModifierNodeElement<TextAnnotatedStringNode>() {
53 
createnull54     override fun create(): TextAnnotatedStringNode =
55         TextAnnotatedStringNode(
56             text,
57             style,
58             fontFamilyResolver,
59             onTextLayout,
60             overflow,
61             softWrap,
62             maxLines,
63             minLines,
64             placeholders,
65             onPlaceholderLayout,
66             selectionController,
67             color,
68             autoSize,
69             onShowTranslation
70         )
71 
72     override fun update(node: TextAnnotatedStringNode) {
73         node.doInvalidations(
74             drawChanged = node.updateDraw(color, style),
75             textChanged = node.updateText(text = text),
76             layoutChanged =
77                 node.updateLayoutRelatedArgs(
78                     style = style,
79                     placeholders = placeholders,
80                     minLines = minLines,
81                     maxLines = maxLines,
82                     softWrap = softWrap,
83                     fontFamilyResolver = fontFamilyResolver,
84                     overflow = overflow,
85                     autoSize = autoSize
86                 ),
87             callbacksChanged =
88                 node.updateCallbacks(
89                     onTextLayout = onTextLayout,
90                     onPlaceholderLayout = onPlaceholderLayout,
91                     selectionController = selectionController,
92                     onShowTranslation = onShowTranslation
93                 )
94         )
95     }
96 
equalsnull97     override fun equals(other: Any?): Boolean {
98         if (this === other) return true
99 
100         if (other !is TextAnnotatedStringElement) return false
101 
102         // these three are most likely to actually change
103         if (color != other.color) return false
104         if (text != other.text) return false /* expensive to check, do it after color */
105         if (style != other.style) return false
106         if (placeholders != other.placeholders) return false
107 
108         // these are equally unlikely to change
109         if (fontFamilyResolver != other.fontFamilyResolver) return false
110         if (onTextLayout !== other.onTextLayout) return false
111         if (onShowTranslation !== other.onShowTranslation) return false
112         if (overflow != other.overflow) return false
113         if (softWrap != other.softWrap) return false
114         if (maxLines != other.maxLines) return false
115         if (minLines != other.minLines) return false
116 
117         // these never change, but check anyway for correctness
118         if (onPlaceholderLayout !== other.onPlaceholderLayout) return false
119         if (selectionController != other.selectionController) return false
120 
121         return true
122     }
123 
hashCodenull124     override fun hashCode(): Int {
125         var result = text.hashCode()
126         result = 31 * result + style.hashCode()
127         result = 31 * result + fontFamilyResolver.hashCode()
128         result = 31 * result + (onTextLayout?.hashCode() ?: 0)
129         result = 31 * result + overflow.hashCode()
130         result = 31 * result + softWrap.hashCode()
131         result = 31 * result + maxLines
132         result = 31 * result + minLines
133         result = 31 * result + (placeholders?.hashCode() ?: 0)
134         result = 31 * result + (onPlaceholderLayout?.hashCode() ?: 0)
135         result = 31 * result + (selectionController?.hashCode() ?: 0)
136         result = 31 * result + (color?.hashCode() ?: 0)
137         result = 31 * result + (onShowTranslation?.hashCode() ?: 0)
138         return result
139     }
140 
inspectablePropertiesnull141     override fun InspectorInfo.inspectableProperties() {
142         // Show nothing in the inspector.
143     }
144 }
145