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_button.h"
17 #include "animator/interpolation.h"
18 #include "common/image.h"
19 #include "draw/draw_image.h"
20 #include "engines/gfx/gfx_engine_manager.h"
21 #include "gfx_utils/graphic_log.h"
22 #include "gfx_utils/style.h"
23 #include "imgdecode/cache_manager.h"
24 #include "themes/theme_manager.h"
25
26 namespace OHOS {
UIButton()27 UIButton::UIButton()
28 : defaultImgSrc_(nullptr),
29 triggeredImgSrc_(nullptr),
30 currentImgSrc_(ButtonImageSrc::BTN_IMAGE_DEFAULT),
31 imgX_(0),
32 imgY_(0),
33 contentWidth_(0),
34 contentHeight_(0),
35 state_(RELEASED),
36 styleState_(RELEASED),
37 #if DEFAULT_ANIMATION
38 enableAnimation_(true),
39 animator_(*this),
40 #endif
41 buttonStyleAllocFlag_(false)
42 {
43 touchable_ = true;
44 SetupThemeStyles();
45 }
46
~UIButton()47 UIButton::~UIButton()
48 {
49 if (defaultImgSrc_ != nullptr) {
50 delete defaultImgSrc_;
51 defaultImgSrc_ = nullptr;
52 }
53
54 if (triggeredImgSrc_ != nullptr) {
55 delete triggeredImgSrc_;
56 triggeredImgSrc_ = nullptr;
57 }
58
59 if (buttonStyleAllocFlag_) {
60 for (uint8_t i = 0; i < BTN_STATE_NUM; i++) {
61 delete buttonStyles_[i];
62 buttonStyles_[i] = nullptr;
63 }
64 buttonStyleAllocFlag_ = false;
65 }
66 }
67
DrawImg(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,OpacityType opaScale)68 void UIButton::DrawImg(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, OpacityType opaScale)
69 {
70 const Image* image = GetCurImageSrc();
71 if (image == nullptr) {
72 return;
73 }
74
75 ImageHeader header = {0};
76 image->GetHeader(header);
77 Rect coords;
78 Rect viewRect = GetContentRect();
79 coords.SetLeft(viewRect.GetLeft() + GetImageX());
80 coords.SetTop(viewRect.GetTop() + GetImageY());
81 coords.SetWidth(header.width);
82 coords.SetHeight(header.height);
83
84 Rect trunc(invalidatedArea);
85 if (trunc.Intersect(trunc, viewRect)) {
86 image->DrawImage(gfxDstBuffer, coords, trunc, *buttonStyles_[state_], opaScale);
87 }
88 }
89
OnDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)90 void UIButton::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
91 {
92 OpacityType opa = GetMixOpaScale();
93 BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, GetOrigRect(), invalidatedArea, *buttonStyles_[state_], opa);
94 DrawImg(gfxDstBuffer, invalidatedArea, opa);
95 }
96
SetupThemeStyles()97 void UIButton::SetupThemeStyles()
98 {
99 Theme* theme = ThemeManager::GetInstance().GetCurrent();
100
101 if (theme == nullptr) {
102 buttonStyles_[RELEASED] = &(StyleDefault::GetButtonReleasedStyle());
103 buttonStyles_[PRESSED] = &(StyleDefault::GetButtonPressedStyle());
104 buttonStyles_[INACTIVE] = &(StyleDefault::GetButtonInactiveStyle());
105 } else {
106 buttonStyles_[RELEASED] = &(theme->GetButtonStyle().released);
107 buttonStyles_[PRESSED] = &(theme->GetButtonStyle().pressed);
108 buttonStyles_[INACTIVE] = &(theme->GetButtonStyle().inactive);
109 }
110 style_ = buttonStyles_[RELEASED];
111 }
112
GetStyle(uint8_t key) const113 int64_t UIButton::GetStyle(uint8_t key) const
114 {
115 return GetStyleForState(key, styleState_);
116 }
117
SetStyle(uint8_t key,int64_t value)118 void UIButton::SetStyle(uint8_t key, int64_t value)
119 {
120 SetStyleForState(key, value, styleState_);
121 }
122
GetStyleForState(uint8_t key,ButtonState state) const123 int64_t UIButton::GetStyleForState(uint8_t key, ButtonState state) const
124 {
125 if (state < BTN_STATE_NUM) {
126 return (buttonStyles_[state])->GetStyle(key);
127 }
128 return 0;
129 }
130
SetStyleForState(uint8_t key,int64_t value,ButtonState state)131 void UIButton::SetStyleForState(uint8_t key, int64_t value, ButtonState state)
132 {
133 if (state < BTN_STATE_NUM) {
134 if (!buttonStyleAllocFlag_) {
135 for (uint8_t i = 0; i < BTN_STATE_NUM; i++) {
136 Style styleSaved = *buttonStyles_[i];
137 buttonStyles_[i] = new Style;
138 if (buttonStyles_[i] == nullptr) {
139 GRAPHIC_LOGE("new Style fail");
140 return;
141 }
142 *(buttonStyles_[i]) = styleSaved;
143 }
144 buttonStyleAllocFlag_ = true;
145 }
146 style_ = buttonStyles_[RELEASED];
147 int16_t width = GetWidth();
148 int16_t height = GetHeight();
149 int16_t x = GetX();
150 int16_t y = GetY();
151 buttonStyles_[state]->SetStyle(key, value);
152 Rect rect(x, y, x + width - 1, y + height - 1);
153 UpdateRectInfo(key, rect);
154 }
155 }
156
OnPressEvent(const PressEvent & event)157 bool UIButton::OnPressEvent(const PressEvent& event)
158 {
159 currentImgSrc_ = ButtonImageSrc::BTN_IMAGE_TRIGGERED;
160 SetState(PRESSED);
161 Resize(contentWidth_, contentHeight_);
162 Invalidate();
163 #if DEFAULT_ANIMATION
164 if (enableAnimation_) {
165 animator_.Start();
166 }
167 #endif
168 return UIView::OnPressEvent(event);
169 }
170
OnReleaseEvent(const ReleaseEvent & event)171 bool UIButton::OnReleaseEvent(const ReleaseEvent& event)
172 {
173 currentImgSrc_ = ButtonImageSrc::BTN_IMAGE_DEFAULT;
174 SetState(RELEASED);
175 Resize(contentWidth_, contentHeight_);
176 Invalidate();
177 #if DEFAULT_ANIMATION
178 if (enableAnimation_) {
179 animator_.Start();
180 }
181 #endif
182 return UIView::OnReleaseEvent(event);
183 }
184
OnCancelEvent(const CancelEvent & event)185 bool UIButton::OnCancelEvent(const CancelEvent& event)
186 {
187 currentImgSrc_ = ButtonImageSrc::BTN_IMAGE_DEFAULT;
188 SetState(RELEASED);
189 Resize(contentWidth_, contentHeight_);
190 Invalidate();
191 #if DEFAULT_ANIMATION
192 if (enableAnimation_) {
193 animator_.Start();
194 }
195 #endif
196 return UIView::OnCancelEvent(event);
197 }
198
GetCurImageSrc() const199 const Image* UIButton::GetCurImageSrc() const
200 {
201 if (currentImgSrc_ == ButtonImageSrc::BTN_IMAGE_DEFAULT) {
202 return defaultImgSrc_;
203 } else if (currentImgSrc_ == ButtonImageSrc::BTN_IMAGE_TRIGGERED) {
204 return triggeredImgSrc_;
205 } else {
206 return nullptr;
207 }
208 }
209
SetImageSrc(const char * defaultImgSrc,const char * triggeredImgSrc)210 void UIButton::SetImageSrc(const char* defaultImgSrc, const char* triggeredImgSrc)
211 {
212 if (!InitImage()) {
213 return;
214 }
215 defaultImgSrc_->SetSrc(defaultImgSrc);
216 triggeredImgSrc_->SetSrc(triggeredImgSrc);
217 }
218
SetImageSrc(const ImageInfo * defaultImgSrc,const ImageInfo * triggeredImgSrc)219 void UIButton::SetImageSrc(const ImageInfo* defaultImgSrc, const ImageInfo* triggeredImgSrc)
220 {
221 if (!InitImage()) {
222 return;
223 }
224 defaultImgSrc_->SetSrc(defaultImgSrc);
225 triggeredImgSrc_->SetSrc(triggeredImgSrc);
226 }
227
Disable()228 void UIButton::Disable()
229 {
230 SetState(INACTIVE);
231 touchable_ = false;
232 }
233
Enable()234 void UIButton::Enable()
235 {
236 SetState(RELEASED);
237 touchable_ = true;
238 }
239
SetState(ButtonState state)240 void UIButton::SetState(ButtonState state)
241 {
242 state_ = state;
243 style_ = buttonStyles_[state_];
244 Invalidate();
245 }
246
InitImage()247 bool UIButton::InitImage()
248 {
249 if (defaultImgSrc_ == nullptr) {
250 defaultImgSrc_ = new Image();
251 if (defaultImgSrc_ == nullptr) {
252 GRAPHIC_LOGE("new Image fail");
253 return false;
254 }
255 }
256 if (triggeredImgSrc_ == nullptr) {
257 triggeredImgSrc_ = new Image();
258 if (triggeredImgSrc_ == nullptr) {
259 GRAPHIC_LOGE("new Image fail");
260 return false;
261 }
262 }
263 return true;
264 }
265
OnPreDraw(Rect & invalidatedArea) const266 bool UIButton::OnPreDraw(Rect& invalidatedArea) const
267 {
268 Rect rect(GetRect());
269 int16_t r = buttonStyles_[styleState_]->borderRadius_;
270 if (r == COORD_MAX) {
271 return true;
272 }
273
274 if (r != 0) {
275 r = ((r & 0x1) == 0) ? (r >> 1) : ((r + 1) >> 1);
276 rect.SetLeft(rect.GetX() + r);
277 rect.SetWidth(rect.GetWidth() - r);
278 rect.SetTop(rect.GetY() + r);
279 rect.SetHeight(rect.GetHeight() - r);
280 }
281 if (rect.IsContains(invalidatedArea)) {
282 return true;
283 }
284 invalidatedArea.Intersect(invalidatedArea, rect);
285 return false;
286 }
287
288 #if DEFAULT_ANIMATION
OnPostDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)289 void UIButton::OnPostDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
290 {
291 if (state_ == ButtonState::PRESSED && enableAnimation_) {
292 animator_.DrawMask(gfxDstBuffer, invalidatedArea);
293 }
294 UIView::OnPostDraw(gfxDstBuffer, invalidatedArea);
295 }
296
297 namespace {
298 constexpr float FULL_SCALE = 1.0f;
299 constexpr float SHRINK_SCALE = 0.8f;
300 constexpr uint32_t SHRINK_DURATION = 150;
301 constexpr uint32_t RECOVER_DURATION = 200;
302 constexpr int64_t MASK_OPA = 25;
303 constexpr float BEZIER_CONTROL = 0.2f;
304 } // namespace
305
Start()306 void UIButton::ButtonAnimator::Start()
307 {
308 bool isReverse = (button_.state_ == UIButton::ButtonState::PRESSED);
309 float targetScale = isReverse ? SHRINK_SCALE : FULL_SCALE;
310 if ((animator_.GetState() == Animator::STOP) && FloatEqual(targetScale, scale_)) {
311 return;
312 }
313
314 if (isReverse) {
315 animator_.SetTime(SHRINK_DURATION);
316 } else {
317 animator_.SetTime(RECOVER_DURATION);
318 }
319 animator_.Start();
320 /* reverse the animator direction */
321 float x = isReverseAnimation_ ? (FULL_SCALE - scale_) : (scale_ - SHRINK_SCALE);
322 float y = x / (FULL_SCALE - SHRINK_SCALE);
323 x = Interpolation::GetBezierY(FULL_SCALE - y, 0, BEZIER_CONTROL, FULL_SCALE, BEZIER_CONTROL);
324 animator_.SetRunTime(static_cast<uint32_t>(animator_.GetTime() * x));
325 isReverseAnimation_ = isReverse;
326 }
327
DrawMask(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)328 void UIButton::ButtonAnimator::DrawMask(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
329 {
330 Style maskStyle;
331 maskStyle.SetStyle(STYLE_BACKGROUND_COLOR, Color::White().full);
332 maskStyle.SetStyle(STYLE_BACKGROUND_OPA, MASK_OPA);
333 maskStyle.SetStyle(STYLE_BORDER_RADIUS, button_.GetStyle(STYLE_BORDER_RADIUS));
334 OpacityType opa = button_.GetMixOpaScale();
335 BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, button_.GetRect(), invalidatedArea, maskStyle, opa);
336 }
337
ScaleButton(UIButton & button,float scale)338 static inline void ScaleButton(UIButton& button, float scale)
339 {
340 Vector2<float> scaleValue_ = {scale, scale};
341 Vector2<float> centrePoint(button.GetWidth() / 2.0f, button.GetHeight() / 2.0f);
342 button.Scale(scaleValue_, centrePoint);
343 }
344
Callback(UIView * view)345 void UIButton::ButtonAnimator::Callback(UIView* view)
346 {
347 float x = static_cast<float>(animator_.GetRunTime()) / animator_.GetTime();
348 float offset = Interpolation::GetBezierY(x, BEZIER_CONTROL, 0, BEZIER_CONTROL, FULL_SCALE);
349 float scale = (FULL_SCALE - SHRINK_SCALE) * offset;
350
351 scale_ = isReverseAnimation_ ? (FULL_SCALE - scale) : (scale + SHRINK_SCALE);
352 ScaleButton(button_, scale_);
353 }
354
OnStop(UIView & view)355 void UIButton::ButtonAnimator::OnStop(UIView& view)
356 {
357 if (isReverseAnimation_) {
358 scale_ = SHRINK_SCALE;
359 ScaleButton(button_, SHRINK_SCALE);
360 } else {
361 scale_ = FULL_SCALE;
362 button_.ResetTransParameter();
363 }
364 }
365 #endif
366 } // namespace OHOS
367