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 1 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_INVALIDATE_LAYOUT: 337 { 338 perform_data_invalidate_layout* data 339 = (perform_data_invalidate_layout*)_data; 340 BSlider::InvalidateLayout(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 fLocation.Set(9.0f, 0.0f); 367 fUpdateText = UpdateText(); 368 369 #if USE_OFF_SCREEN_VIEW 370 BRect bounds(Bounds()); 371 372 if (!fOffScreenView) { 373 fOffScreenView = new BView(bounds, "", B_FOLLOW_ALL, B_WILL_DRAW); 374 375 BFont font; 376 GetFont(&font); 377 fOffScreenView->SetFont(&font); 378 } 379 380 if (!fOffScreenBits) { 381 fOffScreenBits = new BBitmap(bounds, B_RGBA32, true, false); 382 383 if (fOffScreenBits && fOffScreenView) 384 fOffScreenBits->AddChild(fOffScreenView); 385 386 } else if (fOffScreenView) 387 fOffScreenBits->AddChild(fOffScreenView); 388 #endif // USE_OFF_SCREEN_VIEW 389 390 BControl::AttachedToWindow(); 391 392 BView* view = OffscreenView(); 393 if (view && view->LockLooper()) { 394 view->SetViewColor(B_TRANSPARENT_COLOR); 395 view->SetLowColor(ViewColor()); 396 view->UnlockLooper(); 397 } 398 399 SetValue(Value()); 400 // makes sure the value is within valid bounds 401 } 402 403 404 void 405 BSlider::AllAttached() 406 { 407 BControl::AllAttached(); 408 } 409 410 411 void 412 BSlider::AllDetached() 413 { 414 BControl::AllDetached(); 415 } 416 417 418 void 419 BSlider::DetachedFromWindow() 420 { 421 BControl::DetachedFromWindow(); 422 423 #if USE_OFF_SCREEN_VIEW 424 if (fOffScreenBits) { 425 delete fOffScreenBits; 426 fOffScreenBits = NULL; 427 fOffScreenView = NULL; 428 } 429 #endif 430 } 431 432 433 void 434 BSlider::MessageReceived(BMessage *msg) 435 { 436 BControl::MessageReceived(msg); 437 } 438 439 440 void 441 BSlider::FrameMoved(BPoint new_position) 442 { 443 BControl::FrameMoved(new_position); 444 } 445 446 447 void 448 BSlider::FrameResized(float w,float h) 449 { 450 BControl::FrameResized(w, h); 451 452 BRect bounds(Bounds()); 453 454 if (bounds.right <= 0.0f || bounds.bottom <= 0.0f) 455 return; 456 457 #if USE_OFF_SCREEN_VIEW 458 if (fOffScreenBits) { 459 fOffScreenBits->RemoveChild(fOffScreenView); 460 delete fOffScreenBits; 461 462 fOffScreenView->ResizeTo(bounds.Width(), bounds.Height()); 463 464 fOffScreenBits = new BBitmap(Bounds(), B_RGBA32, true, false); 465 fOffScreenBits->AddChild(fOffScreenView); 466 } 467 #endif 468 469 Invalidate(); 470 } 471 472 473 void 474 BSlider::KeyDown(const char *bytes, int32 numBytes) 475 { 476 if (!IsEnabled() || IsHidden()) 477 return; 478 479 int32 newValue = Value(); 480 481 switch (bytes[0]) { 482 case B_LEFT_ARROW: 483 case B_DOWN_ARROW: { 484 newValue -= KeyIncrementValue(); 485 break; 486 } 487 case B_RIGHT_ARROW: 488 case B_UP_ARROW: { 489 newValue += KeyIncrementValue(); 490 break; 491 } 492 case B_HOME: 493 newValue = fMinValue; 494 break; 495 case B_END: 496 newValue = fMaxValue; 497 break; 498 default: 499 BControl::KeyDown(bytes, numBytes); 500 return; 501 } 502 503 if (newValue != Value()) { 504 SetValue(newValue); 505 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 506 } 507 } 508 509 510 /*! 511 Makes sure the \a point is within valid bounds. 512 Returns \c true if the relevant coordinate (depending on the orientation 513 of the slider) differs from \a comparePoint. 514 */ 515 bool 516 BSlider::_ConstrainPoint(BPoint& point, BPoint comparePoint) const 517 { 518 if (fOrientation == B_HORIZONTAL) { 519 if (point.x != comparePoint.x) { 520 if (point.x < _MinPosition()) 521 point.x = _MinPosition(); 522 else if (point.x > _MaxPosition()) 523 point.x = _MaxPosition(); 524 525 return true; 526 } 527 } else { 528 if (point.y != comparePoint.y) { 529 if (point.y > _MinPosition()) 530 point.y = _MinPosition(); 531 else if (point.y < _MaxPosition()) 532 point.y = _MaxPosition(); 533 534 return true; 535 } 536 } 537 538 return false; 539 } 540 541 542 void 543 BSlider::MouseDown(BPoint point) 544 { 545 if (!IsEnabled()) 546 return; 547 548 if (BarFrame().Contains(point) || ThumbFrame().Contains(point)) 549 fInitialLocation = _Location(); 550 551 uint32 buttons; 552 GetMouse(&point, &buttons, true); 553 554 _ConstrainPoint(point, fInitialLocation); 555 SetValue(ValueForPoint(point)); 556 557 if (_Location() != fInitialLocation) 558 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 559 560 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) { 561 SetTracking(true); 562 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); 563 } else { 564 // synchronous mouse tracking 565 BPoint prevPoint; 566 567 while (buttons) { 568 prevPoint = point; 569 570 snooze(SnoozeAmount()); 571 GetMouse(&point, &buttons, true); 572 573 if (_ConstrainPoint(point, prevPoint)) { 574 int32 value = ValueForPoint(point); 575 if (value != Value()) { 576 SetValue(value); 577 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 578 } 579 } 580 } 581 if (_Location() != fInitialLocation) 582 Invoke(); 583 } 584 } 585 586 587 void 588 BSlider::MouseUp(BPoint point) 589 { 590 if (IsTracking()) { 591 if (_Location() != fInitialLocation) 592 Invoke(); 593 594 SetTracking(false); 595 } else 596 BControl::MouseUp(point); 597 } 598 599 600 void 601 BSlider::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 602 { 603 if (IsTracking()) { 604 if (_ConstrainPoint(point, _Location())) { 605 int32 value = ValueForPoint(point); 606 if (value != Value()) { 607 SetValue(value); 608 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 609 } 610 } 611 } else 612 BControl::MouseMoved(point, transit, message); 613 } 614 615 616 void 617 BSlider::Pulse() 618 { 619 BControl::Pulse(); 620 } 621 622 623 void 624 BSlider::SetLabel(const char *label) 625 { 626 BControl::SetLabel(label); 627 } 628 629 630 void 631 BSlider::SetLimitLabels(const char *minLabel, const char *maxLabel) 632 { 633 free(fMinLimitLabel); 634 fMinLimitLabel = minLabel ? strdup(minLabel) : NULL; 635 636 free(fMaxLimitLabel); 637 fMaxLimitLabel = maxLabel ? strdup(maxLabel) : NULL; 638 639 InvalidateLayout(); 640 641 // TODO: This is for backwards compatibility and should 642 // probably be removed when breaking binary compatiblity. 643 // Applications like our own Mouse rely on this behavior. 644 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 645 ResizeToPreferred(); 646 647 Invalidate(); 648 } 649 650 651 const char* 652 BSlider::MinLimitLabel() const 653 { 654 return fMinLimitLabel; 655 } 656 657 658 const char* 659 BSlider::MaxLimitLabel() const 660 { 661 return fMaxLimitLabel; 662 } 663 664 665 void 666 BSlider::SetValue(int32 value) 667 { 668 if (value < fMinValue) 669 value = fMinValue; 670 if (value > fMaxValue) 671 value = fMaxValue; 672 673 if (value == Value()) 674 return; 675 676 BPoint loc; 677 float range = (float)(fMaxValue - fMinValue); 678 if (range == 0) 679 range = 1; 680 681 float pos = (float)(value - fMinValue) / range * 682 (_MaxPosition() - _MinPosition()); 683 684 if (fOrientation == B_HORIZONTAL) { 685 loc.x = ceil(_MinPosition() + pos); 686 loc.y = 0; 687 } else { 688 loc.x = 0; 689 loc.y = floor(_MaxPosition() - pos); 690 } 691 692 BRect oldThumbFrame = ThumbFrame(); 693 694 // While it would be enough to do this dependent on fUseFillColor, 695 // that doesn't work out if DrawBar() has been overridden by a sub class 696 if (fOrientation == B_HORIZONTAL) 697 oldThumbFrame.top = BarFrame().top; 698 else 699 oldThumbFrame.right = BarFrame().right; 700 701 _SetLocation(loc); 702 703 BControl::SetValueNoUpdate(value); 704 BRect invalid = oldThumbFrame | ThumbFrame(); 705 706 if (Style() == B_TRIANGLE_THUMB) { 707 // 1) we need to take care of pixels touched because of 708 // anti-aliasing 709 // 2) we need to update the region with the focus mark as well 710 // (a method BSlider::FocusMarkFrame() would be nice as well) 711 if (fOrientation == B_HORIZONTAL) { 712 if (IsFocus()) 713 invalid.bottom += 2; 714 invalid.InsetBy(-1, 0); 715 } else { 716 if (IsFocus()) 717 invalid.left -= 2; 718 invalid.InsetBy(0, -1); 719 } 720 } 721 722 Invalidate(invalid); 723 724 UpdateTextChanged(); 725 } 726 727 728 int32 729 BSlider::ValueForPoint(BPoint location) const 730 { 731 float min; 732 float max; 733 float position; 734 if (fOrientation == B_HORIZONTAL) { 735 min = _MinPosition(); 736 max = _MaxPosition(); 737 position = location.x; 738 } else { 739 max = _MinPosition(); 740 min = _MaxPosition(); 741 position = min + (max - location.y); 742 } 743 744 if (position < min) 745 position = min; 746 if (position > max) 747 position = max; 748 749 return (int32)roundf(((position - min) * (fMaxValue - fMinValue) / (max - min)) + fMinValue); 750 } 751 752 753 void 754 BSlider::SetPosition(float position) 755 { 756 if (position <= 0.0f) 757 BControl::SetValue(fMinValue); 758 else if (position >= 1.0f) 759 BControl::SetValue(fMaxValue); 760 else 761 BControl::SetValue((int32)(position * (fMaxValue - fMinValue) + fMinValue)); 762 } 763 764 765 float 766 BSlider::Position() const 767 { 768 float range = (float)(fMaxValue - fMinValue); 769 if (range == 0.0f) 770 range = 1.0f; 771 772 return (float)(Value() - fMinValue) / range; 773 } 774 775 776 void 777 BSlider::SetEnabled(bool on) 778 { 779 BControl::SetEnabled(on); 780 } 781 782 783 void 784 BSlider::GetLimits(int32 *minimum, int32 *maximum) const 785 { 786 if (minimum != NULL) 787 *minimum = fMinValue; 788 if (maximum != NULL) 789 *maximum = fMaxValue; 790 } 791 792 793 // #pragma mark - drawing 794 795 796 void 797 BSlider::Draw(BRect updateRect) 798 { 799 // clear out background 800 BRegion background(updateRect); 801 background.Exclude(BarFrame()); 802 803 // ToDo: the triangle thumb doesn't delete its background, so we still have 804 // to do it Note, this also creates a different behaviour for subclasses, 805 // depending on the thumb style - if possible this should be avoided. 806 if (Style() == B_BLOCK_THUMB) { 807 BRect thumbFrame = ThumbFrame(); 808 if (be_control_look != NULL) { 809 // fill background where shadow will be... 810 // TODO: Such drawint dependent behavior should be moved into 811 // BControlLook of course. 812 thumbFrame.right--; 813 thumbFrame.bottom--; 814 } 815 background.Exclude(thumbFrame); 816 } 817 818 #if USE_OFF_SCREEN_VIEW 819 if (!fOffScreenBits) 820 return; 821 822 if (fOffScreenBits->Lock()) { 823 fOffScreenView->SetViewColor(ViewColor()); 824 fOffScreenView->SetLowColor(ViewColor()); 825 #endif 826 827 if (background.Frame().IsValid()) 828 OffscreenView()->FillRegion(&background, B_SOLID_LOW); 829 830 #if USE_OFF_SCREEN_VIEW 831 fOffScreenView->Sync(); 832 fOffScreenBits->Unlock(); 833 } 834 #endif 835 836 DrawSlider(); 837 } 838 839 840 void 841 BSlider::DrawSlider() 842 { 843 if (LockLooper()) { 844 #if USE_OFF_SCREEN_VIEW 845 if (!fOffScreenBits) 846 return; 847 if (fOffScreenBits->Lock()) { 848 #endif 849 DrawBar(); 850 DrawHashMarks(); 851 DrawThumb(); 852 DrawFocusMark(); 853 DrawText(); 854 855 #if USE_OFF_SCREEN_VIEW 856 fOffScreenView->Sync(); 857 fOffScreenBits->Unlock(); 858 859 DrawBitmap(fOffScreenBits, B_ORIGIN); 860 } 861 #endif 862 UnlockLooper(); 863 } 864 } 865 866 867 void 868 BSlider::DrawBar() 869 { 870 BRect frame = BarFrame(); 871 BView *view = OffscreenView(); 872 873 if (be_control_look != NULL) { 874 uint32 flags = be_control_look->Flags(this); 875 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 876 rgb_color rightFillColor = fBarColor; 877 rgb_color leftFillColor = fUseFillColor ? fFillColor : fBarColor; 878 be_control_look->DrawSliderBar(view, frame, frame, base, leftFillColor, 879 rightFillColor, Position(), flags, fOrientation); 880 return; 881 } 882 883 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 884 rgb_color lightenmax; 885 rgb_color darken1; 886 rgb_color darken2; 887 rgb_color darkenmax; 888 889 rgb_color barColor; 890 rgb_color fillColor; 891 892 if (IsEnabled()) { 893 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 894 darken1 = tint_color(no_tint, B_DARKEN_1_TINT); 895 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 896 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 897 barColor = fBarColor; 898 fillColor = fFillColor; 899 } else { 900 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 901 darken1 = no_tint; 902 darken2 = tint_color(no_tint, B_DARKEN_1_TINT); 903 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT); 904 905 barColor.red = (fBarColor.red + no_tint.red) / 2; 906 barColor.green = (fBarColor.green + no_tint.green) / 2; 907 barColor.blue = (fBarColor.blue + no_tint.blue) / 2; 908 barColor.alpha = 255; 909 910 fillColor.red = (fFillColor.red + no_tint.red) / 2; 911 fillColor.green = (fFillColor.green + no_tint.green) / 2; 912 fillColor.blue = (fFillColor.blue + no_tint.blue) / 2; 913 fillColor.alpha = 255; 914 } 915 916 // exclude the block thumb from the bar filling 917 918 BRect lowerFrame = frame.InsetByCopy(1, 1); 919 lowerFrame.top++; 920 lowerFrame.left++; 921 BRect upperFrame = lowerFrame; 922 BRect thumbFrame; 923 924 if (Style() == B_BLOCK_THUMB) { 925 thumbFrame = ThumbFrame(); 926 927 if (fOrientation == B_HORIZONTAL) { 928 lowerFrame.right = thumbFrame.left; 929 upperFrame.left = thumbFrame.right; 930 } else { 931 lowerFrame.top = thumbFrame.bottom; 932 upperFrame.bottom = thumbFrame.top; 933 } 934 } else if (fUseFillColor) { 935 if (fOrientation == B_HORIZONTAL) { 936 lowerFrame.right = floor(lowerFrame.left - 1 + Position() 937 * (lowerFrame.Width() + 1)); 938 upperFrame.left = lowerFrame.right; 939 } else { 940 lowerFrame.top = floor(lowerFrame.bottom + 1 - Position() 941 * (lowerFrame.Height() + 1)); 942 upperFrame.bottom = lowerFrame.top; 943 } 944 } 945 946 view->SetHighColor(barColor); 947 view->FillRect(upperFrame); 948 949 if (Style() == B_BLOCK_THUMB || fUseFillColor) { 950 if (fUseFillColor) 951 view->SetHighColor(fillColor); 952 view->FillRect(lowerFrame); 953 } 954 955 if (Style() == B_BLOCK_THUMB) { 956 // We don't want to stroke the lines over the thumb 957 958 PushState(); 959 960 BRegion region; 961 GetClippingRegion(®ion); 962 region.Exclude(thumbFrame); 963 ConstrainClippingRegion(®ion); 964 } 965 966 view->SetHighColor(darken1); 967 view->StrokeLine(BPoint(frame.left, frame.top), 968 BPoint(frame.left + 1.0f, frame.top)); 969 view->StrokeLine(BPoint(frame.left, frame.bottom), 970 BPoint(frame.left + 1.0f, frame.bottom)); 971 view->StrokeLine(BPoint(frame.right - 1.0f, frame.top), 972 BPoint(frame.right, frame.top)); 973 974 view->SetHighColor(darken2); 975 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 976 BPoint(frame.right - 1.0f, frame.top)); 977 view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f), 978 BPoint(frame.left, frame.top + 1.0f)); 979 980 view->SetHighColor(lightenmax); 981 view->StrokeLine(BPoint(frame.left + 1.0f, frame.bottom), 982 BPoint(frame.right, frame.bottom)); 983 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 984 BPoint(frame.right, frame.top + 1.0f)); 985 986 frame.InsetBy(1.0f, 1.0f); 987 988 view->SetHighColor(darkenmax); 989 view->StrokeLine(BPoint(frame.left, frame.bottom), 990 BPoint(frame.left, frame.top)); 991 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 992 BPoint(frame.right, frame.top)); 993 994 if (Style() == B_BLOCK_THUMB) 995 PopState(); 996 } 997 998 999 void 1000 BSlider::DrawHashMarks() 1001 { 1002 if (fHashMarks == B_HASH_MARKS_NONE) 1003 return; 1004 1005 BRect frame = HashMarksFrame(); 1006 BView* view = OffscreenView(); 1007 1008 if (be_control_look) { 1009 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1010 uint32 flags = be_control_look->Flags(this); 1011 be_control_look->DrawSliderHashMarks(view, frame, frame, base, 1012 fHashMarkCount, fHashMarks, flags, fOrientation); 1013 return; 1014 } 1015 1016 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 1017 rgb_color lightenmax; 1018 rgb_color darken2; 1019 1020 if (IsEnabled()) { 1021 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 1022 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 1023 } else { 1024 lightenmax = tint_color(no_tint, B_LIGHTEN_2_TINT); 1025 darken2 = tint_color(no_tint, B_DARKEN_1_TINT); 1026 } 1027 1028 float pos = _MinPosition(); 1029 int32 hashMarkCount = max_c(fHashMarkCount, 2); 1030 // draw at least two hashmarks at min/max if 1031 // fHashMarks != B_HASH_MARKS_NONE 1032 float factor = (_MaxPosition() - pos) / (hashMarkCount - 1); 1033 1034 if (fHashMarks & B_HASH_MARKS_TOP) { 1035 1036 view->BeginLineArray(hashMarkCount * 2); 1037 1038 if (fOrientation == B_HORIZONTAL) { 1039 for (int32 i = 0; i < hashMarkCount; i++) { 1040 view->AddLine(BPoint(pos, frame.top), 1041 BPoint(pos, frame.top + 5), darken2); 1042 view->AddLine(BPoint(pos + 1, frame.top), 1043 BPoint(pos + 1, frame.top + 5), lightenmax); 1044 1045 pos += factor; 1046 } 1047 } else { 1048 for (int32 i = 0; i < hashMarkCount; i++) { 1049 view->AddLine(BPoint(frame.left, pos), 1050 BPoint(frame.left + 5, pos), darken2); 1051 view->AddLine(BPoint(frame.left, pos + 1), 1052 BPoint(frame.left + 5, pos + 1), lightenmax); 1053 1054 pos += factor; 1055 } 1056 } 1057 1058 view->EndLineArray(); 1059 } 1060 1061 pos = _MinPosition(); 1062 1063 if (fHashMarks & B_HASH_MARKS_BOTTOM) { 1064 1065 view->BeginLineArray(hashMarkCount * 2); 1066 1067 if (fOrientation == B_HORIZONTAL) { 1068 for (int32 i = 0; i < hashMarkCount; i++) { 1069 view->AddLine(BPoint(pos, frame.bottom - 5), 1070 BPoint(pos, frame.bottom), darken2); 1071 view->AddLine(BPoint(pos + 1, frame.bottom - 5), 1072 BPoint(pos + 1, frame.bottom), lightenmax); 1073 1074 pos += factor; 1075 } 1076 } else { 1077 for (int32 i = 0; i < hashMarkCount; i++) { 1078 view->AddLine(BPoint(frame.right - 5, pos), 1079 BPoint(frame.right, pos), darken2); 1080 view->AddLine(BPoint(frame.right - 5, pos + 1), 1081 BPoint(frame.right, pos + 1), lightenmax); 1082 1083 pos += factor; 1084 } 1085 } 1086 1087 view->EndLineArray(); 1088 } 1089 } 1090 1091 1092 void 1093 BSlider::DrawThumb() 1094 { 1095 if (Style() == B_BLOCK_THUMB) 1096 _DrawBlockThumb(); 1097 else 1098 _DrawTriangleThumb(); 1099 } 1100 1101 1102 void 1103 BSlider::DrawFocusMark() 1104 { 1105 if (!IsFocus()) 1106 return; 1107 1108 OffscreenView()->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 1109 1110 BRect frame = ThumbFrame(); 1111 1112 if (fStyle == B_BLOCK_THUMB) { 1113 frame.left += 2.0f; 1114 frame.top += 2.0f; 1115 frame.right -= 3.0f; 1116 frame.bottom -= 3.0f; 1117 OffscreenView()->StrokeRect(frame); 1118 } else { 1119 if (fOrientation == B_HORIZONTAL) { 1120 OffscreenView()->StrokeLine(BPoint(frame.left, frame.bottom + 2.0f), 1121 BPoint(frame.right, frame.bottom + 2.0f)); 1122 } else { 1123 OffscreenView()->StrokeLine(BPoint(frame.left - 2.0f, frame.top), 1124 BPoint(frame.left - 2.0f, frame.bottom)); 1125 } 1126 } 1127 } 1128 1129 1130 void 1131 BSlider::DrawText() 1132 { 1133 BRect bounds(Bounds()); 1134 BView *view = OffscreenView(); 1135 1136 if (IsEnabled()) { 1137 view->SetHighColor(0, 0, 0); 1138 } else { 1139 view->SetHighColor(tint_color(LowColor(), B_DISABLED_LABEL_TINT)); 1140 } 1141 1142 font_height fontHeight; 1143 GetFontHeight(&fontHeight); 1144 if (Orientation() == B_HORIZONTAL) { 1145 if (Label()) 1146 view->DrawString(Label(), BPoint(0.0, ceilf(fontHeight.ascent))); 1147 1148 // the update text is updated in SetValue() only 1149 if (fUpdateText != NULL) { 1150 view->DrawString(fUpdateText, BPoint(bounds.right 1151 - StringWidth(fUpdateText), ceilf(fontHeight.ascent))); 1152 } 1153 1154 if (fMinLimitLabel) { 1155 view->DrawString(fMinLimitLabel, BPoint(0.0, bounds.bottom 1156 - fontHeight.descent)); 1157 } 1158 1159 if (fMaxLimitLabel) { 1160 view->DrawString(fMaxLimitLabel, BPoint(bounds.right 1161 - StringWidth(fMaxLimitLabel), bounds.bottom 1162 - fontHeight.descent)); 1163 } 1164 } else { 1165 float lineHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent) 1166 + ceilf(fontHeight.leading); 1167 float baseLine = ceilf(fontHeight.ascent); 1168 1169 if (Label()) { 1170 view->DrawString(Label(), BPoint((bounds.Width() 1171 - StringWidth(Label())) / 2.0, baseLine)); 1172 baseLine += lineHeight; 1173 } 1174 1175 if (fMaxLimitLabel) { 1176 view->DrawString(fMaxLimitLabel, BPoint((bounds.Width() 1177 - StringWidth(fMaxLimitLabel)) / 2.0, baseLine)); 1178 } 1179 1180 baseLine = bounds.bottom - ceilf(fontHeight.descent); 1181 1182 if (fMinLimitLabel) { 1183 view->DrawString(fMinLimitLabel, BPoint((bounds.Width() 1184 - StringWidth(fMinLimitLabel)) / 2.0, baseLine)); 1185 baseLine -= lineHeight; 1186 } 1187 1188 if (fUpdateText != NULL) { 1189 view->DrawString(fUpdateText, BPoint((bounds.Width() 1190 - StringWidth(fUpdateText)) / 2.0, baseLine)); 1191 } 1192 } 1193 } 1194 1195 1196 // #pragma mark - 1197 1198 1199 const char* 1200 BSlider::UpdateText() const 1201 { 1202 return NULL; 1203 } 1204 1205 1206 void 1207 BSlider::UpdateTextChanged() 1208 { 1209 // update text label 1210 float oldWidth = 0.0; 1211 if (fUpdateText != NULL) 1212 oldWidth = StringWidth(fUpdateText); 1213 1214 const char* oldUpdateText = fUpdateText; 1215 fUpdateText = UpdateText(); 1216 bool updateTextOnOff = (fUpdateText == NULL && oldUpdateText != NULL) 1217 || (fUpdateText != NULL && oldUpdateText == NULL); 1218 1219 float newWidth = 0.0; 1220 if (fUpdateText != NULL) 1221 newWidth = StringWidth(fUpdateText); 1222 1223 float width = ceilf(max_c(newWidth, oldWidth)) + 2.0f; 1224 if (width != 0) { 1225 font_height fontHeight; 1226 GetFontHeight(&fontHeight); 1227 1228 float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1229 float lineHeight = height + ceilf(fontHeight.leading); 1230 BRect invalid(Bounds()); 1231 if (fOrientation == B_HORIZONTAL) 1232 invalid = BRect(invalid.right - width, 0, invalid.right, height); 1233 else { 1234 if (!updateTextOnOff) { 1235 invalid.left = (invalid.left + invalid.right - width) / 2; 1236 invalid.right = invalid.left + width; 1237 if (fMinLimitLabel) 1238 invalid.bottom -= lineHeight; 1239 invalid.top = invalid.bottom - height; 1240 } 1241 } 1242 Invalidate(invalid); 1243 } 1244 1245 float oldMaxUpdateTextWidth = fMaxUpdateTextWidth; 1246 fMaxUpdateTextWidth = MaxUpdateTextWidth(); 1247 if (oldMaxUpdateTextWidth != fMaxUpdateTextWidth) 1248 InvalidateLayout(); 1249 } 1250 1251 1252 BRect 1253 BSlider::BarFrame() const 1254 { 1255 BRect frame(Bounds()); 1256 1257 font_height fontHeight; 1258 GetFontHeight(&fontHeight); 1259 1260 float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1261 float leading = ceilf(fontHeight.leading); 1262 1263 float thumbInset; 1264 if (fStyle == B_BLOCK_THUMB) 1265 thumbInset = 8.0; 1266 else 1267 thumbInset = 7.0; 1268 1269 if (Orientation() == B_HORIZONTAL) { 1270 frame.left = thumbInset; 1271 frame.top = 6.0 + (Label() || fUpdateText ? textHeight + 4.0 : 0.0); 1272 frame.right -= thumbInset; 1273 frame.bottom = frame.top + fBarThickness; 1274 } else { 1275 frame.left = floorf((frame.Width() - fBarThickness) / 2.0); 1276 frame.top = thumbInset; 1277 if (Label()) 1278 frame.top += textHeight; 1279 if (fMaxLimitLabel) { 1280 frame.top += textHeight; 1281 if (Label()) 1282 frame.top += leading; 1283 } 1284 1285 frame.right = frame.left + fBarThickness; 1286 frame.bottom = frame.bottom - thumbInset; 1287 if (fMinLimitLabel) 1288 frame.bottom -= textHeight; 1289 if (fUpdateText) { 1290 frame.bottom -= textHeight; 1291 if (fMinLimitLabel) 1292 frame.bottom -= leading; 1293 } 1294 } 1295 1296 return frame; 1297 } 1298 1299 1300 BRect 1301 BSlider::HashMarksFrame() const 1302 { 1303 BRect frame(BarFrame()); 1304 1305 if (fOrientation == B_HORIZONTAL) { 1306 frame.top -= 6.0; 1307 frame.bottom += 6.0; 1308 } else { 1309 frame.left -= 6.0; 1310 frame.right += 6.0; 1311 } 1312 1313 return frame; 1314 } 1315 1316 1317 BRect 1318 BSlider::ThumbFrame() const 1319 { 1320 // TODO: The slider looks really ugly and broken when it is too little. 1321 // I would suggest using BarFrame() here to get the top and bottom coords 1322 // and spread them further apart for the thumb 1323 1324 BRect frame = Bounds(); 1325 1326 font_height fontHeight; 1327 GetFontHeight(&fontHeight); 1328 1329 float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1330 1331 if (fStyle == B_BLOCK_THUMB) { 1332 if (Orientation() == B_HORIZONTAL) { 1333 frame.left = floorf(Position() * (_MaxPosition() 1334 - _MinPosition()) + _MinPosition()) - 8; 1335 frame.top = 2 + (Label() || fUpdateText ? textHeight + 4 : 0); 1336 frame.right = frame.left + 17; 1337 frame.bottom = frame.top + fBarThickness + 7; 1338 } else { 1339 frame.left = floor((frame.Width() - fBarThickness) / 2) - 4; 1340 frame.top = floorf(Position() * (_MaxPosition() 1341 - _MinPosition()) + _MinPosition()) - 8; 1342 frame.right = frame.left + fBarThickness + 7; 1343 frame.bottom = frame.top + 17; 1344 } 1345 } else { 1346 if (Orientation() == B_HORIZONTAL) { 1347 frame.left = floorf(Position() * (_MaxPosition() 1348 - _MinPosition()) + _MinPosition()) - 6; 1349 frame.right = frame.left + 12; 1350 frame.top = 3 + fBarThickness + (Label() ? textHeight + 4 : 0); 1351 frame.bottom = frame.top + 8; 1352 } else { 1353 frame.left = floorf((frame.Width() + fBarThickness) / 2) - 3; 1354 frame.top = floorf(Position() * (_MaxPosition() 1355 - _MinPosition())) + _MinPosition() - 6; 1356 frame.right = frame.left + 8; 1357 frame.bottom = frame.top + 12; 1358 } 1359 } 1360 1361 return frame; 1362 } 1363 1364 1365 void 1366 BSlider::SetFlags(uint32 flags) 1367 { 1368 BControl::SetFlags(flags); 1369 } 1370 1371 1372 void 1373 BSlider::SetResizingMode(uint32 mode) 1374 { 1375 BControl::SetResizingMode(mode); 1376 } 1377 1378 1379 void 1380 BSlider::GetPreferredSize(float* _width, float* _height) 1381 { 1382 BSize preferredSize = PreferredSize(); 1383 1384 if (_width) { 1385 // *_width = preferredSize.width; 1386 // NOTE: For compatibility reasons, the BSlider never shrinks 1387 // horizontally. This only affects applications which do not 1388 // use the new layout system. 1389 *_width = max_c(Bounds().Width(), preferredSize.width); 1390 } 1391 1392 if (_height) 1393 *_height = preferredSize.height; 1394 } 1395 1396 1397 void 1398 BSlider::ResizeToPreferred() 1399 { 1400 BControl::ResizeToPreferred(); 1401 } 1402 1403 1404 status_t 1405 BSlider::Invoke(BMessage* message) 1406 { 1407 return BControl::Invoke(message); 1408 } 1409 1410 1411 BHandler* 1412 BSlider::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 1413 int32 command, const char *property) 1414 { 1415 return BControl::ResolveSpecifier(message, index, specifier, command, 1416 property); 1417 } 1418 1419 1420 status_t 1421 BSlider::GetSupportedSuites(BMessage* message) 1422 { 1423 return BControl::GetSupportedSuites(message); 1424 } 1425 1426 1427 void 1428 BSlider::SetModificationMessage(BMessage* message) 1429 { 1430 delete fModificationMessage; 1431 fModificationMessage = message; 1432 } 1433 1434 1435 BMessage* 1436 BSlider::ModificationMessage() const 1437 { 1438 return fModificationMessage; 1439 } 1440 1441 1442 void 1443 BSlider::SetSnoozeAmount(int32 snoozeTime) 1444 { 1445 if (snoozeTime < 10000) 1446 snoozeTime = 10000; 1447 else if (snoozeTime > 1000000) 1448 snoozeTime = 1000000; 1449 1450 fSnoozeAmount = snoozeTime; 1451 } 1452 1453 1454 int32 1455 BSlider::SnoozeAmount() const 1456 { 1457 return fSnoozeAmount; 1458 } 1459 1460 1461 void 1462 BSlider::SetKeyIncrementValue(int32 incrementValue) 1463 { 1464 fKeyIncrementValue = incrementValue; 1465 } 1466 1467 1468 int32 1469 BSlider::KeyIncrementValue() const 1470 { 1471 return fKeyIncrementValue; 1472 } 1473 1474 1475 void 1476 BSlider::SetHashMarkCount(int32 hashMarkCount) 1477 { 1478 fHashMarkCount = hashMarkCount; 1479 Invalidate(); 1480 } 1481 1482 1483 int32 1484 BSlider::HashMarkCount() const 1485 { 1486 return fHashMarkCount; 1487 } 1488 1489 1490 void 1491 BSlider::SetHashMarks(hash_mark_location where) 1492 { 1493 fHashMarks = where; 1494 // TODO: enable if the hashmark look is influencing the control size! 1495 // InvalidateLayout(); 1496 Invalidate(); 1497 } 1498 1499 1500 hash_mark_location 1501 BSlider::HashMarks() const 1502 { 1503 return fHashMarks; 1504 } 1505 1506 1507 void 1508 BSlider::SetStyle(thumb_style style) 1509 { 1510 fStyle = style; 1511 InvalidateLayout(); 1512 Invalidate(); 1513 } 1514 1515 1516 thumb_style 1517 BSlider::Style() const 1518 { 1519 return fStyle; 1520 } 1521 1522 1523 void 1524 BSlider::SetBarColor(rgb_color barColor) 1525 { 1526 fBarColor = barColor; 1527 Invalidate(BarFrame()); 1528 } 1529 1530 1531 rgb_color 1532 BSlider::BarColor() const 1533 { 1534 return fBarColor; 1535 } 1536 1537 1538 void 1539 BSlider::UseFillColor(bool useFill, const rgb_color* barColor) 1540 { 1541 fUseFillColor = useFill; 1542 1543 if (useFill && barColor) 1544 fFillColor = *barColor; 1545 1546 Invalidate(BarFrame()); 1547 } 1548 1549 1550 bool 1551 BSlider::FillColor(rgb_color* barColor) const 1552 { 1553 if (barColor && fUseFillColor) 1554 *barColor = fFillColor; 1555 1556 return fUseFillColor; 1557 } 1558 1559 1560 BView* 1561 BSlider::OffscreenView() const 1562 { 1563 #if USE_OFF_SCREEN_VIEW 1564 return fOffScreenView; 1565 #else 1566 return (BView*)this; 1567 #endif 1568 } 1569 1570 1571 orientation 1572 BSlider::Orientation() const 1573 { 1574 return fOrientation; 1575 } 1576 1577 1578 void 1579 BSlider::SetOrientation(orientation posture) 1580 { 1581 if (fOrientation == posture) 1582 return; 1583 1584 fOrientation = posture; 1585 InvalidateLayout(); 1586 Invalidate(); 1587 } 1588 1589 1590 float 1591 BSlider::BarThickness() const 1592 { 1593 return fBarThickness; 1594 } 1595 1596 1597 void 1598 BSlider::SetBarThickness(float thickness) 1599 { 1600 if (thickness < 1.0) 1601 thickness = 1.0; 1602 else 1603 thickness = roundf(thickness); 1604 1605 if (thickness != fBarThickness) { 1606 // calculate invalid barframe and extend by hashmark size 1607 float hInset = 0.0; 1608 float vInset = 0.0; 1609 if (fOrientation == B_HORIZONTAL) 1610 vInset = -6.0; 1611 else 1612 hInset = -6.0; 1613 BRect invalid = BarFrame().InsetByCopy(hInset, vInset) | ThumbFrame(); 1614 1615 fBarThickness = thickness; 1616 1617 invalid = invalid | BarFrame().InsetByCopy(hInset, vInset) 1618 | ThumbFrame(); 1619 Invalidate(invalid); 1620 InvalidateLayout(); 1621 } 1622 } 1623 1624 1625 void 1626 BSlider::SetFont(const BFont *font, uint32 properties) 1627 { 1628 BControl::SetFont(font, properties); 1629 1630 #if USE_OFF_SCREEN_VIEW 1631 if (fOffScreenView && fOffScreenBits) { 1632 if (fOffScreenBits->Lock()) { 1633 fOffScreenView->SetFont(font, properties); 1634 fOffScreenBits->Unlock(); 1635 } 1636 } 1637 #endif 1638 1639 InvalidateLayout(); 1640 } 1641 1642 1643 void 1644 BSlider::SetLimits(int32 minimum, int32 maximum) 1645 { 1646 if (minimum <= maximum) { 1647 fMinValue = minimum; 1648 fMaxValue = maximum; 1649 1650 int32 value = Value(); 1651 value = max_c(minimum, value); 1652 value = min_c(maximum, value); 1653 1654 if (value != Value()) { 1655 SetValue(value); 1656 } 1657 } 1658 } 1659 1660 1661 float 1662 BSlider::MaxUpdateTextWidth() 1663 { 1664 // very simplistic implementation that assumes the string will be widest 1665 // at the maximum value 1666 int32 value = Value(); 1667 SetValueNoUpdate(fMaxValue); 1668 float width = StringWidth(UpdateText()); 1669 SetValueNoUpdate(value); 1670 // in case the derived class uses a fixed buffer, the contents 1671 // should be reset for the old value 1672 UpdateText(); 1673 return width; 1674 } 1675 1676 1677 // #pragma mark - layout related 1678 1679 1680 void 1681 BSlider::InvalidateLayout(bool descendants) 1682 { 1683 // invalidate cached preferred size 1684 fMinSize.Set(-1, -1); 1685 1686 BControl::InvalidateLayout(descendants); 1687 } 1688 1689 1690 BSize 1691 BSlider::MinSize() 1692 { 1693 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 1694 _ValidateMinSize()); 1695 } 1696 1697 1698 BSize 1699 BSlider::MaxSize() 1700 { 1701 BSize maxSize = _ValidateMinSize(); 1702 if (fOrientation == B_HORIZONTAL) 1703 maxSize.width = B_SIZE_UNLIMITED; 1704 else 1705 maxSize.height = B_SIZE_UNLIMITED; 1706 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize); 1707 } 1708 1709 1710 BSize 1711 BSlider::PreferredSize() 1712 { 1713 BSize preferredSize = _ValidateMinSize(); 1714 if (fOrientation == B_HORIZONTAL) 1715 preferredSize.width = max_c(100.0, preferredSize.width); 1716 else 1717 preferredSize.height = max_c(100.0, preferredSize.height); 1718 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), preferredSize); 1719 } 1720 1721 1722 // #pragma mark - private 1723 1724 void 1725 BSlider::_DrawBlockThumb() 1726 { 1727 BRect frame = ThumbFrame(); 1728 BView *view = OffscreenView(); 1729 1730 if (be_control_look != NULL) { 1731 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1732 uint32 flags = be_control_look->Flags(this); 1733 be_control_look->DrawSliderThumb(view, frame, frame, base, flags, 1734 fOrientation); 1735 return; 1736 } 1737 1738 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 1739 rgb_color lighten2; 1740 rgb_color lighten1; 1741 rgb_color darken2; 1742 rgb_color darken3; 1743 rgb_color darkenmax; 1744 1745 if (IsEnabled()) { 1746 lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT); 1747 lighten1 = no_tint; 1748 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 1749 darken3 = tint_color(no_tint, B_DARKEN_3_TINT); 1750 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 1751 } else { 1752 lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT); 1753 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT); 1754 darken2 = tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); 1755 darken3 = tint_color(no_tint, B_DARKEN_1_TINT); 1756 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT); 1757 } 1758 1759 // blank background for shadow 1760 // ToDo: this also draws over the hash marks (though it's not *that* noticeable) 1761 view->SetHighColor(no_tint); 1762 view->StrokeLine(BPoint(frame.left, frame.top), 1763 BPoint(frame.left, frame.top)); 1764 1765 BRect barFrame = BarFrame(); 1766 if (barFrame.right >= frame.right) { 1767 // leave out barFrame from shadow background clearing 1768 view->StrokeLine(BPoint(frame.right, frame.top), 1769 BPoint(frame.right, barFrame.top - 1.0f)); 1770 view->StrokeLine(BPoint(frame.right, barFrame.bottom + 1.0f), 1771 BPoint(frame.right, frame.bottom)); 1772 } else { 1773 view->StrokeLine(BPoint(frame.right, frame.top), 1774 BPoint(frame.right, frame.bottom)); 1775 } 1776 1777 view->StrokeLine(BPoint(frame.left, frame.bottom), 1778 BPoint(frame.right - 1.0f, frame.bottom)); 1779 view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f), 1780 BPoint(frame.left, frame.bottom - 1.0f)); 1781 view->StrokeLine(BPoint(frame.right - 1.0f, frame.top), 1782 BPoint(frame.right - 1.0f, frame.top)); 1783 1784 // Outline (top, left) 1785 view->SetHighColor(darken3); 1786 view->StrokeLine(BPoint(frame.left, frame.bottom - 2.0f), 1787 BPoint(frame.left, frame.top + 1.0f)); 1788 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 1789 BPoint(frame.right - 2.0f, frame.top)); 1790 1791 // Shadow 1792 view->SetHighColor(0, 0, 0, IsEnabled() ? 100 : 50); 1793 view->SetDrawingMode(B_OP_ALPHA); 1794 view->StrokeLine(BPoint(frame.right, frame.top + 2.0f), 1795 BPoint(frame.right, frame.bottom - 1.0f)); 1796 view->StrokeLine(BPoint(frame.left + 2.0f, frame.bottom), 1797 BPoint(frame.right - 1.0f, frame.bottom)); 1798 1799 view->SetDrawingMode(B_OP_COPY); 1800 view->SetHighColor(darken3); 1801 view->StrokeLine(BPoint(frame.right - 1.0f, frame.bottom - 1.0f), 1802 BPoint(frame.right - 1.0f, frame.bottom - 1.0f)); 1803 1804 1805 // First bevel 1806 frame.InsetBy(1.0f, 1.0f); 1807 1808 view->SetHighColor(darkenmax); 1809 view->StrokeLine(BPoint(frame.left, frame.bottom), 1810 BPoint(frame.right - 1.0f, frame.bottom)); 1811 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 1812 BPoint(frame.right, frame.top)); 1813 1814 view->SetHighColor(lighten2); 1815 view->StrokeLine(BPoint(frame.left, frame.top), 1816 BPoint(frame.left, frame.bottom - 1.0f)); 1817 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 1818 BPoint(frame.right - 1.0f, frame.top)); 1819 1820 frame.InsetBy(1.0f, 1.0f); 1821 1822 view->FillRect(BRect(frame.left, frame.top, frame.right - 1.0f, frame.bottom - 1.0f)); 1823 1824 // Second bevel and center dots 1825 view->SetHighColor(darken2); 1826 view->StrokeLine(BPoint(frame.left, frame.bottom), 1827 BPoint(frame.right, frame.bottom)); 1828 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 1829 BPoint(frame.right, frame.top)); 1830 1831 if (Orientation() == B_HORIZONTAL) { 1832 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 2.0f), 1833 BPoint(frame.left + 6.0f, frame.top + 2.0f)); 1834 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 4.0f), 1835 BPoint(frame.left + 6.0f, frame.top + 4.0f)); 1836 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f), 1837 BPoint(frame.left + 6.0f, frame.top + 6.0f)); 1838 } else { 1839 view->StrokeLine(BPoint(frame.left + 2.0f, frame.top + 6.0f), 1840 BPoint(frame.left + 2.0f, frame.top + 6.0f)); 1841 view->StrokeLine(BPoint(frame.left + 4.0f, frame.top + 6.0f), 1842 BPoint(frame.left + 4.0f, frame.top + 6.0f)); 1843 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f), 1844 BPoint(frame.left + 6.0f, frame.top + 6.0f)); 1845 } 1846 1847 frame.InsetBy(1.0f, 1.0f); 1848 1849 // Third bevel 1850 view->SetHighColor(lighten1); 1851 view->StrokeLine(BPoint(frame.left, frame.bottom), 1852 BPoint(frame.right, frame.bottom)); 1853 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 1854 BPoint(frame.right, frame.top)); 1855 } 1856 1857 1858 void 1859 BSlider::_DrawTriangleThumb() 1860 { 1861 BRect frame = ThumbFrame(); 1862 BView *view = OffscreenView(); 1863 1864 if (be_control_look != NULL) { 1865 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1866 uint32 flags = be_control_look->Flags(this); 1867 be_control_look->DrawSliderTriangle(view, frame, frame, base, flags, 1868 fOrientation); 1869 return; 1870 } 1871 1872 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 1873 rgb_color lightenmax; 1874 rgb_color lighten1; 1875 rgb_color darken2; 1876 rgb_color darken3; 1877 rgb_color darkenmax; 1878 1879 if (IsEnabled()) { 1880 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 1881 lighten1 = no_tint; 1882 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 1883 darken3 = tint_color(no_tint, B_DARKEN_3_TINT); 1884 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 1885 } else { 1886 lightenmax = tint_color(no_tint, B_LIGHTEN_2_TINT); 1887 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT); 1888 darken2 = tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2); 1889 darken3 = tint_color(no_tint, B_DARKEN_1_TINT); 1890 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT); 1891 } 1892 1893 if (Orientation() == B_HORIZONTAL) { 1894 view->SetHighColor(lighten1); 1895 view->FillTriangle( 1896 BPoint(frame.left + 1, frame.bottom - 3), 1897 BPoint((frame.left + frame.right) / 2, frame.top + 1), 1898 BPoint(frame.right - 1, frame.bottom - 3)); 1899 1900 view->SetHighColor(no_tint); 1901 view->StrokeLine(BPoint(frame.right - 2, frame.bottom - 3), 1902 BPoint(frame.left + 3, frame.bottom - 3)); 1903 1904 view->SetHighColor(darkenmax); 1905 view->StrokeLine(BPoint(frame.left, frame.bottom - 1), 1906 BPoint(frame.right, frame.bottom - 1)); 1907 view->StrokeLine(BPoint(frame.right, frame.bottom - 2), 1908 BPoint((frame.left + frame.right) / 2, frame.top)); 1909 1910 view->SetHighColor(darken2); 1911 view->StrokeLine(BPoint(frame.right - 1, frame.bottom - 2), 1912 BPoint(frame.left + 1, frame.bottom - 2)); 1913 view->SetHighColor(darken3); 1914 view->StrokeLine(BPoint(frame.left, frame.bottom - 2), 1915 BPoint((frame.left + frame.right) / 2 - 1, frame.top + 1)); 1916 1917 view->SetHighColor(lightenmax); 1918 view->StrokeLine(BPoint(frame.left + 2, frame.bottom - 3), 1919 BPoint((frame.left + frame.right) / 2, frame.top + 1)); 1920 1921 // Shadow 1922 view->SetHighColor(0, 0, 0, IsEnabled() ? 80 : 40); 1923 view->SetDrawingMode(B_OP_ALPHA); 1924 view->StrokeLine(BPoint(frame.left + 1, frame.bottom), 1925 BPoint(frame.right, frame.bottom)); 1926 } else { 1927 view->SetHighColor(lighten1); 1928 view->FillTriangle( 1929 BPoint(frame.left, (frame.top + frame.bottom) / 2), 1930 BPoint(frame.right - 1, frame.top + 1), 1931 BPoint(frame.right - 1, frame.bottom - 1)); 1932 1933 view->SetHighColor(darkenmax); 1934 view->StrokeLine(BPoint(frame.right - 1, frame.top), 1935 BPoint(frame.right - 1, frame.bottom)); 1936 view->StrokeLine(BPoint(frame.right - 1, frame.bottom), 1937 BPoint(frame.right - 2, frame.bottom)); 1938 1939 view->SetHighColor(darken2); 1940 view->StrokeLine(BPoint(frame.right - 2, frame.top + 2), 1941 BPoint(frame.right - 2, frame.bottom - 1)); 1942 view->StrokeLine( 1943 BPoint(frame.left, (frame.top + frame.bottom) / 2), 1944 BPoint(frame.right - 2, frame.top)); 1945 view->SetHighColor(darken3); 1946 view->StrokeLine( 1947 BPoint(frame.left + 1, (frame.top + frame.bottom) / 2 + 1), 1948 BPoint(frame.right - 3, frame.bottom - 1)); 1949 1950 view->SetHighColor(lightenmax); 1951 view->StrokeLine( 1952 BPoint(frame.left + 1, (frame.top + frame.bottom) / 2), 1953 BPoint(frame.right - 2, frame.top + 1)); 1954 1955 // Shadow 1956 view->SetHighColor(0, 0, 0, IsEnabled() ? 80 : 40); 1957 view->SetDrawingMode(B_OP_ALPHA); 1958 view->StrokeLine(BPoint(frame.right, frame.top + 1), 1959 BPoint(frame.right, frame.bottom)); 1960 } 1961 1962 view->SetDrawingMode(B_OP_COPY); 1963 } 1964 1965 1966 BPoint 1967 BSlider::_Location() const 1968 { 1969 return fLocation; 1970 } 1971 1972 1973 void 1974 BSlider::_SetLocation(BPoint p) 1975 { 1976 fLocation = p; 1977 } 1978 1979 1980 float 1981 BSlider::_MinPosition() const 1982 { 1983 if (fOrientation == B_HORIZONTAL) 1984 return BarFrame().left + 1.0f; 1985 1986 return BarFrame().bottom - 1.0f; 1987 } 1988 1989 1990 float 1991 BSlider::_MaxPosition() const 1992 { 1993 if (fOrientation == B_HORIZONTAL) 1994 return BarFrame().right - 1.0f; 1995 1996 return BarFrame().top + 1.0f; 1997 } 1998 1999 2000 BSize 2001 BSlider::_ValidateMinSize() 2002 { 2003 if (fMinSize.width >= 0) { 2004 // the preferred size is up to date 2005 return fMinSize; 2006 } 2007 2008 font_height fontHeight; 2009 GetFontHeight(&fontHeight); 2010 2011 float width = 0.0; 2012 float height = 0.0; 2013 2014 if (fMaxUpdateTextWidth < 0.0) 2015 fMaxUpdateTextWidth = MaxUpdateTextWidth(); 2016 2017 if (Orientation() == B_HORIZONTAL) { 2018 height = 12.0 + fBarThickness; 2019 int32 rows = 0; 2020 2021 float labelWidth = 0; 2022 int32 labelRows = 0; 2023 float labelSpacing = StringWidth("M") * 2; 2024 if (Label()) { 2025 labelWidth = StringWidth(Label()); 2026 labelRows = 1; 2027 } 2028 if (fMaxUpdateTextWidth > 0.0) { 2029 if (labelWidth > 0) 2030 labelWidth += labelSpacing; 2031 labelWidth += fMaxUpdateTextWidth; 2032 labelRows = 1; 2033 } 2034 rows += labelRows; 2035 2036 if (MinLimitLabel()) 2037 width = StringWidth(MinLimitLabel()); 2038 if (MaxLimitLabel()) { 2039 // some space between the labels 2040 if (MinLimitLabel()) 2041 width += labelSpacing; 2042 2043 width += StringWidth(MaxLimitLabel()); 2044 } 2045 2046 if (labelWidth > width) 2047 width = labelWidth; 2048 if (width < 32.0) 2049 width = 32.0; 2050 2051 if (MinLimitLabel() || MaxLimitLabel()) 2052 rows++; 2053 2054 height += rows * (ceilf(fontHeight.ascent) 2055 + ceilf(fontHeight.descent) + 4.0); 2056 } else { 2057 // B_VERTICAL 2058 width = 12.0 + fBarThickness; 2059 height = 32.0; 2060 2061 float lineHeightNoLeading = ceilf(fontHeight.ascent) 2062 + ceilf(fontHeight.descent); 2063 float lineHeight = lineHeightNoLeading + ceilf(fontHeight.leading); 2064 2065 // find largest label 2066 float labelWidth = 0; 2067 if (Label()) { 2068 labelWidth = StringWidth(Label()); 2069 height += lineHeightNoLeading; 2070 } 2071 if (MaxLimitLabel()) { 2072 labelWidth = max_c(labelWidth, StringWidth(MaxLimitLabel())); 2073 height += Label() ? lineHeight : lineHeightNoLeading; 2074 } 2075 if (MinLimitLabel()) { 2076 labelWidth = max_c(labelWidth, StringWidth(MinLimitLabel())); 2077 height += lineHeightNoLeading; 2078 } 2079 if (fMaxUpdateTextWidth > 0.0) { 2080 labelWidth = max_c(labelWidth, fMaxUpdateTextWidth); 2081 height += MinLimitLabel() ? lineHeight : lineHeightNoLeading; 2082 } 2083 2084 width = max_c(labelWidth, width); 2085 } 2086 2087 fMinSize.width = width; 2088 fMinSize.height = height; 2089 2090 return fMinSize; 2091 } 2092 2093 2094 // #pragma mark - FBC padding 2095 2096 void BSlider::_ReservedSlider6() {} 2097 void BSlider::_ReservedSlider7() {} 2098 void BSlider::_ReservedSlider8() {} 2099 void BSlider::_ReservedSlider9() {} 2100 void BSlider::_ReservedSlider10() {} 2101 void BSlider::_ReservedSlider11() {} 2102 void BSlider::_ReservedSlider12() {} 2103 2104 2105 BSlider & 2106 BSlider::operator=(const BSlider &) 2107 { 2108 return *this; 2109 } 2110 2111 2112 // #pragma mark - BeOS compatibility 2113 2114 2115 #if __GNUC__ < 3 2116 2117 extern "C" void 2118 GetLimits__7BSliderPlT1(BSlider* slider, int32* minimum, int32* maximum) 2119 { 2120 slider->GetLimits(minimum, maximum); 2121 } 2122 2123 2124 extern "C" void 2125 _ReservedSlider4__7BSlider(BSlider *slider, int32 minimum, int32 maximum) 2126 { 2127 slider->BSlider::SetLimits(minimum, maximum); 2128 } 2129 2130 extern "C" float 2131 _ReservedSlider5__7BSlider(BSlider *slider) 2132 { 2133 return slider->BSlider::MaxUpdateTextWidth(); 2134 } 2135 2136 2137 extern "C" void 2138 _ReservedSlider1__7BSlider(BSlider* slider, orientation _orientation) 2139 { 2140 slider->BSlider::SetOrientation(_orientation); 2141 } 2142 2143 2144 extern "C" void 2145 _ReservedSlider2__7BSlider(BSlider* slider, float thickness) 2146 { 2147 slider->BSlider::SetBarThickness(thickness); 2148 } 2149 2150 2151 extern "C" void 2152 _ReservedSlider3__7BSlider(BSlider* slider, const BFont* font, 2153 uint32 properties) 2154 { 2155 slider->BSlider::SetFont(font, properties); 2156 } 2157 2158 #endif // __GNUC__ < 3 2159