1 /* 2 * Copyright 2001-2009, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Stephan Aßmus <superstippi@gmx.de> 8 * Axel Dörfler, axeld@pinc-software.de 9 */ 10 11 12 #include <Slider.h> 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include <Bitmap.h> 19 #include <ControlLook.h> 20 #include <Errors.h> 21 #include <LayoutUtils.h> 22 #include <Message.h> 23 #include <Region.h> 24 #include <String.h> 25 #include <Window.h> 26 27 #include <binary_compatibility/Interface.h> 28 29 30 #define USE_OFF_SCREEN_VIEW 0 31 32 33 BSlider::BSlider(BRect frame, const char* name, const char* label, 34 BMessage* message, int32 minValue, int32 maxValue, 35 thumb_style thumbType, uint32 resizingMode, uint32 flags) 36 : BControl(frame, name, label, message, resizingMode, flags), 37 fModificationMessage(NULL), 38 fSnoozeAmount(20000), 39 40 fMinLimitLabel(NULL), 41 fMaxLimitLabel(NULL), 42 43 fMinValue(minValue), 44 fMaxValue(maxValue), 45 fKeyIncrementValue(1), 46 47 fHashMarkCount(0), 48 fHashMarks(B_HASH_MARKS_NONE), 49 50 fStyle(thumbType), 51 52 fOrientation(B_HORIZONTAL), 53 fBarThickness(6.0) 54 { 55 _InitBarColor(); 56 57 _InitObject(); 58 SetValue(0); 59 } 60 61 62 BSlider::BSlider(BRect frame, const char *name, const char *label, 63 BMessage *message, int32 minValue, int32 maxValue, 64 orientation posture, thumb_style thumbType, uint32 resizingMode, 65 uint32 flags) 66 : BControl(frame, name, label, message, resizingMode, flags), 67 fModificationMessage(NULL), 68 fSnoozeAmount(20000), 69 70 fMinLimitLabel(NULL), 71 fMaxLimitLabel(NULL), 72 73 fMinValue(minValue), 74 fMaxValue(maxValue), 75 fKeyIncrementValue(1), 76 77 fHashMarkCount(0), 78 fHashMarks(B_HASH_MARKS_NONE), 79 80 fStyle(thumbType), 81 82 fOrientation(posture), 83 fBarThickness(6.0) 84 { 85 _InitBarColor(); 86 87 _InitObject(); 88 SetValue(0); 89 } 90 91 92 BSlider::BSlider(const char *name, const char *label, BMessage *message, 93 int32 minValue, int32 maxValue, orientation posture, 94 thumb_style thumbType, uint32 flags) 95 : BControl(name, label, message, flags), 96 fModificationMessage(NULL), 97 fSnoozeAmount(20000), 98 99 fMinLimitLabel(NULL), 100 fMaxLimitLabel(NULL), 101 102 fMinValue(minValue), 103 fMaxValue(maxValue), 104 fKeyIncrementValue(1), 105 106 fHashMarkCount(0), 107 fHashMarks(B_HASH_MARKS_NONE), 108 109 fStyle(thumbType), 110 111 fOrientation(posture), 112 fBarThickness(6.0) 113 { 114 _InitBarColor(); 115 116 _InitObject(); 117 SetValue(0); 118 } 119 120 121 BSlider::BSlider(BMessage *archive) 122 : BControl(archive) 123 { 124 fModificationMessage = NULL; 125 126 if (archive->HasMessage("_mod_msg")) { 127 BMessage* message = new BMessage; 128 129 archive->FindMessage("_mod_msg", message); 130 131 SetModificationMessage(message); 132 } 133 134 if (archive->FindInt32("_sdelay", &fSnoozeAmount) != B_OK) 135 SetSnoozeAmount(20000); 136 137 rgb_color color; 138 if (archive->FindInt32("_fcolor", (int32 *)&color) == B_OK) 139 UseFillColor(true, &color); 140 else 141 UseFillColor(false); 142 143 int32 orient; 144 if (archive->FindInt32("_orient", &orient) == B_OK) 145 fOrientation = (orientation)orient; 146 else 147 fOrientation = B_HORIZONTAL; 148 149 fMinLimitLabel = NULL; 150 fMaxLimitLabel = NULL; 151 152 const char* minlbl = NULL; 153 const char* maxlbl = NULL; 154 155 archive->FindString("_minlbl", &minlbl); 156 archive->FindString("_maxlbl", &maxlbl); 157 158 SetLimitLabels(minlbl, maxlbl); 159 160 if (archive->FindInt32("_min", &fMinValue) != B_OK) 161 fMinValue = 0; 162 163 if (archive->FindInt32("_max", &fMaxValue) != B_OK) 164 fMaxValue = 100; 165 166 if (archive->FindInt32("_incrementvalue", &fKeyIncrementValue) != B_OK) 167 fKeyIncrementValue = 1; 168 169 if (archive->FindInt32("_hashcount", &fHashMarkCount) != B_OK) 170 fHashMarkCount = 11; 171 172 int16 hashloc; 173 if (archive->FindInt16("_hashloc", &hashloc) == B_OK) 174 fHashMarks = (hash_mark_location)hashloc; 175 else 176 fHashMarks = B_HASH_MARKS_NONE; 177 178 int16 sstyle; 179 if (archive->FindInt16("_sstyle", &sstyle) == B_OK) 180 fStyle = (thumb_style)sstyle; 181 else 182 fStyle = B_BLOCK_THUMB; 183 184 if (archive->FindInt32("_bcolor", (int32 *)&color) != B_OK) 185 color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT); 186 SetBarColor(color); 187 188 float bthickness; 189 if (archive->FindFloat("_bthickness", &bthickness) == B_OK) 190 fBarThickness = bthickness; 191 else 192 fBarThickness = 6.0f; 193 194 _InitObject(); 195 } 196 197 198 BSlider::~BSlider() 199 { 200 #if USE_OFF_SCREEN_VIEW 201 delete fOffScreenBits; 202 #endif 203 204 delete fModificationMessage; 205 free(fMinLimitLabel); 206 free(fMaxLimitLabel); 207 } 208 209 210 void 211 BSlider::_InitBarColor() 212 { 213 if (be_control_look != NULL) { 214 SetBarColor(be_control_look->SliderBarColor( 215 ui_color(B_PANEL_BACKGROUND_COLOR))); 216 } else { 217 SetBarColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 218 B_DARKEN_4_TINT)); 219 } 220 221 UseFillColor(false, NULL); 222 } 223 224 225 void 226 BSlider::_InitObject() 227 { 228 fLocation.x = 0; 229 fLocation.y = 0; 230 fInitialLocation.x = 0; 231 fInitialLocation.y = 0; 232 233 #if USE_OFF_SCREEN_VIEW 234 fOffScreenBits = NULL; 235 fOffScreenView = NULL; 236 #endif 237 238 fUpdateText = NULL; 239 fMinSize.Set(-1, -1); 240 fMaxUpdateTextWidth = -1.0; 241 } 242 243 244 BArchivable* 245 BSlider::Instantiate(BMessage *archive) 246 { 247 if (validate_instantiation(archive, "BSlider")) 248 return new BSlider(archive); 249 250 return NULL; 251 } 252 253 254 status_t 255 BSlider::Archive(BMessage *archive, bool deep) const 256 { 257 status_t ret = BControl::Archive(archive, deep); 258 259 if (ModificationMessage() && ret == B_OK) 260 ret = archive->AddMessage("_mod_msg", ModificationMessage()); 261 262 if (ret == B_OK) 263 ret = archive->AddInt32("_sdelay", fSnoozeAmount); 264 if (ret == B_OK) 265 ret = archive->AddInt32("_bcolor", (const uint32 &)fBarColor); 266 267 if (FillColor(NULL) && ret == B_OK) 268 ret = archive->AddInt32("_fcolor", (const uint32 &)fFillColor); 269 270 if (ret == B_OK && fMinLimitLabel) 271 ret = archive->AddString("_minlbl", fMinLimitLabel); 272 273 if (ret == B_OK && fMaxLimitLabel) 274 ret = archive->AddString("_maxlbl", fMaxLimitLabel); 275 276 if (ret == B_OK) 277 ret = archive->AddInt32("_min", fMinValue); 278 if (ret == B_OK) 279 ret = archive->AddInt32("_max", fMaxValue); 280 281 if (ret == B_OK) 282 ret = archive->AddInt32("_incrementvalue", fKeyIncrementValue); 283 if (ret == B_OK) 284 ret = archive->AddInt32("_hashcount", fHashMarkCount); 285 if (ret == B_OK) 286 ret = archive->AddInt16("_hashloc", fHashMarks); 287 if (ret == B_OK) 288 ret = archive->AddInt16("_sstyle", fStyle); 289 if (ret == B_OK) 290 ret = archive->AddInt32("_orient", fOrientation); 291 if (ret == B_OK) 292 ret = archive->AddFloat("_bthickness", fBarThickness); 293 294 return ret; 295 } 296 297 298 status_t 299 BSlider::Perform(perform_code code, void* _data) 300 { 301 switch (code) { 302 case PERFORM_CODE_MIN_SIZE: 303 ((perform_data_min_size*)_data)->return_value 304 = BSlider::MinSize(); 305 return B_OK; 306 case PERFORM_CODE_MAX_SIZE: 307 ((perform_data_max_size*)_data)->return_value 308 = BSlider::MaxSize(); 309 return B_OK; 310 case PERFORM_CODE_PREFERRED_SIZE: 311 ((perform_data_preferred_size*)_data)->return_value 312 = BSlider::PreferredSize(); 313 return B_OK; 314 case PERFORM_CODE_LAYOUT_ALIGNMENT: 315 ((perform_data_layout_alignment*)_data)->return_value 316 = BSlider::LayoutAlignment(); 317 return B_OK; 318 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: 319 ((perform_data_has_height_for_width*)_data)->return_value 320 = BSlider::HasHeightForWidth(); 321 return B_OK; 322 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: 323 { 324 perform_data_get_height_for_width* data 325 = (perform_data_get_height_for_width*)_data; 326 BSlider::GetHeightForWidth(data->width, &data->min, &data->max, 327 &data->preferred); 328 return B_OK; 329 } 330 case PERFORM_CODE_SET_LAYOUT: 331 { 332 perform_data_set_layout* data = (perform_data_set_layout*)_data; 333 BSlider::SetLayout(data->layout); 334 return B_OK; 335 } 336 case PERFORM_CODE_LAYOUT_INVALIDATED: 337 { 338 perform_data_layout_invalidated* data 339 = (perform_data_layout_invalidated*)_data; 340 BSlider::LayoutInvalidated(data->descendants); 341 return B_OK; 342 } 343 case PERFORM_CODE_DO_LAYOUT: 344 { 345 BSlider::DoLayout(); 346 return B_OK; 347 } 348 } 349 350 return BControl::Perform(code, _data); 351 } 352 353 354 void 355 BSlider::WindowActivated(bool state) 356 { 357 BControl::WindowActivated(state); 358 } 359 360 361 void 362 BSlider::AttachedToWindow() 363 { 364 ResizeToPreferred(); 365 366 #if USE_OFF_SCREEN_VIEW 367 BRect bounds(Bounds()); 368 369 if (!fOffScreenView) { 370 fOffScreenView = new BView(bounds, "", B_FOLLOW_ALL, B_WILL_DRAW); 371 372 BFont font; 373 GetFont(&font); 374 fOffScreenView->SetFont(&font); 375 } 376 377 if (!fOffScreenBits) { 378 fOffScreenBits = new BBitmap(bounds, B_RGBA32, true, false); 379 380 if (fOffScreenBits && fOffScreenView) 381 fOffScreenBits->AddChild(fOffScreenView); 382 383 } else if (fOffScreenView) 384 fOffScreenBits->AddChild(fOffScreenView); 385 #endif // USE_OFF_SCREEN_VIEW 386 387 BControl::AttachedToWindow(); 388 389 BView* view = OffscreenView(); 390 if (view && view->LockLooper()) { 391 view->SetViewColor(B_TRANSPARENT_COLOR); 392 view->SetLowColor(LowColor()); 393 view->UnlockLooper(); 394 } 395 396 int32 value = Value(); 397 SetValue(value); 398 // makes sure the value is within valid bounds 399 _SetLocationForValue(Value()); 400 // makes sure the location is correct 401 UpdateTextChanged(); 402 } 403 404 405 void 406 BSlider::AllAttached() 407 { 408 BControl::AllAttached(); 409 } 410 411 412 void 413 BSlider::AllDetached() 414 { 415 BControl::AllDetached(); 416 } 417 418 419 void 420 BSlider::DetachedFromWindow() 421 { 422 BControl::DetachedFromWindow(); 423 424 #if USE_OFF_SCREEN_VIEW 425 if (fOffScreenBits) { 426 delete fOffScreenBits; 427 fOffScreenBits = NULL; 428 fOffScreenView = NULL; 429 } 430 #endif 431 } 432 433 434 void 435 BSlider::MessageReceived(BMessage *msg) 436 { 437 BControl::MessageReceived(msg); 438 } 439 440 441 void 442 BSlider::FrameMoved(BPoint new_position) 443 { 444 BControl::FrameMoved(new_position); 445 } 446 447 448 void 449 BSlider::FrameResized(float w,float h) 450 { 451 BControl::FrameResized(w, h); 452 453 BRect bounds(Bounds()); 454 455 if (bounds.right <= 0.0f || bounds.bottom <= 0.0f) 456 return; 457 458 #if USE_OFF_SCREEN_VIEW 459 if (fOffScreenBits) { 460 fOffScreenBits->RemoveChild(fOffScreenView); 461 delete fOffScreenBits; 462 463 fOffScreenView->ResizeTo(bounds.Width(), bounds.Height()); 464 465 fOffScreenBits = new BBitmap(Bounds(), B_RGBA32, true, false); 466 fOffScreenBits->AddChild(fOffScreenView); 467 } 468 #endif 469 470 Invalidate(); 471 } 472 473 474 void 475 BSlider::KeyDown(const char *bytes, int32 numBytes) 476 { 477 if (!IsEnabled() || IsHidden()) 478 return; 479 480 int32 newValue = Value(); 481 482 switch (bytes[0]) { 483 case B_LEFT_ARROW: 484 case B_DOWN_ARROW: 485 newValue -= KeyIncrementValue(); 486 break; 487 488 case B_RIGHT_ARROW: 489 case B_UP_ARROW: 490 newValue += KeyIncrementValue(); 491 break; 492 493 case B_HOME: 494 newValue = fMinValue; 495 break; 496 case B_END: 497 newValue = fMaxValue; 498 break; 499 500 default: 501 BControl::KeyDown(bytes, numBytes); 502 return; 503 } 504 505 if (newValue < fMinValue) 506 newValue = fMinValue; 507 if (newValue > fMaxValue) 508 newValue = fMaxValue; 509 510 if (newValue != Value()) { 511 fInitialLocation = _Location(); 512 SetValue(newValue); 513 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 514 } 515 } 516 517 void 518 BSlider::KeyUp(const char *bytes, int32 numBytes) 519 { 520 if (fInitialLocation != _Location()) { 521 // The last KeyDown event triggered the modification message or no 522 // notification at all, we may also have sent the modification message 523 // continually while the user kept pressing the key. In either case, 524 // finish with the final message to make the behavior consistent with 525 // changing the value by mouse. 526 Invoke(); 527 } 528 } 529 530 531 /*! 532 Makes sure the \a point is within valid bounds. 533 Returns \c true if the relevant coordinate (depending on the orientation 534 of the slider) differs from \a comparePoint. 535 */ 536 bool 537 BSlider::_ConstrainPoint(BPoint& point, BPoint comparePoint) const 538 { 539 if (fOrientation == B_HORIZONTAL) { 540 if (point.x != comparePoint.x) { 541 if (point.x < _MinPosition()) 542 point.x = _MinPosition(); 543 else if (point.x > _MaxPosition()) 544 point.x = _MaxPosition(); 545 546 return true; 547 } 548 } else { 549 if (point.y != comparePoint.y) { 550 if (point.y > _MinPosition()) 551 point.y = _MinPosition(); 552 else if (point.y < _MaxPosition()) 553 point.y = _MaxPosition(); 554 555 return true; 556 } 557 } 558 559 return false; 560 } 561 562 563 void 564 BSlider::MouseDown(BPoint point) 565 { 566 if (!IsEnabled()) 567 return; 568 569 if (BarFrame().Contains(point) || ThumbFrame().Contains(point)) 570 fInitialLocation = _Location(); 571 572 uint32 buttons; 573 GetMouse(&point, &buttons, true); 574 575 _ConstrainPoint(point, fInitialLocation); 576 SetValue(ValueForPoint(point)); 577 578 if (_Location() != fInitialLocation) 579 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 580 581 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) { 582 SetTracking(true); 583 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); 584 } else { 585 // synchronous mouse tracking 586 BPoint prevPoint; 587 588 while (buttons) { 589 prevPoint = point; 590 591 snooze(SnoozeAmount()); 592 GetMouse(&point, &buttons, true); 593 594 if (_ConstrainPoint(point, prevPoint)) { 595 int32 value = ValueForPoint(point); 596 if (value != Value()) { 597 SetValue(value); 598 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 599 } 600 } 601 } 602 if (_Location() != fInitialLocation) 603 Invoke(); 604 } 605 } 606 607 608 void 609 BSlider::MouseUp(BPoint point) 610 { 611 if (IsTracking()) { 612 if (_Location() != fInitialLocation) 613 Invoke(); 614 615 SetTracking(false); 616 } else 617 BControl::MouseUp(point); 618 } 619 620 621 void 622 BSlider::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 623 { 624 if (IsTracking()) { 625 if (_ConstrainPoint(point, _Location())) { 626 int32 value = ValueForPoint(point); 627 if (value != Value()) { 628 SetValue(value); 629 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 630 } 631 } 632 } else 633 BControl::MouseMoved(point, transit, message); 634 } 635 636 637 void 638 BSlider::Pulse() 639 { 640 BControl::Pulse(); 641 } 642 643 644 void 645 BSlider::SetLabel(const char *label) 646 { 647 BControl::SetLabel(label); 648 } 649 650 651 void 652 BSlider::SetLimitLabels(const char *minLabel, const char *maxLabel) 653 { 654 free(fMinLimitLabel); 655 fMinLimitLabel = minLabel ? strdup(minLabel) : NULL; 656 657 free(fMaxLimitLabel); 658 fMaxLimitLabel = maxLabel ? strdup(maxLabel) : NULL; 659 660 InvalidateLayout(); 661 662 // TODO: This is for backwards compatibility and should 663 // probably be removed when breaking binary compatiblity. 664 // Applications like our own Mouse rely on this behavior. 665 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 666 ResizeToPreferred(); 667 668 Invalidate(); 669 } 670 671 672 const char* 673 BSlider::MinLimitLabel() const 674 { 675 return fMinLimitLabel; 676 } 677 678 679 const char* 680 BSlider::MaxLimitLabel() const 681 { 682 return fMaxLimitLabel; 683 } 684 685 686 void 687 BSlider::SetValue(int32 value) 688 { 689 if (value < fMinValue) 690 value = fMinValue; 691 if (value > fMaxValue) 692 value = fMaxValue; 693 694 if (value == Value()) 695 return; 696 697 _SetLocationForValue(value); 698 699 BRect oldThumbFrame = ThumbFrame(); 700 701 // While it would be enough to do this dependent on fUseFillColor, 702 // that doesn't work out if DrawBar() has been overridden by a sub class 703 if (fOrientation == B_HORIZONTAL) 704 oldThumbFrame.top = BarFrame().top; 705 else 706 oldThumbFrame.left = BarFrame().left; 707 708 BControl::SetValueNoUpdate(value); 709 BRect invalid = oldThumbFrame | ThumbFrame(); 710 711 if (Style() == B_TRIANGLE_THUMB) { 712 // 1) We need to take care of pixels touched because of anti-aliasing. 713 // 2) We need to update the region with the focus mark as well. (A 714 // method BSlider::FocusMarkFrame() would be nice as well.) 715 if (fOrientation == B_HORIZONTAL) { 716 if (IsFocus()) 717 invalid.bottom += 2; 718 invalid.InsetBy(-1, 0); 719 } else { 720 if (IsFocus()) 721 invalid.left -= 2; 722 invalid.InsetBy(0, -1); 723 } 724 } 725 726 Invalidate(invalid); 727 728 UpdateTextChanged(); 729 } 730 731 732 int32 733 BSlider::ValueForPoint(BPoint location) const 734 { 735 float min; 736 float max; 737 float position; 738 if (fOrientation == B_HORIZONTAL) { 739 min = _MinPosition(); 740 max = _MaxPosition(); 741 position = location.x; 742 } else { 743 max = _MinPosition(); 744 min = _MaxPosition(); 745 position = min + (max - location.y); 746 } 747 748 if (position < min) 749 position = min; 750 if (position > max) 751 position = max; 752 753 return (int32)roundf(((position - min) * (fMaxValue - fMinValue) / (max - min)) + fMinValue); 754 } 755 756 757 void 758 BSlider::SetPosition(float position) 759 { 760 if (position <= 0.0f) 761 BControl::SetValue(fMinValue); 762 else if (position >= 1.0f) 763 BControl::SetValue(fMaxValue); 764 else 765 BControl::SetValue((int32)(position * (fMaxValue - fMinValue) + fMinValue)); 766 } 767 768 769 float 770 BSlider::Position() const 771 { 772 float range = (float)(fMaxValue - fMinValue); 773 if (range == 0.0f) 774 range = 1.0f; 775 776 return (float)(Value() - fMinValue) / range; 777 } 778 779 780 void 781 BSlider::SetEnabled(bool on) 782 { 783 BControl::SetEnabled(on); 784 } 785 786 787 void 788 BSlider::GetLimits(int32 *minimum, int32 *maximum) const 789 { 790 if (minimum != NULL) 791 *minimum = fMinValue; 792 if (maximum != NULL) 793 *maximum = fMaxValue; 794 } 795 796 797 // #pragma mark - drawing 798 799 800 void 801 BSlider::Draw(BRect updateRect) 802 { 803 // clear out background 804 BRegion background(updateRect); 805 background.Exclude(BarFrame()); 806 bool drawBackground = true; 807 if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0) { 808 // This view is embedded somewhere, most likely the Tracker Desktop 809 // shelf. 810 drawBackground = false; 811 } 812 813 #if USE_OFF_SCREEN_VIEW 814 if (!fOffScreenBits) 815 return; 816 817 if (fOffScreenBits->Lock()) { 818 fOffScreenView->SetViewColor(ViewColor()); 819 fOffScreenView->SetLowColor(LowColor()); 820 #endif 821 822 if (drawBackground && background.Frame().IsValid()) 823 OffscreenView()->FillRegion(&background, B_SOLID_LOW); 824 825 #if USE_OFF_SCREEN_VIEW 826 fOffScreenView->Sync(); 827 fOffScreenBits->Unlock(); 828 } 829 #endif 830 831 DrawSlider(); 832 } 833 834 835 void 836 BSlider::DrawSlider() 837 { 838 if (LockLooper()) { 839 #if USE_OFF_SCREEN_VIEW 840 if (!fOffScreenBits) 841 return; 842 if (fOffScreenBits->Lock()) { 843 #endif 844 DrawBar(); 845 DrawHashMarks(); 846 DrawThumb(); 847 DrawFocusMark(); 848 DrawText(); 849 850 #if USE_OFF_SCREEN_VIEW 851 fOffScreenView->Sync(); 852 fOffScreenBits->Unlock(); 853 854 DrawBitmap(fOffScreenBits, B_ORIGIN); 855 } 856 #endif 857 UnlockLooper(); 858 } 859 } 860 861 862 void 863 BSlider::DrawBar() 864 { 865 BRect frame = BarFrame(); 866 BView *view = OffscreenView(); 867 868 if (be_control_look != NULL) { 869 uint32 flags = be_control_look->Flags(this); 870 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 871 rgb_color rightFillColor = fBarColor; 872 rgb_color leftFillColor = fUseFillColor ? fFillColor : fBarColor; 873 be_control_look->DrawSliderBar(view, frame, frame, base, leftFillColor, 874 rightFillColor, Position(), flags, fOrientation); 875 return; 876 } 877 878 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 879 rgb_color lightenmax; 880 rgb_color darken1; 881 rgb_color darken2; 882 rgb_color darkenmax; 883 884 rgb_color barColor; 885 rgb_color fillColor; 886 887 if (IsEnabled()) { 888 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 889 darken1 = tint_color(no_tint, B_DARKEN_1_TINT); 890 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 891 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 892 barColor = fBarColor; 893 fillColor = fFillColor; 894 } else { 895 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 896 darken1 = no_tint; 897 darken2 = tint_color(no_tint, B_DARKEN_1_TINT); 898 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT); 899 900 barColor.red = (fBarColor.red + no_tint.red) / 2; 901 barColor.green = (fBarColor.green + no_tint.green) / 2; 902 barColor.blue = (fBarColor.blue + no_tint.blue) / 2; 903 barColor.alpha = 255; 904 905 fillColor.red = (fFillColor.red + no_tint.red) / 2; 906 fillColor.green = (fFillColor.green + no_tint.green) / 2; 907 fillColor.blue = (fFillColor.blue + no_tint.blue) / 2; 908 fillColor.alpha = 255; 909 } 910 911 // exclude the block thumb from the bar filling 912 913 BRect lowerFrame = frame.InsetByCopy(1, 1); 914 lowerFrame.top++; 915 lowerFrame.left++; 916 BRect upperFrame = lowerFrame; 917 BRect thumbFrame; 918 919 if (Style() == B_BLOCK_THUMB) { 920 thumbFrame = ThumbFrame(); 921 922 if (fOrientation == B_HORIZONTAL) { 923 lowerFrame.right = thumbFrame.left; 924 upperFrame.left = thumbFrame.right; 925 } else { 926 lowerFrame.top = thumbFrame.bottom; 927 upperFrame.bottom = thumbFrame.top; 928 } 929 } else if (fUseFillColor) { 930 if (fOrientation == B_HORIZONTAL) { 931 lowerFrame.right = floor(lowerFrame.left - 1 + Position() 932 * (lowerFrame.Width() + 1)); 933 upperFrame.left = lowerFrame.right; 934 } else { 935 lowerFrame.top = floor(lowerFrame.bottom + 1 - Position() 936 * (lowerFrame.Height() + 1)); 937 upperFrame.bottom = lowerFrame.top; 938 } 939 } 940 941 view->SetHighColor(barColor); 942 view->FillRect(upperFrame); 943 944 if (Style() == B_BLOCK_THUMB || fUseFillColor) { 945 if (fUseFillColor) 946 view->SetHighColor(fillColor); 947 view->FillRect(lowerFrame); 948 } 949 950 if (Style() == B_BLOCK_THUMB) { 951 // We don't want to stroke the lines over the thumb 952 953 PushState(); 954 955 BRegion region; 956 GetClippingRegion(®ion); 957 region.Exclude(thumbFrame); 958 ConstrainClippingRegion(®ion); 959 } 960 961 view->SetHighColor(darken1); 962 view->StrokeLine(BPoint(frame.left, frame.top), 963 BPoint(frame.left + 1.0f, frame.top)); 964 view->StrokeLine(BPoint(frame.left, frame.bottom), 965 BPoint(frame.left + 1.0f, frame.bottom)); 966 view->StrokeLine(BPoint(frame.right - 1.0f, frame.top), 967 BPoint(frame.right, frame.top)); 968 969 view->SetHighColor(darken2); 970 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 971 BPoint(frame.right - 1.0f, frame.top)); 972 view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f), 973 BPoint(frame.left, frame.top + 1.0f)); 974 975 view->SetHighColor(lightenmax); 976 view->StrokeLine(BPoint(frame.left + 1.0f, frame.bottom), 977 BPoint(frame.right, frame.bottom)); 978 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 979 BPoint(frame.right, frame.top + 1.0f)); 980 981 frame.InsetBy(1.0f, 1.0f); 982 983 view->SetHighColor(darkenmax); 984 view->StrokeLine(BPoint(frame.left, frame.bottom), 985 BPoint(frame.left, frame.top)); 986 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 987 BPoint(frame.right, frame.top)); 988 989 if (Style() == B_BLOCK_THUMB) 990 PopState(); 991 } 992 993 994 void 995 BSlider::DrawHashMarks() 996 { 997 if (fHashMarks == B_HASH_MARKS_NONE) 998 return; 999 1000 BRect frame = HashMarksFrame(); 1001 BView* view = OffscreenView(); 1002 1003 if (be_control_look != NULL) { 1004 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1005 uint32 flags = be_control_look->Flags(this); 1006 be_control_look->DrawSliderHashMarks(view, frame, frame, base, 1007 fHashMarkCount, fHashMarks, flags, fOrientation); 1008 return; 1009 } 1010 1011 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 1012 rgb_color lightenmax; 1013 rgb_color darken2; 1014 1015 if (IsEnabled()) { 1016 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 1017 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 1018 } else { 1019 lightenmax = tint_color(no_tint, B_LIGHTEN_2_TINT); 1020 darken2 = tint_color(no_tint, B_DARKEN_1_TINT); 1021 } 1022 1023 float pos = _MinPosition(); 1024 int32 hashMarkCount = max_c(fHashMarkCount, 2); 1025 // draw at least two hashmarks at min/max if 1026 // fHashMarks != B_HASH_MARKS_NONE 1027 float factor = (_MaxPosition() - pos) / (hashMarkCount - 1); 1028 1029 if (fHashMarks & B_HASH_MARKS_TOP) { 1030 1031 view->BeginLineArray(hashMarkCount * 2); 1032 1033 if (fOrientation == B_HORIZONTAL) { 1034 for (int32 i = 0; i < hashMarkCount; i++) { 1035 view->AddLine(BPoint(pos, frame.top), 1036 BPoint(pos, frame.top + 5), darken2); 1037 view->AddLine(BPoint(pos + 1, frame.top), 1038 BPoint(pos + 1, frame.top + 5), lightenmax); 1039 1040 pos += factor; 1041 } 1042 } else { 1043 for (int32 i = 0; i < hashMarkCount; i++) { 1044 view->AddLine(BPoint(frame.left, pos), 1045 BPoint(frame.left + 5, pos), darken2); 1046 view->AddLine(BPoint(frame.left, pos + 1), 1047 BPoint(frame.left + 5, pos + 1), lightenmax); 1048 1049 pos += factor; 1050 } 1051 } 1052 1053 view->EndLineArray(); 1054 } 1055 1056 pos = _MinPosition(); 1057 1058 if (fHashMarks & B_HASH_MARKS_BOTTOM) { 1059 1060 view->BeginLineArray(hashMarkCount * 2); 1061 1062 if (fOrientation == B_HORIZONTAL) { 1063 for (int32 i = 0; i < hashMarkCount; i++) { 1064 view->AddLine(BPoint(pos, frame.bottom - 5), 1065 BPoint(pos, frame.bottom), darken2); 1066 view->AddLine(BPoint(pos + 1, frame.bottom - 5), 1067 BPoint(pos + 1, frame.bottom), lightenmax); 1068 1069 pos += factor; 1070 } 1071 } else { 1072 for (int32 i = 0; i < hashMarkCount; i++) { 1073 view->AddLine(BPoint(frame.right - 5, pos), 1074 BPoint(frame.right, pos), darken2); 1075 view->AddLine(BPoint(frame.right - 5, pos + 1), 1076 BPoint(frame.right, pos + 1), lightenmax); 1077 1078 pos += factor; 1079 } 1080 } 1081 1082 view->EndLineArray(); 1083 } 1084 } 1085 1086 1087 void 1088 BSlider::DrawThumb() 1089 { 1090 if (Style() == B_BLOCK_THUMB) 1091 _DrawBlockThumb(); 1092 else 1093 _DrawTriangleThumb(); 1094 } 1095 1096 1097 void 1098 BSlider::DrawFocusMark() 1099 { 1100 if (!IsFocus()) 1101 return; 1102 1103 OffscreenView()->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 1104 1105 BRect frame = ThumbFrame(); 1106 1107 if (fStyle == B_BLOCK_THUMB) { 1108 frame.left += 2.0f; 1109 frame.top += 2.0f; 1110 frame.right -= 3.0f; 1111 frame.bottom -= 3.0f; 1112 OffscreenView()->StrokeRect(frame); 1113 } else { 1114 if (fOrientation == B_HORIZONTAL) { 1115 OffscreenView()->StrokeLine(BPoint(frame.left, frame.bottom + 2.0f), 1116 BPoint(frame.right, frame.bottom + 2.0f)); 1117 } else { 1118 OffscreenView()->StrokeLine(BPoint(frame.left - 2.0f, frame.top), 1119 BPoint(frame.left - 2.0f, frame.bottom)); 1120 } 1121 } 1122 } 1123 1124 1125 void 1126 BSlider::DrawText() 1127 { 1128 BRect bounds(Bounds()); 1129 BView *view = OffscreenView(); 1130 1131 rgb_color base = LowColor(); 1132 uint32 flags = 0; 1133 if (be_control_look == NULL) { 1134 if (IsEnabled()) { 1135 view->SetHighColor(0, 0, 0); 1136 } else { 1137 view->SetHighColor(tint_color(LowColor(), B_DISABLED_LABEL_TINT)); 1138 } 1139 } else 1140 flags = be_control_look->Flags(this); 1141 1142 font_height fontHeight; 1143 GetFontHeight(&fontHeight); 1144 if (Orientation() == B_HORIZONTAL) { 1145 if (Label()) { 1146 if (be_control_look == NULL) { 1147 view->DrawString(Label(), 1148 BPoint(0.0, ceilf(fontHeight.ascent))); 1149 } else { 1150 be_control_look->DrawLabel(view, Label(), base, flags, 1151 BPoint(0.0, ceilf(fontHeight.ascent))); 1152 } 1153 } 1154 1155 // the update text is updated in SetValue() only 1156 if (fUpdateText != NULL) { 1157 if (be_control_look == NULL) { 1158 view->DrawString(fUpdateText, BPoint(bounds.right 1159 - StringWidth(fUpdateText), ceilf(fontHeight.ascent))); 1160 } else { 1161 be_control_look->DrawLabel(view, fUpdateText, base, flags, 1162 BPoint(bounds.right - StringWidth(fUpdateText), 1163 ceilf(fontHeight.ascent))); 1164 } 1165 } 1166 1167 if (fMinLimitLabel) { 1168 if (be_control_look == NULL) { 1169 view->DrawString(fMinLimitLabel, BPoint(0.0, bounds.bottom 1170 - fontHeight.descent)); 1171 } else { 1172 be_control_look->DrawLabel(view, fMinLimitLabel, base, flags, 1173 BPoint(0.0, bounds.bottom - fontHeight.descent)); 1174 } 1175 } 1176 1177 if (fMaxLimitLabel) { 1178 if (be_control_look == NULL) { 1179 view->DrawString(fMaxLimitLabel, BPoint(bounds.right 1180 - StringWidth(fMaxLimitLabel), bounds.bottom 1181 - fontHeight.descent)); 1182 } else { 1183 be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags, 1184 BPoint(bounds.right - StringWidth(fMaxLimitLabel), 1185 bounds.bottom - fontHeight.descent)); 1186 } 1187 } 1188 } else { 1189 float lineHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent) 1190 + ceilf(fontHeight.leading); 1191 float baseLine = ceilf(fontHeight.ascent); 1192 1193 if (Label()) { 1194 if (be_control_look == NULL) { 1195 view->DrawString(Label(), BPoint((bounds.Width() 1196 - StringWidth(Label())) / 2.0, baseLine)); 1197 } else { 1198 be_control_look->DrawLabel(view, Label(), base, flags, 1199 BPoint((bounds.Width() - StringWidth(Label())) / 2.0, 1200 baseLine)); 1201 } 1202 baseLine += lineHeight; 1203 } 1204 1205 if (fMaxLimitLabel) { 1206 if (be_control_look == NULL) { 1207 view->DrawString(fMaxLimitLabel, BPoint((bounds.Width() 1208 - StringWidth(fMaxLimitLabel)) / 2.0, baseLine)); 1209 } else { 1210 be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags, 1211 BPoint((bounds.Width() 1212 - StringWidth(fMaxLimitLabel)) / 2.0, baseLine)); 1213 } 1214 } 1215 1216 baseLine = bounds.bottom - ceilf(fontHeight.descent); 1217 1218 if (fMinLimitLabel) { 1219 if (be_control_look == NULL) { 1220 view->DrawString(fMinLimitLabel, BPoint((bounds.Width() 1221 - StringWidth(fMinLimitLabel)) / 2.0, baseLine)); 1222 } else { 1223 be_control_look->DrawLabel(view, fMinLimitLabel, base, flags, 1224 BPoint((bounds.Width() 1225 - StringWidth(fMinLimitLabel)) / 2.0, baseLine)); 1226 } 1227 baseLine -= lineHeight; 1228 } 1229 1230 if (fUpdateText != NULL) { 1231 if (be_control_look == NULL) { 1232 view->DrawString(fUpdateText, BPoint((bounds.Width() 1233 - StringWidth(fUpdateText)) / 2.0, baseLine)); 1234 } else { 1235 be_control_look->DrawLabel(view, fUpdateText, base, flags, 1236 BPoint((bounds.Width() 1237 - StringWidth(fUpdateText)) / 2.0, baseLine)); 1238 } 1239 } 1240 } 1241 } 1242 1243 1244 // #pragma mark - 1245 1246 1247 const char* 1248 BSlider::UpdateText() const 1249 { 1250 return NULL; 1251 } 1252 1253 1254 void 1255 BSlider::UpdateTextChanged() 1256 { 1257 // update text label 1258 float oldWidth = 0.0; 1259 if (fUpdateText != NULL) 1260 oldWidth = StringWidth(fUpdateText); 1261 1262 const char* oldUpdateText = fUpdateText; 1263 fUpdateText = UpdateText(); 1264 bool updateTextOnOff = (fUpdateText == NULL && oldUpdateText != NULL) 1265 || (fUpdateText != NULL && oldUpdateText == NULL); 1266 1267 float newWidth = 0.0; 1268 if (fUpdateText != NULL) 1269 newWidth = StringWidth(fUpdateText); 1270 1271 float width = ceilf(max_c(newWidth, oldWidth)) + 2.0f; 1272 if (width != 0) { 1273 font_height fontHeight; 1274 GetFontHeight(&fontHeight); 1275 1276 float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1277 float lineHeight = height + ceilf(fontHeight.leading); 1278 BRect invalid(Bounds()); 1279 if (fOrientation == B_HORIZONTAL) 1280 invalid = BRect(invalid.right - width, 0, invalid.right, height); 1281 else { 1282 if (!updateTextOnOff) { 1283 invalid.left = (invalid.left + invalid.right - width) / 2; 1284 invalid.right = invalid.left + width; 1285 if (fMinLimitLabel) 1286 invalid.bottom -= lineHeight; 1287 invalid.top = invalid.bottom - height; 1288 } 1289 } 1290 Invalidate(invalid); 1291 } 1292 1293 float oldMaxUpdateTextWidth = fMaxUpdateTextWidth; 1294 fMaxUpdateTextWidth = MaxUpdateTextWidth(); 1295 if (oldMaxUpdateTextWidth != fMaxUpdateTextWidth) 1296 InvalidateLayout(); 1297 } 1298 1299 1300 BRect 1301 BSlider::BarFrame() const 1302 { 1303 BRect frame(Bounds()); 1304 1305 font_height fontHeight; 1306 GetFontHeight(&fontHeight); 1307 1308 float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1309 float leading = ceilf(fontHeight.leading); 1310 1311 float thumbInset; 1312 if (fStyle == B_BLOCK_THUMB) 1313 thumbInset = 8.0; 1314 else 1315 thumbInset = 7.0; 1316 1317 if (Orientation() == B_HORIZONTAL) { 1318 frame.left = thumbInset; 1319 frame.top = 6.0 + (Label() || fUpdateText ? textHeight + 4.0 : 0.0); 1320 frame.right -= thumbInset; 1321 frame.bottom = frame.top + fBarThickness; 1322 } else { 1323 frame.left = floorf((frame.Width() - fBarThickness) / 2.0); 1324 frame.top = thumbInset; 1325 if (Label()) 1326 frame.top += textHeight; 1327 if (fMaxLimitLabel) { 1328 frame.top += textHeight; 1329 if (Label()) 1330 frame.top += leading; 1331 } 1332 1333 frame.right = frame.left + fBarThickness; 1334 frame.bottom = frame.bottom - thumbInset; 1335 if (fMinLimitLabel) 1336 frame.bottom -= textHeight; 1337 if (fUpdateText) { 1338 frame.bottom -= textHeight; 1339 if (fMinLimitLabel) 1340 frame.bottom -= leading; 1341 } 1342 } 1343 1344 return frame; 1345 } 1346 1347 1348 BRect 1349 BSlider::HashMarksFrame() const 1350 { 1351 BRect frame(BarFrame()); 1352 1353 if (fOrientation == B_HORIZONTAL) { 1354 frame.top -= 6.0; 1355 frame.bottom += 6.0; 1356 } else { 1357 frame.left -= 6.0; 1358 frame.right += 6.0; 1359 } 1360 1361 return frame; 1362 } 1363 1364 1365 BRect 1366 BSlider::ThumbFrame() const 1367 { 1368 // TODO: The slider looks really ugly and broken when it is too little. 1369 // I would suggest using BarFrame() here to get the top and bottom coords 1370 // and spread them further apart for the thumb 1371 1372 BRect frame = Bounds(); 1373 1374 font_height fontHeight; 1375 GetFontHeight(&fontHeight); 1376 1377 float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1378 1379 if (fStyle == B_BLOCK_THUMB) { 1380 if (Orientation() == B_HORIZONTAL) { 1381 frame.left = floorf(Position() * (_MaxPosition() 1382 - _MinPosition()) + _MinPosition()) - 8; 1383 frame.top = 2 + (Label() || fUpdateText ? textHeight + 4 : 0); 1384 frame.right = frame.left + 17; 1385 frame.bottom = frame.top + fBarThickness + 7; 1386 } else { 1387 frame.left = floor((frame.Width() - fBarThickness) / 2) - 4; 1388 frame.top = floorf(Position() * (_MaxPosition() 1389 - _MinPosition()) + _MinPosition()) - 8; 1390 frame.right = frame.left + fBarThickness + 7; 1391 frame.bottom = frame.top + 17; 1392 } 1393 } else { 1394 if (Orientation() == B_HORIZONTAL) { 1395 frame.left = floorf(Position() * (_MaxPosition() 1396 - _MinPosition()) + _MinPosition()) - 6; 1397 frame.right = frame.left + 12; 1398 frame.top = 3 + fBarThickness + (Label() ? textHeight + 4 : 0); 1399 frame.bottom = frame.top + 8; 1400 } else { 1401 frame.left = floorf((frame.Width() + fBarThickness) / 2) - 3; 1402 frame.top = floorf(Position() * (_MaxPosition() 1403 - _MinPosition())) + _MinPosition() - 6; 1404 frame.right = frame.left + 8; 1405 frame.bottom = frame.top + 12; 1406 } 1407 } 1408 1409 return frame; 1410 } 1411 1412 1413 void 1414 BSlider::SetFlags(uint32 flags) 1415 { 1416 BControl::SetFlags(flags); 1417 } 1418 1419 1420 void 1421 BSlider::SetResizingMode(uint32 mode) 1422 { 1423 BControl::SetResizingMode(mode); 1424 } 1425 1426 1427 void 1428 BSlider::GetPreferredSize(float* _width, float* _height) 1429 { 1430 BSize preferredSize = PreferredSize(); 1431 1432 if (Orientation() == B_HORIZONTAL) { 1433 if (_width != NULL) { 1434 // NOTE: For compatibility reasons, a horizontal BSlider 1435 // never shrinks horizontally. This only affects applications 1436 // which do not use the new layout system. 1437 *_width = max_c(Bounds().Width(), preferredSize.width); 1438 } 1439 1440 if (_height != NULL) 1441 *_height = preferredSize.height; 1442 } else { 1443 if (_width != NULL) 1444 *_width = preferredSize.width; 1445 1446 if (_height != NULL) { 1447 // NOTE: Similarly, a vertical BSlider never shrinks 1448 // vertically. This only affects applications which do not 1449 // use the new layout system. 1450 *_height = max_c(Bounds().Height(), preferredSize.height); 1451 } 1452 } 1453 } 1454 1455 1456 void 1457 BSlider::ResizeToPreferred() 1458 { 1459 BControl::ResizeToPreferred(); 1460 } 1461 1462 1463 status_t 1464 BSlider::Invoke(BMessage* message) 1465 { 1466 return BControl::Invoke(message); 1467 } 1468 1469 1470 BHandler* 1471 BSlider::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 1472 int32 command, const char *property) 1473 { 1474 return BControl::ResolveSpecifier(message, index, specifier, command, 1475 property); 1476 } 1477 1478 1479 status_t 1480 BSlider::GetSupportedSuites(BMessage* message) 1481 { 1482 return BControl::GetSupportedSuites(message); 1483 } 1484 1485 1486 void 1487 BSlider::SetModificationMessage(BMessage* message) 1488 { 1489 delete fModificationMessage; 1490 fModificationMessage = message; 1491 } 1492 1493 1494 BMessage* 1495 BSlider::ModificationMessage() const 1496 { 1497 return fModificationMessage; 1498 } 1499 1500 1501 void 1502 BSlider::SetSnoozeAmount(int32 snoozeTime) 1503 { 1504 if (snoozeTime < 10000) 1505 snoozeTime = 10000; 1506 else if (snoozeTime > 1000000) 1507 snoozeTime = 1000000; 1508 1509 fSnoozeAmount = snoozeTime; 1510 } 1511 1512 1513 int32 1514 BSlider::SnoozeAmount() const 1515 { 1516 return fSnoozeAmount; 1517 } 1518 1519 1520 void 1521 BSlider::SetKeyIncrementValue(int32 incrementValue) 1522 { 1523 fKeyIncrementValue = incrementValue; 1524 } 1525 1526 1527 int32 1528 BSlider::KeyIncrementValue() const 1529 { 1530 return fKeyIncrementValue; 1531 } 1532 1533 1534 void 1535 BSlider::SetHashMarkCount(int32 hashMarkCount) 1536 { 1537 fHashMarkCount = hashMarkCount; 1538 Invalidate(); 1539 } 1540 1541 1542 int32 1543 BSlider::HashMarkCount() const 1544 { 1545 return fHashMarkCount; 1546 } 1547 1548 1549 void 1550 BSlider::SetHashMarks(hash_mark_location where) 1551 { 1552 fHashMarks = where; 1553 // TODO: enable if the hashmark look is influencing the control size! 1554 // InvalidateLayout(); 1555 Invalidate(); 1556 } 1557 1558 1559 hash_mark_location 1560 BSlider::HashMarks() const 1561 { 1562 return fHashMarks; 1563 } 1564 1565 1566 void 1567 BSlider::SetStyle(thumb_style style) 1568 { 1569 fStyle = style; 1570 InvalidateLayout(); 1571 Invalidate(); 1572 } 1573 1574 1575 thumb_style 1576 BSlider::Style() const 1577 { 1578 return fStyle; 1579 } 1580 1581 1582 void 1583 BSlider::SetBarColor(rgb_color barColor) 1584 { 1585 fBarColor = barColor; 1586 Invalidate(BarFrame()); 1587 } 1588 1589 1590 rgb_color 1591 BSlider::BarColor() const 1592 { 1593 return fBarColor; 1594 } 1595 1596 1597 void 1598 BSlider::UseFillColor(bool useFill, const rgb_color* barColor) 1599 { 1600 fUseFillColor = useFill; 1601 1602 if (useFill && barColor) 1603 fFillColor = *barColor; 1604 1605 Invalidate(BarFrame()); 1606 } 1607 1608 1609 bool 1610 BSlider::FillColor(rgb_color* barColor) const 1611 { 1612 if (barColor && fUseFillColor) 1613 *barColor = fFillColor; 1614 1615 return fUseFillColor; 1616 } 1617 1618 1619 BView* 1620 BSlider::OffscreenView() const 1621 { 1622 #if USE_OFF_SCREEN_VIEW 1623 return fOffScreenView; 1624 #else 1625 return (BView*)this; 1626 #endif 1627 } 1628 1629 1630 orientation 1631 BSlider::Orientation() const 1632 { 1633 return fOrientation; 1634 } 1635 1636 1637 void 1638 BSlider::SetOrientation(orientation posture) 1639 { 1640 if (fOrientation == posture) 1641 return; 1642 1643 fOrientation = posture; 1644 InvalidateLayout(); 1645 Invalidate(); 1646 } 1647 1648 1649 float 1650 BSlider::BarThickness() const 1651 { 1652 return fBarThickness; 1653 } 1654 1655 1656 void 1657 BSlider::SetBarThickness(float thickness) 1658 { 1659 if (thickness < 1.0) 1660 thickness = 1.0; 1661 else 1662 thickness = roundf(thickness); 1663 1664 if (thickness != fBarThickness) { 1665 // calculate invalid barframe and extend by hashmark size 1666 float hInset = 0.0; 1667 float vInset = 0.0; 1668 if (fOrientation == B_HORIZONTAL) 1669 vInset = -6.0; 1670 else 1671 hInset = -6.0; 1672 BRect invalid = BarFrame().InsetByCopy(hInset, vInset) | ThumbFrame(); 1673 1674 fBarThickness = thickness; 1675 1676 invalid = invalid | BarFrame().InsetByCopy(hInset, vInset) 1677 | ThumbFrame(); 1678 Invalidate(invalid); 1679 InvalidateLayout(); 1680 } 1681 } 1682 1683 1684 void 1685 BSlider::SetFont(const BFont *font, uint32 properties) 1686 { 1687 BControl::SetFont(font, properties); 1688 1689 #if USE_OFF_SCREEN_VIEW 1690 if (fOffScreenView && fOffScreenBits) { 1691 if (fOffScreenBits->Lock()) { 1692 fOffScreenView->SetFont(font, properties); 1693 fOffScreenBits->Unlock(); 1694 } 1695 } 1696 #endif 1697 1698 InvalidateLayout(); 1699 } 1700 1701 1702 void 1703 BSlider::SetLimits(int32 minimum, int32 maximum) 1704 { 1705 if (minimum <= maximum) { 1706 fMinValue = minimum; 1707 fMaxValue = maximum; 1708 1709 int32 value = Value(); 1710 value = max_c(minimum, value); 1711 value = min_c(maximum, value); 1712 1713 if (value != Value()) { 1714 SetValue(value); 1715 } 1716 } 1717 } 1718 1719 1720 float 1721 BSlider::MaxUpdateTextWidth() 1722 { 1723 // very simplistic implementation that assumes the string will be widest 1724 // at the maximum value 1725 int32 value = Value(); 1726 SetValueNoUpdate(fMaxValue); 1727 float width = StringWidth(UpdateText()); 1728 SetValueNoUpdate(value); 1729 // in case the derived class uses a fixed buffer, the contents 1730 // should be reset for the old value 1731 UpdateText(); 1732 return width; 1733 } 1734 1735 1736 // #pragma mark - layout related 1737 1738 1739 BSize 1740 BSlider::MinSize() 1741 { 1742 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 1743 _ValidateMinSize()); 1744 } 1745 1746 1747 BSize 1748 BSlider::MaxSize() 1749 { 1750 BSize maxSize = _ValidateMinSize(); 1751 if (fOrientation == B_HORIZONTAL) 1752 maxSize.width = B_SIZE_UNLIMITED; 1753 else 1754 maxSize.height = B_SIZE_UNLIMITED; 1755 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize); 1756 } 1757 1758 1759 BSize 1760 BSlider::PreferredSize() 1761 { 1762 BSize preferredSize = _ValidateMinSize(); 1763 if (fOrientation == B_HORIZONTAL) 1764 preferredSize.width = max_c(100.0, preferredSize.width); 1765 else 1766 preferredSize.height = max_c(100.0, preferredSize.height); 1767 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), preferredSize); 1768 } 1769 1770 1771 void 1772 BSlider::LayoutInvalidated(bool descendants) 1773 { 1774 // invalidate cached preferred size 1775 fMinSize.Set(-1, -1); 1776 } 1777 1778 1779 // #pragma mark - private 1780 1781 void 1782 BSlider::_DrawBlockThumb() 1783 { 1784 BRect frame = ThumbFrame(); 1785 BView *view = OffscreenView(); 1786 1787 if (be_control_look != NULL) { 1788 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1789 uint32 flags = be_control_look->Flags(this); 1790 be_control_look->DrawSliderThumb(view, frame, frame, base, flags, 1791 fOrientation); 1792 return; 1793 } 1794 1795 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 1796 rgb_color lighten2; 1797 rgb_color lighten1; 1798 rgb_color darken2; 1799 rgb_color darken3; 1800 rgb_color darkenmax; 1801 1802 if (IsEnabled()) { 1803 lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT); 1804 lighten1 = no_tint; 1805 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 1806 darken3 = tint_color(no_tint, B_DARKEN_3_TINT); 1807 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 1808 } else { 1809 lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT); 1810 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT); 1811 darken2 = tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); 1812 darken3 = tint_color(no_tint, B_DARKEN_1_TINT); 1813 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT); 1814 } 1815 1816 // blank background for shadow 1817 // ToDo: this also draws over the hash marks (though it's not *that* noticeable) 1818 view->SetHighColor(no_tint); 1819 view->StrokeLine(BPoint(frame.left, frame.top), 1820 BPoint(frame.left, frame.top)); 1821 1822 BRect barFrame = BarFrame(); 1823 if (barFrame.right >= frame.right) { 1824 // leave out barFrame from shadow background clearing 1825 view->StrokeLine(BPoint(frame.right, frame.top), 1826 BPoint(frame.right, barFrame.top - 1.0f)); 1827 view->StrokeLine(BPoint(frame.right, barFrame.bottom + 1.0f), 1828 BPoint(frame.right, frame.bottom)); 1829 } else { 1830 view->StrokeLine(BPoint(frame.right, frame.top), 1831 BPoint(frame.right, frame.bottom)); 1832 } 1833 1834 view->StrokeLine(BPoint(frame.left, frame.bottom), 1835 BPoint(frame.right - 1.0f, frame.bottom)); 1836 view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f), 1837 BPoint(frame.left, frame.bottom - 1.0f)); 1838 view->StrokeLine(BPoint(frame.right - 1.0f, frame.top), 1839 BPoint(frame.right - 1.0f, frame.top)); 1840 1841 // Outline (top, left) 1842 view->SetHighColor(darken3); 1843 view->StrokeLine(BPoint(frame.left, frame.bottom - 2.0f), 1844 BPoint(frame.left, frame.top + 1.0f)); 1845 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 1846 BPoint(frame.right - 2.0f, frame.top)); 1847 1848 // Shadow 1849 view->SetHighColor(0, 0, 0, IsEnabled() ? 100 : 50); 1850 view->SetDrawingMode(B_OP_ALPHA); 1851 view->StrokeLine(BPoint(frame.right, frame.top + 2.0f), 1852 BPoint(frame.right, frame.bottom - 1.0f)); 1853 view->StrokeLine(BPoint(frame.left + 2.0f, frame.bottom), 1854 BPoint(frame.right - 1.0f, frame.bottom)); 1855 1856 view->SetDrawingMode(B_OP_COPY); 1857 view->SetHighColor(darken3); 1858 view->StrokeLine(BPoint(frame.right - 1.0f, frame.bottom - 1.0f), 1859 BPoint(frame.right - 1.0f, frame.bottom - 1.0f)); 1860 1861 1862 // First bevel 1863 frame.InsetBy(1.0f, 1.0f); 1864 1865 view->SetHighColor(darkenmax); 1866 view->StrokeLine(BPoint(frame.left, frame.bottom), 1867 BPoint(frame.right - 1.0f, frame.bottom)); 1868 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 1869 BPoint(frame.right, frame.top)); 1870 1871 view->SetHighColor(lighten2); 1872 view->StrokeLine(BPoint(frame.left, frame.top), 1873 BPoint(frame.left, frame.bottom - 1.0f)); 1874 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 1875 BPoint(frame.right - 1.0f, frame.top)); 1876 1877 frame.InsetBy(1.0f, 1.0f); 1878 1879 view->FillRect(BRect(frame.left, frame.top, frame.right - 1.0f, frame.bottom - 1.0f)); 1880 1881 // Second bevel and center dots 1882 view->SetHighColor(darken2); 1883 view->StrokeLine(BPoint(frame.left, frame.bottom), 1884 BPoint(frame.right, frame.bottom)); 1885 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 1886 BPoint(frame.right, frame.top)); 1887 1888 if (Orientation() == B_HORIZONTAL) { 1889 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 2.0f), 1890 BPoint(frame.left + 6.0f, frame.top + 2.0f)); 1891 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 4.0f), 1892 BPoint(frame.left + 6.0f, frame.top + 4.0f)); 1893 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f), 1894 BPoint(frame.left + 6.0f, frame.top + 6.0f)); 1895 } else { 1896 view->StrokeLine(BPoint(frame.left + 2.0f, frame.top + 6.0f), 1897 BPoint(frame.left + 2.0f, frame.top + 6.0f)); 1898 view->StrokeLine(BPoint(frame.left + 4.0f, frame.top + 6.0f), 1899 BPoint(frame.left + 4.0f, frame.top + 6.0f)); 1900 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f), 1901 BPoint(frame.left + 6.0f, frame.top + 6.0f)); 1902 } 1903 1904 frame.InsetBy(1.0f, 1.0f); 1905 1906 // Third bevel 1907 view->SetHighColor(lighten1); 1908 view->StrokeLine(BPoint(frame.left, frame.bottom), 1909 BPoint(frame.right, frame.bottom)); 1910 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 1911 BPoint(frame.right, frame.top)); 1912 } 1913 1914 1915 void 1916 BSlider::_DrawTriangleThumb() 1917 { 1918 BRect frame = ThumbFrame(); 1919 BView *view = OffscreenView(); 1920 1921 if (be_control_look != NULL) { 1922 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1923 uint32 flags = be_control_look->Flags(this); 1924 be_control_look->DrawSliderTriangle(view, frame, frame, base, flags, 1925 fOrientation); 1926 return; 1927 } 1928 1929 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 1930 rgb_color lightenmax; 1931 rgb_color lighten1; 1932 rgb_color darken2; 1933 rgb_color darken3; 1934 rgb_color darkenmax; 1935 1936 if (IsEnabled()) { 1937 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 1938 lighten1 = no_tint; 1939 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 1940 darken3 = tint_color(no_tint, B_DARKEN_3_TINT); 1941 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 1942 } else { 1943 lightenmax = tint_color(no_tint, B_LIGHTEN_2_TINT); 1944 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT); 1945 darken2 = tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2); 1946 darken3 = tint_color(no_tint, B_DARKEN_1_TINT); 1947 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT); 1948 } 1949 1950 if (Orientation() == B_HORIZONTAL) { 1951 view->SetHighColor(lighten1); 1952 view->FillTriangle( 1953 BPoint(frame.left + 1, frame.bottom - 3), 1954 BPoint((frame.left + frame.right) / 2, frame.top + 1), 1955 BPoint(frame.right - 1, frame.bottom - 3)); 1956 1957 view->SetHighColor(no_tint); 1958 view->StrokeLine(BPoint(frame.right - 2, frame.bottom - 3), 1959 BPoint(frame.left + 3, frame.bottom - 3)); 1960 1961 view->SetHighColor(darkenmax); 1962 view->StrokeLine(BPoint(frame.left, frame.bottom - 1), 1963 BPoint(frame.right, frame.bottom - 1)); 1964 view->StrokeLine(BPoint(frame.right, frame.bottom - 2), 1965 BPoint((frame.left + frame.right) / 2, frame.top)); 1966 1967 view->SetHighColor(darken2); 1968 view->StrokeLine(BPoint(frame.right - 1, frame.bottom - 2), 1969 BPoint(frame.left + 1, frame.bottom - 2)); 1970 view->SetHighColor(darken3); 1971 view->StrokeLine(BPoint(frame.left, frame.bottom - 2), 1972 BPoint((frame.left + frame.right) / 2 - 1, frame.top + 1)); 1973 1974 view->SetHighColor(lightenmax); 1975 view->StrokeLine(BPoint(frame.left + 2, frame.bottom - 3), 1976 BPoint((frame.left + frame.right) / 2, frame.top + 1)); 1977 1978 // Shadow 1979 view->SetHighColor(0, 0, 0, IsEnabled() ? 80 : 40); 1980 view->SetDrawingMode(B_OP_ALPHA); 1981 view->StrokeLine(BPoint(frame.left + 1, frame.bottom), 1982 BPoint(frame.right, frame.bottom)); 1983 } else { 1984 view->SetHighColor(lighten1); 1985 view->FillTriangle( 1986 BPoint(frame.left, (frame.top + frame.bottom) / 2), 1987 BPoint(frame.right - 1, frame.top + 1), 1988 BPoint(frame.right - 1, frame.bottom - 1)); 1989 1990 view->SetHighColor(darkenmax); 1991 view->StrokeLine(BPoint(frame.right - 1, frame.top), 1992 BPoint(frame.right - 1, frame.bottom)); 1993 view->StrokeLine(BPoint(frame.right - 1, frame.bottom), 1994 BPoint(frame.right - 2, frame.bottom)); 1995 1996 view->SetHighColor(darken2); 1997 view->StrokeLine(BPoint(frame.right - 2, frame.top + 2), 1998 BPoint(frame.right - 2, frame.bottom - 1)); 1999 view->StrokeLine( 2000 BPoint(frame.left, (frame.top + frame.bottom) / 2), 2001 BPoint(frame.right - 2, frame.top)); 2002 view->SetHighColor(darken3); 2003 view->StrokeLine( 2004 BPoint(frame.left + 1, (frame.top + frame.bottom) / 2 + 1), 2005 BPoint(frame.right - 3, frame.bottom - 1)); 2006 2007 view->SetHighColor(lightenmax); 2008 view->StrokeLine( 2009 BPoint(frame.left + 1, (frame.top + frame.bottom) / 2), 2010 BPoint(frame.right - 2, frame.top + 1)); 2011 2012 // Shadow 2013 view->SetHighColor(0, 0, 0, IsEnabled() ? 80 : 40); 2014 view->SetDrawingMode(B_OP_ALPHA); 2015 view->StrokeLine(BPoint(frame.right, frame.top + 1), 2016 BPoint(frame.right, frame.bottom)); 2017 } 2018 2019 view->SetDrawingMode(B_OP_COPY); 2020 } 2021 2022 2023 BPoint 2024 BSlider::_Location() const 2025 { 2026 return fLocation; 2027 } 2028 2029 2030 void 2031 BSlider::_SetLocationForValue(int32 value) 2032 { 2033 BPoint loc; 2034 float range = (float)(fMaxValue - fMinValue); 2035 if (range == 0) 2036 range = 1; 2037 2038 float pos = (float)(value - fMinValue) / range * 2039 (_MaxPosition() - _MinPosition()); 2040 2041 if (fOrientation == B_HORIZONTAL) { 2042 loc.x = ceil(_MinPosition() + pos); 2043 loc.y = 0; 2044 } else { 2045 loc.x = 0; 2046 loc.y = floor(_MaxPosition() - pos); 2047 } 2048 fLocation = loc; 2049 } 2050 2051 2052 float 2053 BSlider::_MinPosition() const 2054 { 2055 if (fOrientation == B_HORIZONTAL) 2056 return BarFrame().left + 1.0f; 2057 2058 return BarFrame().bottom - 1.0f; 2059 } 2060 2061 2062 float 2063 BSlider::_MaxPosition() const 2064 { 2065 if (fOrientation == B_HORIZONTAL) 2066 return BarFrame().right - 1.0f; 2067 2068 return BarFrame().top + 1.0f; 2069 } 2070 2071 2072 BSize 2073 BSlider::_ValidateMinSize() 2074 { 2075 if (fMinSize.width >= 0) { 2076 // the preferred size is up to date 2077 return fMinSize; 2078 } 2079 2080 font_height fontHeight; 2081 GetFontHeight(&fontHeight); 2082 2083 float width = 0.0; 2084 float height = 0.0; 2085 2086 if (fMaxUpdateTextWidth < 0.0) 2087 fMaxUpdateTextWidth = MaxUpdateTextWidth(); 2088 2089 if (Orientation() == B_HORIZONTAL) { 2090 height = 12.0 + fBarThickness; 2091 int32 rows = 0; 2092 2093 float labelWidth = 0; 2094 int32 labelRows = 0; 2095 float labelSpacing = StringWidth("M") * 2; 2096 if (Label()) { 2097 labelWidth = StringWidth(Label()); 2098 labelRows = 1; 2099 } 2100 if (fMaxUpdateTextWidth > 0.0) { 2101 if (labelWidth > 0) 2102 labelWidth += labelSpacing; 2103 labelWidth += fMaxUpdateTextWidth; 2104 labelRows = 1; 2105 } 2106 rows += labelRows; 2107 2108 if (MinLimitLabel()) 2109 width = StringWidth(MinLimitLabel()); 2110 if (MaxLimitLabel()) { 2111 // some space between the labels 2112 if (MinLimitLabel()) 2113 width += labelSpacing; 2114 2115 width += StringWidth(MaxLimitLabel()); 2116 } 2117 2118 if (labelWidth > width) 2119 width = labelWidth; 2120 if (width < 32.0) 2121 width = 32.0; 2122 2123 if (MinLimitLabel() || MaxLimitLabel()) 2124 rows++; 2125 2126 height += rows * (ceilf(fontHeight.ascent) 2127 + ceilf(fontHeight.descent) + 4.0); 2128 } else { 2129 // B_VERTICAL 2130 width = 12.0 + fBarThickness; 2131 height = 32.0; 2132 2133 float lineHeightNoLeading = ceilf(fontHeight.ascent) 2134 + ceilf(fontHeight.descent); 2135 float lineHeight = lineHeightNoLeading + ceilf(fontHeight.leading); 2136 2137 // find largest label 2138 float labelWidth = 0; 2139 if (Label()) { 2140 labelWidth = StringWidth(Label()); 2141 height += lineHeightNoLeading; 2142 } 2143 if (MaxLimitLabel()) { 2144 labelWidth = max_c(labelWidth, StringWidth(MaxLimitLabel())); 2145 height += Label() ? lineHeight : lineHeightNoLeading; 2146 } 2147 if (MinLimitLabel()) { 2148 labelWidth = max_c(labelWidth, StringWidth(MinLimitLabel())); 2149 height += lineHeightNoLeading; 2150 } 2151 if (fMaxUpdateTextWidth > 0.0) { 2152 labelWidth = max_c(labelWidth, fMaxUpdateTextWidth); 2153 height += MinLimitLabel() ? lineHeight : lineHeightNoLeading; 2154 } 2155 2156 width = max_c(labelWidth, width); 2157 } 2158 2159 fMinSize.width = width; 2160 fMinSize.height = height; 2161 2162 ResetLayoutInvalidation(); 2163 2164 return fMinSize; 2165 } 2166 2167 2168 // #pragma mark - FBC padding 2169 2170 void BSlider::_ReservedSlider6() {} 2171 void BSlider::_ReservedSlider7() {} 2172 void BSlider::_ReservedSlider8() {} 2173 void BSlider::_ReservedSlider9() {} 2174 void BSlider::_ReservedSlider10() {} 2175 void BSlider::_ReservedSlider11() {} 2176 void BSlider::_ReservedSlider12() {} 2177 2178 2179 BSlider & 2180 BSlider::operator=(const BSlider &) 2181 { 2182 return *this; 2183 } 2184 2185 2186 // #pragma mark - BeOS compatibility 2187 2188 2189 #if __GNUC__ < 3 2190 2191 extern "C" void 2192 GetLimits__7BSliderPlT1(BSlider* slider, int32* minimum, int32* maximum) 2193 { 2194 slider->GetLimits(minimum, maximum); 2195 } 2196 2197 2198 extern "C" void 2199 _ReservedSlider4__7BSlider(BSlider *slider, int32 minimum, int32 maximum) 2200 { 2201 slider->BSlider::SetLimits(minimum, maximum); 2202 } 2203 2204 extern "C" float 2205 _ReservedSlider5__7BSlider(BSlider *slider) 2206 { 2207 return slider->BSlider::MaxUpdateTextWidth(); 2208 } 2209 2210 2211 extern "C" void 2212 _ReservedSlider1__7BSlider(BSlider* slider, orientation _orientation) 2213 { 2214 slider->BSlider::SetOrientation(_orientation); 2215 } 2216 2217 2218 extern "C" void 2219 _ReservedSlider2__7BSlider(BSlider* slider, float thickness) 2220 { 2221 slider->BSlider::SetBarThickness(thickness); 2222 } 2223 2224 2225 extern "C" void 2226 _ReservedSlider3__7BSlider(BSlider* slider, const BFont* font, 2227 uint32 properties) 2228 { 2229 slider->BSlider::SetFont(font, properties); 2230 } 2231 2232 2233 #endif // __GNUC__ < 3 2234 2235 2236 extern "C" void 2237 B_IF_GCC_2(InvalidateLayout__7BSliderb, _ZN7BSlider16InvalidateLayoutEb)( 2238 BView* view, bool descendants) 2239 { 2240 perform_data_layout_invalidated data; 2241 data.descendants = descendants; 2242 2243 view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data); 2244 } 2245 2246