1 /*
2 * Copyright (c) 2021 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 #include "core/components/common/properties/text_style.h"
17 #include "core/components_ng/pattern/text/text_styles.h"
18 #include "ui/base/utils/utils.h"
19
20 namespace OHOS::Ace {
21 const std::vector<WordBreak> WORD_BREAK_TYPES = { WordBreak::NORMAL, WordBreak::BREAK_ALL, WordBreak::BREAK_WORD,
22 WordBreak::HYPHENATION };
23 const std::vector<LineBreakStrategy> LINE_BREAK_STRATEGY_TYPES = { LineBreakStrategy::GREEDY,
24 LineBreakStrategy::HIGH_QUALITY, LineBreakStrategy::BALANCED };
TextStyle(const std::vector<std::string> & fontFamilies,double fontSize,FontWeight fontWeight,FontStyle fontStyle,const Color & textColor)25 TextStyle::TextStyle(const std::vector<std::string>& fontFamilies, double fontSize, FontWeight fontWeight,
26 FontStyle fontStyle, const Color& textColor)
27 : propFontFamilies_(fontFamilies), propFontStyle_(fontStyle), propTextColor_(textColor),
28 fontWeight_(fontWeight)
29 {
30 SetFontSize(Dimension(fontSize));
31 }
32
operator ==(const TextStyle & rhs) const33 bool TextStyle::operator==(const TextStyle& rhs) const
34 {
35 return propFontFamilies_ == rhs.propFontFamilies_ && fontFeatures_ == rhs.fontFeatures_ &&
36 propTextDecorationStyle_ == rhs.propTextDecorationStyle_ && preferFontSizes_ == rhs.preferFontSizes_ &&
37 fontSize_.value == rhs.fontSize_.value && adaptMinFontSize_.value == rhs.adaptMinFontSize_.value &&
38 adaptMaxFontSize_.value == rhs.adaptMaxFontSize_.value &&
39 propAdaptFontSizeStep_.value == rhs.propAdaptFontSizeStep_.value &&
40 lineHeight_.value == rhs.lineHeight_.value && fontWeight_ == rhs.fontWeight_ &&
41 propFontStyle_ == rhs.propFontStyle_ && propTextBaseline_ == rhs.propTextBaseline_ &&
42 propTextOverflow_ == rhs.propTextOverflow_ && propTextAlign_ == rhs.propTextAlign_ &&
43 propTextColor_ == rhs.propTextColor_ && propTextDecoration_ == rhs.propTextDecoration_ &&
44 propTextShadows_ == rhs.propTextShadows_ && propLetterSpacing_.value == rhs.propLetterSpacing_.value &&
45 propMaxLines_ == rhs.propMaxLines_ && adaptTextSize_ == rhs.adaptTextSize_ &&
46 propAllowScale_ == rhs.propAllowScale_ && propWordBreak_ == rhs.propWordBreak_ &&
47 propTextDecorationColor_ == rhs.propTextDecorationColor_ && propTextCase_ == rhs.propTextCase_ &&
48 propBaselineOffset_.value == rhs.propBaselineOffset_.value && adaptHeight_ == rhs.adaptHeight_ &&
49 propTextIndent_.value == rhs.propTextIndent_.value && propTextVerticalAlign_ == rhs.propTextVerticalAlign_ &&
50 propWordSpacing_.value == rhs.propWordSpacing_.value && propEllipsisMode_ == rhs.propEllipsisMode_ &&
51 propLineBreakStrategy_ == rhs.propLineBreakStrategy_ &&
52 propTextBackgroundStyle_ == rhs.propTextBackgroundStyle_ &&
53 NearEqual(propLineThicknessScale_, rhs.propLineThicknessScale_);
54 }
55
operator !=(const TextStyle & rhs) const56 bool TextStyle::operator!=(const TextStyle& rhs) const
57 {
58 return !(rhs == *this);
59 }
60
SetAdaptTextSize(const Dimension & maxFontSize,const Dimension & minFontSize,const Dimension & fontSizeStep)61 void TextStyle::SetAdaptTextSize(
62 const Dimension& maxFontSize, const Dimension& minFontSize, const Dimension& fontSizeStep)
63 {
64 adaptMaxFontSize_.value = maxFontSize;
65 adaptMinFontSize_.value = minFontSize;
66 propAdaptFontSizeStep_.value = fontSizeStep;
67 adaptTextSize_ = true;
68 }
69
ToJsonValue(std::unique_ptr<JsonValue> & json,const std::optional<TextBackgroundStyle> & style,const NG::InspectorFilter & filter)70 void TextBackgroundStyle::ToJsonValue(std::unique_ptr<JsonValue>& json, const std::optional<TextBackgroundStyle>& style,
71 const NG::InspectorFilter& filter)
72 {
73 NG::BorderRadiusProperty defaultRadius;
74 TextBackgroundStyle exportStyle = { .backgroundColor = Color::TRANSPARENT, .backgroundRadius = defaultRadius };
75 if (style.has_value()) {
76 exportStyle.backgroundColor = style.value().backgroundColor.value_or(Color::TRANSPARENT);
77 exportStyle.backgroundRadius = style.value().backgroundRadius.value_or(defaultRadius);
78 }
79
80 auto styleJson = JsonUtil::Create(true);
81 styleJson->Put("color", exportStyle.backgroundColor->ColorToString().c_str());
82 auto radiusJson = JsonUtil::Create(true);
83 exportStyle.backgroundRadius->ToJsonValue(radiusJson, styleJson, filter);
84
85 json->PutExtAttr("textBackgroundStyle", styleJson, filter);
86 }
87
ToJsonValue(std::unique_ptr<JsonValue> & json,const std::optional<TextStyle> & style,const NG::InspectorFilter & filter)88 void TextStyle::ToJsonValue(std::unique_ptr<JsonValue>& json, const std::optional<TextStyle>& style,
89 const NG::InspectorFilter& filter)
90 {
91 CHECK_NULL_VOID(json);
92 CHECK_NULL_VOID(style);
93 /* no fixed attr below, just return */
94 if (filter.IsFastFilter()) {
95 return;
96 }
97 json->PutExtAttr("decoration", GetDeclarationString(style->GetTextDecorationColor(), style->GetTextDecoration(),
98 style->GetTextDecorationStyle(), style->GetLineThicknessScale()).c_str(), filter);
99 }
100
GetDeclarationString(const std::optional<Color> & color,const std::vector<TextDecoration> & textDecorations,const std::optional<TextDecorationStyle> & textDecorationStyle,const std::optional<float> & lineThicknessScale)101 std::string TextStyle::GetDeclarationString(
102 const std::optional<Color>& color, const std::vector<TextDecoration>& textDecorations,
103 const std::optional<TextDecorationStyle>& textDecorationStyle, const std::optional<float>& lineThicknessScale)
104 {
105 auto jsonSpanDeclaration = JsonUtil::Create(true);
106 jsonSpanDeclaration->Put(
107 "type", V2::ConvertWrapTextDecorationToStirng(textDecorations).c_str());
108 jsonSpanDeclaration->Put("color", (color.value_or(Color::BLACK).ColorToString()).c_str());
109 jsonSpanDeclaration->Put("style", V2::ConvertWrapTextDecorationStyleToString(
110 textDecorationStyle.value_or(TextDecorationStyle::SOLID)).c_str());
111 jsonSpanDeclaration->Put("thicknessScale",
112 StringUtils::DoubleToString(static_cast<double>(lineThicknessScale.value_or(1.0f))).c_str());
113 return jsonSpanDeclaration->ToString();
114 }
115
UpdateColorByResourceId()116 void TextStyle::UpdateColorByResourceId()
117 {
118 propTextColor_.UpdateColorByResourceId();
119 propTextDecorationColor_.UpdateColorByResourceId();
120 if (propTextBackgroundStyle_.has_value()) {
121 propTextBackgroundStyle_->UpdateColorByResourceId();
122 }
123 std::for_each(propRenderColors_.begin(), propRenderColors_.end(), [](Color& cl) { cl.UpdateColorByResourceId(); });
124 std::for_each(propTextShadows_.begin(), propTextShadows_.end(), [](Shadow& sd) { sd.UpdateColorByResourceId(); });
125 }
126
ToString() const127 std::string TextStyle::ToString() const
128 {
129 auto jsonValue = JsonUtil::Create(true);
130 JSON_STRING_PUT_STRINGABLE(jsonValue, fontSize_.value);
131 JSON_STRING_PUT_STRINGABLE(jsonValue, propTextColor_);
132 JSON_STRING_PUT_STRINGABLE(jsonValue, lineHeight_.value);
133 JSON_STRING_PUT_STRINGABLE(jsonValue, propBaselineOffset_.value);
134 JSON_STRING_PUT_STRINGABLE(jsonValue, propWordSpacing_.value);
135 JSON_STRING_PUT_STRINGABLE(jsonValue, propTextIndent_.value);
136 JSON_STRING_PUT_STRINGABLE(jsonValue, propLetterSpacing_.value);
137 JSON_STRING_PUT_STRINGABLE(jsonValue, lineSpacing_.value);
138
139 JSON_STRING_PUT_INT(jsonValue, fontWeight_);
140 JSON_STRING_PUT_INT(jsonValue, propFontStyle_);
141 JSON_STRING_PUT_INT(jsonValue, propTextBaseline_);
142 JSON_STRING_PUT_INT(jsonValue, propTextOverflow_);
143 JSON_STRING_PUT_INT(jsonValue, propTextVerticalAlign_);
144 JSON_STRING_PUT_INT(jsonValue, propTextAlign_);
145 JSON_STRING_PUT_INT(jsonValue, propTextDecorationStyle_);
146 JSON_STRING_PUT_INT(
147 jsonValue, propTextDecoration_.size() > 0 ? propTextDecoration_[0] : TextDecoration::NONE);
148 JSON_STRING_PUT_INT(jsonValue, propLineThicknessScale_);
149 JSON_STRING_PUT_INT(jsonValue, propWhiteSpace_);
150 JSON_STRING_PUT_INT(jsonValue, propWordBreak_);
151 JSON_STRING_PUT_INT(jsonValue, propTextCase_);
152 JSON_STRING_PUT_INT(jsonValue, propEllipsisMode_);
153 JSON_STRING_PUT_INT(jsonValue, propLineBreakStrategy_);
154
155 std::stringstream ss;
156 std::for_each(
157 propRenderColors_.begin(), propRenderColors_.end(), [&ss](const Color& c) { ss << c.ToString() << ","; });
158 jsonValue->Put("renderColors", ss.str().c_str());
159 JSON_STRING_PUT_INT(jsonValue, propRenderStrategy_);
160 JSON_STRING_PUT_INT(jsonValue, propEffectStrategy_);
161 JSON_STRING_PUT_OPTIONAL_STRINGABLE(jsonValue, GetInnerSymbolEffectOptions());
162
163 return jsonValue->ToString();
164 }
165
CompareCommonSubType(const std::optional<NG::SymbolEffectOptions> & options,const std::optional<NG::SymbolEffectOptions> & oldOptions)166 void TextStyle::CompareCommonSubType(const std::optional<NG::SymbolEffectOptions>& options,
167 const std::optional<NG::SymbolEffectOptions>& oldOptions)
168 {
169 auto newOpts = options.value();
170 auto oldOpts = oldOptions.value();
171 if (newOpts.GetCommonSubType().has_value()) {
172 auto commonType = static_cast<uint16_t>(newOpts.GetCommonSubType().value());
173 if (oldOpts.GetCommonSubType().has_value()) {
174 auto oldCommonType = static_cast<uint16_t>(oldOpts.GetCommonSubType().value());
175 if (commonType != oldCommonType) {
176 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::COMMONSUB_TYPE));
177 }
178 }
179 } else {
180 if (oldOpts.GetCommonSubType().has_value()) {
181 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::COMMONSUB_TYPE));
182 }
183 }
184 }
185
CompareAnimationMode(const std::optional<NG::SymbolEffectOptions> & options,const std::optional<NG::SymbolEffectOptions> & oldOptions)186 void TextStyle::CompareAnimationMode(const std::optional<NG::SymbolEffectOptions>& options,
187 const std::optional<NG::SymbolEffectOptions>& oldOptions)
188 {
189 auto newOpts = options.value();
190 auto oldOpts = oldOptions.value();
191 auto effectType = newOpts.GetEffectType();
192 if (effectType == SymbolEffectType::HIERARCHICAL && newOpts.GetFillStyle().has_value()) {
193 if (oldOpts.GetFillStyle().has_value()) {
194 if (newOpts.GetFillStyle().value() != oldOpts.GetFillStyle().value()) {
195 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::ANIMATION_MODE));
196 }
197 } else {
198 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::ANIMATION_MODE));
199 }
200 return;
201 }
202 if (newOpts.GetScopeType().has_value()) {
203 if (oldOpts.GetScopeType().has_value()) {
204 if (newOpts.GetScopeType().value() != oldOpts.GetScopeType().value()) {
205 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::ANIMATION_MODE));
206 }
207 } else {
208 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::ANIMATION_MODE));
209 }
210 }
211 }
212
SetWhenOnlyOneOptionIsValid(const std::optional<NG::SymbolEffectOptions> & options)213 void TextStyle::SetWhenOnlyOneOptionIsValid(const std::optional<NG::SymbolEffectOptions>& options)
214 {
215 auto symbolOptions = options.value();
216 if (symbolOptions.GetEffectType() != SymbolEffectType::NONE) {
217 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::EFFECT_STRATEGY));
218 }
219 if (symbolOptions.GetIsTxtActive()) {
220 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::ANIMATION_START));
221 }
222 if (symbolOptions.GetCommonSubType().has_value()) {
223 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::COMMONSUB_TYPE));
224 }
225 if (symbolOptions.GetFillStyle().has_value() || symbolOptions.GetScopeType().has_value()) {
226 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::ANIMATION_MODE));
227 }
228 }
229
SetSymbolEffectOptions(const std::optional<NG::SymbolEffectOptions> & symbolEffectOptions)230 void TextStyle::SetSymbolEffectOptions(const std::optional<NG::SymbolEffectOptions>& symbolEffectOptions)
231 {
232 auto innerSymbolEffectOptions = GetInnerSymbolEffectOptions();
233 if (symbolEffectOptions.has_value() && innerSymbolEffectOptions.has_value()) {
234 auto options = symbolEffectOptions.value();
235 auto oldOptions = innerSymbolEffectOptions.value();
236 if (oldOptions.GetEffectType() != options.GetEffectType()) {
237 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::EFFECT_STRATEGY));
238 }
239 if (oldOptions.GetIsTxtActive() != options.GetIsTxtActive() ||
240 (options.GetTriggerNum().has_value() && options.GetIsTxtActive())) {
241 reLayoutSymbolStyleBitmap_.set(static_cast<int32_t>(SymbolStyleAttribute::ANIMATION_START));
242 }
243 CompareCommonSubType(options, oldOptions);
244 CompareAnimationMode(options, oldOptions);
245 } else {
246 if (innerSymbolEffectOptions.has_value()) {
247 auto oldOptions = innerSymbolEffectOptions.value();
248 SetWhenOnlyOneOptionIsValid(oldOptions);
249 }
250 }
251 SetInnerSymbolEffectOptionsWithoutMark(symbolEffectOptions);
252 }
253 } // namespace OHOS::Ace
254