• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 package com.android.internal.widget.remotecompose.core.operations.layout;
17 
18 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
19 
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 
23 import com.android.internal.widget.remotecompose.core.Operation;
24 import com.android.internal.widget.remotecompose.core.Operations;
25 import com.android.internal.widget.remotecompose.core.PaintContext;
26 import com.android.internal.widget.remotecompose.core.PaintOperation;
27 import com.android.internal.widget.remotecompose.core.RemoteContext;
28 import com.android.internal.widget.remotecompose.core.SerializableToString;
29 import com.android.internal.widget.remotecompose.core.WireBuffer;
30 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
31 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Measurable;
32 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
33 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
34 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
35 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
36 import com.android.internal.widget.remotecompose.core.serialize.SerializeTags;
37 
38 import java.util.List;
39 
40 /** Represents the root layout component. Entry point to the component tree layout/paint. */
41 public class RootLayoutComponent extends Component {
42     private int mCurrentId = -1;
43     private boolean mHasTouchListeners = false;
44 
RootLayoutComponent( int componentId, float x, float y, float width, float height, @Nullable Component parent, int animationId)45     public RootLayoutComponent(
46             int componentId,
47             float x,
48             float y,
49             float width,
50             float height,
51             @Nullable Component parent,
52             int animationId) {
53         super(parent, componentId, animationId, x, y, width, height);
54     }
55 
RootLayoutComponent( int componentId, float x, float y, float width, float height, @Nullable Component parent)56     public RootLayoutComponent(
57             int componentId,
58             float x,
59             float y,
60             float width,
61             float height,
62             @Nullable Component parent) {
63         super(parent, componentId, -1, x, y, width, height);
64     }
65 
66     @NonNull
67     @Override
toString()68     public String toString() {
69         return "ROOT "
70                 + mComponentId
71                 + " ("
72                 + mX
73                 + ", "
74                 + mY
75                 + " - "
76                 + mWidth
77                 + " x "
78                 + mHeight
79                 + ") "
80                 + Visibility.toString(mVisibility);
81     }
82 
83     @Override
serializeToString(int indent, @NonNull StringSerializer serializer)84     public void serializeToString(int indent, @NonNull StringSerializer serializer) {
85         serializer.append(
86                 indent,
87                 "ROOT ["
88                         + mComponentId
89                         + ":"
90                         + mAnimationId
91                         + "] = ["
92                         + mX
93                         + ", "
94                         + mY
95                         + ", "
96                         + mWidth
97                         + ", "
98                         + mHeight
99                         + "] "
100                         + Visibility.toString(mVisibility));
101     }
102 
103     /**
104      * Set the flag to traverse the tree when touch events happen
105      *
106      * @param value true to indicate that the tree has touch listeners
107      */
setHasTouchListeners(boolean value)108     public void setHasTouchListeners(boolean value) {
109         mHasTouchListeners = value;
110     }
111 
112     /**
113      * Traverse the hierarchy and assign generated ids to component without ids. Most components
114      * would already have ids assigned during the document creation, but this allow us to take care
115      * of any components added during the inflation.
116      *
117      * @param lastId the last known generated id
118      */
assignIds(int lastId)119     public void assignIds(int lastId) {
120         mCurrentId = lastId;
121         assignId(this);
122     }
123 
assignId(@onNull Component component)124     private void assignId(@NonNull Component component) {
125         if (component.mComponentId == -1) {
126             mCurrentId--;
127             component.mComponentId = mCurrentId;
128         }
129         for (Operation op : component.mList) {
130             if (op instanceof Component) {
131                 assignId((Component) op);
132             }
133         }
134     }
135 
136     /** This will measure then layout the tree of components */
layout(@onNull RemoteContext context)137     public void layout(@NonNull RemoteContext context) {
138         if (!mNeedsMeasure) {
139             return;
140         }
141         context.mLastComponent = this;
142         setWidth(context.mWidth);
143         setHeight(context.mHeight);
144 
145         // TODO: reuse MeasurePass
146         MeasurePass measurePass = new MeasurePass();
147         for (Operation op : mList) {
148             if (op instanceof Measurable) {
149                 Measurable m = (Measurable) op;
150                 m.measure(context.getPaintContext(), 0f, mWidth, 0f, mHeight, measurePass);
151                 m.layout(context, measurePass);
152             }
153         }
154         mNeedsMeasure = false;
155     }
156 
157     @Override
paint(@onNull PaintContext context)158     public void paint(@NonNull PaintContext context) {
159         mNeedsRepaint = false;
160         RemoteContext remoteContext = context.getContext();
161         remoteContext.mLastComponent = this;
162 
163         context.save();
164 
165         if (mParent == null) { // root layout
166             context.clipRect(0f, 0f, mWidth, mHeight);
167         }
168 
169         for (Operation op : mList) {
170             if (op instanceof PaintOperation) {
171                 ((PaintOperation) op).paint(context);
172                 remoteContext.incrementOpCount();
173             }
174         }
175 
176         context.restore();
177     }
178 
179     /**
180      * Display the component hierarchy
181      *
182      * @return a formatted string containing the component hierarchy
183      */
184     @NonNull
displayHierarchy()185     public String displayHierarchy() {
186         StringSerializer serializer = new StringSerializer();
187         displayHierarchy(this, 0, serializer);
188         return serializer.toString();
189     }
190 
191     /**
192      * Display the component hierarchy
193      *
194      * @param component the current component
195      * @param indent the current indentation level
196      * @param serializer the serializer we write to
197      */
displayHierarchy( @onNull Component component, int indent, @NonNull StringSerializer serializer)198     public void displayHierarchy(
199             @NonNull Component component, int indent, @NonNull StringSerializer serializer) {
200         component.serializeToString(indent, serializer);
201         for (Operation c : component.mList) {
202             if (c instanceof ComponentModifiers) {
203                 ((ComponentModifiers) c).serializeToString(indent + 1, serializer);
204             } else if (c instanceof Component) {
205                 displayHierarchy((Component) c, indent + 1, serializer);
206             } else if (c instanceof SerializableToString) {
207                 ((SerializableToString) c).serializeToString(indent + 1, serializer);
208             }
209         }
210     }
211 
212     /**
213      * The name of the class
214      *
215      * @return the name
216      */
217     @NonNull
name()218     public static String name() {
219         return "RootLayout";
220     }
221 
222     /**
223      * The OP_CODE for this command
224      *
225      * @return the opcode
226      */
id()227     public static int id() {
228         return Operations.LAYOUT_ROOT;
229     }
230 
231     /**
232      * Write the operation on the buffer
233      *
234      * @param buffer
235      * @param componentId
236      */
apply(@onNull WireBuffer buffer, int componentId)237     public static void apply(@NonNull WireBuffer buffer, int componentId) {
238         buffer.start(Operations.LAYOUT_ROOT);
239         buffer.writeInt(componentId);
240     }
241 
242     /**
243      * Read this operation and add it to the list of operations
244      *
245      * @param buffer the buffer to read
246      * @param operations the list of operations that will be added to
247      */
read(@onNull WireBuffer buffer, @NonNull List<Operation> operations)248     public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
249         int componentId = buffer.readInt();
250         operations.add(new RootLayoutComponent(componentId, 0, 0, 0, 0, null, -1));
251     }
252 
253     /**
254      * Populate the documentation with a description of this operation
255      *
256      * @param doc to append the description to.
257      */
documentation(@onNull DocumentationBuilder doc)258     public static void documentation(@NonNull DocumentationBuilder doc) {
259         doc.operation("Layout Operations", id(), name())
260                 .field(INT, "COMPONENT_ID", "unique id for this component")
261                 .description(
262                         "Root element for a document. Other components / layout managers are"
263                                 + " children in the component tree starting from"
264                                 + "this Root component.");
265     }
266 
267     @Override
write(@onNull WireBuffer buffer)268     public void write(@NonNull WireBuffer buffer) {
269         apply(buffer, mComponentId);
270     }
271 
272     /**
273      * Returns true if we have components with a touch listener
274      *
275      * @return true if listeners, false otherwise
276      */
hasTouchListeners()277     public boolean hasTouchListeners() {
278         return mHasTouchListeners;
279     }
280 
281     @Override
serialize(MapSerializer serializer)282     public void serialize(MapSerializer serializer) {
283         super.serialize(serializer);
284         serializer.addTags(SerializeTags.COMPONENT);
285         serializer.addType("RootLayoutComponent");
286     }
287 }
288