1/*
2 * Copyright (c) 2020-2021 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 "components/ui_checkbox.h"
17#include "default_resource/check_box_res.h"
18#include "draw/draw_image.h"
19#include "engines/gfx/gfx_engine_manager.h"
20#include "imgdecode/cache_manager.h"
21
22namespace OHOS {
23namespace {
24constexpr uint8_t DEFAULT_UNSELECT_BG_OPA = 168;     // default background opacity
25constexpr float DEFAULT_COEFFICIENT_START_DX = 0.22; // start point: x-cordinate offset
26constexpr float DEFAULT_COEFFICIENT_START_DY = 0.5;  // start point: y-cordinate offset
27constexpr float DEFAULT_COEFFICIENT_MID_DX = 0.2;    // middle point: y-cordinate offset
28constexpr float DEFAULT_COEFFICIENT_MID_DY = 0.38;   // middle point: y-cordinate offset
29constexpr int16_t DEFAULT_RATIO_BORDER_RADIUS_LINE_WIDTH = 4;
30constexpr uint8_t DEFAULT_BG_RED = 31;
31constexpr uint8_t DEFAULT_BG_GREEN = 113;
32constexpr uint8_t DEFAULT_BG_BLUE = 255;
33#if DEFAULT_ANIMATION
34constexpr int16_t DEFAULT_ANIMATOR_TIME = 200;
35constexpr float BEZIER_CONTROL_POINT_X_1 = 0.33;
36constexpr float BEZIER_CONTROL_POINT_X_2 = 0.67;
37#endif
38} // namespace
39UICheckBox::UICheckBox()
40    : state_(UNSELECTED),
41      onStateChangeListener_(nullptr),
42      width_(DEFAULT_HOT_WIDTH),
43      height_(DEFAULT_HOT_HEIGHT),
44      borderWidth_(DEFAULT_BORDER_WIDTH),
45      backgroundOpacity_(0)
46{
47    touchable_ = true;
48    style_ = &(StyleDefault::GetBackgroundTransparentStyle());
49#if defined(ENABLE_DEFAULT_CHECKBOX_IMAGE) && (ENABLE_DEFAULT_CHECKBOX_IMAGE == 1)
50    image_[UNSELECTED].SetSrc(GetCheckBoxOffInfo());
51    image_[SELECTED].SetSrc(GetCheckBoxOnInfo());
52#endif
53    ImageHeader header = {0};
54    image_[UNSELECTED].GetHeader(header);
55    Resize(header.width, header.height);
56#if DEFAULT_ANIMATION
57    runTime_ = 0;
58    checkBoxAnimator_ = Animator(this, this, DEFAULT_ANIMATOR_TIME, false);
59#endif
60    selectedStateColor_ = Color::GetColorFromRGB(DEFAULT_BG_RED, DEFAULT_BG_GREEN, DEFAULT_BG_BLUE);
61}
62
63void UICheckBox::SetState(UICheckBoxState state, bool needAnimater)
64{
65    if (state_ == state) {
66        return;
67    }
68    state_ = state;
69    if ((image_[SELECTED].GetSrcType() == IMG_SRC_UNKNOWN) || (image_[UNSELECTED].GetSrcType() == IMG_SRC_UNKNOWN)) {
70#if DEFAULT_ANIMATION
71        if (needAnimater) {
72            checkBoxAnimator_.Start();
73            ResetCallback();
74        } else {
75            backgroundOpacity_ = (state_ == SELECTED) ? OPA_OPAQUE : 0;
76        }
77#else
78        backgroundOpacity_ = (state_ == SELECTED) ? OPA_OPAQUE : 0;
79#endif
80    }
81    if (onStateChangeListener_ != nullptr) {
82        onStateChangeListener_->OnChange(state);
83    }
84    Invalidate();
85}
86
87void UICheckBox::ReverseState()
88{
89    if (state_ == SELECTED) {
90        SetState(UNSELECTED, true);
91    } else {
92        SetState(SELECTED, true);
93    }
94}
95
96bool UICheckBox::OnClickEvent(const ClickEvent& event)
97{
98    ReverseState();
99    Invalidate();
100    return UIView::OnClickEvent(event);
101}
102
103void UICheckBox::SetImages(const char* selectedImageSrc, const char* unselectedImageSrc)
104{
105    image_[SELECTED].SetSrc(selectedImageSrc);
106    image_[UNSELECTED].SetSrc(unselectedImageSrc);
107}
108
109void UICheckBox::SetImages(const ImageInfo* selectedImageSrc, const ImageInfo* unselectedImageSrc)
110{
111    image_[SELECTED].SetSrc(selectedImageSrc);
112    image_[UNSELECTED].SetSrc(unselectedImageSrc);
113}
114
115void UICheckBox::CalculateSize()
116{
117    int16_t width = GetWidth();
118    int16_t height = GetHeight();
119    if ((width_ == width) && (height_ == height)) {
120        return;
121    }
122    width_ = width;
123    height_ = height;
124    int16_t minValue = (width_ > height_) ? height_ : width_;
125    borderWidth_ = DEFAULT_BORDER_WIDTH * minValue / DEFAULT_HOT_WIDTH;
126}
127
128void UICheckBox::SelectedStateSoftwareDrawing(BufferInfo& gfxDstBuffer,
129                                              Rect rect,
130                                              Rect trunc,
131                                              int16_t borderRadius,
132                                              int16_t rectLineWidth)
133{
134    if (backgroundOpacity_ == 0) {
135        return;
136    }
137    Style styleSelect = StyleDefault::GetBackgroundTransparentStyle();
138    styleSelect.borderRadius_ = borderRadius;
139    styleSelect.bgColor_ = selectedStateColor_;
140    styleSelect.bgOpa_ = backgroundOpacity_;
141    BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance();
142    baseGfxEngine->DrawRect(gfxDstBuffer, rect, trunc, styleSelect, opaScale_);
143    int16_t dx = static_cast<int16_t>(borderWidth_ * DEFAULT_COEFFICIENT_START_DX);
144    int16_t dy = static_cast<int16_t>(borderWidth_ * DEFAULT_COEFFICIENT_START_DY);
145    Point start = {static_cast<int16_t>(rect.GetX() + dx), static_cast<int16_t>(rect.GetY() + dy)};
146    dx = static_cast<int16_t>(borderWidth_ * DEFAULT_COEFFICIENT_MID_DX);
147    Point mid = {static_cast<int16_t>(start.x + dx), static_cast<int16_t>(start.y + dx)};
148    dx = static_cast<int16_t>(borderWidth_ * DEFAULT_COEFFICIENT_MID_DY);
149    Point end = {static_cast<int16_t>(mid.x + dx), static_cast<int16_t>(mid.y - dx)};
150    const int16_t half = 2; // 2 :half
151    ArcInfo arcInfoLeft = {start,
152                           {0, 0},
153                           static_cast<uint16_t>(rectLineWidth),
154                           SEMICIRCLE_IN_DEGREE + QUARTER_IN_DEGREE / half,
155                           QUARTER_IN_DEGREE / half,
156                           nullptr};
157    ArcInfo arcInfoMid = {mid,
158                          {0, 0},
159                          static_cast<uint16_t>(rectLineWidth),
160                          SEMICIRCLE_IN_DEGREE - QUARTER_IN_DEGREE / half,
161                          SEMICIRCLE_IN_DEGREE + QUARTER_IN_DEGREE / half,
162                          nullptr};
163    ArcInfo arcInfoRight = {end,
164                            {0, 0},
165                            static_cast<uint16_t>(rectLineWidth),
166                            CIRCLE_IN_DEGREE - QUARTER_IN_DEGREE / half,
167                            SEMICIRCLE_IN_DEGREE - QUARTER_IN_DEGREE / half,
168                            nullptr};
169    styleSelect.lineColor_ = Color::White();
170    styleSelect.lineOpa_ = backgroundOpacity_;
171    uint8_t opa = DrawUtils::GetMixOpacity(opaScale_, backgroundOpacity_);
172    baseGfxEngine->DrawArc(gfxDstBuffer, arcInfoLeft, trunc, styleSelect, opaScale_, CapType::CAP_NONE);
173    // 2 : double
174    baseGfxEngine->DrawLine(gfxDstBuffer, start, mid, trunc, rectLineWidth * 2, Color::White(), opa);
175    baseGfxEngine->DrawArc(gfxDstBuffer, arcInfoMid, trunc, styleSelect, opaScale_, CapType::CAP_NONE);
176    // 2 : double
177    baseGfxEngine->DrawLine(gfxDstBuffer, mid, end, trunc, rectLineWidth * 2, Color::White(), opa);
178    baseGfxEngine->DrawArc(gfxDstBuffer, arcInfoRight, trunc, styleSelect, opaScale_, CapType::CAP_NONE);
179}
180
181void UICheckBox::UnSelectedStateSoftwareDrawing(BufferInfo& gfxDstBuffer,
182                                                Rect rect,
183                                                Rect trunc,
184                                                int16_t borderRadius,
185                                                int16_t rectLineWidth)
186{
187    Style styleUnSelect = StyleDefault::GetBackgroundTransparentStyle();
188    styleUnSelect.borderWidth_ = rectLineWidth;
189    styleUnSelect.borderRadius_ = borderRadius;
190    styleUnSelect.borderColor_ = Color::White();
191    styleUnSelect.borderOpa_ = DEFAULT_UNSELECT_BG_OPA;
192    BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, rect, trunc, styleUnSelect, opaScale_);
193}
194
195#if DEFAULT_ANIMATION
196void UICheckBox::ResetCallback()
197{
198    if ((runTime_ != 0) && (checkBoxAnimator_.GetTime() != runTime_)) {
199        checkBoxAnimator_.SetRunTime(checkBoxAnimator_.GetTime() - runTime_);
200    }
201}
202
203void UICheckBox::Callback(UIView* view)
204{
205    runTime_ = checkBoxAnimator_.GetRunTime();
206    float x = static_cast<float>(runTime_) / checkBoxAnimator_.GetTime();
207    float coefficient = Interpolation::GetBezierY(x, BEZIER_CONTROL_POINT_X_1, 0, BEZIER_CONTROL_POINT_X_2, 1);
208    backgroundOpacity_ = (state_ == SELECTED) ? (static_cast<uint8_t>(coefficient * OPA_OPAQUE)) :
209                                                (static_cast<uint8_t>((1 - coefficient) * OPA_OPAQUE));
210    Invalidate();
211}
212
213void UICheckBox::OnStop(UIView& view)
214{
215    backgroundOpacity_ = (state_ == SELECTED) ? OPA_OPAQUE : 0;
216    Invalidate();
217}
218#endif
219
220void UICheckBox::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
221{
222    Rect trunc = invalidatedArea;
223    if ((image_[SELECTED].GetSrcType() != IMG_SRC_UNKNOWN) && (image_[UNSELECTED].GetSrcType() != IMG_SRC_UNKNOWN)) {
224        ImageHeader header = {0};
225        image_[state_].GetHeader(header);
226        int16_t imgWidth = header.width;
227        int16_t imgHeight = header.height;
228        Rect coords = GetContentRect();
229        coords.SetWidth(imgWidth);
230        coords.SetHeight(imgHeight);
231        BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, GetRect(), invalidatedArea, *style_, opaScale_);
232        int16_t offsetLeft = (GetWidth() - imgWidth) / 2;  // 2 : half
233        int16_t offsetTop = (GetHeight() - imgHeight) / 2; // 2 : half
234        coords.SetX(coords.GetX() + offsetLeft);
235        coords.SetY(coords.GetY() + offsetTop);
236        if (trunc.Intersect(trunc, coords)) {
237            image_[state_].DrawImage(gfxDstBuffer, coords, trunc, *style_, opaScale_);
238        }
239    } else {
240        Rect contentRect = GetContentRect();
241        bool isIntersect = trunc.Intersect(trunc, contentRect);
242        if (!isIntersect) {
243            return;
244        }
245        CalculateSize();
246        int16_t rectLineWidth = borderWidth_ / DEFAULT_BORDER_WIDTH;
247        int16_t borderRadius = rectLineWidth * DEFAULT_RATIO_BORDER_RADIUS_LINE_WIDTH;
248        BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, GetRect(), invalidatedArea, *style_, opaScale_);
249        int16_t x = contentRect.GetX() + (width_ - borderWidth_) / 2;  // 2: half
250        int16_t y = contentRect.GetY() + (height_ - borderWidth_) / 2; // 2: half
251        Rect rect(x, y, x + borderWidth_, y + borderWidth_);
252#if DEFAULT_ANIMATION
253        UnSelectedStateSoftwareDrawing(gfxDstBuffer, rect, trunc, borderRadius, rectLineWidth);
254        SelectedStateSoftwareDrawing(gfxDstBuffer, rect, trunc, borderRadius, rectLineWidth);
255#else
256        if (state_ == SELECTED) {
257            SelectedStateSoftwareDrawing(gfxDstBuffer, rect, trunc, borderRadius, rectLineWidth);
258        } else {
259            UnSelectedStateSoftwareDrawing(gfxDstBuffer, rect, trunc, borderRadius, rectLineWidth);
260        }
261#endif
262    }
263}
264
265void UICheckBox::SetSelectedStateColor(ColorType color)
266{
267    selectedStateColor_ = color;
268}
269
270ColorType UICheckBox::GetSelectedStateColor() const
271{
272    return selectedStateColor_;
273}
274} // namespace OHOS
275