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