1 /*
2  * Copyright (c) 2021-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 
16 #include "core/components/picker/rosen_render_picker_base.h"
17 
18 #ifndef USE_ROSEN_DRAWING
19 #include "include/effects/SkGradientShader.h"
20 #else
21 #include "core/components_ng/render/drawing.h"
22 #endif
23 
24 namespace OHOS::Ace {
25 namespace {
26 
27 const uint32_t SEARCH_MAX_DEPTH = 16;
28 constexpr Dimension FOCUS_RADIUS = 8.0_vp;
29 constexpr Dimension FOCUS_BORDER_THICKNESS = 2.0_vp;
30 constexpr uint32_t FOCUS_BORDER_COLOR = 0xFF0A59F7;
31 
32 } // namespace
33 
GetOptionsRect(const Offset & offset,const RefPtr<RenderPickerColumn> & pickerColumn)34 Rect RosenRenderPickerBase::GetOptionsRect(const Offset& offset, const RefPtr<RenderPickerColumn>& pickerColumn)
35 {
36     // Calculate rect of all options.
37     double top = offset.GetY() + pickerColumn->GetPosition().GetY();
38     double left = offset.GetX() + pickerColumn->GetPosition().GetX() - pickerColumn->GetXOffset();
39 
40     // Get pickerColumn's relative position.
41     auto parent = pickerColumn->GetParent().Upgrade();
42     uint32_t depth = 0;
43     while ((parent) && (AceType::RawPtr(parent) != this) && (depth < SEARCH_MAX_DEPTH)) {
44         depth++;
45         top += parent->GetPosition().GetY();
46         left += parent->GetPosition().GetX();
47         parent = parent->GetParent().Upgrade();
48     }
49 
50     // Get first and last options relative position.
51     auto bottom = top + pickerColumn->GetLayoutSize().Height();
52     return Rect(left, top, columnParent_->GetLayoutSize().Width(), bottom - top);
53 }
54 
Paint(RenderContext & context,const Offset & offset)55 void RosenRenderPickerBase::Paint(RenderContext& context, const Offset& offset)
56 {
57     auto canvas = static_cast<RosenRenderContext*>(&context)->GetCanvas();
58     auto rsNode = static_cast<RosenRenderContext*>(&context)->GetRSNode();
59     if (!canvas || !rsNode) {
60         LOGE("Paint canvas is null");
61         return;
62     }
63     rsNode->SetPaintOrder(true);
64 
65 #ifdef OHOS_PLATFORM
66 #ifndef USE_ROSEN_DRAWING
67     auto recordingCanvas = static_cast<Rosen::RSRecordingCanvas*>(canvas);
68     recordingCanvas->MultiplyAlpha((disabled_ ? 102.0 / 255 : 1));
69 #else
70     LOGE("Drawing is not supported");
71 #endif
72 #endif
73 
74     RenderNode::Paint(context, offset);
75 
76     if (!data_) {
77         return;
78     }
79     auto theme = data_->GetTheme();
80     if (columns_.empty() || !theme || !box_ || !columnParent_) {
81         return;
82     }
83     auto anchorColumn = GetTextDirection() == TextDirection::RTL ? columns_.back() : columns_.front();
84     if (!anchorColumn) {
85         return;
86     }
87     auto dividerThickness = NormalizeToPx(theme->GetDividerThickness());
88     if (NearZero(dividerThickness) && NearZero(theme->GetGradientHeight().Value())) {
89         // No divider and no gradient, directly return.
90         return;
91     }
92 
93     // Draw two dividers on both sides of selected option.
94 #ifndef USE_ROSEN_DRAWING
95     SkPaint paint;
96     paint.setColor(theme->GetDividerColor().GetValue());
97 #else
98     RSPen pen;
99     pen.SetColor(theme->GetDividerColor().GetValue());
100 #endif
101     auto rect = GetOptionsRect(offset, anchorColumn);
102     auto dividerSpacing = NormalizeToPx(theme->GetDividerSpacing());
103     if (data_->GetDefaultHeight()) {
104         if (NormalizeToPx(data_->GetColumnHeight()) > 0) {
105             if (data_->GetColumnHeight().Unit() == DimensionUnit::PERCENT) {
106                 dividerSpacing = data_->GetColumnHeight().Value() * dividerSpacing;
107             } else {
108                 dividerSpacing = NormalizeToPx(data_->GetColumnHeight());
109             }
110         } else {
111             dividerSpacing = 0;
112         }
113     }
114 
115     double upperLine = rect.Top() + rect.Height() / 2.0 - dividerSpacing / 2.0;
116     double downLine = rect.Top() + rect.Height() / 2.0 + dividerSpacing / 2.0;
117     double leftLine = rect.Left();
118     double rightLine = rect.Right();
119 
120     if (!NearZero(dividerThickness) && !data_->GetSubsidiary()) {
121 #ifndef USE_ROSEN_DRAWING
122         canvas->drawRect({ leftLine, upperLine - dividerThickness, rightLine, upperLine }, paint);
123         canvas->drawRect({ leftLine, downLine, rightLine, downLine + dividerThickness }, paint);
124 #else
125         canvas->AttachPen(pen);
126         canvas->DrawRect(RSRect(leftLine, upperLine - dividerThickness, rightLine, upperLine));
127         canvas->DrawRect(RSRect(leftLine, downLine, rightLine, downLine + dividerThickness));
128         canvas->DetachPen();
129 #endif
130     }
131     // Paint gradient at top and bottom.
132     PaintGradient(canvas, offset, rect, theme);
133 
134     // Need to use PipelineContext::ShowFocusAnimation
135     PaintFocusOptionBorder(canvas);
136 }
137 
138 #ifndef USE_ROSEN_DRAWING
PaintGradient(SkCanvas * canvas,const Offset & offset,const Rect & rect,const RefPtr<PickerTheme> & theme)139 void RosenRenderPickerBase::PaintGradient(
140     SkCanvas* canvas, const Offset& offset, const Rect& rect, const RefPtr<PickerTheme>& theme)
141 #else
142 void RosenRenderPickerBase::PaintGradient(
143     RSCanvas* canvas, const Offset& offset, const Rect& rect, const RefPtr<PickerTheme>& theme)
144 #endif
145 {
146     if (data_ && data_->SubsidiaryShowed()) {
147         return;
148     }
149 
150     double gradientHeight = NormalizeToPx(theme->GetGradientHeight());
151     if (NearZero(gradientHeight)) {
152         return;
153     }
154     // Paint gradient rect over the picker content.
155 #ifndef USE_ROSEN_DRAWING
156     SkPaint paint;
157     SkPoint beginPoint = SkPoint::Make(SkDoubleToScalar(rect.Left()), SkDoubleToScalar(rect.Top()));
158     SkPoint endPoint = SkPoint::Make(SkDoubleToScalar(rect.Left()), SkDoubleToScalar(rect.Bottom()));
159     SkPoint points[2] = { beginPoint, endPoint };
160 #else
161     RSPen pen;
162     RSPoint beginPoint(static_cast<RSScalar>(rect.Left()), static_cast<RSScalar>(rect.Top()));
163     RSPoint endPoint(static_cast<RSScalar>(rect.Left()), static_cast<RSScalar>(rect.Bottom()));
164     std::vector<RSPoint> points = { beginPoint, endPoint };
165 #endif
166     auto backDecoration = theme->GetPopupDecoration(false);
167     Color endColor = backDecoration ? backDecoration->GetBackgroundColor() : Color::WHITE;
168 
169     auto renderBox = GetBgColorBox();
170     if (data_ && data_->GetHasBackgroundColor() && renderBox) {
171         endColor = renderBox->GetColor();
172     }
173 
174     Color middleColor = endColor.ChangeAlpha(0);
175 #ifndef USE_ROSEN_DRAWING
176     SkColor colors[] = { endColor.GetValue(), middleColor.GetValue(), middleColor.GetValue(), endColor.GetValue() };
177     const float stopPositions[] = { 0.0f, gradientHeight / rect.Height(),
178         (rect.Height() - gradientHeight) / rect.Height(), 1.0f };
179 
180     paint.setShader(SkGradientShader::MakeLinear(points, colors, stopPositions, std::size(colors), SkTileMode::kClamp));
181     canvas->drawRect({ rect.Left(), rect.Top(), rect.Right(), rect.Bottom() }, paint);
182 #else  // USE_ROSEN_DRAWING
183     std::vector<RSColorQuad> colors = { endColor.GetValue(), middleColor.GetValue(), middleColor.GetValue(),
184         endColor.GetValue() };
185     const std::vector<RSScalar> stopPositions = { 0.0, static_cast<RSScalar>(gradientHeight / rect.Height()),
186         static_cast<RSScalar>((rect.Height() - gradientHeight) / rect.Height()), 1.0f };
187     pen.SetShaderEffect(
188         RSShaderEffect::CreateLinearGradient(points.at(0), points.at(1), colors, stopPositions, RSTileMode::CLAMP));
189     canvas->AttachPen(pen);
190     canvas->DrawRect(RSRect(rect.Left(), rect.Top(), rect.Right(), rect.Bottom()));
191     canvas->DetachPen();
192 #endif // USE_ROSEN_DRAWING
193 }
194 
195 #ifndef USE_ROSEN_DRAWING
PaintFocusOptionBorder(SkCanvas * canvas)196 void RosenRenderPickerBase::PaintFocusOptionBorder(SkCanvas* canvas)
197 #else
198 void RosenRenderPickerBase::PaintFocusOptionBorder(RSCanvas* canvas)
199 #endif
200 {
201     auto pipeline = context_.Upgrade();
202     if (!pipeline || !pipeline->GetIsTabKeyPressed()) {
203         return;
204     }
205 
206     for (const auto& column : columns_) {
207         if (!column->IsFocused()) {
208             continue;
209         }
210 
211         InitializeSelectedOption(column);
212         double focusBorderThickness = NormalizeToPx(FOCUS_BORDER_THICKNESS);
213         double focusOffsetX = focusBoxOffset_.GetX() - focusBorderThickness / 2.0;
214         double focusOffsetY = focusBoxOffset_.GetY() - focusBorderThickness / 2.0;
215         double focusBorderWidth = focusBoxSize_.Width() + focusBorderThickness;
216         double focusBorderHeight = focusBoxSize_.Height() + focusBorderThickness;
217         double focusRadius = NormalizeToPx(FOCUS_RADIUS);
218 #ifndef USE_ROSEN_DRAWING
219         SkPaint paint;
220         paint.setColor(FOCUS_BORDER_COLOR);
221         paint.setStyle(SkPaint::Style::kStroke_Style);
222         paint.setStrokeWidth(focusBorderThickness);
223         paint.setAntiAlias(true);
224         SkRRect rRect;
225         rRect.setRectXY(SkRect::MakeIWH(focusBorderWidth, focusBorderHeight), focusRadius, focusRadius);
226         rRect.offset(focusOffsetX, focusOffsetY);
227         canvas->drawRRect(rRect, paint);
228 #else
229         RSPen pen;
230         pen.SetColor(FOCUS_BORDER_COLOR);
231         pen.SetWidth(focusBorderThickness);
232         pen.SetAntiAlias(true);
233         RSRoundRect rRect(RSRect(0, 0, focusBorderWidth, focusBorderHeight), focusRadius, focusRadius);
234         rRect.Offset(focusOffsetX, focusOffsetY);
235         canvas->AttachPen(pen);
236         canvas->DrawRoundRect(rRect);
237         canvas->DetachPen();
238 #endif
239         break;
240     }
241 }
242 
243 } // namespace OHOS::Ace
244