1 /* 2 * Copyright 2012, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Stefano Ceccherini (stefano.ceccherini@gmail.com) 8 * John Scipione (jscipione@gmail.com) 9 */ 10 11 12 #include "InlineScrollView.h" 13 14 #include <ControlLook.h> 15 #include <Debug.h> 16 #include <InterfaceDefs.h> 17 #include <Menu.h> 18 #include <Point.h> 19 #include <Screen.h> 20 #include <Window.h> 21 22 23 const int kDefaultScrollStep = 19; 24 const int kScrollerDimension = 12; 25 26 27 class ScrollArrow : public BView { 28 public: 29 ScrollArrow(BRect frame); 30 virtual ~ScrollArrow(); 31 32 bool IsEnabled() const { return fEnabled; }; 33 void SetEnabled(bool enabled); 34 35 private: 36 bool fEnabled; 37 }; 38 39 40 class UpScrollArrow : public ScrollArrow { 41 public: 42 UpScrollArrow(BRect frame); 43 virtual ~UpScrollArrow(); 44 45 virtual void Draw(BRect updateRect); 46 virtual void MouseDown(BPoint where); 47 }; 48 49 50 class DownScrollArrow : public ScrollArrow { 51 public: 52 DownScrollArrow(BRect frame); 53 virtual ~DownScrollArrow(); 54 55 virtual void Draw(BRect updateRect); 56 virtual void MouseDown(BPoint where); 57 }; 58 59 60 class LeftScrollArrow : public ScrollArrow { 61 public: 62 LeftScrollArrow(BRect frame); 63 virtual ~LeftScrollArrow(); 64 65 virtual void Draw(BRect updateRect); 66 virtual void MouseDown(BPoint where); 67 }; 68 69 70 class RightScrollArrow : public ScrollArrow { 71 public: 72 RightScrollArrow(BRect frame); 73 virtual ~RightScrollArrow(); 74 75 virtual void Draw(BRect updateRect); 76 virtual void MouseDown(BPoint where); 77 }; 78 79 80 // #pragma mark - 81 82 83 ScrollArrow::ScrollArrow(BRect frame) 84 : 85 BView(frame, "menu scroll arrow", B_FOLLOW_NONE, B_WILL_DRAW), 86 fEnabled(false) 87 { 88 SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); 89 } 90 91 92 ScrollArrow::~ScrollArrow() 93 { 94 } 95 96 97 void 98 ScrollArrow::SetEnabled(bool enabled) 99 { 100 fEnabled = enabled; 101 Invalidate(); 102 } 103 104 105 // #pragma mark - 106 107 108 UpScrollArrow::UpScrollArrow(BRect frame) 109 : 110 ScrollArrow(frame) 111 { 112 } 113 114 115 UpScrollArrow::~UpScrollArrow() 116 { 117 } 118 119 120 void 121 UpScrollArrow::Draw(BRect updateRect) 122 { 123 SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 124 B_DARKEN_1_TINT)); 125 126 if (IsEnabled()) 127 SetHighColor(0, 0, 0); 128 else { 129 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 130 B_DARKEN_2_TINT)); 131 } 132 133 FillRect(Bounds(), B_SOLID_LOW); 134 135 float middle = Bounds().right / 2; 136 FillTriangle(BPoint(middle, (kScrollerDimension / 2) - 3), 137 BPoint(middle + 5, (kScrollerDimension / 2) + 2), 138 BPoint(middle - 5, (kScrollerDimension / 2) + 2)); 139 } 140 141 142 void 143 UpScrollArrow::MouseDown(BPoint where) 144 { 145 if (!IsEnabled()) 146 return; 147 148 TInlineScrollView* parent = dynamic_cast<TInlineScrollView*>(Parent()); 149 if (parent == NULL) 150 return; 151 152 float smallStep; 153 float largeStep; 154 parent->GetSteps(&smallStep, &largeStep); 155 156 BMessage* message = Window()->CurrentMessage(); 157 int32 modifiers = 0; 158 message->FindInt32("modifiers", &modifiers); 159 // pressing the shift key scrolls faster 160 if ((modifiers & B_SHIFT_KEY) != 0) 161 parent->ScrollBy(-largeStep); 162 else 163 parent->ScrollBy(-smallStep); 164 165 snooze(5000); 166 } 167 168 169 // #pragma mark - 170 171 172 DownScrollArrow::DownScrollArrow(BRect frame) 173 : 174 ScrollArrow(frame) 175 { 176 } 177 178 179 DownScrollArrow::~DownScrollArrow() 180 { 181 } 182 183 184 void 185 DownScrollArrow::Draw(BRect updateRect) 186 { 187 SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 188 B_DARKEN_1_TINT)); 189 190 if (IsEnabled()) 191 SetHighColor(0, 0, 0); 192 else { 193 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 194 B_DARKEN_2_TINT)); 195 } 196 197 BRect frame = Bounds(); 198 FillRect(frame, B_SOLID_LOW); 199 200 float middle = Bounds().right / 2; 201 FillTriangle(BPoint(middle, frame.bottom - (kScrollerDimension / 2) + 3), 202 BPoint(middle + 5, frame.bottom - (kScrollerDimension / 2) - 2), 203 BPoint(middle - 5, frame.bottom - (kScrollerDimension / 2) - 2)); 204 } 205 206 207 void 208 DownScrollArrow::MouseDown(BPoint where) 209 { 210 if (!IsEnabled()) 211 return; 212 213 TInlineScrollView* grandparent 214 = dynamic_cast<TInlineScrollView*>(Parent()->Parent()); 215 if (grandparent == NULL) 216 return; 217 218 float smallStep; 219 float largeStep; 220 grandparent->GetSteps(&smallStep, &largeStep); 221 222 BMessage* message = Window()->CurrentMessage(); 223 int32 modifiers = 0; 224 message->FindInt32("modifiers", &modifiers); 225 // pressing the shift key scrolls faster 226 if ((modifiers & B_SHIFT_KEY) != 0) 227 grandparent->ScrollBy(largeStep); 228 else 229 grandparent->ScrollBy(smallStep); 230 231 snooze(5000); 232 } 233 234 235 // #pragma mark - 236 237 238 LeftScrollArrow::LeftScrollArrow(BRect frame) 239 : 240 ScrollArrow(frame) 241 { 242 } 243 244 245 LeftScrollArrow::~LeftScrollArrow() 246 { 247 } 248 249 250 void 251 LeftScrollArrow::Draw(BRect updateRect) 252 { 253 SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); 254 255 if (IsEnabled()) 256 SetHighColor(0, 0, 0); 257 else { 258 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 259 B_DARKEN_2_TINT)); 260 } 261 262 FillRect(Bounds(), B_SOLID_LOW); 263 264 float middle = Bounds().bottom / 2; 265 FillTriangle(BPoint((kScrollerDimension / 2) - 3, middle), 266 BPoint((kScrollerDimension / 2) + 2, middle + 5), 267 BPoint((kScrollerDimension / 2) + 2, middle - 5)); 268 } 269 270 271 void 272 LeftScrollArrow::MouseDown(BPoint where) 273 { 274 if (!IsEnabled()) 275 return; 276 277 TInlineScrollView* parent = dynamic_cast<TInlineScrollView*>(Parent()); 278 if (parent == NULL) 279 return; 280 281 float smallStep; 282 float largeStep; 283 parent->GetSteps(&smallStep, &largeStep); 284 285 BMessage* message = Window()->CurrentMessage(); 286 int32 modifiers = 0; 287 message->FindInt32("modifiers", &modifiers); 288 // pressing the shift key scrolls faster 289 if ((modifiers & B_SHIFT_KEY) != 0) 290 parent->ScrollBy(-largeStep); 291 else 292 parent->ScrollBy(-smallStep); 293 294 snooze(5000); 295 } 296 297 298 // #pragma mark - 299 300 301 RightScrollArrow::RightScrollArrow(BRect frame) 302 : 303 ScrollArrow(frame) 304 { 305 } 306 307 308 RightScrollArrow::~RightScrollArrow() 309 { 310 } 311 312 313 void 314 RightScrollArrow::Draw(BRect updateRect) 315 { 316 SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); 317 318 if (IsEnabled()) 319 SetHighColor(0, 0, 0); 320 else { 321 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 322 B_DARKEN_2_TINT)); 323 } 324 325 BRect frame = Bounds(); 326 FillRect(frame, B_SOLID_LOW); 327 328 float middle = Bounds().bottom / 2; 329 FillTriangle(BPoint(kScrollerDimension / 2 + 3, middle), 330 BPoint(kScrollerDimension / 2 - 2, middle + 5), 331 BPoint(kScrollerDimension / 2 - 2, middle - 5)); 332 } 333 334 335 void 336 RightScrollArrow::MouseDown(BPoint where) 337 { 338 if (!IsEnabled()) 339 return; 340 341 TInlineScrollView* grandparent 342 = dynamic_cast<TInlineScrollView*>(Parent()->Parent()); 343 if (grandparent == NULL) 344 return; 345 346 float smallStep; 347 float largeStep; 348 grandparent->GetSteps(&smallStep, &largeStep); 349 350 BMessage* message = Window()->CurrentMessage(); 351 int32 modifiers = 0; 352 message->FindInt32("modifiers", &modifiers); 353 // pressing the shift key scrolls faster 354 if ((modifiers & B_SHIFT_KEY) != 0) 355 grandparent->ScrollBy(largeStep); 356 else 357 grandparent->ScrollBy(smallStep); 358 359 snooze(5000); 360 } 361 362 363 // #pragma mark - 364 365 366 TInlineScrollView::TInlineScrollView(BView* target, 367 enum orientation orientation) 368 : 369 BView(BRect(0, 0, 0, 0), "inline scroll view", B_FOLLOW_NONE, B_WILL_DRAW), 370 fTarget(target), 371 fBeginScrollArrow(NULL), 372 fEndScrollArrow(NULL), 373 fScrollStep(kDefaultScrollStep), 374 fScrollValue(0), 375 fScrollLimit(0), 376 fOrientation(orientation) 377 { 378 } 379 380 381 TInlineScrollView::~TInlineScrollView() 382 { 383 if (fBeginScrollArrow != NULL) { 384 fBeginScrollArrow->RemoveSelf(); 385 delete fBeginScrollArrow; 386 fBeginScrollArrow = NULL; 387 } 388 389 if (fEndScrollArrow != NULL) { 390 fEndScrollArrow->RemoveSelf(); 391 delete fEndScrollArrow; 392 fEndScrollArrow = NULL; 393 } 394 } 395 396 397 void 398 TInlineScrollView::AttachedToWindow() 399 { 400 BView::AttachedToWindow(); 401 402 if (fTarget == NULL) 403 return; 404 405 AddChild(fTarget); 406 fTarget->MoveTo(0, 0); 407 } 408 409 410 void 411 TInlineScrollView::DetachedFromWindow() 412 { 413 BView::DetachedFromWindow(); 414 415 if (fTarget != NULL) 416 fTarget->RemoveSelf(); 417 418 if (fBeginScrollArrow != NULL) 419 fBeginScrollArrow->RemoveSelf(); 420 421 if (fEndScrollArrow != NULL) 422 fEndScrollArrow->RemoveSelf(); 423 } 424 425 426 void 427 TInlineScrollView::Draw(BRect updateRect) 428 { 429 BRect frame = Bounds(); 430 be_control_look->DrawButtonBackground(this, frame, updateRect, 431 ui_color(B_MENU_BACKGROUND_COLOR)); 432 } 433 434 435 // #pragma mark - 436 437 438 void 439 TInlineScrollView::AttachScrollers() 440 { 441 if (fTarget == NULL) 442 return; 443 444 BRect frame = Bounds(); 445 446 if (HasScrollers()) { 447 if (fOrientation == B_VERTICAL) { 448 fScrollLimit = fTarget->Bounds().Height() 449 - (frame.Height() - 2 * kScrollerDimension); 450 } else { 451 fScrollLimit = fTarget->Bounds().Width() 452 - (frame.Width() - 2 * kScrollerDimension); 453 } 454 455 if (fScrollValue > fScrollLimit) { 456 // If scroll value is above limit scroll back 457 float delta = fScrollLimit - fScrollValue; 458 if (fOrientation == B_VERTICAL) 459 fTarget->ScrollBy(0, delta); 460 else 461 fTarget->ScrollBy(delta, 0); 462 463 fScrollValue = fScrollLimit; 464 } 465 return; 466 } 467 468 fTarget->MakeFocus(true); 469 470 if (fOrientation == B_VERTICAL) { 471 if (fBeginScrollArrow == NULL) { 472 fBeginScrollArrow = new UpScrollArrow( 473 BRect(frame.left, frame.top, frame.right, 474 kScrollerDimension - 1)); 475 AddChild(fBeginScrollArrow); 476 } 477 478 if (fEndScrollArrow == NULL) { 479 fEndScrollArrow = new DownScrollArrow( 480 BRect(0, frame.bottom - 2 * kScrollerDimension + 1, frame.right, 481 frame.bottom - kScrollerDimension)); 482 fTarget->AddChild(fEndScrollArrow); 483 } 484 485 fTarget->MoveBy(0, kScrollerDimension); 486 487 fScrollLimit = fTarget->Bounds().Height() 488 - (frame.Height() - 2 * kScrollerDimension); 489 } else { 490 if (fBeginScrollArrow == NULL) { 491 fBeginScrollArrow = new LeftScrollArrow( 492 BRect(frame.left, frame.top, 493 frame.left + kScrollerDimension - 1, frame.bottom)); 494 AddChild(fBeginScrollArrow); 495 } 496 497 if (fEndScrollArrow == NULL) { 498 fEndScrollArrow = new RightScrollArrow( 499 BRect(frame.right - 2 * kScrollerDimension + 1, frame.top, 500 frame.right, frame.bottom)); 501 fTarget->AddChild(fEndScrollArrow); 502 } 503 504 fTarget->MoveBy(kScrollerDimension, 0); 505 506 fScrollLimit = fTarget->Bounds().Width() 507 - (frame.Width() - 2 * kScrollerDimension); 508 } 509 510 fBeginScrollArrow->SetEnabled(false); 511 fEndScrollArrow->SetEnabled(true); 512 513 fScrollValue = 0; 514 } 515 516 517 void 518 TInlineScrollView::DetachScrollers() 519 { 520 if (!HasScrollers()) 521 return; 522 523 if (fEndScrollArrow) { 524 fEndScrollArrow->RemoveSelf(); 525 delete fEndScrollArrow; 526 fEndScrollArrow = NULL; 527 } 528 529 if (fBeginScrollArrow) { 530 fBeginScrollArrow->RemoveSelf(); 531 delete fBeginScrollArrow; 532 fBeginScrollArrow = NULL; 533 } 534 535 if (fTarget) { 536 // We don't remember the position where the last scrolling 537 // ended, so scroll back to the beginning. 538 if (fOrientation == B_VERTICAL) 539 fTarget->MoveBy(0, -kScrollerDimension); 540 else 541 fTarget->MoveBy(-kScrollerDimension, 0); 542 543 fTarget->ScrollTo(0, 0); 544 fScrollValue = 0; 545 } 546 } 547 548 549 bool 550 TInlineScrollView::HasScrollers() const 551 { 552 return fTarget != NULL && fBeginScrollArrow != NULL 553 && fEndScrollArrow != NULL; 554 } 555 556 557 void 558 TInlineScrollView::SetSmallStep(float step) 559 { 560 fScrollStep = step; 561 } 562 563 564 void 565 TInlineScrollView::GetSteps(float* _smallStep, float* _largeStep) const 566 { 567 if (_smallStep != NULL) 568 *_smallStep = fScrollStep; 569 if (_largeStep != NULL) { 570 *_largeStep = fScrollStep * 3; 571 } 572 } 573 574 575 void 576 TInlineScrollView::ScrollBy(const float& step) 577 { 578 if (!HasScrollers()) 579 return; 580 581 if (step > 0) { 582 if (fScrollValue == 0) 583 fBeginScrollArrow->SetEnabled(true); 584 585 if (fScrollValue + step >= fScrollLimit) { 586 // If we reached the limit, only scroll to the end 587 if (fOrientation == B_VERTICAL) { 588 fTarget->ScrollBy(0, fScrollLimit - fScrollValue); 589 fEndScrollArrow->MoveBy(0, fScrollLimit - fScrollValue); 590 } else { 591 fTarget->ScrollBy(fScrollLimit - fScrollValue, 0); 592 fEndScrollArrow->MoveBy(fScrollLimit - fScrollValue, 0); 593 } 594 fEndScrollArrow->SetEnabled(false); 595 fScrollValue = fScrollLimit; 596 } else { 597 if (fOrientation == B_VERTICAL) { 598 fTarget->ScrollBy(0, step); 599 fEndScrollArrow->MoveBy(0, step); 600 } else { 601 fTarget->ScrollBy(step, 0); 602 fEndScrollArrow->MoveBy(step, 0); 603 } 604 fScrollValue += step; 605 } 606 } else if (step < 0) { 607 if (fScrollValue == fScrollLimit) 608 fEndScrollArrow->SetEnabled(true); 609 610 if (fScrollValue + step <= 0) { 611 if (fOrientation == B_VERTICAL) { 612 fTarget->ScrollBy(0, -fScrollValue); 613 fEndScrollArrow->MoveBy(0, -fScrollValue); 614 } else { 615 fTarget->ScrollBy(-fScrollValue, 0); 616 fEndScrollArrow->MoveBy(-fScrollValue, 0); 617 } 618 fBeginScrollArrow->SetEnabled(false); 619 fScrollValue = 0; 620 } else { 621 if (fOrientation == B_VERTICAL) { 622 fTarget->ScrollBy(0, step); 623 fEndScrollArrow->MoveBy(0, step); 624 } else { 625 fTarget->ScrollBy(step, 0); 626 fEndScrollArrow->MoveBy(step, 0); 627 } 628 fScrollValue += step; 629 } 630 } 631 632 //fTarget->Invalidate(); 633 } 634