1 /*
2  * Copyright (c) 2022-2024 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/text_picker/textpicker_paint_method.h"
17 
18 #include "core/components/common/properties/color.h"
19 #include "core/components/picker/picker_theme.h"
20 #include "core/components_ng/pattern/text_picker/textpicker_layout_property.h"
21 #include "core/components_ng/pattern/text_picker/textpicker_pattern.h"
22 #include "core/components_ng/render/drawing.h"
23 #include "core/components_ng/render/drawing_prop_convertor.h"
24 #include "core/pipeline_ng/pipeline_context.h"
25 
26 namespace OHOS::Ace::NG {
27 
28 namespace {
29 constexpr uint8_t DOUBLE = 2;
30 const Dimension PICKER_DIALOG_DIVIDER_MARGIN = 24.0_vp;
31 } // namespace
32 
GetForegroundDrawFunction(PaintWrapper * paintWrapper)33 CanvasDrawFunction TextPickerPaintMethod::GetForegroundDrawFunction(PaintWrapper* paintWrapper)
34 {
35     const auto& geometryNode = paintWrapper->GetGeometryNode();
36     CHECK_NULL_RETURN(geometryNode, nullptr);
37     auto frameRect = geometryNode->GetFrameRect();
38 
39     auto renderContext = paintWrapper->GetRenderContext();
40     CHECK_NULL_RETURN(renderContext, nullptr);
41     auto pickerNode = renderContext->GetHost();
42     CHECK_NULL_RETURN(pickerNode, nullptr);
43     auto layoutProperty = pickerNode->GetLayoutProperty<TextPickerLayoutProperty>();
44     CHECK_NULL_RETURN(layoutProperty, nullptr);
45 
46     auto textPickerPattern = pickerNode->GetPattern<TextPickerPattern>();
47     CHECK_NULL_RETURN(textPickerPattern, nullptr);
48     auto fontScale = textPickerPattern->GetPaintDividerSpacing();
49 
50     return
51         [weak = WeakClaim(this), layoutProperty, frameRect,
52             fontScale, pattern = pattern_](RSCanvas& canvas) {
53             auto picker = weak.Upgrade();
54             CHECK_NULL_VOID(picker);
55             auto textPickerPattern = DynamicCast<TextPickerPattern>(pattern.Upgrade());
56             CHECK_NULL_VOID(textPickerPattern);
57 
58             PaddingPropertyF padding = layoutProperty->CreatePaddingAndBorder();
59             RectF contentRect = { padding.left.value_or(0), padding.top.value_or(0),
60                 frameRect.Width() - padding.Width(), frameRect.Height() - padding.Height() };
61 
62             double dividerHeight = picker->defaultPickerItemHeight_ * fontScale;
63             if (textPickerPattern->GetResizeFlag()) {
64                 dividerHeight = textPickerPattern->GetResizePickerItemHeight() * fontScale;
65             }
66 
67             if (contentRect.Width() >= 0.0f && (contentRect.Height() >= dividerHeight)) {
68                 if (textPickerPattern->GetCustomDividerFlag()) {
69                     auto divider = textPickerPattern->GetDivider();
70                     auto textDirection = layoutProperty->GetNonAutoLayoutDirection();
71                     divider.isRtl = (textDirection == TextDirection::RTL) ? true : false;
72                     picker->PaintCustomDividerLines(canvas, contentRect, frameRect, divider, dividerHeight);
73                 } else {
74                     picker->PaintDefaultDividerLines(canvas, contentRect, dividerHeight);
75                 }
76             }
77         };
78 }
79 
PaintCustomDividerLines(RSCanvas & canvas,const RectF & contentRect,const RectF & frameRect,const ItemDivider & divider,double dividerHeight)80 void TextPickerPaintMethod::PaintCustomDividerLines(RSCanvas& canvas, const RectF &contentRect, const RectF &frameRect,
81     const ItemDivider &divider, double dividerHeight)
82 {
83     DividerInfo info;
84     if (NeedPaintDividerLines(contentRect, divider, dividerHeight, info)) {
85         PaintDividerLines(canvas, contentRect, info, false);
86     }
87 }
88 
PaintDefaultDividerLines(RSCanvas & canvas,const RectF & contentRect,double dividerHeight)89 void TextPickerPaintMethod::PaintDefaultDividerLines(RSCanvas& canvas, const RectF &contentRect,
90     double dividerHeight)
91 {
92     auto pipeline = PipelineContext::GetCurrentContext();
93     CHECK_NULL_VOID(pipeline);
94     auto theme = pipeline->GetTheme<PickerTheme>();
95     auto dividerColor = theme->GetDividerColor();
96     auto dividerLineWidth = theme->GetDividerThickness().ConvertToPx();
97 
98     auto dividerLength = contentRect.Width();
99     auto dividerMargin = contentRect.GetX();
100     auto textPickerPattern = DynamicCast<TextPickerPattern>(pattern_.Upgrade());
101     CHECK_NULL_VOID(textPickerPattern);
102     if (textPickerPattern->GetIsShowInDialog()) {
103         dividerLength -= PICKER_DIALOG_DIVIDER_MARGIN.ConvertToPx() * DOUBLE;
104         dividerMargin += PICKER_DIALOG_DIVIDER_MARGIN.ConvertToPx();
105     }
106 
107     DividerInfo info;
108     info.dividerColor = dividerColor;
109     info.dividerWidth = dividerLineWidth;
110     info.dividerLength = dividerLength;
111     info.dividerMargin = dividerMargin;
112     info.dividerHeight = dividerHeight;
113     PaintDividerLines(canvas, contentRect, info);
114 }
115 
SetStrokeWidth(const ItemDivider & divider,double dividerHeight,DividerInfo & info)116 bool TextPickerPaintMethod::SetStrokeWidth(const ItemDivider &divider, double dividerHeight, DividerInfo& info)
117 {
118     if (divider.strokeWidth.ConvertToPx() > dividerHeight / DOUBLE) {
119         auto pipeline = PipelineContext::GetCurrentContext();
120         if (!pipeline) {
121             return false;
122         }
123         auto theme = pipeline->GetTheme<PickerTheme>();
124         if (theme) {
125             info.dividerWidth = theme->GetDividerThickness().ConvertToPx();
126         } else {
127             info.dividerWidth = 0.0f;
128         }
129     } else {
130         info.dividerWidth = divider.strokeWidth.ConvertToPx();
131     }
132 
133     if (info.dividerWidth <= 0.0f) {
134         return false;
135     }
136     return true;
137 }
138 
NeedPaintDividerLines(const RectF & contentRect,const ItemDivider & divider,double dividerHeight,DividerInfo & info)139 bool TextPickerPaintMethod::NeedPaintDividerLines(const RectF &contentRect, const ItemDivider &divider,
140     double dividerHeight, DividerInfo& info)
141 {
142     if (!SetStrokeWidth(divider, dividerHeight, info)) {
143         return false;
144     }
145     info.dividerHeight = dividerHeight;
146     info.startMargin = std::max(0.0, divider.startMargin.ConvertToPx());
147     info.endMargin = std::max(0.0, divider.endMargin.ConvertToPx());
148     info.dividerColor = divider.color;
149 
150     auto dividerLength = contentRect.Width();
151     auto dividerMargin = contentRect.GetX();
152     auto textPickerPattern = DynamicCast<TextPickerPattern>(pattern_.Upgrade());
153     if (!textPickerPattern) {
154         return false;
155     }
156     if (textPickerPattern->GetIsShowInDialog()) {
157         dividerLength -= PICKER_DIALOG_DIVIDER_MARGIN.ConvertToPx() * DOUBLE;
158         dividerMargin += PICKER_DIALOG_DIVIDER_MARGIN.ConvertToPx();
159     }
160 
161     float checkMargin = dividerLength - info.startMargin - info.endMargin;
162     if (NearZero(checkMargin)) {
163         return false;
164     }
165     if (LessNotEqual(checkMargin, 0.0f)) {
166         LOGE("StartMagin and endMargin are set to 0, because the parameters are wrong");
167         info.startMargin = 0.0f;
168         info.endMargin = 0.0f;
169     }
170     if (divider.isRtl) {
171         dividerMargin += info.endMargin;
172     } else {
173         dividerMargin += info.startMargin;
174     }
175     dividerLength = dividerLength - info.startMargin - info.endMargin;
176     info.dividerMargin = dividerMargin;
177     info.dividerLength = dividerLength;
178     return true;
179 }
180 
PaintDividerLines(RSCanvas & canvas,const RectF & contentRect,const DividerInfo & info,bool isDefaultLine)181 void TextPickerPaintMethod::PaintDividerLines(RSCanvas& canvas, const RectF& contentRect, const DividerInfo &info,
182     bool isDefaultLine)
183 {
184     double upperLine = 0.0f;
185     double downLine = 0.0f;
186     if (isDefaultLine) {
187         upperLine = (contentRect.Height() - info.dividerHeight) / DOUBLE + contentRect.GetY();
188         downLine = (contentRect.Height() + info.dividerHeight) / DOUBLE + contentRect.GetY();
189     } else {
190         upperLine = (contentRect.Height() - info.dividerHeight - info.dividerWidth) / DOUBLE + contentRect.GetY();
191         downLine = (contentRect.Height() + info.dividerHeight  - info.dividerWidth) / DOUBLE + contentRect.GetY();
192     }
193     OffsetF offset = OffsetF(info.dividerMargin, upperLine);
194     PaintLine(offset, info, canvas);
195     OffsetF offsetY = OffsetF(info.dividerMargin, downLine);
196     PaintLine(offsetY, info, canvas);
197 }
198 
PaintLine(const OffsetF & offset,const DividerInfo & info,RSCanvas & canvas)199 void TextPickerPaintMethod::PaintLine(const OffsetF& offset, const DividerInfo &info, RSCanvas& canvas)
200 {
201     canvas.Save();
202     RSBrush brush;
203     brush.SetColor(info.dividerColor.GetValue());
204     canvas.AttachBrush(brush);
205 
206     auto startPointX = offset.GetX();
207     auto startPointY = offset.GetY();
208     auto endPointX = offset.GetX() + info.dividerLength;
209     auto endPointY = offset.GetY() + info.dividerWidth;
210 
211     canvas.DrawRect(RSRect(startPointX, startPointY, endPointX, endPointY));
212     canvas.DetachBrush();
213     canvas.Restore();
214 }
215 
216 } // namespace OHOS::Ace::NG
217