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