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