1 /*
<lambda>null2  * Copyright 2020 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.ui.node
18 
19 import androidx.compose.runtime.snapshots.SnapshotStateObserver
20 
21 /**
22  * Performs snapshot observation for blocks like draw and layout which should be re-invoked
23  * automatically when the snapshot value has been changed.
24  */
25 @Suppress("CallbackName") // TODO rename this and SnapshotStateObserver. b/173401548
26 internal class OwnerSnapshotObserver(onChangedExecutor: (callback: () -> Unit) -> Unit) {
27 
28     private val observer = SnapshotStateObserver(onChangedExecutor)
29 
30     private val onCommitAffectingLookaheadMeasure: (LayoutNode) -> Unit = { layoutNode ->
31         if (layoutNode.isValidOwnerScope) {
32             layoutNode.requestLookaheadRemeasure()
33         }
34     }
35 
36     private val onCommitAffectingMeasure: (LayoutNode) -> Unit = { layoutNode ->
37         if (layoutNode.isValidOwnerScope) {
38             layoutNode.requestRemeasure()
39         }
40     }
41 
42     private val onCommitAffectingSemantics: (LayoutNode) -> Unit = { layoutNode ->
43         if (layoutNode.isValidOwnerScope) {
44             layoutNode.invalidateSemantics()
45         }
46     }
47 
48     private val onCommitAffectingLayout: (LayoutNode) -> Unit = { layoutNode ->
49         if (layoutNode.isValidOwnerScope) {
50             layoutNode.requestRelayout()
51         }
52     }
53 
54     private val onCommitAffectingLayoutModifier: (LayoutNode) -> Unit = { layoutNode ->
55         if (layoutNode.isValidOwnerScope) {
56             layoutNode.requestRelayout()
57         }
58     }
59 
60     private val onCommitAffectingLayoutModifierInLookahead: (LayoutNode) -> Unit = { layoutNode ->
61         if (layoutNode.isValidOwnerScope) {
62             layoutNode.requestLookaheadRelayout()
63         }
64     }
65 
66     private val onCommitAffectingLookahead: (LayoutNode) -> Unit = { layoutNode ->
67         if (layoutNode.isValidOwnerScope) {
68             layoutNode.requestLookaheadRelayout()
69         }
70     }
71 
72     /** Observe snapshot reads during layout of [node], executed in [block]. */
73     internal fun observeLayoutSnapshotReads(
74         node: LayoutNode,
75         affectsLookahead: Boolean = true,
76         block: () -> Unit
77     ) {
78         if (affectsLookahead && node.lookaheadRoot != null) {
79             observeReads(node, onCommitAffectingLookahead, block)
80         } else {
81             observeReads(node, onCommitAffectingLayout, block)
82         }
83     }
84 
85     /** Observe snapshot reads during layout of [node]'s LayoutModifiers, executed in [block]. */
86     internal fun observeLayoutModifierSnapshotReads(
87         node: LayoutNode,
88         affectsLookahead: Boolean = true,
89         block: () -> Unit
90     ) {
91         if (affectsLookahead && node.lookaheadRoot != null) {
92             observeReads(node, onCommitAffectingLayoutModifierInLookahead, block)
93         } else {
94             observeReads(node, onCommitAffectingLayoutModifier, block)
95         }
96     }
97 
98     /** Observe snapshot reads during measure of [node], executed in [block]. */
99     internal fun observeMeasureSnapshotReads(
100         node: LayoutNode,
101         affectsLookahead: Boolean = true,
102         block: () -> Unit
103     ) {
104         if (affectsLookahead && node.lookaheadRoot != null) {
105             observeReads(node, onCommitAffectingLookaheadMeasure, block)
106         } else {
107             observeReads(node, onCommitAffectingMeasure, block)
108         }
109     }
110 
111     internal fun observeSemanticsReads(node: LayoutNode, block: () -> Unit) {
112         observeReads(node, onCommitAffectingSemantics, block)
113     }
114 
115     /**
116      * Observe snapshot reads for any target, allowing consumers to determine how to respond to
117      * state changes.
118      */
119     internal fun <T : OwnerScope> observeReads(
120         target: T,
121         onChanged: (T) -> Unit,
122         block: () -> Unit
123     ) {
124         observer.observeReads(target, onChanged, block)
125     }
126 
127     internal fun clearInvalidObservations() {
128         observer.clearIf { !(it as OwnerScope).isValidOwnerScope }
129     }
130 
131     internal fun clear(target: Any) {
132         observer.clear(target)
133     }
134 
135     internal fun startObserving() {
136         observer.start()
137     }
138 
139     internal fun stopObserving() {
140         observer.stop()
141         observer.clear()
142     }
143 }
144