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 23const int kDefaultScrollStep = 19; 24const int kScrollerDimension = 12; 25 26 27class ScrollArrow : public BView { 28public: 29 ScrollArrow(BRect frame); 30 virtual ~ScrollArrow(); 31 32 bool IsEnabled() const { return fEnabled; }; 33 void SetEnabled(bool enabled); 34 35private: 36 bool fEnabled; 37}; 38 39 40class UpScrollArrow : public ScrollArrow { 41public: 42 UpScrollArrow(BRect frame); 43 virtual ~UpScrollArrow(); 44 45 virtual void Draw(BRect updateRect); 46 virtual void MouseDown(BPoint where); 47}; 48 49 50class DownScrollArrow : public ScrollArrow { 51public: 52 DownScrollArrow(BRect frame); 53 virtual ~DownScrollArrow(); 54 55 virtual void Draw(BRect updateRect); 56 virtual void MouseDown(BPoint where); 57}; 58 59 60class LeftScrollArrow : public ScrollArrow { 61public: 62 LeftScrollArrow(BRect frame); 63 virtual ~LeftScrollArrow(); 64 65 virtual void Draw(BRect updateRect); 66 virtual void MouseDown(BPoint where); 67}; 68 69 70class RightScrollArrow : public ScrollArrow { 71public: 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 83ScrollArrow::ScrollArrow(BRect frame) 84 : 85 BView(frame, "menu scroll arrow", B_FOLLOW_NONE, B_WILL_DRAW), 86 fEnabled(false) 87{ 88 SetViewUIColor(B_MENU_BACKGROUND_COLOR); 89} 90 91 92ScrollArrow::~ScrollArrow() 93{ 94} 95 96 97void 98ScrollArrow::SetEnabled(bool enabled) 99{ 100 fEnabled = enabled; 101 Invalidate(); 102} 103 104 105// #pragma mark - 106 107 108UpScrollArrow::UpScrollArrow(BRect frame) 109 : 110 ScrollArrow(frame) 111{ 112} 113 114 115UpScrollArrow::~UpScrollArrow() 116{ 117} 118 119 120void 121UpScrollArrow::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 142void 143UpScrollArrow::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 172DownScrollArrow::DownScrollArrow(BRect frame) 173 : 174 ScrollArrow(frame) 175{ 176} 177 178 179DownScrollArrow::~DownScrollArrow() 180{ 181} 182 183 184void 185DownScrollArrow::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 207void 208DownScrollArrow::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 238LeftScrollArrow::LeftScrollArrow(BRect frame) 239 : 240 ScrollArrow(frame) 241{ 242} 243 244 245LeftScrollArrow::~LeftScrollArrow() 246{ 247} 248 249 250void 251LeftScrollArrow::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 271void 272LeftScrollArrow::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 301RightScrollArrow::RightScrollArrow(BRect frame) 302 : 303 ScrollArrow(frame) 304{ 305} 306 307 308RightScrollArrow::~RightScrollArrow() 309{ 310} 311 312 313void 314RightScrollArrow::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 335void 336RightScrollArrow::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 366TInlineScrollView::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 381TInlineScrollView::~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 397void 398TInlineScrollView::AttachedToWindow() 399{ 400 BView::AttachedToWindow(); 401 402 if (fTarget == NULL) 403 return; 404 405 AddChild(fTarget); 406 fTarget->MoveTo(0, 0); 407} 408 409 410void 411TInlineScrollView::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 426void 427TInlineScrollView::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 438void 439TInlineScrollView::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 517void 518TInlineScrollView::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 549bool 550TInlineScrollView::HasScrollers() const 551{ 552 return fTarget != NULL && fBeginScrollArrow != NULL 553 && fEndScrollArrow != NULL; 554} 555 556 557void 558TInlineScrollView::SetSmallStep(float step) 559{ 560 fScrollStep = step; 561} 562 563 564void 565TInlineScrollView::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 575void 576TInlineScrollView::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