• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "core/components_ng/pattern/radio/radio_pattern.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components/checkable/checkable_theme.h"
20 #include "core/components_ng/pattern/radio/radio_paint_property.h"
21 #include "core/components_ng/pattern/stage/page_event_hub.h"
22 #include "core/components_ng/property/property.h"
23 #include "core/event/touch_event.h"
24 #include "core/pipeline_ng/pipeline_context.h"
25 
26 namespace OHOS::Ace::NG {
27 
28 namespace {
29 constexpr int FOR_HOTZONESIZE_CALCULATE_MULTIPLY_TWO = 2;
30 } // namespace
31 
OnAttachToFrameNode()32 void RadioPattern::OnAttachToFrameNode()
33 {
34     auto host = GetHost();
35     CHECK_NULL_VOID(host);
36     host->GetLayoutProperty()->UpdateAlignment(Alignment::CENTER);
37 }
38 
OnDetachFromFrameNode(FrameNode * frameNode)39 void RadioPattern::OnDetachFromFrameNode(FrameNode* frameNode)
40 {
41     CHECK_NULL_VOID(frameNode);
42     auto pipelineContext = PipelineContext::GetCurrentContext();
43     CHECK_NULL_VOID(pipelineContext);
44     auto stageManager = pipelineContext->GetStageManager();
45     CHECK_NULL_VOID(stageManager);
46     auto pageNode = stageManager->GetLastPage();
47     CHECK_NULL_VOID(pageNode);
48     auto pageEventHub = pageNode->GetEventHub<NG::PageEventHub>();
49     CHECK_NULL_VOID(pageEventHub);
50     auto radioEventHub = frameNode->GetEventHub<NG::RadioEventHub>();
51     CHECK_NULL_VOID(radioEventHub);
52     pageEventHub->RemoveRadioFromGroup(radioEventHub->GetGroup(), frameNode->GetId());
53 }
54 
OnModifyDone()55 void RadioPattern::OnModifyDone()
56 {
57     Pattern::OnModifyDone();
58     UpdateState();
59     auto host = GetHost();
60     CHECK_NULL_VOID(host);
61     auto pipeline = PipelineBase::GetCurrentContext();
62     CHECK_NULL_VOID(pipeline);
63     auto radioTheme = pipeline->GetTheme<RadioTheme>();
64     CHECK_NULL_VOID(radioTheme);
65     auto layoutProperty = host->GetLayoutProperty();
66     CHECK_NULL_VOID(layoutProperty);
67     MarginProperty margin;
68     margin.left = CalcLength(radioTheme->GetHotZoneHorizontalPadding().Value());
69     margin.right = CalcLength(radioTheme->GetHotZoneHorizontalPadding().Value());
70     margin.top = CalcLength(radioTheme->GetHotZoneVerticalPadding().Value());
71     margin.bottom = CalcLength(radioTheme->GetHotZoneVerticalPadding().Value());
72     auto& setMargin = layoutProperty->GetMarginProperty();
73     if (setMargin) {
74         if (setMargin->left.has_value()) {
75             margin.left = setMargin->left;
76         }
77         if (setMargin->right.has_value()) {
78             margin.right = setMargin->right;
79         }
80         if (setMargin->top.has_value()) {
81             margin.top = setMargin->top;
82         }
83         if (setMargin->bottom.has_value()) {
84             margin.bottom = setMargin->bottom;
85         }
86     }
87     layoutProperty->UpdateMargin(margin);
88     hotZoneHorizontalPadding_ = radioTheme->GetHotZoneHorizontalPadding();
89     hotZoneVerticalPadding_ = radioTheme->GetHotZoneVerticalPadding();
90     InitClickEvent();
91     InitTouchEvent();
92     InitMouseEvent();
93     auto focusHub = host->GetFocusHub();
94     CHECK_NULL_VOID(focusHub);
95     InitOnKeyEvent(focusHub);
96 }
97 
InitClickEvent()98 void RadioPattern::InitClickEvent()
99 {
100     if (clickListener_) {
101         return;
102     }
103     auto host = GetHost();
104     CHECK_NULL_VOID(host);
105     auto gesture = host->GetOrCreateGestureEventHub();
106     CHECK_NULL_VOID(gesture);
107     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
108         auto radioPattern = weak.Upgrade();
109         CHECK_NULL_VOID(radioPattern);
110         radioPattern->OnClick();
111     };
112     clickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
113     gesture->AddClickEvent(clickListener_);
114 }
115 
InitTouchEvent()116 void RadioPattern::InitTouchEvent()
117 {
118     if (touchListener_) {
119         return;
120     }
121     auto host = GetHost();
122     CHECK_NULL_VOID(host);
123     auto gesture = host->GetOrCreateGestureEventHub();
124     CHECK_NULL_VOID(gesture);
125     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
126         auto radioPattern = weak.Upgrade();
127         CHECK_NULL_VOID(radioPattern);
128         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
129             radioPattern->OnTouchDown();
130         }
131         if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
132             info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
133             radioPattern->OnTouchUp();
134         }
135     };
136     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
137     gesture->AddTouchEvent(touchListener_);
138 }
139 
InitMouseEvent()140 void RadioPattern::InitMouseEvent()
141 {
142     if (mouseEvent_) {
143         return;
144     }
145     auto host = GetHost();
146     CHECK_NULL_VOID(host);
147     auto gesture = host->GetOrCreateGestureEventHub();
148     CHECK_NULL_VOID(gesture);
149     auto eventHub = host->GetEventHub<RadioEventHub>();
150     auto inputHub = eventHub->GetOrCreateInputEventHub();
151 
152     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
153         auto pattern = weak.Upgrade();
154         if (pattern) {
155             pattern->HandleMouseEvent(isHover);
156         }
157     };
158     mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
159     inputHub->AddOnHoverEvent(mouseEvent_);
160 }
161 
HandleMouseEvent(bool isHover)162 void RadioPattern::HandleMouseEvent(bool isHover)
163 {
164     isHover_ = isHover;
165     if (isHover) {
166         touchHoverType_ = TouchHoverAnimationType::HOVER;
167     } else {
168         touchHoverType_ = TouchHoverAnimationType::NONE;
169     }
170     auto host = GetHost();
171     CHECK_NULL_VOID(host);
172     host->MarkNeedRenderOnly();
173 }
174 
OnClick()175 void RadioPattern::OnClick()
176 {
177     auto host = GetHost();
178     CHECK_NULL_VOID(host);
179     auto paintProperty = host->GetPaintProperty<RadioPaintProperty>();
180     CHECK_NULL_VOID(paintProperty);
181     bool check = false;
182     if (paintProperty->HasRadioCheck()) {
183         check = paintProperty->GetRadioCheckValue();
184     } else {
185         paintProperty->UpdateRadioCheck(false);
186     }
187     if (!preCheck_ && !check) {
188         paintProperty->UpdateRadioCheck(true);
189         UpdateState();
190     }
191 }
192 
OnTouchDown()193 void RadioPattern::OnTouchDown()
194 {
195     if (isHover_) {
196         touchHoverType_ = TouchHoverAnimationType::HOVER_TO_PRESS;
197     } else {
198         touchHoverType_ = TouchHoverAnimationType::PRESS;
199     }
200     auto host = GetHost();
201     CHECK_NULL_VOID(host);
202     isTouch_ = true;
203     host->MarkNeedRenderOnly();
204 }
205 
OnTouchUp()206 void RadioPattern::OnTouchUp()
207 {
208     if (isHover_) {
209         touchHoverType_ = TouchHoverAnimationType::PRESS_TO_HOVER;
210     } else {
211         touchHoverType_ = TouchHoverAnimationType::NONE;
212     }
213     auto host = GetHost();
214     CHECK_NULL_VOID(host);
215     isTouch_ = false;
216     host->MarkNeedRenderOnly();
217 }
218 
CheckPageNode()219 void RadioPattern::CheckPageNode()
220 {
221     auto host = GetHost();
222     CHECK_NULL_VOID(host);
223     auto prePageId = GetPrePageId();
224     auto pipelineContext = PipelineContext::GetCurrentContext();
225     CHECK_NULL_VOID(pipelineContext);
226     auto stageManager = pipelineContext->GetStageManager();
227     CHECK_NULL_VOID(stageManager);
228     auto pageNode = stageManager->GetPageById(host->GetPageId());
229     CHECK_NULL_VOID(pageNode);
230     if (pageNode->GetId() != prePageId) {
231         auto eventHub = host->GetEventHub<RadioEventHub>();
232         CHECK_NULL_VOID(eventHub);
233         auto pageEventHub = pageNode->GetEventHub<NG::PageEventHub>();
234         CHECK_NULL_VOID(pageEventHub);
235         auto group = eventHub->GetGroup();
236 
237         pageEventHub->AddRadioToGroup(group, host->GetId());
238         auto paintProperty = host->GetPaintProperty<RadioPaintProperty>();
239         CHECK_NULL_VOID(paintProperty);
240         bool check = false;
241         if (paintProperty->HasRadioCheck()) {
242             check = paintProperty->GetRadioCheckValue();
243         }
244         UpdateGroupCheckStatus(host, pageNode, check);
245     }
246 }
247 
UpdateState()248 void RadioPattern::UpdateState()
249 {
250     auto host = GetHost();
251     CHECK_NULL_VOID(host);
252     auto eventHub = host->GetEventHub<RadioEventHub>();
253     CHECK_NULL_VOID(eventHub);
254 
255     auto pipelineContext = PipelineContext::GetCurrentContext();
256     CHECK_NULL_VOID(pipelineContext);
257     auto stageManager = pipelineContext->GetStageManager();
258     CHECK_NULL_VOID(stageManager);
259     auto pageNode = stageManager->GetLastPage();
260     CHECK_NULL_VOID(pageNode);
261     auto pageEventHub = pageNode->GetEventHub<NG::PageEventHub>();
262     CHECK_NULL_VOID(pageEventHub);
263     auto preGroup = GetPreGroup();
264     auto group = eventHub->GetGroup();
265     if (!preGroup.has_value()) {
266         pageEventHub->AddRadioToGroup(group, host->GetId());
267         SetPrePageId(pageNode->GetId());
268         auto callback = [weak = WeakClaim(this)]() {
269             auto radio = weak.Upgrade();
270             if (radio) {
271                 radio->CheckPageNode();
272             }
273         };
274         pipelineContext->AddBuildFinishCallBack(callback);
275     }
276     if (preGroup.has_value() && preGroup.value() != group) {
277         pageEventHub->RemoveRadioFromGroup(preGroup.value(), host->GetId());
278         pageEventHub->AddRadioToGroup(group, host->GetId());
279         SetPrePageId(pageNode->GetId());
280         isGroupChanged_ = true;
281     }
282     SetPreGroup(group);
283 
284     auto paintProperty = host->GetPaintProperty<RadioPaintProperty>();
285     CHECK_NULL_VOID(paintProperty);
286 
287     bool check = false;
288     if (paintProperty->HasRadioCheck()) {
289         check = paintProperty->GetRadioCheckValue();
290         /*
291          * Do not set isFirstCreated_ to false if the radio is set to true at creation time. The isFirstCreated_ is set
292          * to false in UpdateGroupCheckStatus because isFirstCreated_ is also required to determine if an onChange event
293          * needs to be triggered.
294          */
295         if (check) {
296             UpdateUIStatus(true);
297             isOnAnimationFlag_ = true;
298         } else {
299             // If the radio is set to false, set isFirstCreated_ to false.
300             isFirstCreated_ = false;
301         }
302     } else {
303         paintProperty->UpdateRadioCheck(false);
304         // If the radio check is not set, set isFirstCreated_ to false.
305         isFirstCreated_ = false;
306     }
307     if (preCheck_ != check || isGroupChanged_) {
308         UpdateGroupCheckStatus(host, pageNode, check);
309     }
310     preCheck_ = check;
311     isGroupChanged_ = false;
312 }
313 
UpdateUncheckStatus(const RefPtr<FrameNode> & frameNode)314 void RadioPattern::UpdateUncheckStatus(const RefPtr<FrameNode>& frameNode)
315 {
316     auto radioPaintProperty = frameNode->GetPaintProperty<RadioPaintProperty>();
317     CHECK_NULL_VOID(radioPaintProperty);
318     radioPaintProperty->UpdateRadioCheck(false);
319     frameNode->MarkNeedRenderOnly();
320 
321     if (preCheck_) {
322         auto radioEventHub = GetEventHub<RadioEventHub>();
323         CHECK_NULL_VOID(radioEventHub);
324         radioEventHub->UpdateChangeEvent(false);
325         isOnAnimationFlag_ = false;
326     }
327     preCheck_ = false;
328 }
329 
UpdateGroupCheckStatus(const RefPtr<FrameNode> & frameNode,const RefPtr<FrameNode> & pageNode,bool check)330 void RadioPattern::UpdateGroupCheckStatus(
331     const RefPtr<FrameNode>& frameNode, const RefPtr<FrameNode>& pageNode, bool check)
332 {
333     frameNode->MarkNeedRenderOnly();
334 
335     auto pageEventHub = pageNode->GetEventHub<NG::PageEventHub>();
336     CHECK_NULL_VOID(pageEventHub);
337 
338     auto radioEventHub = GetEventHub<RadioEventHub>();
339     CHECK_NULL_VOID(radioEventHub);
340     if (check) {
341         pageEventHub->UpdateRadioGroupValue(radioEventHub->GetGroup(), frameNode->GetId());
342     } else {
343         auto radioPaintProperty = frameNode->GetPaintProperty<RadioPaintProperty>();
344         CHECK_NULL_VOID(radioPaintProperty);
345         radioPaintProperty->UpdateRadioCheck(check);
346         if (!isGroupChanged_) {
347             isOnAnimationFlag_ = false;
348         }
349     }
350 
351     if (!isFirstCreated_) {
352         radioEventHub->UpdateChangeEvent(check);
353     }
354 }
355 
UpdateUIStatus(bool check)356 void RadioPattern::UpdateUIStatus(bool check)
357 {
358     uiStatus_ = check ? UIStatus::SELECTED : UIStatus::UNSELECTED;
359     auto host = GetHost();
360     CHECK_NULL_VOID(host);
361     host->MarkNeedRenderOnly();
362 }
363 
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)364 void RadioPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
365 {
366     auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
367         auto pattern = wp.Upgrade();
368         if (pattern) {
369             pattern->GetInnerFocusPaintRect(paintRect);
370         }
371     };
372     focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
373 }
374 
GetInnerFocusPaintRect(RoundRect & paintRect)375 void RadioPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
376 {
377     auto pipeline = PipelineBase::GetCurrentContext();
378     CHECK_NULL_VOID(pipeline);
379     auto radioTheme = pipeline->GetTheme<RadioTheme>();
380     CHECK_NULL_VOID(radioTheme);
381     auto focusPaintPadding = radioTheme->GetFocusPaintPadding().ConvertToPx();
382     float outCircleRadius = size_.Width() / 2 + focusPaintPadding;
383     float originX = offset_.GetX() - focusPaintPadding;
384     float originY = offset_.GetY() - focusPaintPadding;
385     float width = size_.Width() + 2 * focusPaintPadding;
386     float height = size_.Height() + 2 * focusPaintPadding;
387     paintRect.SetRect({ originX, originY, width, height });
388     paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_LEFT_POS, outCircleRadius, outCircleRadius);
389     paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_RIGHT_POS, outCircleRadius, outCircleRadius);
390     paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_LEFT_POS, outCircleRadius, outCircleRadius);
391     paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_RIGHT_POS, outCircleRadius, outCircleRadius);
392 }
393 
GetFocusPattern() const394 FocusPattern RadioPattern::GetFocusPattern() const
395 {
396     auto pipeline = PipelineBase::GetCurrentContext();
397     CHECK_NULL_RETURN(pipeline, FocusPattern());
398     auto radioTheme = pipeline->GetTheme<RadioTheme>();
399     CHECK_NULL_RETURN(radioTheme, FocusPattern());
400     auto activeColor = radioTheme->GetActiveColor();
401     FocusPaintParam focusPaintParam;
402     focusPaintParam.SetPaintColor(activeColor);
403     return { FocusType::NODE, true, FocusStyleType::CUSTOM_REGION, focusPaintParam };
404 }
405 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig &)406 bool RadioPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& /*config*/)
407 {
408     auto geometryNode = dirty->GetGeometryNode();
409     offset_ = geometryNode->GetContentOffset();
410     size_ = geometryNode->GetContentSize();
411     if (!isUserSetResponseRegion_) {
412         AddHotZoneRect();
413     }
414     return true;
415 }
416 
417 // Set the default hot zone for the component.
AddHotZoneRect()418 void RadioPattern::AddHotZoneRect()
419 {
420     hotZoneOffset_.SetX(offset_.GetX() - hotZoneHorizontalPadding_.ConvertToPx());
421     hotZoneOffset_.SetY(offset_.GetY() - hotZoneVerticalPadding_.ConvertToPx());
422     hotZoneSize_.SetWidth(
423         size_.Width() + FOR_HOTZONESIZE_CALCULATE_MULTIPLY_TWO * hotZoneHorizontalPadding_.ConvertToPx());
424     hotZoneSize_.SetHeight(
425         size_.Height() + FOR_HOTZONESIZE_CALCULATE_MULTIPLY_TWO * hotZoneVerticalPadding_.ConvertToPx());
426     DimensionRect hotZoneRegion;
427     hotZoneRegion.SetSize(DimensionSize(Dimension(hotZoneSize_.Width()), Dimension(hotZoneSize_.Height())));
428     hotZoneRegion.SetOffset(DimensionOffset(Dimension(hotZoneOffset_.GetX()), Dimension(hotZoneOffset_.GetY())));
429     auto host = GetHost();
430     CHECK_NULL_VOID(host);
431     auto gestureHub = host->GetOrCreateGestureEventHub();
432     CHECK_NULL_VOID(gestureHub);
433     gestureHub->SetResponseRegion(std::vector<DimensionRect>({ hotZoneRegion }));
434 }
435 
RemoveLastHotZoneRect() const436 void RadioPattern::RemoveLastHotZoneRect() const
437 {
438     auto host = GetHost();
439     CHECK_NULL_VOID(host);
440     host->RemoveLastHotZoneRect();
441 }
442 
ProvideRestoreInfo()443 std::string RadioPattern::ProvideRestoreInfo()
444 {
445     auto jsonObj = JsonUtil::Create(true);
446     auto radioPaintProperty = GetPaintProperty<RadioPaintProperty>();
447     CHECK_NULL_RETURN(radioPaintProperty, "");
448     jsonObj->Put("checked", radioPaintProperty->GetRadioCheck().value_or(false));
449     return jsonObj->ToString();
450 }
451 
OnRestoreInfo(const std::string & restoreInfo)452 void RadioPattern::OnRestoreInfo(const std::string& restoreInfo)
453 {
454     auto radioPaintProperty = GetPaintProperty<RadioPaintProperty>();
455     CHECK_NULL_VOID(radioPaintProperty);
456     auto info = JsonUtil::ParseJsonString(restoreInfo);
457     if (!info->IsValid() || !info->IsObject()) {
458         return;
459     }
460     auto jsonChecked = info->GetValue("checked");
461     radioPaintProperty->UpdateRadioCheck(jsonChecked->GetBool());
462     OnModifyDone();
463 }
464 } // namespace OHOS::Ace::NG
465