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_slider.h"
17 
18 #include "common/image.h"
19 #include "dock/focus_manager.h"
20 #include "dock/vibrator_manager.h"
21 #include "draw/draw_image.h"
22 #include "engines/gfx/gfx_engine_manager.h"
23 #include "gfx_utils/graphic_log.h"
24 #include "imgdecode/cache_manager.h"
25 #include "themes/theme_manager.h"
26 
27 namespace OHOS {
UISlider()28 UISlider::UISlider() : knobWidth_(0), knobStyleAllocFlag_(false), knobImage_(nullptr), listener_(nullptr)
29 {
30     touchable_ = true;
31     draggable_ = true;
32     dragParentInstead_ = false;
33 #if ENABLE_FOCUS_MANAGER
34     focusable_ = true;
35 #endif
36 #if ENABLE_ROTATE_INPUT
37     rotateFactor_ = DEFAULT_SLIDER_ROTATE_FACTOR;
38     cachedRotation_ = 0;
39 #endif
40 
41     Theme* theme = ThemeManager::GetInstance().GetCurrent();
42     if (theme != nullptr) {
43         knobStyle_ = &(theme->GetSliderKnobStyle());
44     } else {
45         knobStyle_ = &(StyleDefault::GetSliderKnobStyle());
46     }
47 }
48 
~UISlider()49 UISlider::~UISlider()
50 {
51     if (knobImage_ != nullptr) {
52         delete knobImage_;
53         knobImage_ = nullptr;
54     }
55 
56     if (knobStyleAllocFlag_) {
57         delete knobStyle_;
58         knobStyle_ = nullptr;
59         knobStyleAllocFlag_ = false;
60     }
61 }
62 
SetKnobStyle(const Style & style)63 void UISlider::SetKnobStyle(const Style& style)
64 {
65     if (!knobStyleAllocFlag_) {
66         knobStyle_ = new Style;
67         if (knobStyle_ == nullptr) {
68             GRAPHIC_LOGE("new Style fail");
69             return;
70         }
71         knobStyleAllocFlag_ = true;
72     }
73     *knobStyle_ = style;
74 }
75 
SetKnobStyle(uint8_t key,int64_t value)76 void UISlider::SetKnobStyle(uint8_t key, int64_t value)
77 {
78     if (!knobStyleAllocFlag_) {
79         knobStyle_ = new Style(*knobStyle_);
80         if (knobStyle_ == nullptr) {
81             GRAPHIC_LOGE("new Style fail");
82             return;
83         }
84         knobStyleAllocFlag_ = true;
85     }
86     knobStyle_->SetStyle(key, value);
87 }
88 
GetKnobStyle() const89 const Style& UISlider::GetKnobStyle() const
90 {
91     return *knobStyle_;
92 }
93 
GetKnobStyle(uint8_t key) const94 int64_t UISlider::GetKnobStyle(uint8_t key) const
95 {
96     return knobStyle_->GetStyle(key);
97 }
98 
SetKnobImage(const ImageInfo * knobImage)99 void UISlider::SetKnobImage(const ImageInfo* knobImage)
100 {
101     if (!InitImage()) {
102         return;
103     }
104     knobImage_->SetSrc(knobImage);
105 }
106 
SetKnobImage(const char * knobImage)107 void UISlider::SetKnobImage(const char* knobImage)
108 {
109     if (!InitImage()) {
110         return;
111     }
112     knobImage_->SetSrc(knobImage);
113 }
114 
DrawKnob(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,const Rect & foregroundRect)115 void UISlider::DrawKnob(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, const Rect& foregroundRect)
116 {
117     int16_t halfKnobWidth = GetKnobWidth() / 2; // 2: half
118     int16_t offset;
119     Rect knobBar;
120     switch (direction_) {
121         case Direction::DIR_LEFT_TO_RIGHT: {
122             offset = (knobWidth_ - progressHeight_) / 2; // 2: half
123             knobBar.SetRect(foregroundRect.GetRight() - halfKnobWidth, foregroundRect.GetTop() - offset,
124                             foregroundRect.GetRight() + halfKnobWidth, foregroundRect.GetBottom() + offset);
125             break;
126         }
127         case Direction::DIR_RIGHT_TO_LEFT: {
128             offset = (knobWidth_ - progressHeight_) / 2; // 2: half
129             knobBar.SetRect(foregroundRect.GetLeft() - halfKnobWidth, foregroundRect.GetTop() - offset,
130                             foregroundRect.GetLeft() + halfKnobWidth, foregroundRect.GetBottom() + offset);
131             break;
132         }
133         case Direction::DIR_BOTTOM_TO_TOP: {
134             offset = (knobWidth_ - progressWidth_) / 2; // 2: half
135             knobBar.SetRect(foregroundRect.GetLeft() - offset, foregroundRect.GetTop() - halfKnobWidth,
136                             foregroundRect.GetRight() + offset, foregroundRect.GetTop() + halfKnobWidth);
137             break;
138         }
139         case Direction::DIR_TOP_TO_BOTTOM: {
140             offset = (knobWidth_ - progressWidth_) / 2; // 2: half
141             knobBar.SetRect(foregroundRect.GetLeft() - offset, foregroundRect.GetBottom() - halfKnobWidth,
142                             foregroundRect.GetRight() + offset, foregroundRect.GetBottom() + halfKnobWidth);
143             break;
144         }
145         default: {
146             GRAPHIC_LOGW("UISlider::DrawKnob Direction error!\n");
147         }
148     }
149     DrawValidRect(gfxDstBuffer, knobImage_, knobBar, invalidatedArea, *knobStyle_, 0);
150 }
151 
InitImage()152 bool UISlider::InitImage()
153 {
154     if (!UIAbstractProgress::InitImage()) {
155         return false;
156     }
157     if (knobImage_ == nullptr) {
158         knobImage_ = new Image();
159         if (knobImage_ == nullptr) {
160             GRAPHIC_LOGE("new Image fail");
161             return false;
162         }
163     }
164     return true;
165 }
166 
SetImage(const ImageInfo * backgroundImage,const ImageInfo * foregroundImage)167 void UISlider::SetImage(const ImageInfo* backgroundImage, const ImageInfo* foregroundImage)
168 {
169     if (!InitImage()) {
170         return;
171     }
172     backgroundImage_->SetSrc(backgroundImage);
173     foregroundImage_->SetSrc(foregroundImage);
174 }
175 
SetImage(const char * backgroundImage,const char * foregroundImage)176 void UISlider::SetImage(const char* backgroundImage, const char* foregroundImage)
177 {
178     if (!InitImage()) {
179         return;
180     }
181     backgroundImage_->SetSrc(backgroundImage);
182     foregroundImage_->SetSrc(foregroundImage);
183 }
184 
DrawForeground(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,Rect & coords)185 void UISlider::DrawForeground(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, Rect& coords)
186 {
187     Point startPoint;
188     int16_t progressWidth;
189     int16_t progressHeight;
190     uint16_t radius;
191     GetBackgroundParam(startPoint, progressWidth, progressHeight, radius, *foregroundStyle_);
192 
193     int16_t left;
194     int16_t right;
195     int16_t top;
196     int16_t bottom;
197     int16_t length;
198     Rect foregroundRect;
199 
200     switch (direction_) {
201         case Direction::DIR_LEFT_TO_RIGHT: {
202             length = GetCurrentPos(progressWidth_ + 1);
203             foregroundRect.SetRect(startPoint.x, startPoint.y, startPoint.x + progressWidth - 1,
204                                    startPoint.y + progressHeight_ - 1);
205 
206             left = startPoint.x - radius - 1;
207             right = left + length;
208             coords.SetRect(left, startPoint.y, right, startPoint.y + progressHeight_ - 1);
209             break;
210         }
211         case Direction::DIR_RIGHT_TO_LEFT: {
212             length = GetCurrentPos(progressWidth_ + 1);
213             foregroundRect.SetRect(startPoint.x, startPoint.y, startPoint.x + progressWidth - 1,
214                                    startPoint.y + progressHeight_ - 1);
215 
216             right = startPoint.x + progressWidth + radius + 1;
217             left = right - length;
218             coords.SetRect(left, startPoint.y, right, startPoint.y + progressHeight_ - 1);
219             break;
220         }
221         case Direction::DIR_TOP_TO_BOTTOM: {
222             length = GetCurrentPos(progressHeight_ + 1);
223             foregroundRect.SetRect(startPoint.x, startPoint.y, startPoint.x + progressWidth_ - 1,
224                                    startPoint.y + progressHeight - 1);
225 
226             top = startPoint.y - radius - 1;
227             bottom = top + length;
228             coords.SetRect(startPoint.x, top, startPoint.x + progressWidth_ - 1, bottom);
229             break;
230         }
231         case Direction::DIR_BOTTOM_TO_TOP: {
232             length = GetCurrentPos(progressHeight_ + 1);
233             foregroundRect.SetRect(startPoint.x, startPoint.y, startPoint.x + progressWidth_ - 1,
234                                    startPoint.y + progressHeight - 1);
235 
236             bottom = startPoint.y + progressHeight + radius + 1;
237             top = bottom - length;
238             coords.SetRect(startPoint.x, top, startPoint.x + progressWidth_ - 1, bottom);
239             break;
240         }
241         default: {
242             GRAPHIC_LOGE("UISlider: DrawForeground direction Err!\n");
243             return;
244         }
245     }
246     if (coords.Intersect(coords, invalidatedArea)) {
247         DrawValidRect(gfxDstBuffer, foregroundImage_, foregroundRect, coords, *foregroundStyle_, radius);
248     }
249 }
250 
OnDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)251 void UISlider::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
252 {
253     BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, GetOrigRect(), invalidatedArea, *style_, opaScale_);
254 
255     Rect trunc(invalidatedArea);
256     if (trunc.Intersect(trunc, GetOrigRect())) {
257         DrawBackground(gfxDstBuffer, trunc);
258         Rect foregroundRect;
259         DrawForeground(gfxDstBuffer, trunc, foregroundRect);
260         DrawKnob(gfxDstBuffer, trunc, foregroundRect);
261     }
262 }
263 
CalculateCurrentValue(int16_t length,int16_t totalLength)264 int32_t UISlider::CalculateCurrentValue(int16_t length, int16_t totalLength)
265 {
266     if (totalLength != 0) {
267         return static_cast<int32_t>(rangeMin_ + (static_cast<int64_t>(rangeMax_) - rangeMin_) * length / totalLength);
268     }
269     return 0;
270 }
271 
UpdateCurrentValue(const Point & knobPosition)272 int32_t UISlider::UpdateCurrentValue(const Point& knobPosition)
273 {
274     Point startPoint;
275     Rect rect = GetOrigRect();
276     // 2: Half of the gap
277     startPoint.x = rect.GetLeft() + style_->borderWidth_ + style_->paddingLeft_ + (GetWidth() - progressWidth_) / 2;
278     // 2: Half of the gap
279     startPoint.y = rect.GetTop() + style_->borderWidth_ + style_->paddingTop_ + (GetHeight() - progressHeight_) / 2;
280 
281     int32_t value = curValue_;
282     switch (direction_) {
283         case Direction::DIR_LEFT_TO_RIGHT:
284             if (knobPosition.x <= startPoint.x) {
285                 value = rangeMin_;
286             } else if (knobPosition.x >= startPoint.x + progressWidth_) {
287                 value = rangeMax_;
288             } else {
289                 value = CalculateCurrentValue(knobPosition.x - startPoint.x, progressWidth_);
290             }
291             break;
292         case Direction::DIR_RIGHT_TO_LEFT:
293             if (knobPosition.x <= startPoint.x) {
294                 value = rangeMax_;
295             } else if (knobPosition.x >= startPoint.x + progressWidth_) {
296                 value = rangeMin_;
297             } else {
298                 value = CalculateCurrentValue(startPoint.x + progressWidth_ - knobPosition.x, progressWidth_);
299             }
300             break;
301         case Direction::DIR_BOTTOM_TO_TOP:
302             if (knobPosition.y <= startPoint.y) {
303                 value = rangeMax_;
304             } else if (knobPosition.y >= startPoint.y + progressHeight_) {
305                 value = rangeMin_;
306             } else {
307                 value = CalculateCurrentValue(startPoint.y + progressHeight_ - knobPosition.y, progressHeight_);
308             }
309             break;
310         case Direction::DIR_TOP_TO_BOTTOM:
311             if (knobPosition.y <= startPoint.y) {
312                 value = rangeMin_;
313             } else if (knobPosition.y >= startPoint.y + progressHeight_) {
314                 value = rangeMax_;
315             } else {
316                 value = CalculateCurrentValue(knobPosition.y - startPoint.y, progressHeight_);
317             }
318             break;
319         default:
320             GRAPHIC_LOGW("UISlider::UpdateCurrentValue Direction error!\n");
321     }
322     SetValue(value);
323     return value;
324 }
325 
OnClickEvent(const ClickEvent & event)326 bool UISlider::OnClickEvent(const ClickEvent& event)
327 {
328     Point knobPosition = event.GetCurrentPos();
329     int32_t value = UpdateCurrentValue(knobPosition);
330     if (listener_ != nullptr) {
331         listener_->OnChange(value);
332     }
333     bool ret = UIView::OnClickEvent(event);
334     Invalidate();
335     return ret;
336 }
337 
OnDragEvent(const DragEvent & event)338 bool UISlider::OnDragEvent(const DragEvent& event)
339 {
340     Point knobPosition = event.GetCurrentPos();
341     int32_t value = UpdateCurrentValue(knobPosition);
342     if (listener_ != nullptr) {
343         listener_->OnChange(value);
344     }
345     Invalidate();
346     return UIView::OnDragEvent(event);
347 }
348 
OnDragEndEvent(const DragEvent & event)349 bool UISlider::OnDragEndEvent(const DragEvent& event)
350 {
351     Point knobPosition = event.GetCurrentPos();
352     int32_t value = UpdateCurrentValue(knobPosition);
353     if (listener_ != nullptr) {
354         listener_->OnChange(value);
355         listener_->OnRelease(value);
356     }
357     Invalidate();
358     return UIView::OnDragEndEvent(event);
359 }
360 
361 #if ENABLE_ROTATE_INPUT
OnRotateEvent(const RotateEvent & event)362 bool UISlider::OnRotateEvent(const RotateEvent& event)
363 {
364     int32_t realRotation = 0;
365     cachedRotation_ += event.GetRotate() * rotateFactor_;
366     realRotation = static_cast<int32_t>(cachedRotation_);
367     if (realRotation == 0) {
368         return UIView::OnRotateEvent(event);
369     }
370     cachedRotation_ = 0;
371 #if ENABLE_VIBRATOR
372     int32_t lastValue = curValue_;
373 #endif
374     SetValue(curValue_ + realRotation);
375     if (listener_ != nullptr) {
376         listener_->OnChange(curValue_);
377     }
378 #if ENABLE_VIBRATOR
379     VibratorFunc vibratorFunc = VibratorManager::GetInstance()->GetVibratorFunc();
380     if (vibratorFunc != nullptr && lastValue != curValue_) {
381         if (curValue_ == rangeMin_ || curValue_ == rangeMax_) {
382             GRAPHIC_LOGI("UISlider::OnRotateEvent calls TYPE_THREE vibrator");
383             vibratorFunc(VibratorType::VIBRATOR_TYPE_THREE);
384         } else {
385             int32_t changedValue = MATH_ABS(curValue_ - lastValue);
386             for (int32_t i = 0; i < changedValue; i++) {
387                 GRAPHIC_LOGI("UISlider::OnRotateEvent calls TYPE_TWO vibrator");
388                 vibratorFunc(VibratorType::VIBRATOR_TYPE_TWO);
389             }
390         }
391     }
392 #endif
393     return UIView::OnRotateEvent(event);
394 }
395 
OnRotateEndEvent(const RotateEvent & event)396 bool UISlider::OnRotateEndEvent(const RotateEvent& event)
397 {
398     cachedRotation_ = 0;
399     return UIView::OnRotateEndEvent(event);
400 }
401 #endif
402 } // namespace OHOS
403