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 #include "ui/base/ace_type.h"
16 #include "ui/base/utils/utils.h"
17 #include "base/geometry/dimension.h"
18 #include "base/utils/utf_helper.h"
19 #include "core/components/common/layout/constants.h"
20 #include "core/components_ng/pattern/security_component/security_component_common.h"
21 #include "core/components_ng/pattern/security_component/security_component_layout_element.h"
22 #include "core/components_ng/pattern/security_component/security_component_layout_property.h"
23 #include "core/components_ng/pattern/security_component/security_component_theme.h"
24 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
25 #include "core/components_ng/pattern/text/text_layout_property.h"
26 #include "core/components_ng/pattern/text/text_pattern.h"
27 #include "core/components_ng/property/measure_property.h"
28 #include "core/pipeline_ng/pipeline_context.h"
29 #ifdef ENABLE_ROSEN_BACKEND
30 #include "core/components/custom_paint/rosen_render_custom_paint.h"
31 #endif
32
33 namespace OHOS::Ace::NG {
34
35 constexpr double DEFAULT_SIZE_24 = 24;
36
Init(const RefPtr<SecurityComponentLayoutProperty> & property,RefPtr<LayoutWrapper> & iconWrap)37 void IconLayoutElement::Init(const RefPtr<SecurityComponentLayoutProperty>& property,
38 RefPtr<LayoutWrapper>& iconWrap)
39 {
40 CHECK_NULL_VOID(property);
41 CHECK_NULL_VOID(iconWrap);
42 secCompProperty_ = property;
43 iconWrap_ = iconWrap;
44 bool isSymbolIcon = iconWrap->GetHostTag() == V2::SYMBOL_ETS_TAG;
45 if (isSymbolIcon &&
46 static_cast<int32_t>(property->GetSymbolIconStyle().value_or(-1)) ==
47 static_cast<int32_t>(SecurityComponentIconStyle::ICON_NULL)) {
48 return;
49 } else if (!isSymbolIcon && property->GetIconStyle().value_or(-1) ==
50 static_cast<int32_t>(SecurityComponentIconStyle::ICON_NULL)) {
51 return;
52 }
53 isExist_ = true;
54
55 auto pipeline = PipelineContext::GetCurrentContextSafely();
56 CHECK_NULL_VOID(pipeline);
57 auto theme = pipeline->GetTheme<SecurityComponentTheme>();
58 CHECK_NULL_VOID(theme);
59 minIconSize_ = theme->GetMinIconSize().ConvertToPx();
60 auto iconNode = iconWrap_->GetHostNode();
61 CHECK_NULL_VOID(iconNode);
62
63 width_ = isSymbolIcon ? Dimension(DEFAULT_SIZE_24, DimensionUnit::VP).ConvertToPx() :
64 theme->GetIconSize().ConvertToPx();
65 height_ = width_ * alpha_;
66
67 UpdateUserSetSize(property);
68
69 std::optional<NG::CalcLength> propWidth;
70 propWidth.emplace(Dimension(Dimension(width_).ConvertToVp(), DimensionUnit::VP));
71 std::optional<NG::CalcLength> propHeight;
72 propHeight.emplace(Dimension(Dimension(height_).ConvertToVp(), DimensionUnit::VP));
73 auto iconLayoutProperty = iconNode->GetLayoutProperty<ImageLayoutProperty>();
74 CHECK_NULL_VOID(iconLayoutProperty);
75 iconLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(propWidth, propHeight));
76 }
77
UpdateUserSetSize(const RefPtr<SecurityComponentLayoutProperty> & property)78 void IconLayoutElement::UpdateUserSetSize(const RefPtr<SecurityComponentLayoutProperty>& property)
79 {
80 if (property->GetIconCalcSize()->Width().has_value() && property->GetIconCalcSize()->Height().has_value()) {
81 isSetSize_ = true;
82 width_ = property->GetIconCalcSize()->Width()->GetDimension().ConvertToPx();
83 height_ = property->GetIconCalcSize()->Height()->GetDimension().ConvertToPx();
84 if (!property->GetImageSourceInfo().has_value()) {
85 double tmp = GreatNotEqual(width_, height_) ? height_ : width_;
86 width_ = height_ = tmp;
87 }
88 } else if (property->GetIconCalcSize()->Width().has_value()) {
89 isSetSize_ = true;
90 width_ = property->GetIconCalcSize()->Width()->GetDimension().ConvertToPx();
91 height_ = width_ * alpha_;
92 } else if (property->GetIconCalcSize()->Height().has_value()) {
93 isSetSize_ = true;
94 height_ = property->GetIconCalcSize()->Height()->GetDimension().ConvertToPx();
95 width_ = (NearEqual(alpha_, 0.0)) ? 0.0 : (height_ / alpha_);
96 } else if (property->GetIconSize().has_value()) {
97 isSetSize_ = true;
98 width_ = height_ = property->GetIconSize().value().ConvertToPx();
99 }
100 }
101
DoMeasure()102 void IconLayoutElement::DoMeasure()
103 {
104 if (!isExist_) {
105 return;
106 }
107 auto iconConstraint = secCompProperty_->CreateChildConstraint();
108 iconConstraint.selfIdealSize.SetWidth(width_);
109 iconConstraint.selfIdealSize.SetHeight(height_);
110 iconWrap_->Measure(iconConstraint);
111 }
112
ShrinkWidth(double reduceSize)113 double IconLayoutElement::ShrinkWidth(double reduceSize)
114 {
115 if (!isExist_ || isSetSize_) {
116 return reduceSize;
117 }
118 if (NearEqual(alpha_, 0.0)) {
119 return reduceSize;
120 }
121
122 if (GreatOrEqual(height_, width_)) {
123 if (GreatNotEqual(minIconSize_, (width_ - reduceSize))) {
124 int remain = reduceSize - (width_ - minIconSize_);
125 width_ = minIconSize_;
126 height_ = width_ * alpha_;
127 return remain;
128 }
129
130 width_ -= reduceSize;
131 height_ = width_ * alpha_;
132 return 0.0;
133 } else {
134 if (GreatNotEqual(minIconSize_, (height_ - reduceSize * alpha_))) {
135 int remain = reduceSize - (height_ - minIconSize_) / alpha_;
136 height_ = minIconSize_;
137 width_ = height_ / alpha_;
138 return remain;
139 }
140
141 width_ -= reduceSize;
142 height_ = width_ * alpha_;
143 return 0.0;
144 }
145 }
146
ShrinkHeight(double reduceSize)147 double IconLayoutElement::ShrinkHeight(double reduceSize)
148 {
149 if (!isExist_ || isSetSize_) {
150 return reduceSize;
151 }
152 if (NearEqual(alpha_, 0.0)) {
153 return reduceSize;
154 }
155
156 if (GreatOrEqual(height_, width_)) {
157 if (GreatNotEqual(minIconSize_, (width_ - reduceSize / alpha_))) {
158 int remain = reduceSize - (width_ - minIconSize_) * alpha_;
159 width_ = minIconSize_;
160 height_ = width_ * alpha_;
161 return remain;
162 }
163
164 height_ -= reduceSize;
165 width_ = height_ / alpha_;
166 return 0.0;
167 } else {
168 if (GreatNotEqual(minIconSize_, (height_ - reduceSize))) {
169 int remain = reduceSize - (height_ - minIconSize_);
170 height_ = minIconSize_;
171 width_ = height_ / alpha_;
172 return remain;
173 }
174
175 height_ -= reduceSize;
176 width_ = height_ / alpha_;
177 return 0.0;
178 }
179 }
180
UpdateFontSize()181 void TextLayoutElement::UpdateFontSize()
182 {
183 auto layoutAlgorithmWrap = textWrap_->GetLayoutAlgorithm();
184 CHECK_NULL_VOID(layoutAlgorithmWrap);
185 auto layoutAlgorithm = AceType::DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrap->GetLayoutAlgorithm());
186 CHECK_NULL_VOID(layoutAlgorithm);
187 auto textStyle = layoutAlgorithm->GetTextStyle();
188 auto textProp = AceType::DynamicCast<TextLayoutProperty>(textWrap_->GetLayoutProperty());
189 CHECK_NULL_VOID(textProp);
190 if (isAdaptive_ && !NearEqual(textStyle.GetFontSize().Value(), 0.0f)) {
191 Dimension fontSize(textStyle.GetFontSize().ConvertToFp(), DimensionUnit::FP);
192 textProp->UpdateFontSize(fontSize);
193 }
194 }
195
GetHeightConstraint(const RefPtr<SecurityComponentLayoutProperty> & property,float height)196 float TextLayoutElement::GetHeightConstraint(const RefPtr<SecurityComponentLayoutProperty>& property, float height)
197 {
198 CHECK_NULL_RETURN(property, 0.0f);
199 auto isVertical = (property->GetTextIconLayoutDirection().value_or(
200 SecurityComponentLayoutDirection::HORIZONTAL) == SecurityComponentLayoutDirection::VERTICAL);
201
202 auto textProp = AceType::DynamicCast<TextLayoutProperty>(textWrap_->GetLayoutProperty());
203 CHECK_NULL_RETURN(textProp, 0.0f);
204 auto context = PipelineContext::GetCurrentContextSafely();
205 CHECK_NULL_RETURN(context, 0.0f);
206 auto theme = context->GetTheme<SecurityComponentTheme>();
207 CHECK_NULL_RETURN(theme, 0.0f);
208 auto topPadding = property->GetBackgroundTopPadding().value_or(theme->GetBackgroundTopPadding());
209 auto bottomPadding = property->GetBackgroundBottomPadding().value_or(theme->GetBackgroundBottomPadding());
210 if (isVertical) {
211 auto iconSize = (property->GetIconSize().value_or(theme->GetIconSize()));
212 auto textIconSpace = (property->GetTextIconSpace().value_or(theme->GetTextIconSpace()));
213 return height - topPadding.Value() - bottomPadding.Value() - iconSize.Value() - textIconSpace.Value();
214 }
215
216 return height - topPadding.Value() - bottomPadding.Value();
217 }
218
Init(const RefPtr<SecurityComponentLayoutProperty> & property,RefPtr<LayoutWrapper> & textWrap)219 void TextLayoutElement::Init(const RefPtr<SecurityComponentLayoutProperty>& property,
220 RefPtr<LayoutWrapper>& textWrap)
221 {
222 secCompProperty_ = property;
223 textWrap_ = textWrap;
224 CHECK_NULL_VOID(property);
225 CHECK_NULL_VOID(textWrap);
226 if (property->GetSecurityComponentDescription().value_or(-1) ==
227 static_cast<int32_t>(SecurityComponentDescription::TEXT_NULL)) {
228 return;
229 }
230 isExist_ = true;
231
232 auto textProp = AceType::DynamicCast<TextLayoutProperty>(textWrap_->GetLayoutProperty());
233 CHECK_NULL_VOID(textProp);
234 auto context = PipelineContext::GetCurrentContextSafely();
235 CHECK_NULL_VOID(context);
236 auto theme = context->GetTheme<SecurityComponentTheme>();
237 CHECK_NULL_VOID(theme);
238 minFontSize_ = theme->GetMinFontSize();
239 if (property->GetAdaptMaxFontSize().has_value() && property->GetAdaptMinFontSize().has_value()) {
240 if (GreatOrEqual(property->GetAdaptMaxFontSize()->ConvertToFp(),
241 property->GetAdaptMinFontSize()->ConvertToFp())) {
242 textProp->UpdateFontSize(property->GetAdaptMaxFontSize().value());
243 isAdaptive_ = true;
244 }
245 isSetSize_ = true;
246 } else if (property->GetFontSize().has_value()) {
247 isSetSize_ = true;
248 } else {
249 defaultFontSize_ = theme->GetFontSize();
250 textProp->UpdateFontSize(defaultFontSize_);
251 }
252
253 auto textConstraint = property->CreateChildConstraint();
254 SizeT<float> maxSize { textConstraint.maxSize.Width(), Infinity<float>() };
255 if (isAdaptive_ && property->GetHeightAdaptivePolicy().has_value() &&
256 property->GetHeightAdaptivePolicy() == TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST) {
257 SC_LOG_DEBUG("Component height constrained.");
258 auto heightConstraint = GetHeightConstraint(property, textConstraint.maxSize.Height());
259 if (LessOrEqual(heightConstraint, 0.0f)) {
260 heightConstraint = 0.0f;
261 }
262 maxSize.SetHeight(heightConstraint);
263 }
264 textConstraint.maxSize = maxSize;
265 textWrap_->Measure(std::optional<LayoutConstraintF>(textConstraint));
266 UpdateFontSize();
267 auto geometryNode = textWrap->GetGeometryNode();
268 CHECK_NULL_VOID(geometryNode);
269 auto textSizeF = geometryNode->GetFrameSize();
270 width_ = textSizeF.Width();
271 height_ = textSizeF.Height();
272 }
273
MeasureForWidth(float width)274 void TextLayoutElement::MeasureForWidth(float width)
275 {
276 auto textProp = AceType::DynamicCast<TextLayoutProperty>(textWrap_->GetLayoutProperty());
277 CHECK_NULL_VOID(textProp);
278 auto textConstraint = textProp->GetContentLayoutConstraint();
279 CHECK_NULL_VOID(textConstraint);
280 textConstraint->selfIdealSize.SetWidth(width);
281 textWrap_->Measure(textConstraint);
282 UpdateFontSize();
283 auto textSizeF = textWrap_->GetGeometryNode()->GetFrameSize();
284 width_ = textSizeF.Width();
285 height_ = textSizeF.Height();
286 }
287
DoMeasure(bool isVertical,float minWidth,float leftSpace)288 void TextLayoutElement::DoMeasure(bool isVertical, float minWidth, float leftSpace)
289 {
290 if (!isExist_) {
291 return;
292 }
293
294 float textMaxWidth;
295 if (isVertical) {
296 textMaxWidth = minWidth > leftSpace ? minWidth : leftSpace;
297 } else {
298 textMaxWidth = minWidth > leftSpace ? minWidth - leftSpace : 0.0;
299 }
300 auto textNode = textWrap_->GetHostNode();
301 CHECK_NULL_VOID(textNode);
302 auto textPattern = textNode->GetPattern<TextPattern>();
303 CHECK_NULL_VOID(textPattern);
304
305 if (GreatNotEqual(width_, textMaxWidth)) {
306 MeasureForWidth(textMaxWidth);
307 auto realWidth = textPattern->GetLineMetrics(0).width;
308 if (LessNotEqual(width_, realWidth)) {
309 MeasureForWidth(realWidth);
310 }
311 }
312 }
313
ChooseExactFontSize(RefPtr<TextLayoutProperty> & property,bool isWidth)314 void TextLayoutElement::ChooseExactFontSize(RefPtr<TextLayoutProperty>& property, bool isWidth)
315 {
316 if (!minTextSize_.has_value()) {
317 property->UpdateFontSize(minFontSize_);
318 return;
319 }
320 constexpr Dimension ADAPT_UNIT = 1.0_fp;
321 Dimension step = ADAPT_UNIT;
322 Dimension fontSize = (property->GetFontSize().has_value()) ? property->GetFontSize().value() : defaultFontSize_;
323 while (fontSize > minFontSize_) {
324 auto tempSize = GetMeasureTextSize(UtfUtils::Str16ToStr8(property->GetContent().value_or(u"")),
325 fontSize,
326 property->GetFontWeight().value_or(FontWeight::NORMAL), 0.0);
327 if (!tempSize.has_value()) {
328 fontSize = minFontSize_;
329 break;
330 }
331 if (isWidth) {
332 if (GreatOrEqual(width_, tempSize.value().Width())) {
333 break;
334 }
335 } else {
336 if (GreatOrEqual(height_, tempSize.value().Height())) {
337 break;
338 }
339 }
340 fontSize -= step;
341 }
342 property->UpdateFontSize(fontSize);
343 }
344
UpdateSize(bool isWidth)345 void TextLayoutElement::UpdateSize(bool isWidth)
346 {
347 auto textProp = AceType::DynamicCast<TextLayoutProperty>(textWrap_->GetLayoutProperty());
348 CHECK_NULL_VOID(textProp);
349 ChooseExactFontSize(textProp, isWidth);
350 auto textConstraint = textProp->GetContentLayoutConstraint();
351 CHECK_NULL_VOID(textConstraint);
352 if (isWidth) {
353 textConstraint->selfIdealSize.SetWidth(width_);
354 } else {
355 textConstraint->selfIdealSize.SetHeight(height_);
356 }
357
358 textWrap_->Measure(textConstraint);
359 UpdateFontSize();
360 auto geometryNode = textWrap_->GetGeometryNode();
361 CHECK_NULL_VOID(geometryNode);
362 auto textSizeF = geometryNode->GetFrameSize();
363 width_ = textSizeF.Width();
364 height_ = textSizeF.Height();
365 }
366
DidExceedMaxLines(std::optional<SizeF> & currentTextSize)367 bool TextLayoutElement::DidExceedMaxLines(std::optional<SizeF>& currentTextSize)
368 {
369 if (!isExist_) {
370 return false;
371 }
372
373 auto textNode = textWrap_->GetHostNode();
374 CHECK_NULL_RETURN(textNode, false);
375 auto textPattern = textNode->GetPattern<TextPattern>();
376 CHECK_NULL_RETURN(textPattern, false);
377 if (textPattern->DidExceedMaxLines()) {
378 return true;
379 }
380
381 auto textProp = AceType::DynamicCast<TextLayoutProperty>(textWrap_->GetLayoutProperty());
382 CHECK_NULL_RETURN(textProp, false);
383 auto textConstraint = textProp->GetContentLayoutConstraint();
384 CHECK_NULL_RETURN(textConstraint, false);
385
386 if (currentTextSize.has_value() && GreatNotEqual(currentTextSize->Height(), textConstraint->maxSize.Height())) {
387 return true;
388 }
389 return false;
390 }
391
GetCurrentTextSize(std::optional<SizeF> & currentTextSize,Dimension & currentFontSize)392 bool TextLayoutElement::GetCurrentTextSize(std::optional<SizeF>& currentTextSize, Dimension& currentFontSize)
393 {
394 if (!isExist_) {
395 return false;
396 }
397
398 auto textProp = AceType::DynamicCast<TextLayoutProperty>(textWrap_->GetLayoutProperty());
399 CHECK_NULL_RETURN(textProp, false);
400 if (!textProp->GetFontSize().has_value()) {
401 return false;
402 }
403 if (!textProp->GetContent().has_value()) {
404 return false;
405 }
406 currentTextSize = GetMeasureTextSize(UtfUtils::Str16ToStr8(textProp->GetContent().value()),
407 textProp->GetFontSize().value(), textProp->GetFontWeight().value_or(FontWeight::NORMAL), width_);
408 if (!currentTextSize.has_value()) {
409 return false;
410 }
411 currentFontSize = textProp->GetFontSize().value();
412 return true;
413 }
414
TryShrinkTextWidth(SizeF & point,SizeF & circlePoint,bool maxSpaceToShrink,float maxDistance,float threshold)415 bool TextLayoutElement::TryShrinkTextWidth(SizeF& point, SizeF& circlePoint, bool maxSpaceToShrink, float maxDistance,
416 float threshold)
417 {
418 #ifdef ENABLE_ROSEN_BACKEND
419 auto textProp = AceType::DynamicCast<TextLayoutProperty>(textWrap_->GetLayoutProperty());
420 CHECK_NULL_RETURN(textProp, false);
421
422 auto stepPx = Dimension(1.0, DimensionUnit::VP).ConvertToPx();
423 auto currentHeight = height_;
424 auto tempWidth = width_;
425 auto currentRectWidth = point.Width();
426 while (NearEqual(currentHeight, height_)) {
427 if (LessOrEqual(tempWidth, threshold)) {
428 MeasureForWidth(tempWidth + stepPx);
429 return false;
430 }
431 auto newWidth = tempWidth - stepPx;
432 currentRectWidth -= stepPx;
433 MeasureForWidth(newWidth);
434 if (!NearEqual(currentHeight, height_)) {
435 MeasureForWidth(tempWidth);
436 return false;
437 }
438 auto distance = pow(currentRectWidth - circlePoint.Width()) + pow(point.Height() - circlePoint.Height());
439 tempWidth = newWidth;
440 if (!GreatNotEqual(distance, maxDistance)) {
441 break;
442 }
443 }
444 return true;
445 #else
446 return false;
447 #endif
448 }
449
GetMeasureTextSize(const std::string & data,const Dimension & fontSize,FontWeight fontWeight,float constraintWidth)450 std::optional<SizeF> TextLayoutElement::GetMeasureTextSize(const std::string& data,
451 const Dimension& fontSize, FontWeight fontWeight, float constraintWidth)
452 {
453 #ifdef ENABLE_ROSEN_BACKEND
454 MeasureContext content;
455 if (!NearEqual(constraintWidth, 0.0)) {
456 content.constraintWidth = Dimension(constraintWidth);
457 }
458 content.textContent = data;
459 content.fontSize = fontSize;
460 auto fontweight = StringUtils::FontWeightToString(fontWeight);
461 content.fontWeight = fontweight;
462 auto size = RosenRenderCustomPaint::MeasureTextSizeInner(content);
463 return SizeF(size.Width(), size.Height());
464 #else
465 return std::nullopt;
466 #endif
467 }
468
MeasureMinTextSize()469 void TextLayoutElement::MeasureMinTextSize()
470 {
471 auto textProp = AceType::DynamicCast<TextLayoutProperty>(textWrap_->GetLayoutProperty());
472 CHECK_NULL_VOID(textProp);
473 minTextSize_ = GetMeasureTextSize(UtfUtils::Str16ToStr8(textProp->GetContent().value_or(u"")),
474 minFontSize_,
475 textProp->GetFontWeight().value_or(FontWeight::NORMAL), 0.0);
476 }
477
ShrinkWidth(double reduceSize)478 double TextLayoutElement::ShrinkWidth(double reduceSize)
479 {
480 if (!isExist_ || isSetSize_) {
481 return reduceSize;
482 }
483 if (!minTextSize_.has_value()) {
484 MeasureMinTextSize();
485 }
486 double minTextWidth = minTextSize_.value_or(SizeT(0.0F, 0.0F)).Width();
487 if (GreatNotEqual(minTextWidth, (width_ - reduceSize))) {
488 int remain = reduceSize - (width_ - minTextWidth);
489 width_ = minTextWidth;
490 UpdateSize(true);
491 return remain;
492 }
493
494 width_ -= reduceSize;
495 UpdateSize(true);
496 return 0.0;
497 }
498
ShrinkHeight(double reduceSize)499 double TextLayoutElement::ShrinkHeight(double reduceSize)
500 {
501 if (!isExist_ || isSetSize_) {
502 return reduceSize;
503 }
504 if (!minTextSize_.has_value()) {
505 MeasureMinTextSize();
506 }
507
508 double minTextHeight = minTextSize_.value_or(SizeT(0.0F, 0.0F)).Height();
509 if (GreatNotEqual(minTextHeight, (height_ - reduceSize))) {
510 double remain = reduceSize - (height_ - minTextHeight);
511 height_ = minTextHeight;
512 UpdateSize(false);
513 return remain;
514 }
515 height_ -= reduceSize;
516 UpdateSize(false);
517 return 0.0;
518 }
519 };
520