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_item_group_paint_method.h"
17 
18 namespace OHOS::Ace::NG {
PaintDivider(PaintWrapper * paintWrapper,RSCanvas & canvas)19 void ListItemGroupPaintMethod::PaintDivider(PaintWrapper* paintWrapper, RSCanvas& canvas)
20 {
21     if (!divider_.strokeWidth.IsValid() || divider_.strokeWidth.Unit() == DimensionUnit::PERCENT) {
22         return;
23     }
24     const auto& geometryNode = paintWrapper->GetGeometryNode();
25     auto frameSize = geometryNode->GetPaddingSize();
26     OffsetF paddingOffset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
27     Axis axis = vertical_ ? Axis::HORIZONTAL : Axis::VERTICAL;
28     DividerGroupInfo info = {
29         .lanes = lanes_ > 1 ? lanes_ : 1,
30         .crossSize = frameSize.CrossSize(axis),
31         .mainPadding = paddingOffset.GetMainOffset(axis),
32         .crossPadding = paddingOffset.GetCrossOffset(axis),
33         .constrainStrokeWidth = divider_.strokeWidth.ConvertToPx(),
34         .halfSpaceWidth = (spaceWidth_ + divider_.strokeWidth.ConvertToPx()) / 2.0f, /* 2.0f half */
35         .startMargin = std::max(0.0, divider_.startMargin.ConvertToPx()),
36         .endMargin = std::max(0.0, divider_.endMargin.ConvertToPx())
37     };
38     fSpacingTotal_ = (info.lanes - 1) * laneGutter_;
39     float laneLen = (info.crossSize - fSpacingTotal_) / info.lanes -
40         info.startMargin - info.endMargin;
41     if (NearZero(laneLen)) return;
42     if (LessNotEqual(laneLen, 0.0f)) {
43         info.startMargin = 0.0f;
44         info.endMargin = 0.0f;
45         laneLen = (info.crossSize - fSpacingTotal_) / info.lanes -
46             info.startMargin - info.endMargin;
47     }
48     DividerPainter dividerPainter(
49         info.constrainStrokeWidth, laneLen, vertical_, divider_.color, LineCap::SQUARE);
50     UpdateDividerList(info, dividerPainter, canvas);
51 }
52 
UpdateDividerList(const DividerGroupInfo & info,const DividerPainter & dividerPainter,RSCanvas & canvas)53 void ListItemGroupPaintMethod::UpdateDividerList(const DividerGroupInfo& info,
54     const DividerPainter& dividerPainter, RSCanvas& canvas)
55 {
56     int32_t laneIdx = 0;
57     bool isFirstItem = (itemPosition_.begin()->first == 0);
58     std::list<int32_t> lastLineIndex;
59     bool nextIsPressed = false;
60     for (const auto& child : itemPosition_) {
61         auto nextId = child.first - info.lanes;
62         nextIsPressed = nextId < 0 ? child.second.isPressed : itemPosition_[nextId].isPressed;
63         if (!isFirstItem && !(child.second.isPressed || nextIsPressed)) {
64             DrawDivider(child.first, laneIdx, info, dividerPainter, canvas);
65         }
66         if (laneIdx == 0) {
67             lastLineIndex.clear();
68         }
69         lastLineIndex.emplace_back(child.first);
70         laneIdx = (laneIdx + 1) >= info.lanes ? 0 : laneIdx + 1;
71         isFirstItem = isFirstItem ? laneIdx > 0 : false;
72     }
73     if (!lastLineIndex.empty() && *lastLineIndex.rbegin() < totalItemCount_ - 1) {
74         int32_t laneIdx = 0;
75         for (auto index : lastLineIndex) {
76             if (index + info.lanes >= totalItemCount_) {
77                 break;
78             }
79             if (!itemPosition_.at(index).isPressed) {
80                 DrawLastLineDivider(index, laneIdx, info, dividerPainter, canvas);
81             }
82             laneIdx++;
83         }
84     }
85 }
86 
DrawDivider(int32_t index,int32_t laneIdx,const DividerGroupInfo & info,const DividerPainter & dividerPainter,RSCanvas & canvas)87 void ListItemGroupPaintMethod::DrawDivider(int32_t index, int32_t laneIdx, const DividerGroupInfo& info,
88     const DividerPainter& dividerPainter, RSCanvas& canvas)
89 {
90     float mainPos = info.mainPadding + itemPosition_.at(index).startPos - info.halfSpaceWidth;
91     float crossPos = info.startMargin + info.crossPadding +
92         laneIdx * ((info.crossSize - fSpacingTotal_) / info.lanes + laneGutter_);
93     if (layoutDirection_ == TextDirection::RTL && vertical_) {
94         mainPos = info.mainPadding + mainSize_ - itemPosition_.at(index).startPos +
95             info.halfSpaceWidth - info.constrainStrokeWidth;
96     } else if (layoutDirection_ == TextDirection::RTL && !vertical_) {
97         float dividerLen = (info.crossSize - fSpacingTotal_) / info.lanes - info.startMargin - info.endMargin;
98         crossPos = info.crossPadding + info.crossSize - info.startMargin - dividerLen -
99             laneIdx * ((info.crossSize - fSpacingTotal_) / info.lanes + laneGutter_);
100     }
101     OffsetF offset = vertical_ ? OffsetF(mainPos, crossPos) : OffsetF(crossPos, mainPos);
102     dividerPainter.DrawLine(canvas, offset);
103 }
104 
DrawLastLineDivider(int32_t index,int32_t laneIdx,const DividerGroupInfo & info,const DividerPainter & dividerPainter,RSCanvas & canvas)105 void ListItemGroupPaintMethod::DrawLastLineDivider(int32_t index, int32_t laneIdx, const DividerGroupInfo& info,
106     const DividerPainter& dividerPainter, RSCanvas& canvas)
107 {
108     float mainPos = info.mainPadding + itemPosition_.at(index).endPos + spaceWidth_ - info.halfSpaceWidth;
109     float crossPos = info.startMargin + info.crossPadding +
110         laneIdx * ((info.crossSize - fSpacingTotal_) / info.lanes + laneGutter_);
111     if (layoutDirection_ == TextDirection::RTL && vertical_) {
112         mainPos = info.mainPadding + mainSize_ - itemPosition_.at(index).endPos - info.halfSpaceWidth;
113     } else if (layoutDirection_ == TextDirection::RTL && !vertical_) {
114         float dividerLen = (info.crossSize - fSpacingTotal_) / info.lanes - info.startMargin - info.endMargin;
115         crossPos = info.crossPadding + info.crossSize - info.startMargin - dividerLen -
116             laneIdx * ((info.crossSize - fSpacingTotal_) / info.lanes + laneGutter_);
117     }
118     OffsetF offset = vertical_ ? OffsetF(mainPos, crossPos) : OffsetF(crossPos, mainPos);
119     dividerPainter.DrawLine(canvas, offset);
120 }
121 
GetContentDrawFunction(PaintWrapper * paintWrapper)122 CanvasDrawFunction ListItemGroupPaintMethod::GetContentDrawFunction(PaintWrapper* paintWrapper)
123 {
124     auto paintFunc = [weak = WeakClaim(this), paintWrapper](RSCanvas& canvas) {
125         auto painter = weak.Upgrade();
126         CHECK_NULL_VOID(painter);
127         painter->PaintDivider(paintWrapper, canvas);
128     };
129     return paintFunc;
130 }
131 } // namespace OHOS::Ace::NG