• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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 #include "frameworks/bridge/declarative_frontend/jsview/js_text_clock.h"
17 
18 #include <regex>
19 #include <string>
20 
21 #include "base/log/ace_scoring_log.h"
22 #include "base/utils/string_utils.h"
23 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
24 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
25 #include "bridge/declarative_frontend/jsview/js_utils.h"
26 #include "bridge/declarative_frontend/jsview/models/text_clock_model_impl.h"
27 #include "core/components/common/properties/text_style.h"
28 #include "core/components/common/properties/text_style_parser.h"
29 #include "core/components_ng/base/view_stack_processor.h"
30 #include "core/components_ng/pattern/text_clock/text_clock_model.h"
31 #include "core/components_ng/pattern/text_clock/text_clock_model_ng.h"
32 
33 namespace OHOS::Ace {
34 
35 std::unique_ptr<TextClockModel> TextClockModel::instance_ = nullptr;
36 std::mutex TextClockModel::mutex_;
37 
GetInstance()38 TextClockModel* TextClockModel::GetInstance()
39 {
40     if (!instance_) {
41         std::lock_guard<std::mutex> lock(mutex_);
42         if (!instance_) {
43 #ifdef NG_BUILD
44             instance_.reset(new NG::TextClockModelNG());
45 #else
46             if (Container::IsCurrentUseNewPipeline()) {
47                 instance_.reset(new NG::TextClockModelNG());
48             } else {
49                 instance_.reset(new Framework::TextClockModelImpl());
50             }
51 #endif
52         }
53     }
54     return instance_.get();
55 }
56 } // namespace OHOS::Ace
57 
58 namespace OHOS::Ace::Framework {
59 
60 namespace {
61 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
62 const std::string DEFAULT_FORMAT = "hms";
63 constexpr int32_t HOURS_WEST_LOWER_LIMIT = -14;
64 constexpr int32_t HOURS_WEST_UPPER_LIMIT = 12;
65 constexpr float HOURS_WEST[] = { 9.5f, 3.5f, -3.5f, -4.5f, -5.5f, -5.75f, -6.5f, -9.5f, -10.5f, -12.75f };
66 
HoursWestIsValid(int32_t hoursWest)67 bool HoursWestIsValid(int32_t hoursWest)
68 {
69     return !(hoursWest < HOURS_WEST_LOWER_LIMIT || hoursWest > HOURS_WEST_UPPER_LIMIT);
70 }
71 
GetHoursWest(float hoursWest)72 float GetHoursWest(float hoursWest)
73 {
74     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
75         for (float i : HOURS_WEST) {
76             if (NearEqual(hoursWest, i)) {
77                 return hoursWest;
78             }
79         }
80     }
81 
82     return int32_t(hoursWest);
83 }
84 } // namespace
85 
Create(const JSCallbackInfo & info)86 void JSTextClock::Create(const JSCallbackInfo& info)
87 {
88     auto controller = TextClockModel::GetInstance()->Create();
89     if (info.Length() < 1 || !info[0]->IsObject()) {
90         SetFontDefault();
91         return;
92     }
93     JSRef<JSObject> optionsObject = JSRef<JSObject>::Cast(info[0]);
94     JSRef<JSVal> hourWestVal = optionsObject->GetProperty("timeZoneOffset");
95     if (hourWestVal->IsNumber() && HoursWestIsValid(hourWestVal->ToNumber<int32_t>())) {
96         float hourWest = GetHoursWest(hourWestVal->ToNumber<float>());
97         TextClockModel::GetInstance()->SetHoursWest(hourWest);
98     } else {
99         TextClockModel::GetInstance()->SetHoursWest(NAN);
100     }
101     auto controllerObj = optionsObject->GetProperty("controller");
102     if (!controllerObj->IsUndefined() && !controllerObj->IsNull() && controllerObj->IsObject()) {
103         auto* jsController = JSRef<JSObject>::Cast(controllerObj)->Unwrap<JSTextClockController>();
104         if (jsController != nullptr) {
105             jsController->SetInstanceId(Container::CurrentId());
106             if (controller) {
107                 jsController->AddController(controller);
108             }
109         }
110         return;
111     }
112 }
113 
JSBind(BindingTarget globalObj)114 void JSTextClock::JSBind(BindingTarget globalObj)
115 {
116     JSClass<JSTextClock>::Declare("TextClock");
117     MethodOptions opt = MethodOptions::NONE;
118     JSClass<JSTextClock>::StaticMethod("create", &JSTextClock::Create, opt);
119     JSClass<JSTextClock>::StaticMethod("format", &JSTextClock::SetFormat, opt);
120     JSClass<JSTextClock>::StaticMethod("onDateChange", &JSTextClock::JsOnDateChange, opt);
121     JSClass<JSTextClock>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
122     JSClass<JSTextClock>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
123     JSClass<JSTextClock>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
124     JSClass<JSTextClock>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
125     JSClass<JSTextClock>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
126     JSClass<JSTextClock>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
127     JSClass<JSTextClock>::StaticMethod("fontColor", &JSTextClock::SetTextColor, opt);
128     JSClass<JSTextClock>::StaticMethod("fontSize", &JSTextClock::SetFontSize, opt);
129     JSClass<JSTextClock>::StaticMethod("fontWeight", &JSTextClock::SetFontWeight, opt);
130     JSClass<JSTextClock>::StaticMethod("fontStyle", &JSTextClock::SetFontStyle, opt);
131     JSClass<JSTextClock>::StaticMethod("fontFamily", &JSTextClock::SetFontFamily, opt);
132     JSClass<JSTextClock>::StaticMethod("textShadow", &JSTextClock::SetTextShadow, opt);
133     JSClass<JSTextClock>::StaticMethod("fontFeature", &JSTextClock::SetFontFeature, opt);
134     JSClass<JSTextClock>::InheritAndBind<JSViewAbstract>(globalObj);
135 }
136 
SetFontDefault()137 void JSTextClock::SetFontDefault()
138 {
139     RefPtr<TextTheme> textTheme = GetTheme<TextTheme>();
140     TextClockModel::GetInstance()->InitFontDefault(textTheme->GetTextStyle());
141 }
142 
SetTextColor(const JSCallbackInfo & info)143 void JSTextClock::SetTextColor(const JSCallbackInfo& info)
144 {
145     if (info.Length() < 1) {
146         return;
147     }
148     Color textColor;
149     if (!ParseJsColor(info[0], textColor)) {
150         auto pipelineContext = PipelineContext::GetCurrentContext();
151         CHECK_NULL_VOID(pipelineContext);
152         auto theme = pipelineContext->GetTheme<TextTheme>();
153         textColor = theme->GetTextStyle().GetTextColor();
154     }
155 
156     TextClockModel::GetInstance()->SetTextColor(textColor);
157 }
158 
SetFontSize(const JSCallbackInfo & info)159 void JSTextClock::SetFontSize(const JSCallbackInfo& info)
160 {
161     if (info.Length() < 1) {
162         return;
163     }
164     auto pipelineContext = PipelineContext::GetCurrentContext();
165     CHECK_NULL_VOID(pipelineContext);
166     auto theme = pipelineContext->GetTheme<TextTheme>();
167     CHECK_NULL_VOID(theme);
168 
169     CalcDimension fontSize;
170     if (!ParseJsDimensionFp(info[0], fontSize)) {
171         fontSize = theme->GetTextStyle().GetFontSize();
172     }
173 
174     if (fontSize.IsNegative() || fontSize.Unit() == DimensionUnit::PERCENT) {
175         auto pipelineContext = PipelineContext::GetCurrentContext();
176         CHECK_NULL_VOID(pipelineContext);
177         auto theme = pipelineContext->GetTheme<TextTheme>();
178         CHECK_NULL_VOID(theme);
179         fontSize = theme->GetTextStyle().GetFontSize();
180     }
181 
182     TextClockModel::GetInstance()->SetFontSize(fontSize);
183 }
184 
SetFontWeight(const JSCallbackInfo & info)185 void JSTextClock::SetFontWeight(const JSCallbackInfo& info)
186 {
187     if (info.Length() < 1) {
188         return;
189     }
190     RefPtr<TextTheme> textTheme = GetTheme<TextTheme>();
191     CHECK_NULL_VOID(textTheme);
192     const auto& fontWeight = info[0];
193     if (fontWeight->IsUndefined()) {
194         TextClockModel::GetInstance()->SetFontWeight(textTheme->GetTextStyle().GetFontWeight());
195         return;
196     }
197 
198     if (!fontWeight->IsNull()) {
199         std::string weight;
200         if (fontWeight->IsNumber()) {
201             weight = std::to_string(fontWeight->ToNumber<int32_t>());
202         } else {
203             ParseJsString(fontWeight, weight);
204         }
205         TextClockModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(weight));
206     } else {
207         TextClockModel::GetInstance()->SetFontWeight(textTheme->GetTextStyle().GetFontWeight());
208     }
209 }
210 
SetFontStyle(int32_t value)211 void JSTextClock::SetFontStyle(int32_t value)
212 {
213     if (value < 0 || value >= static_cast<int32_t>(FONT_STYLES.size())) {
214         return;
215     }
216     TextClockModel::GetInstance()->SetItalicFontStyle(FONT_STYLES[value]);
217 }
218 
SetFontFamily(const JSCallbackInfo & info)219 void JSTextClock::SetFontFamily(const JSCallbackInfo& info)
220 {
221     if (info.Length() < 1) {
222         return;
223     }
224     std::vector<std::string> fontFamilies;
225     if (!ParseJsFontFamilies(info[0], fontFamilies)) {
226         return;
227     }
228     TextClockModel::GetInstance()->SetFontFamily(fontFamilies);
229 }
230 
SetFormat(const JSCallbackInfo & info)231 void JSTextClock::SetFormat(const JSCallbackInfo& info)
232 {
233     if (info.Length() < 1) {
234         return;
235     }
236     if (!info[0]->IsString()) {
237         TextClockModel::GetInstance()->SetFormat(DEFAULT_FORMAT);
238         return;
239     }
240 
241     std::string value;
242     auto format = info[0]->ToString();
243     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)
244             ? (format.length() == 0) : (format.length() == 0 || !StringUtils::IsAscii(format))) {
245         format = DEFAULT_FORMAT;
246         TextClockModel::GetInstance()->SetFormat(format);
247         return;
248     }
249 
250     if (!ParseJsString(info[0], value)) {
251         return;
252     }
253     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
254         std::regex pattern(
255             R"(^([Yy]*[_|\W\s]*[M]*[_|\W\s]*[d]*[_|\W\s]*[D]*[_|\W\s]*[Hh]*[_|\W\s]*[m]*[_|\W\s]*[s]*[_|\W\s]*[S]*)$)");
256         if (!std::regex_match(value, pattern)) {
257             TextClockModel::GetInstance()->SetFormat("hms");
258             return;
259         }
260     }
261 
262     TextClockModel::GetInstance()->SetFormat(value);
263 }
264 
SetTextShadow(const JSCallbackInfo & info)265 void JSTextClock::SetTextShadow(const JSCallbackInfo& info)
266 {
267     if (info.Length() < 1) {
268         return;
269     }
270     std::vector<Shadow> shadows;
271     ParseTextShadowFromShadowObject(info[0], shadows);
272     if (!shadows.empty()) {
273         TextClockModel::GetInstance()->SetTextShadow(shadows);
274     }
275 }
276 
SetFontFeature(const JSCallbackInfo & info)277 void JSTextClock::SetFontFeature(const JSCallbackInfo& info)
278 {
279     if (info.Length() < 1) {
280         return;
281     }
282     if (!info[0]->IsString()) {
283         return;
284     }
285 
286     std::string fontFeatureSettings = info[0]->ToString();
287     TextClockModel::GetInstance()->SetFontFeature(ParseFontFeatureSettings(fontFeatureSettings));
288 }
289 
JsOnDateChange(const JSCallbackInfo & info)290 void JSTextClock::JsOnDateChange(const JSCallbackInfo& info)
291 {
292     if (!info[0]->IsFunction()) {
293         return;
294     }
295 
296     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
297     WeakPtr<NG::FrameNode> targetNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
298     auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
299                         const std::string& value) {
300         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
301         ACE_SCORING_EVENT("TextClock.onDateChange");
302         PipelineContext::SetCallBackNode(node);
303         auto newJSVal = JSRef<JSVal>::Make(ToJSValue(value));
304         func->ExecuteJS(1, &newJSVal);
305     };
306     TextClockModel::GetInstance()->SetOnDateChange(std::move(onChange));
307 }
308 
JSBind(BindingTarget globalObj)309 void JSTextClockController::JSBind(BindingTarget globalObj)
310 {
311     JSClass<JSTextClockController>::Declare("TextClockController");
312     JSClass<JSTextClockController>::Method("start", &JSTextClockController::Start);
313     JSClass<JSTextClockController>::Method("stop", &JSTextClockController::Stop);
314     JSClass<JSTextClockController>::Bind(
315         globalObj, JSTextClockController::Constructor, JSTextClockController::Destructor);
316 }
317 
Constructor(const JSCallbackInfo & args)318 void JSTextClockController::Constructor(const JSCallbackInfo& args)
319 {
320     auto scroller = Referenced::MakeRefPtr<JSTextClockController>();
321     scroller->IncRefCount();
322     args.SetReturnValue(Referenced::RawPtr(scroller));
323 }
324 
Destructor(JSTextClockController * scroller)325 void JSTextClockController::Destructor(JSTextClockController* scroller)
326 {
327     if (scroller != nullptr) {
328         scroller->DecRefCount();
329     }
330 }
331 
Start()332 void JSTextClockController::Start()
333 {
334     ContainerScope scope(instanceId_);
335     if (!controller_.empty()) {
336         for (auto& i : controller_) {
337             i->Start();
338         }
339     }
340 }
341 
Stop()342 void JSTextClockController::Stop()
343 {
344     ContainerScope scope(instanceId_);
345     if (!controller_.empty()) {
346         for (auto& i : controller_) {
347             i->Stop();
348         }
349     }
350 }
351 } // namespace OHOS::Ace::Framework
352