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