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/base/inspector_filter.h" 32 #include "core/components_ng/property/calc_length.h" 33 #include "core/pipeline/pipeline_base.h" 34 35 namespace OHOS::Ace::NG { 36 const std::string BORDERZERO = "0.0"; 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_) && (width_->GetDimension().Unit() != DimensionUnit::AUTO && 62 height_->GetDimension().Unit() != DimensionUnit::AUTO); 63 } 64 IsDimensionUnitAuto()65 bool IsDimensionUnitAuto() const 66 { 67 return IsWidthDimensionUnitAuto() || IsHeightDimensionUnitAuto(); 68 } 69 IsWidthDimensionUnitAuto()70 bool IsWidthDimensionUnitAuto() const 71 { 72 return width_ && width_->GetDimension().Unit() == DimensionUnit::AUTO; 73 } 74 IsHeightDimensionUnitAuto()75 bool IsHeightDimensionUnitAuto() const 76 { 77 return height_ && height_->GetDimension().Unit() == DimensionUnit::AUTO; 78 } 79 Width()80 const std::optional<CalcLength>& Width() const 81 { 82 return width_; 83 } 84 Height()85 const std::optional<CalcLength>& Height() const 86 { 87 return height_; 88 } 89 SetWidth(const std::optional<CalcLength> & width)90 void SetWidth(const std::optional<CalcLength>& width) 91 { 92 width_ = width; 93 } 94 SetHeight(const std::optional<CalcLength> & height)95 void SetHeight(const std::optional<CalcLength>& height) 96 { 97 height_ = height; 98 } 99 SetSizeT(const CalcSize & Size)100 void SetSizeT(const CalcSize& Size) 101 { 102 width_ = Size.Width(); 103 height_ = Size.Height(); 104 } 105 106 bool operator==(const CalcSize& Size) const 107 { 108 return (width_ == Size.width_) && (height_ == Size.height_); 109 } 110 111 bool operator!=(const CalcSize& Size) const 112 { 113 return !operator==(Size); 114 } 115 UpdateSizeWithCheck(const CalcSize & size)116 bool UpdateSizeWithCheck(const CalcSize& size) 117 { 118 if ((width_ == size.width_) && ((height_ == size.height_))) { 119 return false; 120 } 121 if (size.width_) { 122 width_ = size.width_; 123 } 124 if (size.height_) { 125 height_ = size.height_; 126 } 127 return true; 128 } 129 ClearSize(bool clearWidth,bool clearHeight)130 bool ClearSize(bool clearWidth, bool clearHeight) 131 { 132 bool changed = false; 133 if (clearWidth && width_.has_value()) { 134 width_.reset(); 135 changed = true; 136 } 137 if (clearHeight && height_.has_value()) { 138 height_.reset(); 139 changed = true; 140 } 141 return changed; 142 } 143 144 bool WidthFixed(bool checkPercent = true) const 145 { 146 return width_ && (!checkPercent || (checkPercent && width_->GetDimension().Unit() != DimensionUnit::PERCENT)); 147 } 148 149 bool HeightFixed(bool checkPercent = true) const 150 { 151 return height_ && (!checkPercent || (checkPercent && height_->GetDimension().Unit() != DimensionUnit::PERCENT)); 152 } 153 PercentWidth()154 bool PercentWidth() const 155 { 156 return width_ && width_->GetDimension().Unit() == DimensionUnit::PERCENT; 157 } 158 PercentHeight()159 bool PercentHeight() const 160 { 161 return height_ && height_->GetDimension().Unit() == DimensionUnit::PERCENT; 162 } 163 ToString()164 std::string ToString() const 165 { 166 static const int32_t precision = 2; 167 std::stringstream ss; 168 ss << "[" << std::fixed << std::setprecision(precision); 169 ss << (width_ ? width_->ToString() : "NA"); 170 ss << " x "; 171 ss << (height_ ? height_->ToString() : "NA"); 172 ss << "]"; 173 std::string output = ss.str(); 174 return output; 175 } 176 177 private: 178 std::optional<CalcLength> width_; 179 std::optional<CalcLength> height_; 180 }; 181 182 struct MeasureProperty { 183 std::optional<CalcSize> minSize; 184 std::optional<CalcSize> maxSize; 185 std::optional<CalcSize> selfIdealSize; 186 ResetMeasureProperty187 void Reset() 188 { 189 minSize.reset(); 190 maxSize.reset(); 191 selfIdealSize.reset(); 192 } 193 194 bool operator==(const MeasureProperty& measureProperty) const 195 { 196 return (minSize == measureProperty.minSize) && (maxSize == measureProperty.maxSize) && 197 (selfIdealSize == measureProperty.selfIdealSize); 198 } 199 UpdateSelfIdealSizeWithCheckMeasureProperty200 bool UpdateSelfIdealSizeWithCheck(const CalcSize& size) 201 { 202 if (selfIdealSize == size) { 203 return false; 204 } 205 if (selfIdealSize.has_value()) { 206 return selfIdealSize->UpdateSizeWithCheck(size); 207 } 208 selfIdealSize = size; 209 return true; 210 } 211 ClearSelfIdealSizeMeasureProperty212 bool ClearSelfIdealSize(bool clearWidth, bool clearHeight) 213 { 214 if (selfIdealSize.has_value()) { 215 return selfIdealSize->ClearSize(clearWidth, clearHeight); 216 } 217 return false; 218 } 219 UpdateMaxSizeWithCheckMeasureProperty220 bool UpdateMaxSizeWithCheck(const CalcSize& size) 221 { 222 if (maxSize == size) { 223 return false; 224 } 225 if (maxSize.has_value()) { 226 return maxSize->UpdateSizeWithCheck(size); 227 } 228 maxSize = size; 229 return true; 230 } 231 UpdateMinSizeWithCheckMeasureProperty232 bool UpdateMinSizeWithCheck(const CalcSize& size) 233 { 234 if (minSize == size) { 235 return false; 236 } 237 if (minSize.has_value()) { 238 return minSize->UpdateSizeWithCheck(size); 239 } 240 minSize = size; 241 return true; 242 } 243 PercentWidthMeasureProperty244 bool PercentWidth() const 245 { 246 if (selfIdealSize.has_value()) { 247 return selfIdealSize->PercentWidth(); 248 } 249 if (maxSize.has_value()) { 250 return maxSize->PercentWidth(); 251 } 252 if (minSize.has_value()) { 253 return minSize->PercentWidth(); 254 } 255 return false; 256 } 257 PercentHeightMeasureProperty258 bool PercentHeight() const 259 { 260 if (selfIdealSize.has_value()) { 261 return selfIdealSize->PercentHeight(); 262 } 263 if (maxSize.has_value()) { 264 return maxSize->PercentHeight(); 265 } 266 if (minSize.has_value()) { 267 return minSize->PercentHeight(); 268 } 269 return false; 270 } 271 ToStringMeasureProperty272 std::string ToString() const 273 { 274 std::string str; 275 str.append("minSize: [").append(minSize.has_value() ? minSize->ToString() : "NA").append("]"); 276 str.append("maxSize: [").append(maxSize.has_value() ? maxSize->ToString() : "NA").append("]"); 277 str.append("selfIdealSize: [").append(selfIdealSize.has_value() ? selfIdealSize->ToString() : "NA").append("]"); 278 return str; 279 } 280 ToJsonValueMeasureProperty281 void ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const 282 { 283 // this may affect XTS, check later. 284 auto context = PipelineBase::GetCurrentContext(); 285 if (context && context->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_ELEVEN)) { 286 #if !defined(PREVIEW) 287 /* no fixed attr below */ 288 if (!filter.IsFastFilter()) { 289 std::string width = selfIdealSize.has_value() ? 290 (selfIdealSize.value().Width().has_value() ? 291 selfIdealSize.value().Width().value().ToString() : "-") : "-"; 292 std::string height = selfIdealSize.has_value() ? 293 (selfIdealSize.value().Height().has_value() ? 294 selfIdealSize.value().Height().value().ToString() : "-") : "-"; 295 json->PutExtAttr("width", width.c_str(), filter); 296 json->PutExtAttr("height", height.c_str(), filter); 297 298 auto jsonSize = JsonUtil::Create(true); 299 jsonSize->Put("width", width.c_str()); 300 jsonSize->Put("height", height.c_str()); 301 json->PutExtAttr("size", jsonSize, filter); 302 } 303 #else 304 ToJsonValue_GetJsonSize(json, filter); 305 #endif 306 } else { 307 ToJsonValue_GetJsonSize(json, filter); 308 } 309 310 /* no fixed attr below, just return */ 311 if (filter.IsFastFilter()) { 312 return; 313 } 314 auto jsonConstraintSize = JsonUtil::Create(true); 315 jsonConstraintSize->Put("minWidth", 316 minSize.value_or(CalcSize()).Width().value_or(CalcLength(0, DimensionUnit::VP)).ToString().c_str()); 317 jsonConstraintSize->Put("minHeight", 318 minSize.value_or(CalcSize()).Height().value_or(CalcLength(0, DimensionUnit::VP)).ToString().c_str()); 319 jsonConstraintSize->Put("maxWidth", maxSize.value_or(CalcSize()) 320 .Width() 321 .value_or(CalcLength(Infinity<double>(), DimensionUnit::VP)) 322 .ToString() 323 .c_str()); 324 jsonConstraintSize->Put("maxHeight", maxSize.value_or(CalcSize()) 325 .Height() 326 .value_or(CalcLength(Infinity<double>(), DimensionUnit::VP)) 327 .ToString() 328 .c_str()); 329 json->PutExtAttr("constraintSize", jsonConstraintSize->ToString().c_str(), filter); 330 } 331 ToJsonValue_GetJsonSizeMeasureProperty332 void ToJsonValue_GetJsonSize(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const 333 { 334 /* no fixed attr below, just return */ 335 if (filter.IsFastFilter()) { 336 return; 337 } 338 auto jsonSize = JsonUtil::Create(true); 339 if (selfIdealSize.has_value()) { 340 if (selfIdealSize.value().Width().has_value()) { 341 auto widthStr = selfIdealSize.value().Width().value().ToString(); 342 json->PutExtAttr("width", widthStr.c_str(), filter); 343 jsonSize->Put("width", widthStr.c_str()); 344 } 345 if (selfIdealSize.value().Height().has_value()) { 346 auto heightStr = selfIdealSize.value().Height().value().ToString(); 347 json->PutExtAttr("height", heightStr.c_str(), filter); 348 jsonSize->Put("height", heightStr.c_str()); 349 } 350 } 351 json->PutExtAttr("size", jsonSize, filter); 352 } 353 FromJsonMeasureProperty354 static MeasureProperty FromJson(const std::unique_ptr<JsonValue>& json) 355 { 356 MeasureProperty ans; 357 auto width = json->GetString("width"); 358 auto height = json->GetString("height"); 359 if (width != "-" || height != "-") { 360 ans.selfIdealSize = 361 CalcSize(width != "-" ? std::optional<CalcLength>(Dimension::FromString(width)) : std::nullopt, 362 height != "-" ? std::optional<CalcLength>(Dimension::FromString(height)) : std::nullopt); 363 } 364 return ans; 365 } 366 }; 367 368 template<typename T> 369 struct PaddingPropertyT { 370 std::optional<T> left; 371 std::optional<T> right; 372 std::optional<T> top; 373 std::optional<T> bottom; 374 std::optional<T> start; 375 std::optional<T> end; 376 SetEdgesPaddingPropertyT377 void SetEdges(const T& padding) 378 { 379 left = padding; 380 right = padding; 381 top = padding; 382 bottom = padding; 383 } 384 SetEdgesPaddingPropertyT385 void SetEdges(const T& leftValue, const T& rightValue, const T& topValue, const T& bottomValue) 386 { 387 left = leftValue; 388 right = rightValue; 389 top = topValue; 390 bottom = bottomValue; 391 } 392 393 bool operator==(const PaddingPropertyT& value) const 394 { 395 return (left == value.left) && (right == value.right) && (top == value.top) && (bottom == value.bottom); 396 } 397 398 bool operator!=(const PaddingPropertyT& value) const 399 { 400 return !(*this == value); 401 } 402 UpdateWithCheckPaddingPropertyT403 bool UpdateWithCheck(const PaddingPropertyT& value) 404 { 405 if (value.start.has_value() || value.end.has_value()) { 406 return UpdateLocalizedPadding(value); 407 } 408 start.reset(); 409 end.reset(); 410 if (*this != value) { 411 left = value.left; 412 right = value.right; 413 top = value.top; 414 bottom = value.bottom; 415 return true; 416 } 417 return false; 418 } 419 UpdateLocalizedPaddingPaddingPropertyT420 bool UpdateLocalizedPadding(const PaddingPropertyT& value) 421 { 422 bool needUpdate = false; 423 if (value.start.has_value() && start != value.start) { 424 start = value.start; 425 needUpdate = true; 426 } 427 if (value.end.has_value() && end != value.end) { 428 end = value.end; 429 needUpdate = true; 430 } 431 if (value.top.has_value() && top != value.top) { 432 top = value.top; 433 needUpdate = true; 434 } 435 if (value.bottom.has_value() && bottom != value.bottom) { 436 bottom = value.bottom; 437 needUpdate = true; 438 } 439 return needUpdate; 440 } 441 ToStringPaddingPropertyT442 std::string ToString() const 443 { 444 std::string str; 445 str.append("[").append(left.has_value() ? left->ToString() : "NA"); 446 str.append(",").append(right.has_value() ? right->ToString() : "NA"); 447 str.append(",").append(top.has_value() ? top->ToString() : "NA"); 448 str.append(",").append(bottom.has_value() ? bottom->ToString() : "NA").append("]"); 449 return str; 450 } ToJsonStringPaddingPropertyT451 std::string ToJsonString() const 452 { 453 if (top == right && right == bottom && bottom == left) { 454 if (top.has_value()) { 455 return top->ToString(); 456 } 457 return "0.0"; 458 } 459 auto jsonValue = JsonUtil::Create(true); 460 jsonValue->Put("top", top.has_value() ? top->ToString().c_str() : T{}.ToString().c_str()); 461 jsonValue->Put("right", right.has_value() ? right->ToString().c_str() : T{}.ToString().c_str()); 462 jsonValue->Put("bottom", bottom.has_value() ? bottom->ToString().c_str() : T{}.ToString().c_str()); 463 jsonValue->Put("left", left.has_value() ? left->ToString().c_str() : T{}.ToString().c_str()); 464 return jsonValue->ToString(); 465 } 466 FromJsonStringPaddingPropertyT467 static PaddingPropertyT FromJsonString(const std::string& str) 468 { 469 PaddingPropertyT property; 470 471 if (str.empty()) { 472 LOGE("UITree |ERROR| empty string"); 473 return property; 474 } 475 476 if (str[0] >= '0' && str[0] <= '9') { 477 property.top = property.right = property.bottom = property.left = T::FromString(str); 478 } else if (str[0] == '{') { 479 auto json = JsonUtil::ParseJsonString(str); 480 if (!json->IsValid()) { 481 return property; 482 } 483 property.top = T::FromString(json->GetString("top")); 484 property.right = T::FromString(json->GetString("right")); 485 property.bottom = T::FromString(json->GetString("bottom")); 486 property.left = T::FromString(json->GetString("left")); 487 } else { 488 LOGE("UITree |ERROR| invalid str=%{public}s", str.c_str()); 489 } 490 491 return property; 492 } 493 }; 494 495 template<> 496 struct PaddingPropertyT<float> { 497 std::optional<float> left; 498 std::optional<float> right; 499 std::optional<float> top; 500 std::optional<float> bottom; 501 502 bool operator==(const PaddingPropertyT<float>& value) const 503 { 504 if (left.has_value() ^ value.left.has_value()) { 505 return false; 506 } 507 if (!NearEqual(left.value_or(0), value.left.value_or(0))) { 508 return false; 509 } 510 if (right.has_value() ^ value.right.has_value()) { 511 return false; 512 } 513 if (!NearEqual(right.value_or(0), value.right.value_or(0))) { 514 return false; 515 } 516 if (top.has_value() ^ value.top.has_value()) { 517 return false; 518 } 519 if (!NearEqual(top.value_or(0), value.top.value_or(0))) { 520 return false; 521 } 522 if (bottom.has_value() ^ value.bottom.has_value()) { 523 return false; 524 } 525 if (!NearEqual(bottom.value_or(0), value.bottom.value_or(0))) { 526 return false; 527 } 528 return true; 529 } 530 531 std::string ToString() const 532 { 533 std::string str; 534 str.append("[").append(left.has_value() ? std::to_string(left.value()) : "NA"); 535 str.append(",").append(right.has_value() ? std::to_string(right.value()) : "NA"); 536 str.append(",").append(top.has_value() ? std::to_string(top.value()) : "NA"); 537 str.append(",").append(bottom.has_value() ? std::to_string(bottom.value()) : "NA").append("]"); 538 return str; 539 } 540 541 std::string ToJsonString() const 542 { 543 auto jsonValue = JsonUtil::Create(true); 544 jsonValue->Put("top", top.has_value() ? std::to_string(top.value()).c_str() : BORDERZERO.c_str()); 545 jsonValue->Put("right", right.has_value() ? std::to_string(right.value()).c_str() : BORDERZERO.c_str()); 546 jsonValue->Put("bottom", bottom.has_value() ? std::to_string(bottom.value()).c_str() : BORDERZERO.c_str()); 547 jsonValue->Put("left", left.has_value() ? std::to_string(left.value()).c_str() : BORDERZERO.c_str()); 548 return jsonValue->ToString(); 549 } 550 551 float Width() const 552 { 553 return left.value_or(0.0f) + right.value_or(0.0f); 554 } 555 556 float Height() const 557 { 558 return top.value_or(0.0f) + bottom.value_or(0.0f); 559 } 560 561 SizeF Size() const 562 { 563 return SizeF(Width(), Height()); 564 } 565 566 OffsetF Offset() const 567 { 568 return OffsetF(left.value_or(0.0f), top.value_or(0.0f)); 569 } 570 571 bool HasValue() const 572 { 573 return (left && !NearZero(left.value())) || (right && !NearZero(right.value())) || 574 (top && !NearZero(top.value())) || (bottom && !NearZero(bottom.value())); 575 } 576 577 void Reset() 578 { 579 left.reset(); 580 right.reset(); 581 top.reset(); 582 bottom.reset(); 583 } 584 585 bool Empty() 586 { 587 return !left.has_value() && !right.has_value() && !top.has_value() && !bottom.has_value(); 588 } 589 590 PaddingPropertyT<float> Plus(const PaddingPropertyT<float>& another, bool skipNullOpt = true) 591 { 592 return Calculate(another, skipNullOpt, true); 593 } 594 595 PaddingPropertyT<float> Minus(const PaddingPropertyT<float>& another, bool skipNullOpt = true) const 596 { 597 return Calculate(another, skipNullOpt, false); 598 } 599 600 bool AllSidesFilled(bool checkZero = false) 601 { 602 // checking all sides has values and not zero 603 if (checkZero) { 604 return !NearZero(left.value_or(0.0f)) && !NearZero(right.value_or(0.0f)) && !NearZero(top.value_or(0.0f)) && 605 !NearZero(bottom.value_or(0.0f)); 606 } 607 return left.has_value() && right.has_value() && top.has_value() && bottom.has_value(); 608 } 609 610 bool OptionalValueCover(const PaddingPropertyT<float>& another) 611 { 612 if (another.left.has_value() && !left.has_value()) { 613 return false; 614 } 615 if (another.right.has_value() && !right.has_value()) { 616 return false; 617 } 618 if (another.top.has_value() && !top.has_value()) { 619 return false; 620 } 621 if (another.bottom.has_value() && !bottom.has_value()) { 622 return false; 623 } 624 return true; 625 } 626 627 private: 628 PaddingPropertyT<float> Calculate(const PaddingPropertyT<float>& another, bool skipNullOpt, bool isAdd) const 629 { 630 PaddingPropertyT<float> result; 631 // skipNullOpt needs at least one padding has value to keep nullopt if two sides both null, 632 // !skipNullOpt needs both sides has value 633 int32_t factor = isAdd ? 1.0f : -1.0f; 634 // to resolve cyclomatic complexity 635 bool calculateCondition = (!skipNullOpt && left.has_value() && another.left.has_value()) || 636 (skipNullOpt && (left.has_value() || another.left.has_value())); 637 if (calculateCondition) { 638 result.left = left.value_or(0.0f) + factor * another.left.value_or(0.0f); 639 } 640 calculateCondition = (!skipNullOpt && right.has_value() && another.right.has_value()) || 641 (skipNullOpt && (right.has_value() || another.right.has_value())); 642 if (calculateCondition) { 643 result.right = right.value_or(0.0f) + factor * another.right.value_or(0.0f); 644 } 645 calculateCondition = (!skipNullOpt && top.has_value() && another.top.has_value()) || 646 (skipNullOpt && (top.has_value() || another.top.has_value())); 647 if (calculateCondition) { 648 result.top = top.value_or(0.0f) + factor * another.top.value_or(0.0f); 649 } 650 calculateCondition = (!skipNullOpt && bottom.has_value() && another.bottom.has_value()) || 651 (skipNullOpt && (bottom.has_value() || another.bottom.has_value())); 652 if (calculateCondition) { 653 result.bottom = bottom.value_or(0.0f) + factor * another.bottom.value_or(0.0f); 654 } 655 return result; 656 } 657 }; 658 659 using PaddingProperty = PaddingPropertyT<CalcLength>; 660 using MarginProperty = PaddingProperty; 661 using PaddingPropertyF = PaddingPropertyT<float>; 662 using MarginPropertyF = PaddingPropertyT<float>; 663 } // namespace OHOS::Ace::NG 664 665 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PROPERTIES_MEASURE_PROPERTIES_H 666