1 /* 2 * Copyright 2004-2015, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2009 Stephan Aßmus, superstippi@gmx.de. 4 * Copyright 2014-2015 Haiku, Inc. All rights reserved. 5 * Distributed under the terms of the MIT License. 6 * 7 * Authors: 8 * Stephan Aßmus, superstippi@gmx.de 9 * Axel Dörfler, axeld@pinc-software.de 10 * John Scipione, jscpione@gmail.com 11 */ 12 13 14 #include <ScrollView.h> 15 16 #include <ControlLook.h> 17 #include <LayoutUtils.h> 18 #include <Message.h> 19 #include <Region.h> 20 #include <Window.h> 21 22 #include <binary_compatibility/Interface.h> 23 24 25 static const float kFancyBorderSize = 2; 26 static const float kPlainBorderSize = 1; 27 28 29 BScrollView::BScrollView(const char* name, BView* target, uint32 resizingMode, 30 uint32 flags, bool horizontal, bool vertical, border_style border) 31 : 32 BView(BRect(), name, resizingMode, _ModifyFlags(flags, target, border)), 33 fTarget(target), 34 fBorder(border) 35 { 36 _Init(horizontal, vertical); 37 } 38 39 40 BScrollView::BScrollView(const char* name, BView* target, uint32 flags, 41 bool horizontal, bool vertical, border_style border) 42 : 43 BView(name, _ModifyFlags(flags, target, border)), 44 fTarget(target), 45 fBorder(border) 46 { 47 _Init(horizontal, vertical); 48 } 49 50 51 BScrollView::BScrollView(BMessage* archive) 52 : 53 BView(archive), 54 fHighlighted(false) 55 { 56 int32 border; 57 fBorder = archive->FindInt32("_style", &border) == B_OK ? 58 (border_style)border : B_FANCY_BORDER; 59 60 // in a shallow archive, we may not have a target anymore. We must 61 // be prepared for this case 62 63 // don't confuse our scroll bars with our (eventual) target 64 int32 firstBar = 0; 65 if (!archive->FindBool("_no_target_")) { 66 fTarget = ChildAt(0); 67 firstBar++; 68 } else 69 fTarget = NULL; 70 71 // search for our scroll bars 72 // This will not work for managed archives (when the layout kit is used). 73 // In that case the children are attached later, and we perform the search 74 // again in the AllUnarchived method. 75 76 fHorizontalScrollBar = NULL; 77 fVerticalScrollBar = NULL; 78 79 BView* view; 80 while ((view = ChildAt(firstBar++)) != NULL) { 81 BScrollBar *bar = dynamic_cast<BScrollBar *>(view); 82 if (bar == NULL) 83 continue; 84 85 if (bar->Orientation() == B_HORIZONTAL) 86 fHorizontalScrollBar = bar; 87 else if (bar->Orientation() == B_VERTICAL) 88 fVerticalScrollBar = bar; 89 } 90 91 fPreviousWidth = uint16(Bounds().Width()); 92 fPreviousHeight = uint16(Bounds().Height()); 93 94 } 95 96 97 BScrollView::~BScrollView() 98 { 99 } 100 101 102 // #pragma mark - Archiving 103 104 105 BArchivable* 106 BScrollView::Instantiate(BMessage* archive) 107 { 108 if (validate_instantiation(archive, "BScrollView")) 109 return new BScrollView(archive); 110 111 return NULL; 112 } 113 114 115 status_t 116 BScrollView::Archive(BMessage* archive, bool deep) const 117 { 118 status_t status = BView::Archive(archive, deep); 119 if (status != B_OK) 120 return status; 121 122 // If this is a deep archive, the BView class will take care 123 // of our children. 124 125 if (status == B_OK && fBorder != B_FANCY_BORDER) 126 status = archive->AddInt32("_style", fBorder); 127 if (status == B_OK && fTarget == NULL) 128 status = archive->AddBool("_no_target_", true); 129 130 // The highlighted state is not archived, but since it is 131 // usually (or should be) used to indicate focus, this 132 // is probably the right thing to do. 133 134 return status; 135 } 136 137 138 status_t 139 BScrollView::AllUnarchived(const BMessage* archive) 140 { 141 status_t result = BView::AllUnarchived(archive); 142 if (result != B_OK) 143 return result; 144 145 // search for our scroll bars and target 146 int32 firstBar = 0; 147 BView* view; 148 while ((view = ChildAt(firstBar++)) != NULL) { 149 BScrollBar *bar = dynamic_cast<BScrollBar *>(view); 150 // We assume that the first non-scrollbar child view is the target. 151 // So the target view can't be a BScrollBar, but who would do that? 152 if (bar == NULL) { 153 // in a shallow archive, we may not have a target anymore. We must 154 // be prepared for this case 155 if (fTarget == NULL && !archive->FindBool("_no_target_")) 156 fTarget = view; 157 continue; 158 } 159 160 if (bar->Orientation() == B_HORIZONTAL) 161 fHorizontalScrollBar = bar; 162 else if (bar->Orientation() == B_VERTICAL) 163 fVerticalScrollBar = bar; 164 } 165 166 // Now connect the bars to the target, and make the target aware of them 167 if (fHorizontalScrollBar) 168 fHorizontalScrollBar->SetTarget(fTarget); 169 if (fVerticalScrollBar) 170 fVerticalScrollBar->SetTarget(fTarget); 171 172 if (fTarget) 173 fTarget->TargetedByScrollView(this); 174 175 fPreviousWidth = uint16(Bounds().Width()); 176 fPreviousHeight = uint16(Bounds().Height()); 177 178 return B_OK; 179 } 180 181 182 // #pragma mark - Hook methods 183 184 185 void 186 BScrollView::AttachedToWindow() 187 { 188 BView::AttachedToWindow(); 189 190 if ((fHorizontalScrollBar == NULL && fVerticalScrollBar == NULL) 191 || (fHorizontalScrollBar != NULL && fVerticalScrollBar != NULL) 192 || Window()->Look() != B_DOCUMENT_WINDOW_LOOK) { 193 return; 194 } 195 196 // If we have only one bar, we need to check if we are in the 197 // bottom right edge of a window with the B_DOCUMENT_LOOK to 198 // adjust the size of the bar to acknowledge the resize knob. 199 200 BRect bounds = ConvertToScreen(Bounds()); 201 BRect windowBounds = Window()->Frame(); 202 203 if (bounds.right - _BorderSize() != windowBounds.right 204 || bounds.bottom - _BorderSize() != windowBounds.bottom) { 205 return; 206 } 207 208 if (fHorizontalScrollBar != NULL) 209 fHorizontalScrollBar->ResizeBy(-B_V_SCROLL_BAR_WIDTH, 0); 210 else if (fVerticalScrollBar != NULL) 211 fVerticalScrollBar->ResizeBy(0, -B_H_SCROLL_BAR_HEIGHT); 212 } 213 214 215 void 216 BScrollView::DetachedFromWindow() 217 { 218 BView::DetachedFromWindow(); 219 } 220 221 222 void 223 BScrollView::AllAttached() 224 { 225 BView::AllAttached(); 226 } 227 228 229 void 230 BScrollView::AllDetached() 231 { 232 BView::AllDetached(); 233 } 234 235 236 void 237 BScrollView::Draw(BRect updateRect) 238 { 239 uint32 flags = 0; 240 if (fHighlighted && Window()->IsActive()) 241 flags |= BControlLook::B_FOCUSED; 242 243 BRect rect(Bounds()); 244 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 245 246 BRect verticalScrollBarFrame(0, 0, -1, -1); 247 if (fVerticalScrollBar) 248 verticalScrollBarFrame = fVerticalScrollBar->Frame(); 249 250 BRect horizontalScrollBarFrame(0, 0, -1, -1); 251 if (fHorizontalScrollBar) 252 horizontalScrollBarFrame = fHorizontalScrollBar->Frame(); 253 254 be_control_look->DrawScrollViewFrame(this, rect, updateRect, 255 verticalScrollBarFrame, horizontalScrollBarFrame, base, fBorder, 256 flags, fBorders); 257 } 258 259 260 void 261 BScrollView::FrameMoved(BPoint newPosition) 262 { 263 BView::FrameMoved(newPosition); 264 } 265 266 267 void 268 BScrollView::FrameResized(float newWidth, float newHeight) 269 { 270 BView::FrameResized(newWidth, newHeight); 271 272 const BRect bounds = Bounds(); 273 274 if (fTarget != NULL && (fTarget->Flags() & B_SUPPORTS_LAYOUT) != 0 275 && (fTarget->Flags() & B_SCROLL_VIEW_AWARE) == 0) { 276 BSize size = fTarget->PreferredSize(); 277 if (fHorizontalScrollBar != NULL) { 278 float delta = size.Width() - bounds.Width(), 279 proportion = bounds.Width() / size.Width(); 280 if (delta < 0) 281 delta = 0; 282 283 fHorizontalScrollBar->SetRange(0, delta); 284 fHorizontalScrollBar->SetSteps(be_plain_font->Size() * 1.33, 285 bounds.Width()); 286 fHorizontalScrollBar->SetProportion(proportion); 287 } 288 if (fVerticalScrollBar != NULL) { 289 float delta = size.Height() - bounds.Height(), 290 proportion = bounds.Height() / size.Height(); 291 if (delta < 0) 292 delta = 0; 293 294 fVerticalScrollBar->SetRange(0, delta); 295 fVerticalScrollBar->SetSteps(be_plain_font->Size() * 1.33, 296 bounds.Height()); 297 fVerticalScrollBar->SetProportion(proportion); 298 } 299 } 300 301 if (fBorder == B_NO_BORDER) 302 return; 303 304 float border = _BorderSize() - 1; 305 306 if (fHorizontalScrollBar != NULL && fVerticalScrollBar != NULL) { 307 BRect scrollCorner(bounds); 308 scrollCorner.left = min_c( 309 fPreviousWidth - fVerticalScrollBar->Frame().Height(), 310 fHorizontalScrollBar->Frame().right + 1); 311 scrollCorner.top = min_c( 312 fPreviousHeight - fHorizontalScrollBar->Frame().Width(), 313 fVerticalScrollBar->Frame().bottom + 1); 314 Invalidate(scrollCorner); 315 } 316 317 // changes in newWidth 318 319 if (bounds.Width() > fPreviousWidth) { 320 // invalidate the region between the old and the new right border 321 BRect rect = bounds; 322 rect.left += fPreviousWidth - border; 323 rect.right--; 324 Invalidate(rect); 325 } else if (bounds.Width() < fPreviousWidth) { 326 // invalidate the region of the new right border 327 BRect rect = bounds; 328 rect.left = rect.right - border; 329 Invalidate(rect); 330 } 331 332 // changes in newHeight 333 334 if (bounds.Height() > fPreviousHeight) { 335 // invalidate the region between the old and the new bottom border 336 BRect rect = bounds; 337 rect.top += fPreviousHeight - border; 338 rect.bottom--; 339 Invalidate(rect); 340 } else if (bounds.Height() < fPreviousHeight) { 341 // invalidate the region of the new bottom border 342 BRect rect = bounds; 343 rect.top = rect.bottom - border; 344 Invalidate(rect); 345 } 346 347 fPreviousWidth = uint16(bounds.Width()); 348 fPreviousHeight = uint16(bounds.Height()); 349 } 350 351 352 void 353 BScrollView::MessageReceived(BMessage* message) 354 { 355 BView::MessageReceived(message); 356 } 357 358 359 void 360 BScrollView::MouseDown(BPoint where) 361 { 362 BView::MouseDown(where); 363 } 364 365 366 void 367 BScrollView::MouseMoved(BPoint where, uint32 code, 368 const BMessage* dragMessage) 369 { 370 BView::MouseMoved(where, code, dragMessage); 371 } 372 373 374 void 375 BScrollView::MouseUp(BPoint where) 376 { 377 BView::MouseUp(where); 378 } 379 380 381 void 382 BScrollView::WindowActivated(bool active) 383 { 384 if (fHighlighted) 385 Invalidate(); 386 387 BView::WindowActivated(active); 388 } 389 390 391 // #pragma mark - Size methods 392 393 394 void 395 BScrollView::GetPreferredSize(float* _width, float* _height) 396 { 397 BSize size = PreferredSize(); 398 399 if (_width) 400 *_width = size.width; 401 402 if (_height) 403 *_height = size.height; 404 } 405 406 407 void 408 BScrollView::ResizeToPreferred() 409 { 410 if (Window() == NULL) 411 return; 412 BView::ResizeToPreferred(); 413 } 414 415 416 void 417 BScrollView::MakeFocus(bool focus) 418 { 419 BView::MakeFocus(focus); 420 } 421 422 423 BSize 424 BScrollView::MinSize() 425 { 426 BSize size = _ComputeSize(fTarget != NULL ? fTarget->MinSize() 427 : BSize(16, 16)); 428 429 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 430 } 431 432 433 BSize 434 BScrollView::MaxSize() 435 { 436 BSize size = _ComputeSize(fTarget != NULL ? fTarget->MaxSize() 437 : BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); 438 439 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 440 } 441 442 443 BSize 444 BScrollView::PreferredSize() 445 { 446 BSize size = _ComputeSize(fTarget != NULL ? fTarget->PreferredSize() 447 : BSize(32, 32)); 448 449 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size); 450 } 451 452 453 // #pragma mark - BScrollView methods 454 455 456 BScrollBar* 457 BScrollView::ScrollBar(orientation direction) const 458 { 459 if (direction == B_HORIZONTAL) 460 return fHorizontalScrollBar; 461 462 return fVerticalScrollBar; 463 } 464 465 466 void 467 BScrollView::SetBorder(border_style border) 468 { 469 if (fBorder == border) 470 return; 471 472 if ((Flags() & B_SUPPORTS_LAYOUT) != 0) { 473 fBorder = border; 474 SetFlags(_ModifyFlags(Flags(), fTarget, border)); 475 476 DoLayout(); 477 Invalidate(); 478 return; 479 } 480 481 float offset = _BorderSize() - _BorderSize(border); 482 float resize = 2 * offset; 483 484 float horizontalGap = 0, verticalGap = 0; 485 float change = 0; 486 if (border == B_NO_BORDER || fBorder == B_NO_BORDER) { 487 if (fHorizontalScrollBar != NULL) 488 verticalGap = border != B_NO_BORDER ? 1 : -1; 489 if (fVerticalScrollBar != NULL) 490 horizontalGap = border != B_NO_BORDER ? 1 : -1; 491 492 change = border != B_NO_BORDER ? -1 : 1; 493 if (fHorizontalScrollBar == NULL || fVerticalScrollBar == NULL) 494 change *= 2; 495 } 496 497 fBorder = border; 498 499 int32 savedResizingMode = 0; 500 if (fTarget != NULL) { 501 savedResizingMode = fTarget->ResizingMode(); 502 fTarget->SetResizingMode(B_FOLLOW_NONE); 503 } 504 505 MoveBy(offset, offset); 506 ResizeBy(-resize - horizontalGap, -resize - verticalGap); 507 508 if (fTarget != NULL) { 509 fTarget->MoveBy(-offset, -offset); 510 fTarget->SetResizingMode(savedResizingMode); 511 } 512 513 if (fHorizontalScrollBar != NULL) { 514 fHorizontalScrollBar->MoveBy(-offset - verticalGap, offset + verticalGap); 515 fHorizontalScrollBar->ResizeBy(resize + horizontalGap - change, 0); 516 } 517 if (fVerticalScrollBar != NULL) { 518 fVerticalScrollBar->MoveBy(offset + horizontalGap, -offset - horizontalGap); 519 fVerticalScrollBar->ResizeBy(0, resize + verticalGap - change); 520 } 521 522 SetFlags(_ModifyFlags(Flags(), fTarget, border)); 523 } 524 525 526 border_style 527 BScrollView::Border() const 528 { 529 return fBorder; 530 } 531 532 533 void 534 BScrollView::SetBorders(uint32 borders) 535 { 536 if (fBorders == borders || (Flags() & B_SUPPORTS_LAYOUT) == 0) 537 return; 538 539 fBorders = borders; 540 DoLayout(); 541 Invalidate(); 542 } 543 544 545 uint32 546 BScrollView::Borders() const 547 { 548 return fBorders; 549 } 550 551 552 status_t 553 BScrollView::SetBorderHighlighted(bool highlight) 554 { 555 if (fHighlighted == highlight) 556 return B_OK; 557 558 if (fBorder != B_FANCY_BORDER) 559 // highlighting only works for B_FANCY_BORDER 560 return B_ERROR; 561 562 fHighlighted = highlight; 563 564 if (fHorizontalScrollBar != NULL) 565 fHorizontalScrollBar->SetBorderHighlighted(highlight); 566 if (fVerticalScrollBar != NULL) 567 fVerticalScrollBar->SetBorderHighlighted(highlight); 568 569 BRect bounds = Bounds(); 570 bounds.InsetBy(1, 1); 571 572 Invalidate(BRect(bounds.left, bounds.top, bounds.right, bounds.top)); 573 Invalidate(BRect(bounds.left, bounds.top + 1, bounds.left, 574 bounds.bottom - 1)); 575 Invalidate(BRect(bounds.right, bounds.top + 1, bounds.right, 576 bounds.bottom - 1)); 577 Invalidate(BRect(bounds.left, bounds.bottom, bounds.right, bounds.bottom)); 578 579 return B_OK; 580 } 581 582 583 bool 584 BScrollView::IsBorderHighlighted() const 585 { 586 return fHighlighted; 587 } 588 589 590 void 591 BScrollView::SetTarget(BView* target) 592 { 593 if (fTarget == target) 594 return; 595 596 if (fTarget != NULL) { 597 fTarget->TargetedByScrollView(NULL); 598 RemoveChild(fTarget); 599 600 // we are not supposed to delete the view 601 } 602 603 fTarget = target; 604 if (fHorizontalScrollBar != NULL) 605 fHorizontalScrollBar->SetTarget(target); 606 if (fVerticalScrollBar != NULL) 607 fVerticalScrollBar->SetTarget(target); 608 609 if (target != NULL) { 610 float borderSize = _BorderSize(); 611 target->MoveTo((fBorders & BControlLook::B_LEFT_BORDER) != 0 612 ? borderSize : 0, (fBorders & BControlLook::B_TOP_BORDER) != 0 613 ? borderSize : 0); 614 BRect innerFrame = _InnerFrame(); 615 target->ResizeTo(innerFrame.Width() - 1, innerFrame.Height() - 1); 616 target->TargetedByScrollView(this); 617 618 AddChild(target, ChildAt(0)); 619 // This way, we are making sure that the target will 620 // be added top most in the list (which is important 621 // for unarchiving) 622 } 623 624 SetFlags(_ModifyFlags(Flags(), fTarget, fBorder)); 625 } 626 627 628 BView* 629 BScrollView::Target() const 630 { 631 return fTarget; 632 } 633 634 635 // #pragma mark - Scripting methods 636 637 638 639 BHandler* 640 BScrollView::ResolveSpecifier(BMessage* message, int32 index, 641 BMessage* specifier, int32 what, const char* property) 642 { 643 return BView::ResolveSpecifier(message, index, specifier, what, property); 644 } 645 646 647 status_t 648 BScrollView::GetSupportedSuites(BMessage* message) 649 { 650 return BView::GetSupportedSuites(message); 651 } 652 653 654 // #pragma mark - Perform 655 656 657 status_t 658 BScrollView::Perform(perform_code code, void* _data) 659 { 660 switch (code) { 661 case PERFORM_CODE_MIN_SIZE: 662 ((perform_data_min_size*)_data)->return_value 663 = BScrollView::MinSize(); 664 return B_OK; 665 666 case PERFORM_CODE_MAX_SIZE: 667 ((perform_data_max_size*)_data)->return_value 668 = BScrollView::MaxSize(); 669 return B_OK; 670 671 case PERFORM_CODE_PREFERRED_SIZE: 672 ((perform_data_preferred_size*)_data)->return_value 673 = BScrollView::PreferredSize(); 674 return B_OK; 675 676 case PERFORM_CODE_LAYOUT_ALIGNMENT: 677 ((perform_data_layout_alignment*)_data)->return_value 678 = BScrollView::LayoutAlignment(); 679 return B_OK; 680 681 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: 682 ((perform_data_has_height_for_width*)_data)->return_value 683 = BScrollView::HasHeightForWidth(); 684 return B_OK; 685 686 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: 687 { 688 perform_data_get_height_for_width* data 689 = (perform_data_get_height_for_width*)_data; 690 BScrollView::GetHeightForWidth(data->width, &data->min, &data->max, 691 &data->preferred); 692 return B_OK; 693 } 694 695 case PERFORM_CODE_SET_LAYOUT: 696 { 697 perform_data_set_layout* data = (perform_data_set_layout*)_data; 698 BScrollView::SetLayout(data->layout); 699 return B_OK; 700 } 701 702 case PERFORM_CODE_LAYOUT_INVALIDATED: 703 { 704 perform_data_layout_invalidated* data 705 = (perform_data_layout_invalidated*)_data; 706 BScrollView::LayoutInvalidated(data->descendants); 707 return B_OK; 708 } 709 710 case PERFORM_CODE_DO_LAYOUT: 711 { 712 BScrollView::DoLayout(); 713 return B_OK; 714 } 715 } 716 717 return BView::Perform(code, _data); 718 } 719 720 721 // #pragma mark - Protected methods 722 723 724 void 725 BScrollView::LayoutInvalidated(bool descendants) 726 { 727 } 728 729 730 void 731 BScrollView::DoLayout() 732 { 733 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 734 return; 735 736 // If the user set a layout, we let the base class version call its hook. 737 if (GetLayout() != NULL) { 738 BView::DoLayout(); 739 return; 740 } 741 742 BRect innerFrame = _InnerFrame(); 743 744 if (fTarget != NULL) { 745 fTarget->MoveTo(innerFrame.left, innerFrame.top); 746 fTarget->ResizeTo(innerFrame.Width(), innerFrame.Height()); 747 748 //BLayoutUtils::AlignInFrame(fTarget, fTarget->Bounds()); 749 } 750 751 _AlignScrollBars(fHorizontalScrollBar != NULL, fVerticalScrollBar != NULL, 752 innerFrame); 753 } 754 755 756 // #pragma mark - Private methods 757 758 759 void 760 BScrollView::_Init(bool horizontal, bool vertical) 761 { 762 fHorizontalScrollBar = NULL; 763 fVerticalScrollBar = NULL; 764 fHighlighted = false; 765 fBorders = BControlLook::B_ALL_BORDERS; 766 767 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 768 769 if (horizontal) { 770 fHorizontalScrollBar = new BScrollBar(BRect(0, 0, 14, 14), "_HSB_", 771 fTarget, 0, 1000, B_HORIZONTAL); 772 AddChild(fHorizontalScrollBar); 773 } 774 775 if (vertical) { 776 fVerticalScrollBar = new BScrollBar(BRect(0, 0, 14, 14), "_VSB_", 777 fTarget, 0, 1000, B_VERTICAL); 778 AddChild(fVerticalScrollBar); 779 } 780 781 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) { 782 BRect frame = _ComputeFrame(fTarget, fHorizontalScrollBar, 783 fVerticalScrollBar, fBorder, BControlLook::B_ALL_BORDERS); 784 MoveTo(frame.LeftTop()); 785 ResizeTo(frame.Size()); 786 } 787 788 BRect targetFrame; 789 if (fTarget) { 790 // layout target and add it 791 fTarget->TargetedByScrollView(this); 792 fTarget->MoveTo(B_ORIGIN); 793 794 if (fBorder != B_NO_BORDER) 795 fTarget->MoveBy(_BorderSize(), _BorderSize()); 796 797 AddChild(fTarget); 798 targetFrame = fTarget->Frame(); 799 } else { 800 // no target specified 801 targetFrame = Bounds(); 802 if (horizontal) 803 targetFrame.bottom -= fHorizontalScrollBar->PreferredSize().Height() + 1; 804 if (vertical) 805 targetFrame.right -= fVerticalScrollBar->PreferredSize().Width() + 1; 806 if (fBorder == B_FANCY_BORDER) { 807 targetFrame.bottom--; 808 targetFrame.right--; 809 } 810 } 811 812 _AlignScrollBars(horizontal, vertical, targetFrame); 813 814 fPreviousWidth = uint16(Bounds().Width()); 815 fPreviousHeight = uint16(Bounds().Height()); 816 } 817 818 819 float 820 BScrollView::_BorderSize() const 821 { 822 return _BorderSize(fBorder); 823 } 824 825 826 BRect 827 BScrollView::_InnerFrame() const 828 { 829 BRect frame = Bounds(); 830 _InsetBorders(frame, fBorder, fBorders); 831 832 float borderSize = _BorderSize(); 833 834 if (fHorizontalScrollBar != NULL) { 835 frame.bottom -= fHorizontalScrollBar->PreferredSize().Height(); 836 if (borderSize == 0) 837 frame.bottom--; 838 } 839 if (fVerticalScrollBar != NULL) { 840 frame.right -= fVerticalScrollBar->PreferredSize().Width(); 841 if (borderSize == 0) 842 frame.right--; 843 } 844 845 return frame; 846 } 847 848 849 BSize 850 BScrollView::_ComputeSize(BSize targetSize) const 851 { 852 BRect frame = _ComputeFrame( 853 BRect(0, 0, targetSize.width, targetSize.height)); 854 855 return BSize(frame.Width(), frame.Height()); 856 } 857 858 859 BRect 860 BScrollView::_ComputeFrame(BRect targetRect) const 861 { 862 return _ComputeFrame(targetRect, fHorizontalScrollBar, 863 fVerticalScrollBar, fBorder, fBorders); 864 } 865 866 867 void 868 BScrollView::_AlignScrollBars(bool horizontal, bool vertical, BRect targetFrame) 869 { 870 if (horizontal) { 871 BRect rect = targetFrame; 872 rect.top = rect.bottom + 1; 873 rect.bottom = rect.top + fHorizontalScrollBar->PreferredSize().Height(); 874 if (fBorder != B_NO_BORDER || vertical) { 875 // extend scrollbar so that it overlaps one pixel with vertical 876 // scrollbar 877 rect.right++; 878 } 879 880 if (fBorder != B_NO_BORDER) { 881 // the scrollbar draws part of the surrounding frame on the left 882 rect.left--; 883 } 884 885 fHorizontalScrollBar->MoveTo(rect.left, rect.top); 886 fHorizontalScrollBar->ResizeTo(rect.Width(), rect.Height()); 887 } 888 889 if (vertical) { 890 BRect rect = targetFrame; 891 rect.left = rect.right + 1; 892 rect.right = rect.left + fVerticalScrollBar->PreferredSize().Width(); 893 if (fBorder != B_NO_BORDER || horizontal) { 894 // extend scrollbar so that it overlaps one pixel with vertical 895 // scrollbar 896 rect.bottom++; 897 } 898 899 if (fBorder != B_NO_BORDER) { 900 // the scrollbar draws part of the surrounding frame on the left 901 rect.top--; 902 } 903 904 fVerticalScrollBar->MoveTo(rect.left, rect.top); 905 fVerticalScrollBar->ResizeTo(rect.Width(), rect.Height()); 906 } 907 } 908 909 910 /*! This static method is used to calculate the frame that the 911 ScrollView will cover depending on the frame of its target 912 and which border style is used. 913 It is used in the constructor and at other places. 914 */ 915 /*static*/ BRect 916 BScrollView::_ComputeFrame(BRect frame, BScrollBar* horizontal, 917 BScrollBar* vertical, border_style border, uint32 borders) 918 { 919 if (vertical != NULL) { 920 frame.right += vertical->PreferredSize().Width(); 921 922 const float minHeight = vertical->MinSize().Height(); 923 if (frame.Height() >= 0 && frame.Height() < minHeight) 924 frame.bottom += minHeight - frame.Height(); 925 } 926 if (horizontal != NULL) { 927 frame.bottom += horizontal->PreferredSize().Height(); 928 929 const float minWidth = horizontal->MinSize().Width(); 930 if (frame.Width() >= 0 && frame.Width() < minWidth) 931 frame.right += minWidth - frame.Width(); 932 } 933 934 _InsetBorders(frame, border, borders, true); 935 936 if (_BorderSize(border) == 0) { 937 if (vertical != NULL) 938 frame.right++; 939 if (horizontal != NULL) 940 frame.bottom++; 941 } 942 943 return frame; 944 } 945 946 947 /*static*/ BRect 948 BScrollView::_ComputeFrame(BView *target, BScrollBar* horizontal, 949 BScrollBar* vertical, border_style border, uint32 borders) 950 { 951 return _ComputeFrame(target != NULL ? target->Frame() 952 : BRect(0, 0, 16, 16), horizontal, vertical, border, borders); 953 } 954 955 956 /*! This method returns the size of the specified border. 957 */ 958 /*static*/ float 959 BScrollView::_BorderSize(border_style border) 960 { 961 if (border == B_FANCY_BORDER) 962 return kFancyBorderSize; 963 if (border == B_PLAIN_BORDER) 964 return kPlainBorderSize; 965 966 return 0; 967 } 968 969 970 /*! This method changes the "flags" argument as passed on to 971 the BView constructor. 972 */ 973 /*static*/ uint32 974 BScrollView::_ModifyFlags(uint32 flags, BView* target, border_style border) 975 { 976 if (target != NULL && (target->Flags() & B_SUPPORTS_LAYOUT) != 0 977 && (target->Flags() & B_SCROLL_VIEW_AWARE) == 0) 978 flags |= B_FRAME_EVENTS; 979 980 // We either need B_FULL_UPDATE_ON_RESIZE or B_FRAME_EVENTS if we have 981 // to draw a border. 982 if (border != B_NO_BORDER) { 983 flags |= B_WILL_DRAW | ((flags & B_FULL_UPDATE_ON_RESIZE) != 0 ? 984 0 : B_FRAME_EVENTS); 985 } 986 987 return flags; 988 } 989 990 991 /*static*/ void 992 BScrollView::_InsetBorders(BRect& frame, border_style border, uint32 borders, bool expand) 993 { 994 float borderSize = _BorderSize(border); 995 if (expand) 996 borderSize = -borderSize; 997 if ((borders & BControlLook::B_LEFT_BORDER) != 0) 998 frame.left += borderSize; 999 if ((borders & BControlLook::B_TOP_BORDER) != 0) 1000 frame.top += borderSize; 1001 if ((borders & BControlLook::B_RIGHT_BORDER) != 0) 1002 frame.right -= borderSize; 1003 if ((borders & BControlLook::B_BOTTOM_BORDER) != 0) 1004 frame.bottom -= borderSize; 1005 } 1006 1007 1008 // #pragma mark - FBC and forbidden 1009 1010 1011 BScrollView& 1012 BScrollView::operator=(const BScrollView &) 1013 { 1014 return *this; 1015 } 1016 1017 1018 void BScrollView::_ReservedScrollView1() {} 1019 void BScrollView::_ReservedScrollView2() {} 1020 void BScrollView::_ReservedScrollView3() {} 1021 void BScrollView::_ReservedScrollView4() {} 1022 1023 1024 extern "C" void 1025 B_IF_GCC_2(InvalidateLayout__11BScrollViewb, 1026 _ZN11BScrollView16InvalidateLayoutEb)(BScrollView* view, bool descendants) 1027 { 1028 perform_data_layout_invalidated data; 1029 data.descendants = descendants; 1030 1031 view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data); 1032 } 1033