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.modifiers; 17 18 import android.annotation.NonNull; 19 20 import com.android.internal.widget.remotecompose.core.CoreDocument; 21 import com.android.internal.widget.remotecompose.core.PaintContext; 22 import com.android.internal.widget.remotecompose.core.PaintOperation; 23 import com.android.internal.widget.remotecompose.core.RemoteContext; 24 import com.android.internal.widget.remotecompose.core.SerializableToString; 25 import com.android.internal.widget.remotecompose.core.VariableSupport; 26 import com.android.internal.widget.remotecompose.core.WireBuffer; 27 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; 28 import com.android.internal.widget.remotecompose.core.operations.MatrixSave; 29 import com.android.internal.widget.remotecompose.core.operations.layout.ClickHandler; 30 import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation; 31 import com.android.internal.widget.remotecompose.core.operations.layout.Component; 32 import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent; 33 import com.android.internal.widget.remotecompose.core.operations.layout.TouchHandler; 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.Serializable; 37 import com.android.internal.widget.remotecompose.core.serialize.SerializeTags; 38 39 import java.util.ArrayList; 40 41 /** Maintain a list of modifiers */ 42 public class ComponentModifiers extends PaintOperation 43 implements DecoratorComponent, 44 ClickHandler, 45 TouchHandler, 46 SerializableToString, 47 Serializable { 48 @NonNull ArrayList<ModifierOperation> mList = new ArrayList<>(); 49 50 @NonNull getList()51 public ArrayList<ModifierOperation> getList() { 52 return mList; 53 } 54 55 @Override apply(@onNull RemoteContext context)56 public void apply(@NonNull RemoteContext context) { 57 super.apply(context); 58 for (ModifierOperation op : mList) { 59 op.apply(context); 60 context.incrementOpCount(); 61 } 62 } 63 64 @NonNull 65 @Override toString()66 public String toString() { 67 String str = "ComponentModifiers \n"; 68 for (ModifierOperation modifierOperation : mList) { 69 str += " " + modifierOperation.toString() + "\n"; 70 } 71 return str; 72 } 73 74 @Override write(@onNull WireBuffer buffer)75 public void write(@NonNull WireBuffer buffer) { 76 // nothing 77 } 78 79 @Override serializeToString(int indent, @NonNull StringSerializer serializer)80 public void serializeToString(int indent, @NonNull StringSerializer serializer) { 81 serializer.append(indent, "MODIFIERS"); 82 for (ModifierOperation m : mList) { 83 m.serializeToString(indent + 1, serializer); 84 } 85 } 86 87 /** 88 * Add a ModifierOperation 89 * 90 * @param operation a ModifierOperation 91 */ add(@onNull ModifierOperation operation)92 public void add(@NonNull ModifierOperation operation) { 93 mList.add(operation); 94 } 95 96 /** 97 * Returns the size of the modifier list 98 * 99 * @return number of modifiers 100 */ size()101 public int size() { 102 return mList.size(); 103 } 104 105 @Override paint(@onNull PaintContext context)106 public void paint(@NonNull PaintContext context) { 107 float tx = 0f; 108 float ty = 0f; 109 for (ModifierOperation op : mList) { 110 if (op.isDirty() && op instanceof VariableSupport) { 111 ((VariableSupport) op).updateVariables(context.getContext()); 112 op.markNotDirty(); 113 } 114 if (op instanceof PaddingModifierOperation) { 115 PaddingModifierOperation pop = (PaddingModifierOperation) op; 116 context.translate(pop.getLeft(), pop.getTop()); 117 tx += pop.getLeft(); 118 ty += pop.getTop(); 119 } 120 if (op instanceof MatrixSave || op instanceof MatrixRestore) { 121 continue; 122 } 123 if (op instanceof ClickModifierOperation) { 124 context.translate(-tx, -ty); 125 ((ClickModifierOperation) op).paint(context); 126 context.translate(tx, ty); 127 } else if (op instanceof PaintOperation) { 128 ((PaintOperation) op).paint(context); 129 } 130 } 131 // Back out the translates created by paddings 132 // TODO: we should be able to get rid of this when drawing the content of a component 133 context.translate(-tx, -ty); 134 } 135 136 @Override layout( @onNull RemoteContext context, Component component, float width, float height)137 public void layout( 138 @NonNull RemoteContext context, Component component, float width, float height) { 139 float w = width; 140 float h = height; 141 for (ModifierOperation op : mList) { 142 if (op instanceof PaddingModifierOperation) { 143 PaddingModifierOperation pop = (PaddingModifierOperation) op; 144 w -= pop.getLeft() + pop.getRight(); 145 h -= pop.getTop() + pop.getBottom(); 146 } 147 if (op instanceof ClickModifierOperation) { 148 ((DecoratorComponent) op).layout(context, component, width, height); 149 } else if (op instanceof DecoratorComponent) { 150 ((DecoratorComponent) op).layout(context, component, w, h); 151 } 152 } 153 } 154 155 /** 156 * Add the operations to this ComponentModifier 157 * 158 * @param operations list of ModifierOperation 159 */ addAll(@onNull ArrayList<ModifierOperation> operations)160 public void addAll(@NonNull ArrayList<ModifierOperation> operations) { 161 mList.addAll(operations); 162 } 163 164 @Override onClick( @onNull RemoteContext context, @NonNull CoreDocument document, @NonNull Component component, float x, float y)165 public void onClick( 166 @NonNull RemoteContext context, 167 @NonNull CoreDocument document, 168 @NonNull Component component, 169 float x, 170 float y) { 171 for (ModifierOperation op : mList) { 172 if (op instanceof ClickHandler) { 173 ((ClickHandler) op).onClick(context, document, component, x, y); 174 } 175 } 176 } 177 178 @Override onTouchDown( RemoteContext context, CoreDocument document, Component component, float x, float y)179 public void onTouchDown( 180 RemoteContext context, CoreDocument document, Component component, float x, float y) { 181 for (ModifierOperation op : mList) { 182 if (op instanceof TouchHandler) { 183 ((TouchHandler) op).onTouchDown(context, document, component, x, y); 184 } 185 } 186 } 187 188 @Override onTouchUp( RemoteContext context, CoreDocument document, Component component, float x, float y, float dx, float dy)189 public void onTouchUp( 190 RemoteContext context, 191 CoreDocument document, 192 Component component, 193 float x, 194 float y, 195 float dx, 196 float dy) { 197 for (ModifierOperation op : mList) { 198 if (op instanceof TouchHandler) { 199 ((TouchHandler) op).onTouchUp(context, document, component, x, y, dx, dy); 200 } 201 } 202 } 203 204 @Override onTouchCancel( RemoteContext context, CoreDocument document, Component component, float x, float y)205 public void onTouchCancel( 206 RemoteContext context, CoreDocument document, Component component, float x, float y) { 207 for (ModifierOperation op : mList) { 208 if (op instanceof TouchHandler) { 209 ((TouchHandler) op).onTouchCancel(context, document, component, x, y); 210 } 211 } 212 } 213 214 @Override onTouchDrag( RemoteContext context, CoreDocument document, Component component, float x, float y)215 public void onTouchDrag( 216 RemoteContext context, CoreDocument document, Component component, float x, float y) { 217 for (ModifierOperation op : mList) { 218 if (op instanceof TouchHandler) { 219 ((TouchHandler) op).onTouchDrag(context, document, component, x, y); 220 } 221 } 222 } 223 224 /** 225 * Returns true if we have a horizontal scroll modifier 226 * 227 * @return true if we have a horizontal scroll modifier, false otherwise 228 */ hasHorizontalScroll()229 public boolean hasHorizontalScroll() { 230 for (ModifierOperation op : mList) { 231 if (op instanceof ScrollModifierOperation) { 232 ScrollModifierOperation scrollModifier = (ScrollModifierOperation) op; 233 if (scrollModifier.isHorizontalScroll()) { 234 return true; 235 } 236 } 237 } 238 return false; 239 } 240 241 /** 242 * Returns true if we have a vertical scroll modifier 243 * 244 * @return true if we have a vertical scroll modifier, false otherwise 245 */ hasVerticalScroll()246 public boolean hasVerticalScroll() { 247 for (ModifierOperation op : mList) { 248 if (op instanceof ScrollModifierOperation) { 249 ScrollModifierOperation scrollModifier = (ScrollModifierOperation) op; 250 if (scrollModifier.isVerticalScroll()) { 251 return true; 252 } 253 } 254 } 255 return false; 256 } 257 258 /** 259 * Set the horizontal scroll dimension (if we have a scroll modifier) 260 * 261 * @param hostDimension the host component horizontal dimension 262 * @param contentDimension the content horizontal dimension 263 */ setHorizontalScrollDimension(float hostDimension, float contentDimension)264 public void setHorizontalScrollDimension(float hostDimension, float contentDimension) { 265 for (ModifierOperation op : mList) { 266 if (op instanceof ScrollModifierOperation) { 267 ScrollModifierOperation scrollModifier = (ScrollModifierOperation) op; 268 if (scrollModifier.isHorizontalScroll()) { 269 scrollModifier.setHorizontalScrollDimension(hostDimension, contentDimension); 270 } 271 } 272 } 273 } 274 275 /** 276 * Set the vertical scroll dimension (if we have a scroll modifier) 277 * 278 * @param hostDimension the host component vertical dimension 279 * @param contentDimension the content vertical dimension 280 */ setVerticalScrollDimension(float hostDimension, float contentDimension)281 public void setVerticalScrollDimension(float hostDimension, float contentDimension) { 282 for (ModifierOperation op : mList) { 283 if (op instanceof ScrollModifierOperation) { 284 ScrollModifierOperation scrollModifier = (ScrollModifierOperation) op; 285 if (scrollModifier.isVerticalScroll()) { 286 scrollModifier.setVerticalScrollDimension(hostDimension, contentDimension); 287 } 288 } 289 } 290 } 291 292 /** 293 * Returns the horizontal scroll dimension if we have a scroll modifier 294 * 295 * @return the horizontal scroll dimension, or 0 if no scroll modifier 296 */ getHorizontalScrollDimension()297 public float getHorizontalScrollDimension() { 298 for (ModifierOperation op : mList) { 299 if (op instanceof ScrollModifierOperation) { 300 ScrollModifierOperation scrollModifier = (ScrollModifierOperation) op; 301 if (scrollModifier.isHorizontalScroll()) { 302 return scrollModifier.getContentDimension(); 303 } 304 } 305 } 306 return 0f; 307 } 308 309 /** 310 * Returns the vertical scroll dimension if we have a scroll modifier 311 * 312 * @return the vertical scroll dimension, or 0 if no scroll modifier 313 */ getVerticalScrollDimension()314 public float getVerticalScrollDimension() { 315 for (ModifierOperation op : mList) { 316 if (op instanceof ScrollModifierOperation) { 317 ScrollModifierOperation scrollModifier = (ScrollModifierOperation) op; 318 if (scrollModifier.isVerticalScroll()) { 319 return scrollModifier.getContentDimension(); 320 } 321 } 322 } 323 return 0f; 324 } 325 326 @Override serialize(MapSerializer serializer)327 public void serialize(MapSerializer serializer) { 328 serializer 329 .addTags(SerializeTags.MODIFIER) 330 .addType("ComponentModifiers") 331 .add("modifiers", mList); 332 } 333 } 334