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 27namespace OHOS { 28UISlider::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 49UISlider::~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 63void 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 76void 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 89const Style& UISlider::GetKnobStyle() const 90{ 91 return *knobStyle_; 92} 93 94int64_t UISlider::GetKnobStyle(uint8_t key) const 95{ 96 return knobStyle_->GetStyle(key); 97} 98 99void UISlider::SetKnobImage(const ImageInfo* knobImage) 100{ 101 if (!InitImage()) { 102 return; 103 } 104 knobImage_->SetSrc(knobImage); 105} 106 107void UISlider::SetKnobImage(const char* knobImage) 108{ 109 if (!InitImage()) { 110 return; 111 } 112 knobImage_->SetSrc(knobImage); 113} 114 115void 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 152bool 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 167void 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 176void 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 185void 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 251void 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 264int32_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 272int32_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 326bool 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 338bool 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 349bool 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 362bool 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 396bool UISlider::OnRotateEndEvent(const RotateEvent& event) 397{ 398 cachedRotation_ = 0; 399 return UIView::OnRotateEndEvent(event); 400} 401#endif 402} // namespace OHOS 403