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/list/list_paint_method.h"
17 
18 #include "core/components_ng/pattern/scroll/inner/scroll_bar_overlay_modifier.h"
19 #include "core/components_ng/render/divider_painter.h"
20 
21 namespace OHOS::Ace::NG {
PaintEdgeEffect(PaintWrapper * paintWrapper,RSCanvas & canvas)22 void ListPaintMethod::PaintEdgeEffect(PaintWrapper* paintWrapper, RSCanvas& canvas)
23 {
24     auto edgeEffect = edgeEffect_.Upgrade();
25     CHECK_NULL_VOID(edgeEffect);
26     CHECK_NULL_VOID(paintWrapper);
27     auto frameSize = paintWrapper->GetGeometryNode()->GetFrameSize();
28     edgeEffect->Paint(canvas, frameSize, { 0.0f, 0.0f });
29 }
30 
GetForegroundDrawFunction(PaintWrapper * paintWrapper)31 CanvasDrawFunction ListPaintMethod::GetForegroundDrawFunction(PaintWrapper* paintWrapper)
32 {
33     auto paintFunc = [weak = WeakClaim(this), paintWrapper](RSCanvas& canvas) {
34         auto painter = weak.Upgrade();
35         CHECK_NULL_VOID(painter);
36         painter->PaintEdgeEffect(paintWrapper, canvas);
37     };
38     return paintFunc;
39 }
40 
UpdateContentModifier(PaintWrapper * paintWrapper)41 void ListPaintMethod::UpdateContentModifier(PaintWrapper* paintWrapper)
42 {
43     CHECK_NULL_VOID(listContentModifier_);
44     const auto& geometryNode = paintWrapper->GetGeometryNode();
45     OffsetF paddingOffset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
46     auto renderContext = paintWrapper->GetRenderContext();
47     CHECK_NULL_VOID(renderContext);
48     auto frameSize = renderContext->GetPaintRectWithoutTransform().GetSize();
49     auto& padding = geometryNode->GetPadding();
50     if (padding) {
51         frameSize.MinusPadding(*padding->left, *padding->right, *padding->top, *padding->bottom);
52     }
53     UpdateFadingGradient(renderContext);
54 
55     if (TryContentClip(paintWrapper)) {
56         listContentModifier_->SetClip(false);
57     } else {
58         const bool hasPadding = padding && padding->HasValue();
59         bool clip = hasPadding && (!renderContext || renderContext->GetClipEdge().value_or(true));
60         listContentModifier_->SetClipOffset(paddingOffset);
61         listContentModifier_->SetClipSize(frameSize);
62         listContentModifier_->SetClip(clip);
63     }
64 
65     float contentSize = vertical_ ? frameSize.Width() : frameSize.Height();
66     if (!divider_.strokeWidth.IsValid() || totalItemCount_ <= 0 ||
67         divider_.strokeWidth.Unit() == DimensionUnit::PERCENT ||
68         GreatOrEqual(divider_.strokeWidth.ConvertToPx(), contentSize)) {
69         ListDividerMap dividerMap;
70         listContentModifier_->SetDividerMap(std::move(dividerMap));
71         return;
72     }
73     Axis axis = vertical_ ? Axis::HORIZONTAL : Axis::VERTICAL;
74     DividerInfo dividerInfo = {
75         .constrainStrokeWidth = divider_.strokeWidth.ConvertToPx(),
76         .mainSize = vertical_ ? frameSize.Width() : frameSize.Height(),
77         .crossSize = vertical_ ? frameSize.Height() : frameSize.Width(),
78         .mainPadding = paddingOffset.GetMainOffset(axis),
79         .crossPadding = paddingOffset.GetCrossOffset(axis),
80         .startMargin = std::max(0.0, divider_.startMargin.ConvertToPx()),
81         .endMargin = std::max(0.0, divider_.endMargin.ConvertToPx()),
82         .space = space_,
83         .laneGutter = laneGutter_,
84         .lanes = lanes_ > 1 ? lanes_ : 1,
85         .totalItemCount = totalItemCount_,
86         .color = divider_.color,
87         .isVertical = vertical_
88     };
89     float checkMargin = dividerInfo.crossSize / dividerInfo.lanes - dividerInfo.startMargin - dividerInfo.endMargin;
90     if (NearZero(checkMargin)) return;
91     if (LessNotEqual(checkMargin, 0.0f)) {
92         dividerInfo.startMargin = 0.0f;
93         dividerInfo.endMargin = 0.0f;
94     }
95     UpdateDividerList(dividerInfo);
96 }
97 
UpdateDividerList(const DividerInfo & dividerInfo)98 void ListPaintMethod::UpdateDividerList(const DividerInfo& dividerInfo)
99 {
100     listContentModifier_->SetDividerPainter(
101         dividerInfo.constrainStrokeWidth, dividerInfo.isVertical, dividerInfo.color);
102     int32_t lanes = dividerInfo.lanes;
103     int32_t laneIdx = 0;
104     bool lastIsItemGroup = false;
105     bool isFirstItem = (itemPosition_.begin()->first == 0);
106     std::map<int32_t, int32_t> lastLineIndex;
107     ListDividerMap dividerMap;
108     bool nextIsPressed = false;
109     for (const auto& child : itemPosition_) {
110         auto nextId = child.first - lanes;
111         nextIsPressed = nextId < 0 || lastIsItemGroup || child.second.isGroup ?
112             false : itemPosition_[nextId].isPressed;
113         if (!isFirstItem && !(child.second.isPressed || nextIsPressed)) {
114             dividerMap[child.second.id] = HandleDividerList(child.first, lastIsItemGroup, laneIdx, dividerInfo);
115         }
116         if (laneIdx == 0 || child.second.isGroup) {
117             lastLineIndex.clear();
118         }
119         lastLineIndex[child.first] = child.second.id;
120         lastIsItemGroup = child.second.isGroup;
121         laneIdx = (lanes <= 1 || (laneIdx + 1) >= lanes || child.second.isGroup) ? 0 : laneIdx + 1;
122         isFirstItem = isFirstItem ? laneIdx > 0 : false;
123     }
124     if (!lastLineIndex.empty() && lastLineIndex.rbegin()->first < dividerInfo.totalItemCount - 1) {
125         int32_t laneIdx = 0;
126         for (auto index : lastLineIndex) {
127             if (index.first + lanes >= dividerInfo.totalItemCount) {
128                 break;
129             }
130             if (!itemPosition_.at(index.first).isPressed) {
131                 dividerMap[-index.second] = HandleLastLineIndex(index.first, laneIdx, dividerInfo);
132             }
133             laneIdx++;
134         }
135     }
136     listContentModifier_->SetDividerMap(std::move(dividerMap));
137 }
138 
HandleDividerList(int32_t index,bool lastIsGroup,int32_t laneIdx,const DividerInfo & dividerInfo)139 ListDivider ListPaintMethod::HandleDividerList(
140     int32_t index, bool lastIsGroup, int32_t laneIdx, const DividerInfo& dividerInfo)
141 {
142     ListDivider divider;
143     bool laneIdxValid = dividerInfo.lanes > 1 && !lastIsGroup && !itemPosition_.at(index).isGroup;
144     float avgCrossSize = (dividerInfo.crossSize + dividerInfo.laneGutter) / dividerInfo.lanes - dividerInfo.laneGutter;
145     float dividerLen = laneIdxValid ? avgCrossSize : dividerInfo.crossSize;
146     dividerLen = dividerLen - dividerInfo.startMargin - dividerInfo.endMargin;
147     float mainPos = dividerInfo.mainPadding + itemPosition_.at(index).startPos -
148         (dividerInfo.space + dividerInfo.constrainStrokeWidth) / 2; /* 2 half */
149     float crossPos = dividerInfo.startMargin + dividerInfo.crossPadding;
150     if (isRTL_ && dividerInfo.isVertical) {
151         mainPos = dividerInfo.mainPadding + dividerInfo.mainSize - itemPosition_.at(index).startPos +
152             (dividerInfo.space - dividerInfo.constrainStrokeWidth) / 2; /* 2 half */
153         crossPos += (int)laneIdxValid * laneIdx * (avgCrossSize + dividerInfo.laneGutter);
154     } else if (isRTL_ && !dividerInfo.isVertical) {
155         crossPos = dividerInfo.crossPadding + dividerInfo.crossSize - dividerInfo.startMargin - dividerLen;
156         crossPos -= (int)laneIdxValid * laneIdx * (avgCrossSize + dividerInfo.laneGutter);
157     } else {
158         crossPos += (int)laneIdxValid * laneIdx * (avgCrossSize + dividerInfo.laneGutter);
159     }
160     divider.length = dividerLen;
161     divider.offset = dividerInfo.isVertical ?
162         OffsetF(mainPos, crossPos) : OffsetF(crossPos, mainPos);
163     return divider;
164 }
165 
HandleLastLineIndex(int32_t index,int32_t laneIdx,const DividerInfo & dividerInfo)166 ListDivider ListPaintMethod::HandleLastLineIndex(int32_t index, int32_t laneIdx, const DividerInfo& dividerInfo)
167 {
168     ListDivider divider;
169     bool laneIdxValid = dividerInfo.lanes > 1 && !itemPosition_.at(index).isGroup;
170     float avgCrossSize = (dividerInfo.crossSize + dividerInfo.laneGutter) / dividerInfo.lanes - dividerInfo.laneGutter;
171     float dividerLen = laneIdxValid ? avgCrossSize : dividerInfo.crossSize;
172     dividerLen = dividerLen - dividerInfo.startMargin - dividerInfo.endMargin;
173     float mainPos = dividerInfo.mainPadding + itemPosition_.at(index).endPos +
174         (dividerInfo.space - dividerInfo.constrainStrokeWidth) / 2; /* 2 half */
175     float crossPos = dividerInfo.startMargin + dividerInfo.crossPadding;
176     if (isRTL_ && dividerInfo.isVertical) {
177         mainPos = dividerInfo.mainPadding + dividerInfo.mainSize - itemPosition_.at(index).endPos -
178             (dividerInfo.space + dividerInfo.constrainStrokeWidth) / 2; /* 2 half */
179         crossPos += (int)laneIdxValid * laneIdx * (avgCrossSize + dividerInfo.laneGutter);
180     } else if (isRTL_ && !dividerInfo.isVertical) {
181         crossPos = dividerInfo.crossPadding + dividerInfo.crossSize - dividerInfo.startMargin - dividerLen;
182         crossPos -= (int)laneIdxValid * laneIdx * (avgCrossSize + dividerInfo.laneGutter);
183     } else {
184         crossPos += (int)laneIdxValid * laneIdx * (avgCrossSize + dividerInfo.laneGutter);
185     }
186     divider.length = dividerLen;
187     divider.offset = dividerInfo.isVertical ?
188         OffsetF(mainPos, crossPos) : OffsetF(crossPos, mainPos);
189     return divider;
190 }
191 
UpdateOverlayModifier(PaintWrapper * paintWrapper)192 void ListPaintMethod::UpdateOverlayModifier(PaintWrapper* paintWrapper)
193 {
194     CHECK_NULL_VOID(paintWrapper);
195     auto scrollBarOverlayModifier = scrollBarOverlayModifier_.Upgrade();
196     CHECK_NULL_VOID(scrollBarOverlayModifier);
197     auto scrollBar = scrollBar_.Upgrade();
198     CHECK_NULL_VOID(scrollBar);
199     if (scrollBar->GetPositionModeUpdate()) {
200         scrollBarOverlayModifier->SetPositionMode(scrollBar->GetPositionMode());
201     }
202     OffsetF fgOffset(scrollBar->GetActiveRect().Left(), scrollBar->GetActiveRect().Top());
203     scrollBarOverlayModifier->StartBarAnimation(scrollBar->GetHoverAnimationType(),
204         scrollBar->GetOpacityAnimationType(), scrollBar->GetNeedAdaptAnimation(), scrollBar->GetActiveRect());
205     scrollBar->SetHoverAnimationType(HoverAnimationType::NONE);
206     scrollBarOverlayModifier->SetBarColor(scrollBar->GetForegroundColor());
207     scrollBar->SetOpacityAnimationType(OpacityAnimationType::NONE);
208 }
209 } // namespace OHOS::Ace::NG
210