1 /* 2 * Copyright 2006-2009, Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <bonefish@cs.tu-berlin.de> 7 * Stephan Aßmus <superstippi@gmx.de> 8 */ 9 10 #include "ScrollView.h" 11 12 #include <algorithm> 13 #include <stdio.h> 14 #include <string.h> 15 16 #include <Bitmap.h> 17 #ifdef __HAIKU__ 18 # include <LayoutUtils.h> 19 #endif 20 #include <Message.h> 21 #include <ScrollBar.h> 22 #include <Window.h> 23 24 #include "Scrollable.h" 25 #include "ScrollCornerBitmaps.h" 26 27 using namespace std; 28 29 // #pragma mark - InternalScrollBar 30 31 class InternalScrollBar : public BScrollBar { 32 public: 33 InternalScrollBar(ScrollView* scrollView, 34 BRect frame, 35 orientation posture); 36 virtual ~InternalScrollBar(); 37 38 virtual void ValueChanged(float value); 39 40 virtual void MouseDown(BPoint where); 41 virtual void MouseUp(BPoint where); 42 43 private: 44 ScrollView* fScrollView; 45 }; 46 47 // constructor 48 InternalScrollBar::InternalScrollBar(ScrollView* scrollView, BRect frame, 49 orientation posture) 50 : BScrollBar(frame, NULL, NULL, 0, 0, posture), 51 fScrollView(scrollView) 52 { 53 } 54 55 // destructor 56 InternalScrollBar::~InternalScrollBar() 57 { 58 } 59 60 // ValueChanged 61 void 62 InternalScrollBar::ValueChanged(float value) 63 { 64 // Notify our parent scroll view. Note: the value already has changed, 65 // so that we can't check, if it really has changed. 66 if (fScrollView) 67 fScrollView->_ScrollValueChanged(this, value); 68 } 69 70 // MouseDown 71 void 72 InternalScrollBar::MouseDown(BPoint where) 73 { 74 if (fScrollView) 75 fScrollView->_SetScrolling(true); 76 BScrollBar::MouseDown(where); 77 } 78 79 // MouseUp 80 void 81 InternalScrollBar::MouseUp(BPoint where) 82 { 83 BScrollBar::MouseUp(where); 84 if (fScrollView) 85 fScrollView->_SetScrolling(false); 86 } 87 88 89 90 // #pragma mark -ScrollCorner 91 92 class ScrollCorner : public BView { 93 public: 94 ScrollCorner(ScrollView* scrollView); 95 virtual ~ScrollCorner(); 96 97 virtual void MouseDown(BPoint point); 98 virtual void MouseUp(BPoint point); 99 virtual void MouseMoved(BPoint point, uint32 transit, 100 const BMessage* message); 101 102 virtual void Draw(BRect updateRect); 103 virtual void WindowActivated(bool active); 104 105 void SetActive(bool active); 106 inline bool IsActive() const 107 { return fState & STATE_ACTIVE; } 108 109 private: 110 ScrollView* fScrollView; 111 uint32 fState; 112 BPoint fStartPoint; 113 BPoint fStartScrollOffset; 114 BBitmap* fBitmaps[3]; 115 116 inline bool IsEnabled() const 117 { return ((fState & STATE_ENABLED) == 118 STATE_ENABLED); } 119 120 void SetDragging(bool dragging); 121 inline bool IsDragging() const 122 { return (fState & STATE_DRAGGING); } 123 124 enum { 125 STATE_DRAGGING = 0x01, 126 STATE_WINDOW_ACTIVE = 0x02, 127 STATE_ACTIVE = 0x04, 128 STATE_ENABLED = STATE_WINDOW_ACTIVE | STATE_ACTIVE, 129 }; 130 }; 131 132 // constructor 133 ScrollCorner::ScrollCorner(ScrollView* scrollView) 134 : BView(BRect(0.0, 0.0, B_V_SCROLL_BAR_WIDTH - 1.0f, B_H_SCROLL_BAR_HEIGHT - 1.0f), NULL, 135 0, B_WILL_DRAW), 136 fScrollView(scrollView), 137 fState(0), 138 fStartPoint(0, 0), 139 fStartScrollOffset(0, 0) 140 { 141 //printf("ScrollCorner::ScrollCorner(%p)\n", scrollView); 142 SetViewColor(B_TRANSPARENT_32_BIT); 143 //printf("setting up bitmap 0\n"); 144 fBitmaps[0] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth, sBitmapHeight), sColorSpace); 145 // fBitmaps[0]->SetBits((void *)sScrollCornerNormalBits, fBitmaps[0]->BitsLength(), 0L, sColorSpace); 146 char *bits = (char *)fBitmaps[0]->Bits(); 147 int32 bpr = fBitmaps[0]->BytesPerRow(); 148 for (int i = 0; i <= sBitmapHeight; i++, bits += bpr) 149 memcpy(bits, &sScrollCornerNormalBits[i * sBitmapHeight * 4], sBitmapWidth * 4); 150 151 //printf("setting up bitmap 1\n"); 152 fBitmaps[1] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth, sBitmapHeight), sColorSpace); 153 // fBitmaps[1]->SetBits((void *)sScrollCornerPushedBits, fBitmaps[1]->BitsLength(), 0L, sColorSpace); 154 bits = (char *)fBitmaps[1]->Bits(); 155 bpr = fBitmaps[1]->BytesPerRow(); 156 for (int i = 0; i <= sBitmapHeight; i++, bits += bpr) 157 memcpy(bits, &sScrollCornerPushedBits[i * sBitmapHeight * 4], sBitmapWidth * 4); 158 159 //printf("setting up bitmap 2\n"); 160 fBitmaps[2] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth, sBitmapHeight), sColorSpace); 161 // fBitmaps[2]->SetBits((void *)sScrollCornerDisabledBits, fBitmaps[2]->BitsLength(), 0L, sColorSpace); 162 bits = (char *)fBitmaps[2]->Bits(); 163 bpr = fBitmaps[2]->BytesPerRow(); 164 for (int i = 0; i <= sBitmapHeight; i++, bits += bpr) 165 memcpy(bits, &sScrollCornerDisabledBits[i * sBitmapHeight * 4], sBitmapWidth * 4); 166 } 167 168 // destructor 169 ScrollCorner::~ScrollCorner() 170 { 171 for (int i = 0; i < 3; i++) 172 delete fBitmaps[i]; 173 } 174 175 // MouseDown 176 void 177 ScrollCorner::MouseDown(BPoint point) 178 { 179 BView::MouseDown(point); 180 uint32 buttons = 0; 181 Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons); 182 if (buttons & B_PRIMARY_MOUSE_BUTTON) { 183 SetMouseEventMask(B_POINTER_EVENTS); 184 if (fScrollView && IsEnabled() && Bounds().Contains(point)) { 185 SetDragging(true); 186 fStartPoint = point; 187 fStartScrollOffset = fScrollView->ScrollOffset(); 188 } 189 } 190 } 191 192 // MouseUp 193 void 194 ScrollCorner::MouseUp(BPoint point) 195 { 196 BView::MouseUp(point); 197 uint32 buttons = 0; 198 Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons); 199 if (!(buttons & B_PRIMARY_MOUSE_BUTTON)) 200 SetDragging(false); 201 } 202 203 // MouseMoved 204 void 205 ScrollCorner::MouseMoved(BPoint point, uint32 transit, const BMessage* message) 206 { 207 BView::MouseMoved(point, transit, message); 208 if (IsDragging()) { 209 uint32 buttons = 0; 210 Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons); 211 // This is a work-around for a BeOS bug: We sometimes don't get a 212 // MouseUp(), but fortunately it seems, that within the last 213 // MouseMoved() the button is not longer pressed. 214 if (buttons & B_PRIMARY_MOUSE_BUTTON) { 215 BPoint diff = point - fStartPoint; 216 if (fScrollView) { 217 fScrollView->_ScrollCornerValueChanged(fStartScrollOffset 218 - diff); 219 // + diff); 220 } 221 } else 222 SetDragging(false); 223 } 224 } 225 226 // Draw 227 void 228 ScrollCorner::Draw(BRect updateRect) 229 { 230 if (IsEnabled()) { 231 if (IsDragging()) 232 DrawBitmap(fBitmaps[1], BPoint(0.0f, 0.0f)); 233 else 234 DrawBitmap(fBitmaps[0], BPoint(0.0f, 0.0f)); 235 } 236 else 237 DrawBitmap(fBitmaps[2], BPoint(0.0f, 0.0f)); 238 } 239 240 // WindowActivated 241 void 242 ScrollCorner::WindowActivated(bool active) 243 { 244 if (active != (fState & STATE_WINDOW_ACTIVE)) { 245 bool enabled = IsEnabled(); 246 if (active) 247 fState |= STATE_WINDOW_ACTIVE; 248 else 249 fState &= ~STATE_WINDOW_ACTIVE; 250 if (enabled != IsEnabled()) 251 Invalidate(); 252 } 253 } 254 255 // SetActive 256 void 257 ScrollCorner::SetActive(bool active) 258 { 259 if (active != IsActive()) { 260 bool enabled = IsEnabled(); 261 if (active) 262 fState |= STATE_ACTIVE; 263 else 264 fState &= ~STATE_ACTIVE; 265 if (enabled != IsEnabled()) 266 Invalidate(); 267 } 268 } 269 270 // SetDragging 271 void 272 ScrollCorner::SetDragging(bool dragging) 273 { 274 if (dragging != IsDragging()) { 275 if (dragging) 276 fState |= STATE_DRAGGING; 277 else 278 fState &= ~STATE_DRAGGING; 279 Invalidate(); 280 } 281 } 282 283 284 // #pragma mark - ScrollView 285 286 287 // constructor 288 ScrollView::ScrollView(BView* child, uint32 scrollingFlags, BRect frame, 289 const char* name, uint32 resizingMode, uint32 viewFlags, 290 uint32 borderStyle, uint32 borderFlags) 291 : BView(frame, name, resizingMode, 292 viewFlags | B_FRAME_EVENTS | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 293 Scroller() 294 { 295 _Init(child, scrollingFlags, borderStyle, borderFlags); 296 } 297 298 #ifdef __HAIKU__ 299 300 // constructor 301 ScrollView::ScrollView(BView* child, uint32 scrollingFlags, const char* name, 302 uint32 viewFlags, uint32 borderStyle, uint32 borderFlags) 303 : BView(name, viewFlags | B_FRAME_EVENTS | B_WILL_DRAW 304 | B_FULL_UPDATE_ON_RESIZE), 305 Scroller() 306 { 307 _Init(child, scrollingFlags, borderStyle, borderFlags); 308 } 309 310 #endif // __HAIKU__ 311 312 // destructor 313 ScrollView::~ScrollView() 314 { 315 } 316 317 // AllAttached 318 void 319 ScrollView::AllAttached() 320 { 321 // do a first layout 322 _Layout(_UpdateScrollBarVisibility()); 323 } 324 325 // Draw 326 void ScrollView::Draw(BRect updateRect) 327 { 328 if (fBorderStyle == B_NO_BORDER) 329 return; 330 331 rgb_color keyboardFocus = keyboard_navigation_color(); 332 rgb_color light = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 333 B_LIGHTEN_MAX_TINT); 334 rgb_color shadow = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 335 B_DARKEN_1_TINT); 336 rgb_color darkShadow = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 337 B_DARKEN_2_TINT); 338 339 BRect r = Bounds(); 340 341 if (fChildFocused && fWindowActive) { 342 SetHighColor(keyboardFocus); 343 StrokeRect(r); 344 } else { 345 if (fBorderStyle == B_PLAIN_BORDER) { 346 SetHighColor(darkShadow); 347 StrokeRect(r); 348 } else { 349 BeginLineArray(4); 350 AddLine(BPoint(r.left, r.bottom), 351 BPoint(r.left, r.top), shadow); 352 AddLine(BPoint(r.left + 1.0, r.top), 353 BPoint(r.right, r.top), shadow); 354 AddLine(BPoint(r.right, r.top + 1.0), 355 BPoint(r.right, r.bottom), light); 356 AddLine(BPoint(r.right - 1.0, r.bottom), 357 BPoint(r.left + 1.0, r.bottom), light); 358 EndLineArray(); 359 } 360 } 361 if (fBorderStyle == B_PLAIN_BORDER) 362 return; 363 364 // The right and bottom lines will be hidden if the scroll views are 365 // visible. But that doesn't harm. 366 r.InsetBy(1, 1); 367 SetHighColor(darkShadow); 368 StrokeRect(r); 369 } 370 371 // FrameResized 372 void 373 ScrollView::FrameResized(float width, float height) 374 { 375 _Layout(0); 376 } 377 378 // WindowActivated 379 void ScrollView::WindowActivated(bool activated) 380 { 381 fWindowActive = activated; 382 if (fChildFocused) 383 Invalidate(); 384 } 385 386 #ifdef __HAIKU__ 387 388 // MinSize 389 BSize 390 ScrollView::MinSize() 391 { 392 BSize size = (fChild ? fChild->MinSize() : BSize(-1, -1)); 393 return _Size(size); 394 } 395 396 // PreferredSize 397 BSize 398 ScrollView::PreferredSize() 399 { 400 BSize size = (fChild ? fChild->PreferredSize() : BSize(-1, -1)); 401 return _Size(size); 402 } 403 404 #endif // __HAIKU__ 405 406 // #pragma mark - 407 408 // ScrollingFlags 409 uint32 410 ScrollView::ScrollingFlags() const 411 { 412 return fScrollingFlags; 413 } 414 415 // SetVisibleRectIsChildBounds 416 void 417 ScrollView::SetVisibleRectIsChildBounds(bool flag) 418 { 419 if (flag != VisibleRectIsChildBounds()) { 420 if (flag) 421 fScrollingFlags |= SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS; 422 else 423 fScrollingFlags &= ~SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS; 424 if (fChild && _UpdateScrollBarVisibility()) 425 _Layout(0); 426 } 427 } 428 429 // VisibleRectIsChildBounds 430 bool 431 ScrollView::VisibleRectIsChildBounds() const 432 { 433 return (fScrollingFlags & SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS); 434 } 435 436 // Child 437 BView* 438 ScrollView::Child() const 439 { 440 return fChild; 441 } 442 443 // ChildFocusChanged 444 // 445 // To be called by the scroll child, when its has got or lost the focus. 446 // We need this to know, when to draw the blue focus frame. 447 void 448 ScrollView::ChildFocusChanged(bool focused) 449 { 450 if (fChildFocused != focused) { 451 fChildFocused = focused; 452 Invalidate(); 453 } 454 } 455 456 // HScrollBar 457 BScrollBar* 458 ScrollView::HScrollBar() const 459 { 460 return fHScrollBar; 461 } 462 463 // VScrollBar 464 BScrollBar* 465 ScrollView::VScrollBar() const 466 { 467 return fVScrollBar; 468 } 469 470 // HVScrollCorner 471 BView* 472 ScrollView::HVScrollCorner() const 473 { 474 return fScrollCorner; 475 } 476 477 // #pragma mark - 478 479 // SetHSmallStep 480 void 481 ScrollView::SetHSmallStep(float hStep) 482 { 483 SetSmallSteps(hStep, fVSmallStep); 484 } 485 486 // SetVSmallStep 487 void 488 ScrollView::SetVSmallStep(float vStep) 489 { 490 SetSmallSteps(fHSmallStep, vStep); 491 } 492 493 // SetSmallSteps 494 void 495 ScrollView::SetSmallSteps(float hStep, float vStep) 496 { 497 if (fHSmallStep != hStep || fVSmallStep != vStep) { 498 fHSmallStep = hStep; 499 fVSmallStep = vStep; 500 _UpdateScrollBars(); 501 } 502 } 503 504 // GetSmallSteps 505 void 506 ScrollView::GetSmallSteps(float* hStep, float* vStep) const 507 { 508 *hStep = fHSmallStep; 509 *vStep = fVSmallStep; 510 } 511 512 // HSmallStep 513 float 514 ScrollView::HSmallStep() const 515 { 516 return fHSmallStep; 517 } 518 519 // VSmallStep 520 float 521 ScrollView::VSmallStep() const 522 { 523 return fVSmallStep; 524 } 525 526 // IsScrolling 527 bool 528 ScrollView::IsScrolling() const 529 { 530 return fScrolling; 531 } 532 533 void 534 ScrollView::SetScrollingEnabled(bool enabled) 535 { 536 Scroller::SetScrollingEnabled(enabled); 537 if (IsScrollingEnabled()) 538 SetScrollOffset(ScrollOffset()); 539 } 540 541 // #pragma mark - 542 543 // DataRectChanged 544 void 545 ScrollView::DataRectChanged(BRect /*oldDataRect*/, BRect /*newDataRect*/) 546 { 547 if (ScrollTarget()) { 548 if (_UpdateScrollBarVisibility()) 549 _Layout(0); 550 else 551 _UpdateScrollBars(); 552 } 553 } 554 555 // ScrollOffsetChanged 556 void 557 ScrollView::ScrollOffsetChanged(BPoint /*oldOffset*/, BPoint newOffset) 558 { 559 if (fHScrollBar && fHScrollBar->Value() != newOffset.x) 560 fHScrollBar->SetValue(newOffset.x); 561 if (fVScrollBar && fVScrollBar->Value() != newOffset.y) 562 fVScrollBar->SetValue(newOffset.y); 563 } 564 565 // VisibleSizeChanged 566 void 567 ScrollView::VisibleSizeChanged(float /*oldWidth*/, float /*oldHeight*/, 568 float /*newWidth*/, float /*newHeight*/) 569 { 570 if (ScrollTarget()) { 571 if (_UpdateScrollBarVisibility()) 572 _Layout(0); 573 else 574 _UpdateScrollBars(); 575 } 576 } 577 578 // ScrollTargetChanged 579 void 580 ScrollView::ScrollTargetChanged(Scrollable* /*oldTarget*/, 581 Scrollable* newTarget) 582 { 583 /* // remove the old child 584 if (fChild) 585 RemoveChild(fChild); 586 // add the new child 587 BView* view = dynamic_cast<BView*>(newTarget); 588 fChild = view; 589 if (view) 590 AddChild(view); 591 else if (newTarget) // set the scroll target to NULL, if it isn't a BView 592 SetScrollTarget(NULL); 593 */ 594 } 595 596 // _Init 597 void 598 ScrollView::_Init(BView* child, uint32 scrollingFlags, uint32 borderStyle, 599 uint32 borderFlags) 600 { 601 fChild = NULL; 602 fScrollingFlags = scrollingFlags; 603 604 fHScrollBar = NULL; 605 fVScrollBar = NULL; 606 fScrollCorner = NULL; 607 608 fHVisible = true; 609 fVVisible = true; 610 fCornerVisible = true; 611 612 fWindowActive = false; 613 fChildFocused = false; 614 615 fScrolling = false; 616 617 fHSmallStep = 1; 618 fVSmallStep = 1; 619 620 fBorderStyle = borderStyle; 621 fBorderFlags = borderFlags; 622 623 // Set transparent view color -- our area is completely covered by 624 // our children. 625 SetViewColor(B_TRANSPARENT_32_BIT); 626 // create scroll bars 627 if (fScrollingFlags & (SCROLL_HORIZONTAL | SCROLL_HORIZONTAL_MAGIC)) { 628 fHScrollBar = new InternalScrollBar(this, 629 BRect(0.0, 0.0, 100.0, B_H_SCROLL_BAR_HEIGHT), B_HORIZONTAL); 630 AddChild(fHScrollBar); 631 } 632 if (fScrollingFlags & (SCROLL_VERTICAL | SCROLL_VERTICAL_MAGIC)) { 633 fVScrollBar = new InternalScrollBar(this, 634 BRect(0.0, 0.0, B_V_SCROLL_BAR_WIDTH, 100.0), B_VERTICAL); 635 AddChild(fVScrollBar); 636 } 637 // Create a scroll corner, if we can scroll into both direction. 638 if (fHScrollBar && fVScrollBar) { 639 fScrollCorner = new ScrollCorner(this); 640 AddChild(fScrollCorner); 641 } 642 // add child 643 if (child) { 644 fChild = child; 645 AddChild(child); 646 if (Scrollable* scrollable = dynamic_cast<Scrollable*>(child)) 647 SetScrollTarget(scrollable); 648 } 649 } 650 651 652 // _ScrollValueChanged 653 void 654 ScrollView::_ScrollValueChanged(InternalScrollBar* scrollBar, float value) 655 { 656 if (!IsScrollingEnabled()) 657 return; 658 659 switch (scrollBar->Orientation()) { 660 case B_HORIZONTAL: 661 if (fHScrollBar) 662 SetScrollOffset(BPoint(value, ScrollOffset().y)); 663 break; 664 case B_VERTICAL: 665 if (fVScrollBar) 666 SetScrollOffset(BPoint(ScrollOffset().x, value)); 667 break; 668 default: 669 break; 670 } 671 } 672 673 // _ScrollCornerValueChanged 674 void 675 ScrollView::_ScrollCornerValueChanged(BPoint offset) 676 { 677 // The logic in Scrollable::SetScrollOffset() handles offsets, that 678 // are out of range. 679 SetScrollOffset(offset); 680 } 681 682 // #pragma mark - 683 684 // _Layout 685 // 686 // Relayouts all children (fChild, scroll bars). 687 // flags indicates which scrollbars' visibility has changed. 688 // May be overridden to do a custom layout -- the SCROLL_*_MAGIC must 689 // be disabled in this case, or strange things happen. 690 void 691 ScrollView::_Layout(uint32 flags) 692 { 693 bool hbar = (fHScrollBar && fHVisible); 694 bool vbar = (fVScrollBar && fVVisible); 695 bool corner = (fScrollCorner && fCornerVisible); 696 BRect childRect(_ChildRect()); 697 float innerWidth = childRect.Width(); 698 float innerHeight = childRect.Height(); 699 BPoint scrollLT(_InnerRect().LeftTop()); 700 scrollLT.x--; 701 scrollLT.y--; 702 703 BPoint scrollRB(childRect.RightBottom() + BPoint(1.0f, 1.0f)); 704 705 // layout scroll bars and scroll corner 706 if (corner) { 707 // In this case the scrollbars overlap one pixel. 708 fHScrollBar->MoveTo(scrollLT.x, scrollRB.y); 709 fHScrollBar->ResizeTo(innerWidth + 2.0, B_H_SCROLL_BAR_HEIGHT); 710 fVScrollBar->MoveTo(scrollRB.x, scrollLT.y); 711 fVScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, innerHeight + 2.0); 712 fScrollCorner->MoveTo(childRect.right + 2.0, childRect.bottom + 2.0); 713 } else if (hbar) { 714 fHScrollBar->MoveTo(scrollLT.x, scrollRB.y); 715 fHScrollBar->ResizeTo(innerWidth + 2.0, B_H_SCROLL_BAR_HEIGHT); 716 } else if (vbar) { 717 fVScrollBar->MoveTo(scrollRB.x, scrollLT.y); 718 fVScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, innerHeight + 2.0); 719 } 720 // layout child 721 if (fChild) { 722 fChild->MoveTo(childRect.LeftTop()); 723 fChild->ResizeTo(innerWidth, innerHeight); 724 if (VisibleRectIsChildBounds()) 725 SetVisibleSize(innerWidth, innerHeight); 726 // Due to a BeOS bug sometimes the area under a recently hidden 727 // scroll bar isn't updated correctly. 728 // We force this manually: The position of hidden scroll bar isn't 729 // updated any longer, so we can't just invalidate it. 730 if (fChild->Window()) { 731 if (flags & SCROLL_HORIZONTAL && !fHVisible) 732 fChild->Invalidate(fHScrollBar->Frame()); 733 if (flags & SCROLL_VERTICAL && !fVVisible) 734 fChild->Invalidate(fVScrollBar->Frame()); 735 } 736 } 737 } 738 739 // _UpdateScrollBars 740 // 741 // Probably somewhat misnamed. This function updates the scroll bars' 742 // proportion, range attributes and step widths according to the scroll 743 // target's DataRect() and VisibleBounds(). May also be called, if there's 744 // no scroll target -- then the scroll bars are disabled. 745 void 746 ScrollView::_UpdateScrollBars() 747 { 748 BRect dataRect = DataRect(); 749 BRect visibleBounds = VisibleBounds(); 750 if (!fScrollTarget) { 751 dataRect.Set(0.0, 0.0, 0.0, 0.0); 752 visibleBounds.Set(0.0, 0.0, 0.0, 0.0); 753 } 754 float hProportion = min_c(1.0f, (visibleBounds.Width() + 1.0f) 755 / (dataRect.Width() + 1.0f)); 756 float hMaxValue = max_c(dataRect.left, 757 dataRect.right - visibleBounds.Width()); 758 float vProportion = min_c(1.0f, (visibleBounds.Height() + 1.0f) 759 / (dataRect.Height() + 1.0f)); 760 float vMaxValue = max_c(dataRect.top, 761 dataRect.bottom - visibleBounds.Height()); 762 // update horizontal scroll bar 763 if (fHScrollBar) { 764 fHScrollBar->SetProportion(hProportion); 765 fHScrollBar->SetRange(dataRect.left, hMaxValue); 766 // This obviously ineffective line works around a BScrollBar bug: 767 // As documented the scrollbar's value is adjusted, if the range 768 // has been changed and it therefore falls out of the range. But if, 769 // after resetting the range to what it has been before, the user 770 // moves the scrollbar to the original value via one click 771 // it is failed to invoke BScrollBar::ValueChanged(). 772 fHScrollBar->SetValue(fHScrollBar->Value()); 773 fHScrollBar->SetSteps(fHSmallStep, visibleBounds.Width()); 774 } 775 // update vertical scroll bar 776 if (fVScrollBar) { 777 fVScrollBar->SetProportion(vProportion); 778 fVScrollBar->SetRange(dataRect.top, vMaxValue); 779 // This obviously ineffective line works around a BScrollBar bug. 780 fVScrollBar->SetValue(fVScrollBar->Value()); 781 fVScrollBar->SetSteps(fVSmallStep, visibleBounds.Height()); 782 } 783 // update scroll corner 784 if (fScrollCorner) { 785 fScrollCorner->SetActive(hProportion < 1.0f || vProportion < 1.0f); 786 } 787 } 788 789 // set_visible_state 790 // 791 // Convenience function: Sets a view's visibility state to /visible/. 792 // Returns true, if the state was actually changed, false otherwise. 793 // This function never calls Hide() on a hidden or Show() on a visible 794 // view. /view/ must be valid. 795 static inline 796 bool 797 set_visible_state(BView* view, bool visible, bool* currentlyVisible) 798 { 799 bool changed = false; 800 if (*currentlyVisible != visible) { 801 if (visible) 802 view->Show(); 803 else 804 view->Hide(); 805 *currentlyVisible = visible; 806 changed = true; 807 } 808 return changed; 809 } 810 811 // _UpdateScrollBarVisibility 812 // 813 // Checks which of scroll bars need to be visible according to 814 // SCROLL_*_MAGIG and shows/hides them, if necessary. 815 // Returns a bitwise combination of SCROLL_HORIZONTAL and SCROLL_VERTICAL 816 // according to which scroll bar's visibility state has changed, 0 if none. 817 // A return value != 0 usually means that the layout isn't valid any longer. 818 uint32 819 ScrollView::_UpdateScrollBarVisibility() 820 { 821 uint32 changed = 0; 822 BRect childRect(_MaxVisibleRect()); 823 float width = childRect.Width(); 824 float height = childRect.Height(); 825 BRect dataRect = DataRect(); // Invalid if !ScrollTarget(), 826 float dataWidth = dataRect.Width(); // but that doesn't harm. 827 float dataHeight = dataRect.Height(); // 828 bool hbar = (fScrollingFlags & SCROLL_HORIZONTAL_MAGIC); 829 bool vbar = (fScrollingFlags & SCROLL_VERTICAL_MAGIC); 830 if (!ScrollTarget()) { 831 if (hbar) { 832 if (set_visible_state(fHScrollBar, false, &fHVisible)) 833 changed |= SCROLL_HORIZONTAL; 834 } 835 if (vbar) { 836 if (set_visible_state(fVScrollBar, false, &fVVisible)) 837 changed |= SCROLL_VERTICAL; 838 } 839 } else if (hbar && width >= dataWidth && vbar && height >= dataHeight) { 840 // none 841 if (set_visible_state(fHScrollBar, false, &fHVisible)) 842 changed |= SCROLL_HORIZONTAL; 843 if (set_visible_state(fVScrollBar, false, &fVVisible)) 844 changed |= SCROLL_VERTICAL; 845 } else { 846 // The case, that both scroll bars are magic and invisible is catched, 847 // so that while checking one bar we can suppose, that the other one 848 // is visible (if it does exist at all). 849 BRect innerRect(_GuessVisibleRect(fHScrollBar, fVScrollBar)); 850 float innerWidth = innerRect.Width(); 851 float innerHeight = innerRect.Height(); 852 // the horizontal one? 853 if (hbar) { 854 if (innerWidth >= dataWidth) { 855 if (set_visible_state(fHScrollBar, false, &fHVisible)) 856 changed |= SCROLL_HORIZONTAL; 857 } else { 858 if (set_visible_state(fHScrollBar, true, &fHVisible)) 859 changed |= SCROLL_HORIZONTAL; 860 } 861 } 862 // the vertical one? 863 if (vbar) { 864 if (innerHeight >= dataHeight) { 865 if (set_visible_state(fVScrollBar, false, &fVVisible)) 866 changed |= SCROLL_VERTICAL; 867 } else { 868 if (set_visible_state(fVScrollBar, true, &fVVisible)) 869 changed |= SCROLL_VERTICAL; 870 } 871 } 872 } 873 // If anything has changed, update the scroll corner as well. 874 if (changed && fScrollCorner) 875 set_visible_state(fScrollCorner, fHVisible && fVVisible, &fCornerVisible); 876 return changed; 877 } 878 879 // _InnerRect 880 // 881 // Returns the rectangle that actually can be used for the child and the 882 // scroll bars, i.e. the view's Bounds() subtracted the space for the 883 // decorative frame. 884 BRect 885 ScrollView::_InnerRect() const 886 { 887 BRect r = Bounds(); 888 float borderWidth = 0; 889 switch (fBorderStyle) { 890 case B_NO_BORDER: 891 break; 892 case B_PLAIN_BORDER: 893 borderWidth = 1; 894 break; 895 case B_FANCY_BORDER: 896 default: 897 borderWidth = 2; 898 break; 899 } 900 if (fBorderFlags & BORDER_LEFT) 901 r.left += borderWidth; 902 if (fBorderFlags & BORDER_TOP) 903 r.top += borderWidth; 904 if (fBorderFlags & BORDER_RIGHT) 905 r.right -= borderWidth; 906 if (fBorderFlags & BORDER_BOTTOM) 907 r.bottom -= borderWidth; 908 return r; 909 } 910 911 // _ChildRect 912 // 913 // Returns the rectangle, that should be the current child frame. 914 // `should' because 1. we might not have a child at all or 2. a 915 // relayout is pending. 916 BRect 917 ScrollView::_ChildRect() const 918 { 919 return _ChildRect(fHScrollBar && fHVisible, fVScrollBar && fVVisible); 920 } 921 922 // _ChildRect 923 // 924 // The same as _ChildRect() with the exception that not the current 925 // scroll bar visibility, but a fictitious one given by /hbar/ and /vbar/ 926 // is considered. 927 BRect 928 ScrollView::_ChildRect(bool hbar, bool vbar) const 929 { 930 BRect rect(_InnerRect()); 931 if (vbar) 932 rect.right -= B_V_SCROLL_BAR_WIDTH; 933 if (hbar) 934 rect.bottom -= B_H_SCROLL_BAR_HEIGHT; 935 936 return rect; 937 } 938 939 // _GuessVisibleRect 940 // 941 // Returns an approximation of the visible rect for the 942 // fictitious scroll bar visibility given by /hbar/ and /vbar/. 943 // In the case !VisibleRectIsChildBounds() it is simply the current 944 // visible rect. 945 BRect 946 ScrollView::_GuessVisibleRect(bool hbar, bool vbar) const 947 { 948 if (VisibleRectIsChildBounds()) 949 return _ChildRect(hbar, vbar).OffsetToCopy(ScrollOffset()); 950 return VisibleRect(); 951 } 952 953 // _MaxVisibleRect 954 // 955 // Returns the maximal possible visible rect in the current situation, that 956 // is depending on if the visible rect is the child's bounds either the 957 // rectangle the child covers when both scroll bars are hidden (offset to 958 // the scroll offset) or the current visible rect. 959 BRect 960 ScrollView::_MaxVisibleRect() const 961 { 962 return _GuessVisibleRect(true, true); 963 } 964 965 #ifdef __HAIKU__ 966 967 BSize 968 ScrollView::_Size(BSize size) 969 { 970 if (fVVisible) 971 size.width += B_V_SCROLL_BAR_WIDTH; 972 if (fHVisible) 973 size.height += B_H_SCROLL_BAR_HEIGHT; 974 975 switch (fBorderStyle) { 976 case B_NO_BORDER: 977 // one line of pixels from scrollbar possibly hidden 978 if (fBorderFlags & BORDER_RIGHT) 979 size.width += fVVisible ? -1 : 0; 980 if (fBorderFlags & BORDER_BOTTOM) 981 size.height += fHVisible ? -1 : 0; 982 break; 983 984 case B_PLAIN_BORDER: 985 if (fBorderFlags & BORDER_LEFT) 986 size.width += 1; 987 if (fBorderFlags & BORDER_TOP) 988 size.height += 1; 989 // one line of pixels in frame possibly from scrollbar 990 if (fBorderFlags & BORDER_RIGHT) 991 size.width += fVVisible ? 0 : 1; 992 if (fBorderFlags & BORDER_BOTTOM) 993 size.height += fHVisible ? 0 : 1; 994 break; 995 996 case B_FANCY_BORDER: 997 default: 998 if (fBorderFlags & BORDER_LEFT) 999 size.width += 2; 1000 if (fBorderFlags & BORDER_TOP) 1001 size.height += 2; 1002 // one line of pixels in frame possibly from scrollbar 1003 if (fBorderFlags & BORDER_RIGHT) 1004 size.width += fVVisible ? 1 : 2; 1005 if (fBorderFlags & BORDER_BOTTOM) 1006 size.height += fHVisible ? 1 : 2; 1007 break; 1008 } 1009 1010 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 1011 } 1012 1013 #endif // __HAIKU__ 1014 1015 // _SetScrolling 1016 void 1017 ScrollView::_SetScrolling(bool scrolling) 1018 { 1019 fScrolling = scrolling; 1020 } 1021