1 /* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PROPERTIES_MEASURE_PROPERTIES_H 17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PROPERTIES_MEASURE_PROPERTIES_H 18 19 #include <array> 20 #include <cstdint> 21 #include <iomanip> 22 #include <optional> 23 #include <sstream> 24 #include <string> 25 #include <utility> 26 27 #include "base/geometry/ng/offset_t.h" 28 #include "base/json/json_util.h" 29 #include "base/utils/utils.h" 30 #include "core/common/ace_application_info.h" 31 #include "core/components_ng/property/calc_length.h" 32 #include "core/pipeline/pipeline_base.h" 33 34 namespace OHOS::Ace::NG { 35 const std::string BORDERZERO = "0.0"; 36 37 enum class MeasureType { 38 MATCH_PARENT, 39 MATCH_CONTENT, 40 MATCH_PARENT_CROSS_AXIS, 41 MATCH_PARENT_MAIN_AXIS, 42 }; 43 44 class CalcSize { 45 public: 46 CalcSize() = default; 47 ~CalcSize() = default; CalcSize(const CalcLength & width,const CalcLength & height)48 CalcSize(const CalcLength& width, const CalcLength& height) : width_(width), height_(height) {} CalcSize(std::optional<CalcLength> width,std::optional<CalcLength> height)49 CalcSize(std::optional<CalcLength> width, std::optional<CalcLength> height) 50 : width_(std::move(width)), height_(std::move(height)) 51 {} 52 Reset()53 void Reset() 54 { 55 width_.reset(); 56 height_.reset(); 57 } 58 IsValid()59 bool IsValid() const 60 { 61 return width_ && height_; 62 } 63 Width()64 const std::optional<CalcLength>& Width() const 65 { 66 return width_; 67 } 68 Height()69 const std::optional<CalcLength>& Height() const 70 { 71 return height_; 72 } 73 SetWidth(const std::optional<CalcLength> & width)74 void SetWidth(const std::optional<CalcLength>& width) 75 { 76 width_ = width; 77 } 78 SetHeight(const std::optional<CalcLength> & height)79 void SetHeight(const std::optional<CalcLength>& height) 80 { 81 height_ = height; 82 } 83 SetSizeT(const CalcSize & Size)84 void SetSizeT(const CalcSize& Size) 85 { 86 width_ = Size.Width(); 87 height_ = Size.Height(); 88 } 89 90 bool operator==(const CalcSize& Size) const 91 { 92 return (width_ == Size.width_) && (height_ == Size.height_); 93 } 94 95 bool operator!=(const CalcSize& Size) const 96 { 97 return !operator==(Size); 98 } 99 UpdateSizeWithCheck(const CalcSize & size)100 bool UpdateSizeWithCheck(const CalcSize& size) 101 { 102 if ((width_ == size.width_) && ((height_ == size.height_))) { 103 return false; 104 } 105 if (size.width_) { 106 width_ = size.width_; 107 } 108 if (size.height_) { 109 height_ = size.height_; 110 } 111 return true; 112 } 113 ClearSize(bool clearWidth,bool clearHeight)114 bool ClearSize(bool clearWidth, bool clearHeight) 115 { 116 bool changed = false; 117 if (clearWidth && width_.has_value()) { 118 width_.reset(); 119 changed = true; 120 } 121 if (clearHeight && height_.has_value()) { 122 height_.reset(); 123 changed = true; 124 } 125 return changed; 126 } 127 WidthFixed()128 bool WidthFixed() const 129 { 130 return width_ && width_->GetDimension().Unit() != DimensionUnit::PERCENT; 131 } 132 HeightFixed()133 bool HeightFixed() const 134 { 135 return height_ && height_->GetDimension().Unit() != DimensionUnit::PERCENT; 136 } 137 PercentWidth()138 bool PercentWidth() const 139 { 140 return width_ && width_->GetDimension().Unit() == DimensionUnit::PERCENT; 141 } 142 PercentHeight()143 bool PercentHeight() const 144 { 145 return height_ && height_->GetDimension().Unit() == DimensionUnit::PERCENT; 146 } 147 ToString()148 std::string ToString() const 149 { 150 static const int32_t precision = 2; 151 std::stringstream ss; 152 ss << "[" << std::fixed << std::setprecision(precision); 153 ss << (width_ ? width_->ToString() : "NA"); 154 ss << " x "; 155 ss << (height_ ? height_->ToString() : "NA"); 156 ss << "]"; 157 std::string output = ss.str(); 158 return output; 159 } 160 161 private: 162 std::optional<CalcLength> width_; 163 std::optional<CalcLength> height_; 164 }; 165 166 struct MeasureProperty { 167 std::optional<CalcSize> minSize; 168 std::optional<CalcSize> maxSize; 169 std::optional<CalcSize> selfIdealSize; 170 ResetMeasureProperty171 void Reset() 172 { 173 minSize.reset(); 174 maxSize.reset(); 175 selfIdealSize.reset(); 176 } 177 178 bool operator==(const MeasureProperty& measureProperty) const 179 { 180 return (minSize == measureProperty.minSize) && (maxSize == measureProperty.maxSize) && 181 (selfIdealSize == measureProperty.selfIdealSize); 182 } 183 UpdateSelfIdealSizeWithCheckMeasureProperty184 bool UpdateSelfIdealSizeWithCheck(const CalcSize& size) 185 { 186 if (selfIdealSize == size) { 187 return false; 188 } 189 if (selfIdealSize.has_value()) { 190 return selfIdealSize->UpdateSizeWithCheck(size); 191 } 192 selfIdealSize = size; 193 return true; 194 } 195 ClearSelfIdealSizeMeasureProperty196 bool ClearSelfIdealSize(bool clearWidth, bool clearHeight) 197 { 198 if (selfIdealSize.has_value()) { 199 return selfIdealSize->ClearSize(clearWidth, clearHeight); 200 } 201 return false; 202 } 203 UpdateMaxSizeWithCheckMeasureProperty204 bool UpdateMaxSizeWithCheck(const CalcSize& size) 205 { 206 if (maxSize == size) { 207 return false; 208 } 209 if (maxSize.has_value()) { 210 return maxSize->UpdateSizeWithCheck(size); 211 } 212 maxSize = size; 213 return true; 214 } 215 UpdateMinSizeWithCheckMeasureProperty216 bool UpdateMinSizeWithCheck(const CalcSize& size) 217 { 218 if (minSize == size) { 219 return false; 220 } 221 if (minSize.has_value()) { 222 return minSize->UpdateSizeWithCheck(size); 223 } 224 minSize = size; 225 return true; 226 } 227 PercentWidthMeasureProperty228 bool PercentWidth() const 229 { 230 if (selfIdealSize.has_value()) { 231 return selfIdealSize->PercentWidth(); 232 } 233 if (maxSize.has_value()) { 234 return maxSize->PercentWidth(); 235 } 236 if (minSize.has_value()) { 237 return minSize->PercentWidth(); 238 } 239 return false; 240 } 241 PercentHeightMeasureProperty242 bool PercentHeight() const 243 { 244 if (selfIdealSize.has_value()) { 245 return selfIdealSize->PercentHeight(); 246 } 247 if (maxSize.has_value()) { 248 return maxSize->PercentHeight(); 249 } 250 if (minSize.has_value()) { 251 return minSize->PercentHeight(); 252 } 253 return false; 254 } 255 ToStringMeasureProperty256 std::string ToString() const 257 { 258 std::string str; 259 str.append("minSize: [").append(minSize.has_value() ? minSize->ToString() : "NA").append("]"); 260 str.append("maxSize: [").append(maxSize.has_value() ? maxSize->ToString() : "NA").append("]"); 261 str.append("selfIdealSize: [").append(selfIdealSize.has_value() ? selfIdealSize->ToString() : "NA").append("]"); 262 return str; 263 } 264 ToJsonValueMeasureProperty265 void ToJsonValue(std::unique_ptr<JsonValue>& json) const 266 { 267 // this may affect XTS, check later. 268 auto context = PipelineBase::GetCurrentContext(); 269 if (context && context->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_ELEVEN)) { 270 #if !defined(PREVIEW) 271 std::string width = selfIdealSize.has_value() ? 272 (selfIdealSize.value().Width().has_value() ? selfIdealSize.value().Width().value().ToString() : "-") 273 : "-"; 274 std::string height = selfIdealSize.has_value() ? 275 (selfIdealSize.value().Height().has_value() ? selfIdealSize.value().Height().value().ToString() : "-") 276 : "-"; 277 json->Put("width", width.c_str()); 278 json->Put("height", height.c_str()); 279 280 auto jsonSize = JsonUtil::Create(true); 281 jsonSize->Put("width", width.c_str()); 282 jsonSize->Put("height", height.c_str()); 283 json->Put("size", jsonSize); 284 #else 285 ToJsonValue_GetJsonSize(json); 286 #endif 287 } else { 288 ToJsonValue_GetJsonSize(json); 289 } 290 291 auto jsonConstraintSize = JsonUtil::Create(true); 292 jsonConstraintSize->Put("minWidth", 293 minSize.value_or(CalcSize()).Width().value_or(CalcLength(0, DimensionUnit::VP)).ToString().c_str()); 294 jsonConstraintSize->Put("minHeight", 295 minSize.value_or(CalcSize()).Height().value_or(CalcLength(0, DimensionUnit::VP)).ToString().c_str()); 296 jsonConstraintSize->Put("maxWidth", maxSize.value_or(CalcSize()) 297 .Width() 298 .value_or(CalcLength(Infinity<double>(), DimensionUnit::VP)) 299 .ToString() 300 .c_str()); 301 jsonConstraintSize->Put("maxHeight", maxSize.value_or(CalcSize()) 302 .Height() 303 .value_or(CalcLength(Infinity<double>(), DimensionUnit::VP)) 304 .ToString() 305 .c_str()); 306 json->Put("constraintSize", jsonConstraintSize->ToString().c_str()); 307 } 308 ToJsonValue_GetJsonSizeMeasureProperty309 void ToJsonValue_GetJsonSize(std::unique_ptr<JsonValue>& json) const 310 { 311 auto jsonSize = JsonUtil::Create(true); 312 if (selfIdealSize.has_value()) { 313 if (selfIdealSize.value().Width().has_value()) { 314 auto widthStr = selfIdealSize.value().Width().value().ToString(); 315 json->Put("width", widthStr.c_str()); 316 jsonSize->Put("width", widthStr.c_str()); 317 } 318 if (selfIdealSize.value().Height().has_value()) { 319 auto heightStr = selfIdealSize.value().Height().value().ToString(); 320 json->Put("height", heightStr.c_str()); 321 jsonSize->Put("height", heightStr.c_str()); 322 } 323 } 324 json->Put("size", jsonSize); 325 } 326 FromJsonMeasureProperty327 static MeasureProperty FromJson(const std::unique_ptr<JsonValue>& json) 328 { 329 MeasureProperty ans; 330 auto width = json->GetString("width"); 331 auto height = json->GetString("height"); 332 if (width != "-" || height != "-") { 333 ans.selfIdealSize = 334 CalcSize(width != "-" ? std::optional<CalcLength>(Dimension::FromString(width)) : std::nullopt, 335 height != "-" ? std::optional<CalcLength>(Dimension::FromString(height)) : std::nullopt); 336 } 337 return ans; 338 } 339 }; 340 341 template<typename T> 342 struct PaddingPropertyT { 343 std::optional<T> left; 344 std::optional<T> right; 345 std::optional<T> top; 346 std::optional<T> bottom; 347 SetEdgesPaddingPropertyT348 void SetEdges(const T& padding) 349 { 350 left = padding; 351 right = padding; 352 top = padding; 353 bottom = padding; 354 } 355 356 bool operator==(const PaddingPropertyT& value) const 357 { 358 return (left == value.left) && (right == value.right) && (top == value.top) && (bottom == value.bottom); 359 } 360 361 bool operator!=(const PaddingPropertyT& value) const 362 { 363 return !(*this == value); 364 } 365 UpdateWithCheckPaddingPropertyT366 bool UpdateWithCheck(const PaddingPropertyT& value) 367 { 368 if (*this != value) { 369 left = value.left; 370 right = value.right; 371 top = value.top; 372 bottom = value.bottom; 373 return true; 374 } 375 return false; 376 } 377 ToStringPaddingPropertyT378 std::string ToString() const 379 { 380 std::string str; 381 str.append("left: [").append(left.has_value() ? left->ToString() : "NA").append("]"); 382 str.append("right: [").append(right.has_value() ? right->ToString() : "NA").append("]"); 383 str.append("top: [").append(top.has_value() ? top->ToString() : "NA").append("]"); 384 str.append("bottom: [").append(bottom.has_value() ? bottom->ToString() : "NA").append("]"); 385 return str; 386 } ToJsonStringPaddingPropertyT387 std::string ToJsonString() const 388 { 389 if (top == right && right == bottom && bottom == left) { 390 if (top.has_value()) { 391 return top->ToString(); 392 } 393 return "0.0"; 394 } 395 auto jsonValue = JsonUtil::Create(true); 396 jsonValue->Put("top", top->ToString().c_str()); 397 jsonValue->Put("right", right->ToString().c_str()); 398 jsonValue->Put("bottom", bottom->ToString().c_str()); 399 jsonValue->Put("left", left->ToString().c_str()); 400 return jsonValue->ToString(); 401 } 402 FromJsonStringPaddingPropertyT403 static PaddingPropertyT FromJsonString(const std::string& str) 404 { 405 PaddingPropertyT property; 406 407 if (str.empty()) { 408 LOGE("UITree |ERROR| empty string"); 409 return property; 410 } 411 412 if (str[0] >= '0' && str[0] <= '9') { 413 property.top = property.right = property.bottom = property.left = T::FromString(str); 414 } else if (str[0] == '{') { 415 auto json = JsonUtil::ParseJsonString(str); 416 if (!json->IsValid()) { 417 return property; 418 } 419 property.top = T::FromString(json->GetString("top")); 420 property.right = T::FromString(json->GetString("right")); 421 property.bottom = T::FromString(json->GetString("bottom")); 422 property.left = T::FromString(json->GetString("left")); 423 } else { 424 LOGE("UITree |ERROR| invalid str=%{public}s", str.c_str()); 425 } 426 427 return property; 428 } 429 }; 430 431 template<> 432 struct PaddingPropertyT<float> { 433 std::optional<float> left; 434 std::optional<float> right; 435 std::optional<float> top; 436 std::optional<float> bottom; 437 438 bool operator==(const PaddingPropertyT<float>& value) const 439 { 440 if (left.has_value() ^ value.left.has_value()) { 441 return false; 442 } 443 if (!NearEqual(left.value_or(0), value.left.value_or(0))) { 444 return false; 445 } 446 if (right.has_value() ^ value.right.has_value()) { 447 return false; 448 } 449 if (!NearEqual(right.value_or(0), value.right.value_or(0))) { 450 return false; 451 } 452 if (top.has_value() ^ value.top.has_value()) { 453 return false; 454 } 455 if (!NearEqual(top.value_or(0), value.top.value_or(0))) { 456 return false; 457 } 458 if (bottom.has_value() ^ value.bottom.has_value()) { 459 return false; 460 } 461 if (!NearEqual(bottom.value_or(0), value.bottom.value_or(0))) { 462 return false; 463 } 464 return true; 465 } 466 467 std::string ToString() const 468 { 469 std::string str; 470 str.append("left: [").append(left.has_value() ? std::to_string(left.value()) : "NA").append("]"); 471 str.append("right: [").append(right.has_value() ? std::to_string(right.value()) : "NA").append("]"); 472 str.append("top: [").append(top.has_value() ? std::to_string(top.value()) : "NA").append("]"); 473 str.append("bottom: [").append(bottom.has_value() ? std::to_string(bottom.value()) : "NA").append("]"); 474 return str; 475 } 476 477 std::string ToJsonString() const 478 { 479 auto jsonValue = JsonUtil::Create(true); 480 jsonValue->Put("top", top.has_value() ? std::to_string(top.value()).c_str() : BORDERZERO.c_str()); 481 jsonValue->Put("right", right.has_value() ? std::to_string(right.value()).c_str() : BORDERZERO.c_str()); 482 jsonValue->Put("bottom", bottom.has_value() ? std::to_string(bottom.value()).c_str() : BORDERZERO.c_str()); 483 jsonValue->Put("left", left.has_value() ? std::to_string(left.value()).c_str() : BORDERZERO.c_str()); 484 return jsonValue->ToString(); 485 } 486 487 float Width() const 488 { 489 return left.value_or(0.0f) + right.value_or(0.0f); 490 } 491 492 float Height() const 493 { 494 return top.value_or(0.0f) + bottom.value_or(0.0f); 495 } 496 497 SizeF Size() const 498 { 499 return SizeF(Width(), Height()); 500 } 501 502 OffsetF Offset() const 503 { 504 return OffsetF(left.value_or(0.0f), top.value_or(0.0f)); 505 } 506 }; 507 508 using PaddingProperty = PaddingPropertyT<CalcLength>; 509 using MarginProperty = PaddingProperty; 510 using PaddingPropertyF = PaddingPropertyT<float>; 511 using MarginPropertyF = PaddingPropertyT<float>; 512 } // namespace OHOS::Ace::NG 513 514 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PROPERTIES_MEASURE_PROPERTIES_H