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