1/*
2 * Copyright (c) 2023 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
16namespace OHOS::Ace::NG {
17template<typename T>
18void LayoutConstraintT<T>::ApplyAspectRatio(
19    float ratio, const std::optional<CalcSize>& calcSize, bool greaterThanApiTen)
20{
21    if (!Positive(ratio)) {
22        // just in case ratio is illegal value
23        return;
24    }
25    std::optional<bool> useDefinedWidth;
26    if (calcSize) {
27        if (calcSize.value().Width()) {
28            useDefinedWidth = true;
29        } else if (calcSize.value().Height()) {
30            useDefinedWidth = false;
31        }
32    }
33    if (useDefinedWidth) {
34        if (useDefinedWidth.value_or(false)) {
35            if (selfIdealSize.Width()) {
36                selfIdealSize.SetHeight(selfIdealSize.Width().value() / ratio);
37            }
38            minSize.SetHeight(minSize.Width() / ratio);
39            maxSize.SetHeight(maxSize.Width() / ratio);
40            ApplyAspectRatioToParentIdealSize(true, ratio);
41            return;
42        }
43        if (selfIdealSize.Height()) {
44            selfIdealSize.SetWidth(selfIdealSize.Height().value() * ratio);
45        }
46        minSize.SetWidth(minSize.Height() * ratio);
47        maxSize.SetWidth(maxSize.Height() * ratio);
48        ApplyAspectRatioToParentIdealSize(false, ratio);
49        return;
50    } else {
51        if (selfIdealSize.Width()) {
52            selfIdealSize.SetHeight(selfIdealSize.Width().value() / ratio);
53            minSize.SetHeight(minSize.Width() / ratio);
54            maxSize.SetHeight(maxSize.Width() / ratio);
55            ApplyAspectRatioToParentIdealSize(true, ratio);
56            return;
57        }
58        if (selfIdealSize.Height()) {
59            selfIdealSize.SetWidth(selfIdealSize.Height().value() * ratio);
60            minSize.SetWidth(minSize.Height() * ratio);
61            maxSize.SetWidth(maxSize.Height() * ratio);
62            ApplyAspectRatioToParentIdealSize(false, ratio);
63            return;
64        }
65    }
66    // after previous conditions, ideal size does not exist, we use max size to rule aspect ratio
67    // but nothing can be done if both width and height are inf
68    if (NearEqual(maxSize.Width(), Infinity<T>()) && NearEqual(maxSize.Height(), Infinity<T>())) {
69        return;
70    }
71    ApplyAspectRatioByMaxSize(ratio, useDefinedWidth, greaterThanApiTen);
72}
73
74template<typename T>
75void LayoutConstraintT<T>::ApplyAspectRatioToParentIdealSize(bool useWidth, float ratio)
76{
77    if (!Positive(ratio)) {
78        return;
79    }
80    if (useWidth && parentIdealSize.Width()) {
81        parentIdealSize.SetHeight(parentIdealSize.Width().value() / ratio);
82        return;
83    }
84    if (!parentIdealSize.Height()) {
85        return;
86    }
87    parentIdealSize.SetWidth(parentIdealSize.Height().value() * ratio);
88}
89
90template<typename T>
91void LayoutConstraintT<T>::ApplyAspectRatioByMaxSize(
92    float ratio, std::optional<bool> useDefinedWidth, bool greaterThanApiTen)
93{
94    if (!Positive(ratio)) {
95        return;
96    }
97    if (useDefinedWidth) {
98        ApplyAspectRatioWithCalcSize(ratio, useDefinedWidth.value());
99        return;
100    }
101    ApplyAspectRatioWithoutCalcSize(ratio, greaterThanApiTen);
102}
103
104template<typename T>
105void LayoutConstraintT<T>::ApplyAspectRatioWithCalcSize(float ratio, bool useDefinedWidth)
106{
107    if (!Positive(ratio)) {
108        return;
109    }
110    if (useDefinedWidth) {
111        minSize.SetHeight(minSize.Width() / ratio);
112        maxSize.SetHeight(maxSize.Width() / ratio);
113        percentReference.SetHeight(percentReference.Width() / ratio);
114        ApplyAspectRatioToParentIdealSize(true, ratio);
115        return;
116    }
117    minSize.SetWidth(minSize.Height() * ratio);
118    maxSize.SetWidth(maxSize.Height() * ratio);
119    percentReference.SetWidth(percentReference.Height() / ratio);
120    ApplyAspectRatioToParentIdealSize(false, ratio);
121}
122
123template<typename T>
124void LayoutConstraintT<T>::ApplyAspectRatioWithoutCalcSize(float ratio, bool greaterThanApiTen)
125{
126    if (!Positive(ratio)) {
127        return;
128    }
129    if (greaterThanApiTen) {
130        if (percentReference.Height() * ratio > maxSize.Width()) {
131            minSize.SetHeight(minSize.Width() / ratio);
132            maxSize.SetHeight(maxSize.Width() / ratio);
133            percentReference.SetHeight(percentReference.Width() / ratio);
134            ApplyAspectRatioToParentIdealSize(true, ratio);
135            return;
136        }
137        if (percentReference.Width() / ratio > maxSize.Height()) {
138            minSize.SetWidth(minSize.Height() * ratio);
139            maxSize.SetWidth(maxSize.Height() * ratio);
140            percentReference.SetWidth(percentReference.Height() * ratio);
141            ApplyAspectRatioToParentIdealSize(false, ratio);
142        }
143        return;
144    } else {
145        if (maxSize.Width() < maxSize.Height()) {
146            minSize.SetHeight(minSize.Width() / ratio);
147            maxSize.SetHeight(maxSize.Width() / ratio);
148            percentReference.SetHeight(percentReference.Width() / ratio);
149            ApplyAspectRatioToParentIdealSize(true, ratio);
150            return;
151        }
152    }
153    minSize.SetWidth(minSize.Height() * ratio);
154    maxSize.SetWidth(maxSize.Height() * ratio);
155    percentReference.SetWidth(percentReference.Height() / ratio);
156    ApplyAspectRatioToParentIdealSize(false, ratio);
157}
158
159template<typename T>
160void LayoutConstraintT<T>::Reset()
161{
162    scaleProperty.Reset();
163    minSize = { 0, 0 };
164    maxSize = { Infinity<T>(), Infinity<T>() };
165    percentReference = { 0, 0 };
166    parentIdealSize.Reset();
167    selfIdealSize.Reset();
168}
169
170template<typename T>
171void LayoutConstraintT<T>::MinusPadding(const std::optional<T>& left, const std::optional<T>& right,
172    const std::optional<T>& top, const std::optional<T>& bottom)
173{
174    minSize.MinusPadding(left, right, top, bottom);
175    maxSize.MinusPadding(left, right, top, bottom);
176    parentIdealSize.MinusPadding(left, right, top, bottom);
177    selfIdealSize.MinusPadding(left, right, top, bottom);
178    percentReference.MinusPadding(left, right, top, bottom);
179}
180
181template<typename T>
182void LayoutConstraintT<T>::MinusPaddingToNonNegativeSize(const std::optional<T>& left, const std::optional<T>& right,
183    const std::optional<T>& top, const std::optional<T>& bottom)
184{
185    minSize.MinusPaddingToNonNegative(left, right, top, bottom);
186    maxSize.MinusPaddingToNonNegative(left, right, top, bottom);
187    parentIdealSize.MinusPadding(left, right, top, bottom);
188    selfIdealSize.MinusPadding(left, right, top, bottom);
189    percentReference.MinusPaddingToNonNegative(left, right, top, bottom);
190}
191
192template<typename T>
193std::string LayoutConstraintT<T>::ToString() const
194{
195    std::string str;
196    str.append("minSize: [").append(minSize.ToString()).append("]");
197    str.append("maxSize: [").append(maxSize.ToString()).append("]");
198    str.append("percentReference: [").append(percentReference.ToString()).append("]");
199    str.append("parentIdealSize: [").append(parentIdealSize.ToString()).append("]");
200    str.append("selfIdealSize: [").append(selfIdealSize.ToString()).append("]");
201    return str;
202}
203
204template<typename T>
205SizeF LayoutConstraintT<T>::Constrain(const SizeF& size) const
206{
207    SizeF constrainSize;
208    constrainSize.SetWidth(std::clamp(size.Width(), minSize.Width(), maxSize.Width()));
209    constrainSize.SetHeight(std::clamp(size.Height(), minSize.Height(), maxSize.Height()));
210    return constrainSize;
211}
212} // namespace OHOS::Ace::NG