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