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