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/components_ng/property/calc_length.h" 31 32 namespace OHOS::Ace::NG { 33 34 enum class MeasureType { 35 MATCH_PARENT, 36 MATCH_CONTENT, 37 MATCH_PARENT_CROSS_AXIS, 38 MATCH_PARENT_MAIN_AXIS, 39 }; 40 41 class CalcSize { 42 public: 43 CalcSize() = default; 44 ~CalcSize() = default; CalcSize(const CalcLength & width,const CalcLength & height)45 CalcSize(const CalcLength& width, const CalcLength& height) : width_(width), height_(height) {} CalcSize(std::optional<CalcLength> width,std::optional<CalcLength> height)46 CalcSize(std::optional<CalcLength> width, std::optional<CalcLength> height) 47 : width_(std::move(width)), height_(std::move(height)) 48 {} 49 Reset()50 void Reset() 51 { 52 width_.reset(); 53 height_.reset(); 54 } 55 IsValid()56 bool IsValid() const 57 { 58 return width_ && height_; 59 } 60 Width()61 const std::optional<CalcLength>& Width() const 62 { 63 return width_; 64 } 65 Height()66 const std::optional<CalcLength>& Height() const 67 { 68 return height_; 69 } 70 SetWidth(const std::optional<CalcLength> & width)71 void SetWidth(const std::optional<CalcLength>& width) 72 { 73 width_ = width; 74 } 75 SetHeight(const std::optional<CalcLength> & height)76 void SetHeight(const std::optional<CalcLength>& height) 77 { 78 height_ = height; 79 } 80 SetSizeT(const CalcSize & Size)81 void SetSizeT(const CalcSize& Size) 82 { 83 width_ = Size.Width(); 84 height_ = Size.Height(); 85 } 86 87 bool operator==(const CalcSize& Size) const 88 { 89 return (width_ == Size.width_) && (height_ == Size.height_); 90 } 91 92 bool operator!=(const CalcSize& Size) const 93 { 94 return !operator==(Size); 95 } 96 UpdateSizeWithCheck(const CalcSize & size)97 bool UpdateSizeWithCheck(const CalcSize& size) 98 { 99 if ((width_ == size.width_) && ((height_ == size.height_))) { 100 return false; 101 } 102 if (size.width_) { 103 width_ = size.width_; 104 } 105 if (size.height_) { 106 height_ = size.height_; 107 } 108 return true; 109 } 110 ClearSize(bool clearWidth,bool clearHeight)111 bool ClearSize(bool clearWidth, bool clearHeight) 112 { 113 bool changed = false; 114 if (clearWidth && width_.has_value()) { 115 width_.reset(); 116 changed = true; 117 } 118 if (clearHeight && height_.has_value()) { 119 height_.reset(); 120 changed = true; 121 } 122 return changed; 123 } 124 WidthFixed()125 bool WidthFixed() const 126 { 127 return width_ && width_->GetDimension().Unit() != DimensionUnit::PERCENT; 128 } 129 HeightFixed()130 bool HeightFixed() const 131 { 132 return height_ && height_->GetDimension().Unit() != DimensionUnit::PERCENT; 133 } 134 PercentWidth()135 bool PercentWidth() const 136 { 137 return width_ && width_->GetDimension().Unit() == DimensionUnit::PERCENT; 138 } 139 PercentHeight()140 bool PercentHeight() const 141 { 142 return height_ && height_->GetDimension().Unit() == DimensionUnit::PERCENT; 143 } 144 ToString()145 std::string ToString() const 146 { 147 static const int32_t precision = 2; 148 std::stringstream ss; 149 ss << "[" << std::fixed << std::setprecision(precision); 150 ss << (width_ ? width_->ToString() : "NA"); 151 ss << " x "; 152 ss << (height_ ? height_->ToString() : "NA"); 153 ss << "]"; 154 std::string output = ss.str(); 155 return output; 156 } 157 158 private: 159 std::optional<CalcLength> width_; 160 std::optional<CalcLength> height_; 161 }; 162 163 struct MeasureProperty { 164 std::optional<CalcSize> minSize; 165 std::optional<CalcSize> maxSize; 166 std::optional<CalcSize> selfIdealSize; 167 ResetMeasureProperty168 void Reset() 169 { 170 minSize.reset(); 171 maxSize.reset(); 172 selfIdealSize.reset(); 173 } 174 175 bool operator==(const MeasureProperty& measureProperty) const 176 { 177 return (minSize == measureProperty.minSize) && (maxSize == measureProperty.maxSize) && 178 (selfIdealSize == measureProperty.selfIdealSize); 179 } 180 UpdateSelfIdealSizeWithCheckMeasureProperty181 bool UpdateSelfIdealSizeWithCheck(const CalcSize& size) 182 { 183 if (selfIdealSize == size) { 184 return false; 185 } 186 if (selfIdealSize.has_value()) { 187 return selfIdealSize->UpdateSizeWithCheck(size); 188 } 189 selfIdealSize = size; 190 return true; 191 } 192 ClearSelfIdealSizeMeasureProperty193 bool ClearSelfIdealSize(bool clearWidth, bool clearHeight) 194 { 195 if (selfIdealSize.has_value()) { 196 return selfIdealSize->ClearSize(clearWidth, clearHeight); 197 } 198 return false; 199 } 200 UpdateMaxSizeWithCheckMeasureProperty201 bool UpdateMaxSizeWithCheck(const CalcSize& size) 202 { 203 if (maxSize == size) { 204 return false; 205 } 206 if (maxSize.has_value()) { 207 return maxSize->UpdateSizeWithCheck(size); 208 } 209 maxSize = size; 210 return true; 211 } 212 UpdateMinSizeWithCheckMeasureProperty213 bool UpdateMinSizeWithCheck(const CalcSize& size) 214 { 215 if (minSize == size) { 216 return false; 217 } 218 if (minSize.has_value()) { 219 return minSize->UpdateSizeWithCheck(size); 220 } 221 minSize = size; 222 return true; 223 } 224 PercentWidthMeasureProperty225 bool PercentWidth() const 226 { 227 if (selfIdealSize.has_value()) { 228 return selfIdealSize->PercentWidth(); 229 } 230 if (maxSize.has_value()) { 231 return maxSize->PercentWidth(); 232 } 233 if (minSize.has_value()) { 234 return minSize->PercentWidth(); 235 } 236 return false; 237 } 238 PercentHeightMeasureProperty239 bool PercentHeight() const 240 { 241 if (selfIdealSize.has_value()) { 242 return selfIdealSize->PercentHeight(); 243 } 244 if (maxSize.has_value()) { 245 return maxSize->PercentHeight(); 246 } 247 if (minSize.has_value()) { 248 return minSize->PercentHeight(); 249 } 250 return false; 251 } 252 ToStringMeasureProperty253 std::string ToString() const 254 { 255 std::string str; 256 str.append("minSize: [").append(minSize.has_value() ? minSize->ToString() : "NA").append("]"); 257 str.append("maxSize: [").append(maxSize.has_value() ? maxSize->ToString() : "NA").append("]"); 258 str.append("selfIdealSize: [").append(selfIdealSize.has_value() ? selfIdealSize->ToString() : "NA").append("]"); 259 return str; 260 } 261 ToJsonValueMeasureProperty262 void ToJsonValue(std::unique_ptr<JsonValue>& json) const 263 { 264 // this may affect XTS, check later. 265 #if !defined(PREVIEW) 266 std::string width = 267 selfIdealSize.has_value() 268 ? (selfIdealSize.value().Width().has_value() ? selfIdealSize.value().Width().value().ToString() : "-") 269 : "-"; 270 std::string height = 271 selfIdealSize.has_value() 272 ? (selfIdealSize.value().Height().has_value() ? selfIdealSize.value().Height().value().ToString() : "-") 273 : "-"; 274 json->Put("width", width.c_str()); 275 json->Put("height", height.c_str()); 276 277 auto jsonSize = JsonUtil::Create(true); 278 jsonSize->Put("width", width.c_str()); 279 jsonSize->Put("height", height.c_str()); 280 json->Put("size", jsonSize); 281 #else 282 auto jsonSize = JsonUtil::Create(true); 283 if (selfIdealSize.has_value()) { 284 if (selfIdealSize.value().Width().has_value()) { 285 auto widthStr = selfIdealSize.value().Width().value().ToString(); 286 json->Put("width", widthStr.c_str()); 287 jsonSize->Put("width", widthStr.c_str()); 288 } 289 if (selfIdealSize.value().Height().has_value()) { 290 auto heightStr = selfIdealSize.value().Height().value().ToString(); 291 json->Put("height", heightStr.c_str()); 292 jsonSize->Put("height", heightStr.c_str()); 293 } 294 } 295 json->Put("size", jsonSize); 296 #endif 297 298 auto jsonConstraintSize = JsonUtil::Create(true); 299 jsonConstraintSize->Put("minWidth", 300 minSize.value_or(CalcSize()).Width().value_or(CalcLength(0, DimensionUnit::VP)).ToString().c_str()); 301 jsonConstraintSize->Put("minHeight", 302 minSize.value_or(CalcSize()).Height().value_or(CalcLength(0, DimensionUnit::VP)).ToString().c_str()); 303 jsonConstraintSize->Put("maxWidth", maxSize.value_or(CalcSize()) 304 .Width() 305 .value_or(CalcLength(Infinity<double>(), DimensionUnit::VP)) 306 .ToString() 307 .c_str()); 308 jsonConstraintSize->Put("maxHeight", maxSize.value_or(CalcSize()) 309 .Height() 310 .value_or(CalcLength(Infinity<double>(), DimensionUnit::VP)) 311 .ToString() 312 .c_str()); 313 json->Put("constraintSize", jsonConstraintSize->ToString().c_str()); 314 } 315 FromJsonMeasureProperty316 static MeasureProperty FromJson(const std::unique_ptr<JsonValue>& json) 317 { 318 MeasureProperty ans; 319 auto width = json->GetString("width"); 320 auto height = json->GetString("height"); 321 LOGD("UITree width=%{public}s height=%{public}s", width.c_str(), height.c_str()); 322 if (width != "-" || height != "-") { 323 ans.selfIdealSize = 324 CalcSize(width != "-" ? std::optional<CalcLength>(Dimension::FromString(width)) : std::nullopt, 325 height != "-" ? std::optional<CalcLength>(Dimension::FromString(height)) : std::nullopt); 326 } 327 return ans; 328 } 329 }; 330 331 template<typename T> 332 struct PaddingPropertyT { 333 std::optional<T> left; 334 std::optional<T> right; 335 std::optional<T> top; 336 std::optional<T> bottom; 337 SetEdgesPaddingPropertyT338 void SetEdges(const T& padding) 339 { 340 left = padding; 341 right = padding; 342 top = padding; 343 bottom = padding; 344 } 345 346 bool operator==(const PaddingPropertyT& value) const 347 { 348 return (left == value.left) && (right == value.right) && (top == value.top) && (bottom == value.bottom); 349 } 350 351 bool operator!=(const PaddingPropertyT& value) const 352 { 353 return !(*this == value); 354 } 355 UpdateWithCheckPaddingPropertyT356 bool UpdateWithCheck(const PaddingPropertyT& value) 357 { 358 if (*this != value) { 359 left = value.left; 360 right = value.right; 361 top = value.top; 362 bottom = value.bottom; 363 return true; 364 } 365 return false; 366 } 367 ToStringPaddingPropertyT368 std::string ToString() const 369 { 370 std::string str; 371 str.append("left: [").append(left.has_value() ? left->ToString() : "NA").append("]"); 372 str.append("right: [").append(right.has_value() ? right->ToString() : "NA").append("]"); 373 str.append("top: [").append(top.has_value() ? top->ToString() : "NA").append("]"); 374 str.append("bottom: [").append(bottom.has_value() ? bottom->ToString() : "NA").append("]"); 375 return str; 376 } ToJsonStringPaddingPropertyT377 std::string ToJsonString() const 378 { 379 if (top == right && right == bottom && bottom == left) { 380 if (top.has_value()) { 381 return top->ToString(); 382 } 383 return "0.0"; 384 } 385 auto jsonValue = JsonUtil::Create(true); 386 jsonValue->Put("top", top->ToString().c_str()); 387 jsonValue->Put("right", right->ToString().c_str()); 388 jsonValue->Put("bottom", bottom->ToString().c_str()); 389 jsonValue->Put("left", left->ToString().c_str()); 390 return jsonValue->ToString(); 391 } 392 FromJsonStringPaddingPropertyT393 static PaddingPropertyT FromJsonString(const std::string& str) 394 { 395 LOGD("UITree str=%{public}s", str.c_str()); 396 PaddingPropertyT property; 397 398 if (str.empty()) { 399 LOGE("UITree |ERROR| empty string"); 400 return property; 401 } 402 403 if (str[0] >= '0' && str[0] <= '9') { 404 LOGD("UITree decode number"); 405 property.top = property.right = property.bottom = property.left = T::FromString(str); 406 } else if (str[0] == '{') { 407 LOGD("UITree decode json"); 408 auto json = JsonUtil::ParseJsonString(str); 409 if (!json->IsValid()) { 410 LOGD("UITree invalid json [%{public}s]", json->ToString().c_str()); 411 return property; 412 } 413 property.top = T::FromString(json->GetString("top")); 414 property.right = T::FromString(json->GetString("right")); 415 property.bottom = T::FromString(json->GetString("bottom")); 416 property.left = T::FromString(json->GetString("left")); 417 } else { 418 LOGE("UITree |ERROR| invalid str=%{public}s", str.c_str()); 419 } 420 421 return property; 422 } 423 }; 424 425 template<> 426 struct PaddingPropertyT<float> { 427 std::optional<float> left; 428 std::optional<float> right; 429 std::optional<float> top; 430 std::optional<float> bottom; 431 432 bool operator==(const PaddingPropertyT<float>& value) const 433 { 434 if (left.has_value() ^ value.left.has_value()) { 435 return false; 436 } 437 if (!NearEqual(left.value_or(0), value.left.value_or(0))) { 438 return false; 439 } 440 if (right.has_value() ^ value.right.has_value()) { 441 return false; 442 } 443 if (!NearEqual(right.value_or(0), value.right.value_or(0))) { 444 return false; 445 } 446 if (top.has_value() ^ value.top.has_value()) { 447 return false; 448 } 449 if (!NearEqual(top.value_or(0), value.top.value_or(0))) { 450 return false; 451 } 452 if (bottom.has_value() ^ value.bottom.has_value()) { 453 return false; 454 } 455 if (!NearEqual(bottom.value_or(0), value.bottom.value_or(0))) { 456 return false; 457 } 458 return true; 459 } 460 461 std::string ToString() const 462 { 463 std::string str; 464 str.append("left: [").append(left.has_value() ? std::to_string(left.value()) : "NA").append("]"); 465 str.append("right: [").append(right.has_value() ? std::to_string(right.value()) : "NA").append("]"); 466 str.append("top: [").append(top.has_value() ? std::to_string(top.value()) : "NA").append("]"); 467 str.append("bottom: [").append(bottom.has_value() ? std::to_string(bottom.value()) : "NA").append("]"); 468 return str; 469 } 470 471 float Width() const 472 { 473 return left.value_or(0.0f) + right.value_or(0.0f); 474 } 475 476 float Height() const 477 { 478 return top.value_or(0.0f) + bottom.value_or(0.0f); 479 } 480 481 SizeF Size() const 482 { 483 return SizeF(Width(), Height()); 484 } 485 486 OffsetF Offset() const 487 { 488 return OffsetF(left.value_or(0.0f), top.value_or(0.0f)); 489 } 490 }; 491 492 using PaddingProperty = PaddingPropertyT<CalcLength>; 493 using MarginProperty = PaddingProperty; 494 using PaddingPropertyF = PaddingPropertyT<float>; 495 using MarginPropertyF = PaddingPropertyT<float>; 496 } // namespace OHOS::Ace::NG 497 498 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PROPERTIES_MEASURE_PROPERTIES_H