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_toggle_button.h"
17
18#include "common/image.h"
19#include "engines/gfx/gfx_engine_manager.h"
20#include "imgdecode/cache_manager.h"
21namespace {
22constexpr int16_t TOGGLE_BTN_WIDTH = 32;
23constexpr int16_t TOGGLE_BTN_CORNER_RADIUS = 11;
24constexpr int16_t TOGGLE_BTN_RADIUS_DIFF = 2;
25constexpr uint8_t TOGGLE_BTN_UNSELECTED_OPA = 97;
26#if DEFAULT_ANIMATION
27constexpr int16_t DEFAULT_ANIMATOR_TIME = 150;
28constexpr float BEZIER_CONTROL_POINT_X_1 = 0.2;
29constexpr float BEZIER_CONTROL_POINT_X_2 = 0.2;
30#endif
31} // namespace
32namespace OHOS {
33UIToggleButton::UIToggleButton()
34    : corner_(TOGGLE_BTN_CORNER_RADIUS),
35      radius_(TOGGLE_BTN_CORNER_RADIUS - TOGGLE_BTN_RADIUS_DIFF),
36      rectWidth_(TOGGLE_BTN_WIDTH)
37{
38    backgroundOpacity_ = TOGGLE_BTN_UNSELECTED_OPA;
39    image_[UNSELECTED].SetSrc("");
40    image_[SELECTED].SetSrc("");
41#if DEFAULT_ANIMATION
42    checkBoxAnimator_.SetTime(DEFAULT_ANIMATOR_TIME);
43#endif
44    Resize(width_, height_);
45}
46
47void UIToggleButton::SetState(bool state)
48{
49    if (state) {
50        UICheckBox::SetState(SELECTED);
51    } else {
52        UICheckBox::SetState(UNSELECTED);
53    }
54    Invalidate();
55}
56
57void UIToggleButton::CalculateSize()
58{
59    width_ = GetWidth();
60    height_ = GetHeight();
61    int16_t minValue = (width_ > height_) ? height_ : width_;
62    corner_ = TOGGLE_BTN_CORNER_RADIUS * minValue / DEFAULT_HOT_HEIGHT;
63    int16_t radiusDiff = TOGGLE_BTN_RADIUS_DIFF * minValue / DEFAULT_HOT_WIDTH;
64    radius_ = corner_ - radiusDiff;
65    rectWidth_ = TOGGLE_BTN_WIDTH * minValue / DEFAULT_HOT_WIDTH;
66
67    Rect contentRect = GetContentRect();
68    int16_t dx = (width_ - rectWidth_) / 2; // 2: half
69    int16_t dy = (height_ / 2) - corner_;   // 2: half
70    int16_t x = contentRect.GetX() + dx;
71    int16_t y = contentRect.GetY() + dy;
72    leftCenter_ = {static_cast<int16_t>(x + corner_), static_cast<int16_t>(y + corner_)};
73    rightCenter_ = {static_cast<int16_t>(x + rectWidth_ - corner_), static_cast<int16_t>(y + corner_)};
74#if DEFAULT_ANIMATION
75    if (checkBoxAnimator_.GetState() != Animator::START) {
76        if (IsRtl()) {
77            currentCenter_ = (state_ == SELECTED) ? leftCenter_ : rightCenter_;
78        } else {
79            currentCenter_ = (state_ == SELECTED) ? rightCenter_ : leftCenter_;
80        }
81        backgroundOpacity_ = (state_ == SELECTED) ? OPA_OPAQUE : TOGGLE_BTN_UNSELECTED_OPA;
82        bgColor_ = (state_ == SELECTED) ? selectedStateColor_ : Color::White();
83    }
84#else
85    if (IsRtl()) {
86        currentCenter_ = (state_ == SELECTED) ? leftCenter_ : rightCenter_;
87    } else {
88        currentCenter_ = (state_ == SELECTED) ? rightCenter_ : leftCenter_;
89    }
90    backgroundOpacity_ = (state_ == SELECTED) ? OPA_OPAQUE : TOGGLE_BTN_UNSELECTED_OPA;
91    bgColor_ = (state_ == SELECTED) ? selectedStateColor_ : Color::White();
92#endif
93    rectMid_.SetRect(x, y, x + rectWidth_, y + (corner_ << 1) + 1);
94}
95
96void UIToggleButton::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
97{
98    if ((image_[SELECTED].GetSrcType() != IMG_SRC_UNKNOWN) && (image_[UNSELECTED].GetSrcType() != IMG_SRC_UNKNOWN)) {
99        UICheckBox::OnDraw(gfxDstBuffer, invalidatedArea);
100    } else {
101        CalculateSize();
102        BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance();
103        baseGfxEngine->DrawRect(gfxDstBuffer, GetRect(), invalidatedArea, *style_, opaScale_);
104        Rect contentRect = GetContentRect();
105        Rect trunc = invalidatedArea;
106        bool isIntersect = trunc.Intersect(trunc, contentRect);
107        if (!isIntersect) {
108            return;
109        }
110        Style styleUnSelect = StyleDefault::GetBackgroundTransparentStyle();
111        styleUnSelect.bgColor_ = bgColor_;
112        styleUnSelect.bgOpa_ = backgroundOpacity_;
113        styleUnSelect.borderRadius_ = corner_;
114        baseGfxEngine->DrawRect(gfxDstBuffer, rectMid_, trunc, styleUnSelect, opaScale_);
115        ArcInfo arcInfoLeft = {currentCenter_, {0}, radius_, 0, CIRCLE_IN_DEGREE, nullptr};
116        styleUnSelect.lineColor_ = Color::White();
117        styleUnSelect.lineWidth_ = radius_;
118        baseGfxEngine->DrawArc(gfxDstBuffer, arcInfoLeft, trunc, styleUnSelect, OPA_OPAQUE,
119                               CapType::CAP_NONE);
120    }
121}
122#if DEFAULT_ANIMATION
123void UIToggleButton::Callback(UIView* view)
124{
125    runTime_ = checkBoxAnimator_.GetRunTime();
126    float x = static_cast<float>(runTime_) / checkBoxAnimator_.GetTime();
127    float coefficient = Interpolation::GetBezierY(x, BEZIER_CONTROL_POINT_X_1, 0, BEZIER_CONTROL_POINT_X_2, 1);
128    if (state_ == SELECTED) {
129        currentCenter_.y = rightCenter_.y;
130        if (IsRtl()) {
131            currentCenter_.x = rightCenter_.x - static_cast<uint16_t>((rightCenter_.x - leftCenter_.x) * coefficient);
132        } else {
133            currentCenter_.x = static_cast<int16_t>((rightCenter_.x - leftCenter_.x) * coefficient) + leftCenter_.x;
134        }
135        backgroundOpacity_ =
136            static_cast<uint8_t>(TOGGLE_BTN_UNSELECTED_OPA + (OPA_OPAQUE - TOGGLE_BTN_UNSELECTED_OPA) * coefficient);
137        bgColor_ =
138            Color::GetMixColor(selectedStateColor_, Color::White(), static_cast<uint8_t>(OPA_OPAQUE * coefficient));
139    } else {
140        currentCenter_.y = leftCenter_.y;
141        if (IsRtl()) {
142            currentCenter_.x = static_cast<int16_t>((rightCenter_.x - leftCenter_.x) * coefficient) + leftCenter_.x;
143        } else {
144            currentCenter_.x = rightCenter_.x - static_cast<uint16_t>((rightCenter_.x - leftCenter_.x) * coefficient);
145        }
146        backgroundOpacity_ = static_cast<uint8_t>(OPA_OPAQUE - (OPA_OPAQUE - TOGGLE_BTN_UNSELECTED_OPA) * coefficient);
147        bgColor_ = Color::GetMixColor(selectedStateColor_, Color::White(),
148                                      static_cast<uint8_t>(OPA_OPAQUE * (1 - coefficient)));
149    }
150    Invalidate();
151}
152
153void UIToggleButton::OnStop(UIView& view)
154{
155    if (state_ == SELECTED) {
156        bgColor_ = selectedStateColor_;
157    } else {
158        bgColor_ = Color::White();
159    }
160    backgroundOpacity_ = (state_ == SELECTED) ? OPA_OPAQUE : TOGGLE_BTN_UNSELECTED_OPA;
161    Invalidate();
162}
163#endif
164
165void UIToggleButton::EnableRtl(bool isRtl)
166{
167    isRtl_ = isRtl;
168}
169
170bool UIToggleButton::IsRtl()
171{
172    return isRtl_;
173}
174} // namespace OHOS
175