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 fLocation.Set(9.0f, 0.0f); 367 368 #if USE_OFF_SCREEN_VIEW 369 BRect bounds(Bounds()); 370 371 if (!fOffScreenView) { 372 fOffScreenView = new BView(bounds, "", B_FOLLOW_ALL, B_WILL_DRAW); 373 374 BFont font; 375 GetFont(&font); 376 fOffScreenView->SetFont(&font); 377 } 378 379 if (!fOffScreenBits) { 380 fOffScreenBits = new BBitmap(bounds, B_RGBA32, true, false); 381 382 if (fOffScreenBits && fOffScreenView) 383 fOffScreenBits->AddChild(fOffScreenView); 384 385 } else if (fOffScreenView) 386 fOffScreenBits->AddChild(fOffScreenView); 387 #endif // USE_OFF_SCREEN_VIEW 388 389 BControl::AttachedToWindow(); 390 391 BView* view = OffscreenView(); 392 if (view && view->LockLooper()) { 393 view->SetViewColor(B_TRANSPARENT_COLOR); 394 view->SetLowColor(LowColor()); 395 view->UnlockLooper(); 396 } 397 398 SetValue(Value()); 399 // makes sure the value is within valid bounds 400 UpdateTextChanged(); 401 } 402 403 404 void 405 BSlider::AllAttached() 406 { 407 BControl::AllAttached(); 408 } 409 410 411 void 412 BSlider::AllDetached() 413 { 414 BControl::AllDetached(); 415 } 416 417 418 void 419 BSlider::DetachedFromWindow() 420 { 421 BControl::DetachedFromWindow(); 422 423 #if USE_OFF_SCREEN_VIEW 424 if (fOffScreenBits) { 425 delete fOffScreenBits; 426 fOffScreenBits = NULL; 427 fOffScreenView = NULL; 428 } 429 #endif 430 } 431 432 433 void 434 BSlider::MessageReceived(BMessage *msg) 435 { 436 BControl::MessageReceived(msg); 437 } 438 439 440 void 441 BSlider::FrameMoved(BPoint new_position) 442 { 443 BControl::FrameMoved(new_position); 444 } 445 446 447 void 448 BSlider::FrameResized(float w,float h) 449 { 450 BControl::FrameResized(w, h); 451 452 BRect bounds(Bounds()); 453 454 if (bounds.right <= 0.0f || bounds.bottom <= 0.0f) 455 return; 456 457 #if USE_OFF_SCREEN_VIEW 458 if (fOffScreenBits) { 459 fOffScreenBits->RemoveChild(fOffScreenView); 460 delete fOffScreenBits; 461 462 fOffScreenView->ResizeTo(bounds.Width(), bounds.Height()); 463 464 fOffScreenBits = new BBitmap(Bounds(), B_RGBA32, true, false); 465 fOffScreenBits->AddChild(fOffScreenView); 466 } 467 #endif 468 469 Invalidate(); 470 } 471 472 473 void 474 BSlider::KeyDown(const char *bytes, int32 numBytes) 475 { 476 if (!IsEnabled() || IsHidden()) 477 return; 478 479 int32 newValue = Value(); 480 481 switch (bytes[0]) { 482 case B_LEFT_ARROW: 483 case B_DOWN_ARROW: { 484 newValue -= KeyIncrementValue(); 485 break; 486 } 487 case B_RIGHT_ARROW: 488 case B_UP_ARROW: { 489 newValue += KeyIncrementValue(); 490 break; 491 } 492 case B_HOME: 493 newValue = fMinValue; 494 break; 495 case B_END: 496 newValue = fMaxValue; 497 break; 498 default: 499 BControl::KeyDown(bytes, numBytes); 500 return; 501 } 502 503 if (newValue != Value()) { 504 SetValue(newValue); 505 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 506 } 507 } 508 509 510 /*! 511 Makes sure the \a point is within valid bounds. 512 Returns \c true if the relevant coordinate (depending on the orientation 513 of the slider) differs from \a comparePoint. 514 */ 515 bool 516 BSlider::_ConstrainPoint(BPoint& point, BPoint comparePoint) const 517 { 518 if (fOrientation == B_HORIZONTAL) { 519 if (point.x != comparePoint.x) { 520 if (point.x < _MinPosition()) 521 point.x = _MinPosition(); 522 else if (point.x > _MaxPosition()) 523 point.x = _MaxPosition(); 524 525 return true; 526 } 527 } else { 528 if (point.y != comparePoint.y) { 529 if (point.y > _MinPosition()) 530 point.y = _MinPosition(); 531 else if (point.y < _MaxPosition()) 532 point.y = _MaxPosition(); 533 534 return true; 535 } 536 } 537 538 return false; 539 } 540 541 542 void 543 BSlider::MouseDown(BPoint point) 544 { 545 if (!IsEnabled()) 546 return; 547 548 if (BarFrame().Contains(point) || ThumbFrame().Contains(point)) 549 fInitialLocation = _Location(); 550 551 uint32 buttons; 552 GetMouse(&point, &buttons, true); 553 554 _ConstrainPoint(point, fInitialLocation); 555 SetValue(ValueForPoint(point)); 556 557 if (_Location() != fInitialLocation) 558 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 559 560 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) { 561 SetTracking(true); 562 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); 563 } else { 564 // synchronous mouse tracking 565 BPoint prevPoint; 566 567 while (buttons) { 568 prevPoint = point; 569 570 snooze(SnoozeAmount()); 571 GetMouse(&point, &buttons, true); 572 573 if (_ConstrainPoint(point, prevPoint)) { 574 int32 value = ValueForPoint(point); 575 if (value != Value()) { 576 SetValue(value); 577 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 578 } 579 } 580 } 581 if (_Location() != fInitialLocation) 582 Invoke(); 583 } 584 } 585 586 587 void 588 BSlider::MouseUp(BPoint point) 589 { 590 if (IsTracking()) { 591 if (_Location() != fInitialLocation) 592 Invoke(); 593 594 SetTracking(false); 595 } else 596 BControl::MouseUp(point); 597 } 598 599 600 void 601 BSlider::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 602 { 603 if (IsTracking()) { 604 if (_ConstrainPoint(point, _Location())) { 605 int32 value = ValueForPoint(point); 606 if (value != Value()) { 607 SetValue(value); 608 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 609 } 610 } 611 } else 612 BControl::MouseMoved(point, transit, message); 613 } 614 615 616 void 617 BSlider::Pulse() 618 { 619 BControl::Pulse(); 620 } 621 622 623 void 624 BSlider::SetLabel(const char *label) 625 { 626 BControl::SetLabel(label); 627 } 628 629 630 void 631 BSlider::SetLimitLabels(const char *minLabel, const char *maxLabel) 632 { 633 free(fMinLimitLabel); 634 fMinLimitLabel = minLabel ? strdup(minLabel) : NULL; 635 636 free(fMaxLimitLabel); 637 fMaxLimitLabel = maxLabel ? strdup(maxLabel) : NULL; 638 639 InvalidateLayout(); 640 641 // TODO: This is for backwards compatibility and should 642 // probably be removed when breaking binary compatiblity. 643 // Applications like our own Mouse rely on this behavior. 644 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 645 ResizeToPreferred(); 646 647 Invalidate(); 648 } 649 650 651 const char* 652 BSlider::MinLimitLabel() const 653 { 654 return fMinLimitLabel; 655 } 656 657 658 const char* 659 BSlider::MaxLimitLabel() const 660 { 661 return fMaxLimitLabel; 662 } 663 664 665 void 666 BSlider::SetValue(int32 value) 667 { 668 if (value < fMinValue) 669 value = fMinValue; 670 if (value > fMaxValue) 671 value = fMaxValue; 672 673 if (value == Value()) 674 return; 675 676 BPoint loc; 677 float range = (float)(fMaxValue - fMinValue); 678 if (range == 0) 679 range = 1; 680 681 float pos = (float)(value - fMinValue) / range * 682 (_MaxPosition() - _MinPosition()); 683 684 if (fOrientation == B_HORIZONTAL) { 685 loc.x = ceil(_MinPosition() + pos); 686 loc.y = 0; 687 } else { 688 loc.x = 0; 689 loc.y = floor(_MaxPosition() - pos); 690 } 691 692 BRect oldThumbFrame = ThumbFrame(); 693 694 // While it would be enough to do this dependent on fUseFillColor, 695 // that doesn't work out if DrawBar() has been overridden by a sub class 696 if (fOrientation == B_HORIZONTAL) 697 oldThumbFrame.top = BarFrame().top; 698 else 699 oldThumbFrame.right = BarFrame().right; 700 701 _SetLocation(loc); 702 703 BControl::SetValueNoUpdate(value); 704 BRect invalid = oldThumbFrame | ThumbFrame(); 705 706 if (Style() == B_TRIANGLE_THUMB) { 707 // 1) we need to take care of pixels touched because of 708 // anti-aliasing 709 // 2) we need to update the region with the focus mark as well 710 // (a method BSlider::FocusMarkFrame() would be nice as well) 711 if (fOrientation == B_HORIZONTAL) { 712 if (IsFocus()) 713 invalid.bottom += 2; 714 invalid.InsetBy(-1, 0); 715 } else { 716 if (IsFocus()) 717 invalid.left -= 2; 718 invalid.InsetBy(0, -1); 719 } 720 } 721 722 Invalidate(invalid); 723 724 UpdateTextChanged(); 725 } 726 727 728 int32 729 BSlider::ValueForPoint(BPoint location) const 730 { 731 float min; 732 float max; 733 float position; 734 if (fOrientation == B_HORIZONTAL) { 735 min = _MinPosition(); 736 max = _MaxPosition(); 737 position = location.x; 738 } else { 739 max = _MinPosition(); 740 min = _MaxPosition(); 741 position = min + (max - location.y); 742 } 743 744 if (position < min) 745 position = min; 746 if (position > max) 747 position = max; 748 749 return (int32)roundf(((position - min) * (fMaxValue - fMinValue) / (max - min)) + fMinValue); 750 } 751 752 753 void 754 BSlider::SetPosition(float position) 755 { 756 if (position <= 0.0f) 757 BControl::SetValue(fMinValue); 758 else if (position >= 1.0f) 759 BControl::SetValue(fMaxValue); 760 else 761 BControl::SetValue((int32)(position * (fMaxValue - fMinValue) + fMinValue)); 762 } 763 764 765 float 766 BSlider::Position() const 767 { 768 float range = (float)(fMaxValue - fMinValue); 769 if (range == 0.0f) 770 range = 1.0f; 771 772 return (float)(Value() - fMinValue) / range; 773 } 774 775 776 void 777 BSlider::SetEnabled(bool on) 778 { 779 BControl::SetEnabled(on); 780 } 781 782 783 void 784 BSlider::GetLimits(int32 *minimum, int32 *maximum) const 785 { 786 if (minimum != NULL) 787 *minimum = fMinValue; 788 if (maximum != NULL) 789 *maximum = fMaxValue; 790 } 791 792 793 // #pragma mark - drawing 794 795 796 void 797 BSlider::Draw(BRect updateRect) 798 { 799 // clear out background 800 BRegion background(updateRect); 801 background.Exclude(BarFrame()); 802 bool drawBackground = true; 803 if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0) { 804 // This view is embedded somewhere, most likely the Tracker Desktop 805 // shelf. 806 drawBackground = false; 807 } 808 809 // ToDo: the triangle thumb doesn't delete its background, so we still have 810 // to do it Note, this also creates a different behaviour for subclasses, 811 // depending on the thumb style - if possible this should be avoided. 812 if (Style() == B_BLOCK_THUMB) { 813 BRect thumbFrame = ThumbFrame(); 814 if (be_control_look != NULL) { 815 // fill background where shadow will be... 816 // TODO: Such drawint dependent behavior should be moved into 817 // BControlLook of course. 818 thumbFrame.right--; 819 thumbFrame.bottom--; 820 } 821 background.Exclude(thumbFrame); 822 } 823 824 #if USE_OFF_SCREEN_VIEW 825 if (!fOffScreenBits) 826 return; 827 828 if (fOffScreenBits->Lock()) { 829 fOffScreenView->SetViewColor(ViewColor()); 830 fOffScreenView->SetLowColor(LowColor()); 831 #endif 832 833 if (drawBackground && background.Frame().IsValid()) 834 OffscreenView()->FillRegion(&background, B_SOLID_LOW); 835 836 #if USE_OFF_SCREEN_VIEW 837 fOffScreenView->Sync(); 838 fOffScreenBits->Unlock(); 839 } 840 #endif 841 842 DrawSlider(); 843 } 844 845 846 void 847 BSlider::DrawSlider() 848 { 849 if (LockLooper()) { 850 #if USE_OFF_SCREEN_VIEW 851 if (!fOffScreenBits) 852 return; 853 if (fOffScreenBits->Lock()) { 854 #endif 855 DrawBar(); 856 DrawHashMarks(); 857 DrawThumb(); 858 DrawFocusMark(); 859 DrawText(); 860 861 #if USE_OFF_SCREEN_VIEW 862 fOffScreenView->Sync(); 863 fOffScreenBits->Unlock(); 864 865 DrawBitmap(fOffScreenBits, B_ORIGIN); 866 } 867 #endif 868 UnlockLooper(); 869 } 870 } 871 872 873 void 874 BSlider::DrawBar() 875 { 876 BRect frame = BarFrame(); 877 BView *view = OffscreenView(); 878 879 if (be_control_look != NULL) { 880 uint32 flags = be_control_look->Flags(this); 881 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 882 rgb_color rightFillColor = fBarColor; 883 rgb_color leftFillColor = fUseFillColor ? fFillColor : fBarColor; 884 be_control_look->DrawSliderBar(view, frame, frame, base, leftFillColor, 885 rightFillColor, Position(), flags, fOrientation); 886 return; 887 } 888 889 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 890 rgb_color lightenmax; 891 rgb_color darken1; 892 rgb_color darken2; 893 rgb_color darkenmax; 894 895 rgb_color barColor; 896 rgb_color fillColor; 897 898 if (IsEnabled()) { 899 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 900 darken1 = tint_color(no_tint, B_DARKEN_1_TINT); 901 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 902 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 903 barColor = fBarColor; 904 fillColor = fFillColor; 905 } else { 906 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 907 darken1 = no_tint; 908 darken2 = tint_color(no_tint, B_DARKEN_1_TINT); 909 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT); 910 911 barColor.red = (fBarColor.red + no_tint.red) / 2; 912 barColor.green = (fBarColor.green + no_tint.green) / 2; 913 barColor.blue = (fBarColor.blue + no_tint.blue) / 2; 914 barColor.alpha = 255; 915 916 fillColor.red = (fFillColor.red + no_tint.red) / 2; 917 fillColor.green = (fFillColor.green + no_tint.green) / 2; 918 fillColor.blue = (fFillColor.blue + no_tint.blue) / 2; 919 fillColor.alpha = 255; 920 } 921 922 // exclude the block thumb from the bar filling 923 924 BRect lowerFrame = frame.InsetByCopy(1, 1); 925 lowerFrame.top++; 926 lowerFrame.left++; 927 BRect upperFrame = lowerFrame; 928 BRect thumbFrame; 929 930 if (Style() == B_BLOCK_THUMB) { 931 thumbFrame = ThumbFrame(); 932 933 if (fOrientation == B_HORIZONTAL) { 934 lowerFrame.right = thumbFrame.left; 935 upperFrame.left = thumbFrame.right; 936 } else { 937 lowerFrame.top = thumbFrame.bottom; 938 upperFrame.bottom = thumbFrame.top; 939 } 940 } else if (fUseFillColor) { 941 if (fOrientation == B_HORIZONTAL) { 942 lowerFrame.right = floor(lowerFrame.left - 1 + Position() 943 * (lowerFrame.Width() + 1)); 944 upperFrame.left = lowerFrame.right; 945 } else { 946 lowerFrame.top = floor(lowerFrame.bottom + 1 - Position() 947 * (lowerFrame.Height() + 1)); 948 upperFrame.bottom = lowerFrame.top; 949 } 950 } 951 952 view->SetHighColor(barColor); 953 view->FillRect(upperFrame); 954 955 if (Style() == B_BLOCK_THUMB || fUseFillColor) { 956 if (fUseFillColor) 957 view->SetHighColor(fillColor); 958 view->FillRect(lowerFrame); 959 } 960 961 if (Style() == B_BLOCK_THUMB) { 962 // We don't want to stroke the lines over the thumb 963 964 PushState(); 965 966 BRegion region; 967 GetClippingRegion(®ion); 968 region.Exclude(thumbFrame); 969 ConstrainClippingRegion(®ion); 970 } 971 972 view->SetHighColor(darken1); 973 view->StrokeLine(BPoint(frame.left, frame.top), 974 BPoint(frame.left + 1.0f, frame.top)); 975 view->StrokeLine(BPoint(frame.left, frame.bottom), 976 BPoint(frame.left + 1.0f, frame.bottom)); 977 view->StrokeLine(BPoint(frame.right - 1.0f, frame.top), 978 BPoint(frame.right, frame.top)); 979 980 view->SetHighColor(darken2); 981 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 982 BPoint(frame.right - 1.0f, frame.top)); 983 view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f), 984 BPoint(frame.left, frame.top + 1.0f)); 985 986 view->SetHighColor(lightenmax); 987 view->StrokeLine(BPoint(frame.left + 1.0f, frame.bottom), 988 BPoint(frame.right, frame.bottom)); 989 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 990 BPoint(frame.right, frame.top + 1.0f)); 991 992 frame.InsetBy(1.0f, 1.0f); 993 994 view->SetHighColor(darkenmax); 995 view->StrokeLine(BPoint(frame.left, frame.bottom), 996 BPoint(frame.left, frame.top)); 997 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 998 BPoint(frame.right, frame.top)); 999 1000 if (Style() == B_BLOCK_THUMB) 1001 PopState(); 1002 } 1003 1004 1005 void 1006 BSlider::DrawHashMarks() 1007 { 1008 if (fHashMarks == B_HASH_MARKS_NONE) 1009 return; 1010 1011 BRect frame = HashMarksFrame(); 1012 BView* view = OffscreenView(); 1013 1014 if (be_control_look) { 1015 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1016 uint32 flags = be_control_look->Flags(this); 1017 be_control_look->DrawSliderHashMarks(view, frame, frame, base, 1018 fHashMarkCount, fHashMarks, flags, fOrientation); 1019 return; 1020 } 1021 1022 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 1023 rgb_color lightenmax; 1024 rgb_color darken2; 1025 1026 if (IsEnabled()) { 1027 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 1028 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 1029 } else { 1030 lightenmax = tint_color(no_tint, B_LIGHTEN_2_TINT); 1031 darken2 = tint_color(no_tint, B_DARKEN_1_TINT); 1032 } 1033 1034 float pos = _MinPosition(); 1035 int32 hashMarkCount = max_c(fHashMarkCount, 2); 1036 // draw at least two hashmarks at min/max if 1037 // fHashMarks != B_HASH_MARKS_NONE 1038 float factor = (_MaxPosition() - pos) / (hashMarkCount - 1); 1039 1040 if (fHashMarks & B_HASH_MARKS_TOP) { 1041 1042 view->BeginLineArray(hashMarkCount * 2); 1043 1044 if (fOrientation == B_HORIZONTAL) { 1045 for (int32 i = 0; i < hashMarkCount; i++) { 1046 view->AddLine(BPoint(pos, frame.top), 1047 BPoint(pos, frame.top + 5), darken2); 1048 view->AddLine(BPoint(pos + 1, frame.top), 1049 BPoint(pos + 1, frame.top + 5), lightenmax); 1050 1051 pos += factor; 1052 } 1053 } else { 1054 for (int32 i = 0; i < hashMarkCount; i++) { 1055 view->AddLine(BPoint(frame.left, pos), 1056 BPoint(frame.left + 5, pos), darken2); 1057 view->AddLine(BPoint(frame.left, pos + 1), 1058 BPoint(frame.left + 5, pos + 1), lightenmax); 1059 1060 pos += factor; 1061 } 1062 } 1063 1064 view->EndLineArray(); 1065 } 1066 1067 pos = _MinPosition(); 1068 1069 if (fHashMarks & B_HASH_MARKS_BOTTOM) { 1070 1071 view->BeginLineArray(hashMarkCount * 2); 1072 1073 if (fOrientation == B_HORIZONTAL) { 1074 for (int32 i = 0; i < hashMarkCount; i++) { 1075 view->AddLine(BPoint(pos, frame.bottom - 5), 1076 BPoint(pos, frame.bottom), darken2); 1077 view->AddLine(BPoint(pos + 1, frame.bottom - 5), 1078 BPoint(pos + 1, frame.bottom), lightenmax); 1079 1080 pos += factor; 1081 } 1082 } else { 1083 for (int32 i = 0; i < hashMarkCount; i++) { 1084 view->AddLine(BPoint(frame.right - 5, pos), 1085 BPoint(frame.right, pos), darken2); 1086 view->AddLine(BPoint(frame.right - 5, pos + 1), 1087 BPoint(frame.right, pos + 1), lightenmax); 1088 1089 pos += factor; 1090 } 1091 } 1092 1093 view->EndLineArray(); 1094 } 1095 } 1096 1097 1098 void 1099 BSlider::DrawThumb() 1100 { 1101 if (Style() == B_BLOCK_THUMB) 1102 _DrawBlockThumb(); 1103 else 1104 _DrawTriangleThumb(); 1105 } 1106 1107 1108 void 1109 BSlider::DrawFocusMark() 1110 { 1111 if (!IsFocus()) 1112 return; 1113 1114 OffscreenView()->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 1115 1116 BRect frame = ThumbFrame(); 1117 1118 if (fStyle == B_BLOCK_THUMB) { 1119 frame.left += 2.0f; 1120 frame.top += 2.0f; 1121 frame.right -= 3.0f; 1122 frame.bottom -= 3.0f; 1123 OffscreenView()->StrokeRect(frame); 1124 } else { 1125 if (fOrientation == B_HORIZONTAL) { 1126 OffscreenView()->StrokeLine(BPoint(frame.left, frame.bottom + 2.0f), 1127 BPoint(frame.right, frame.bottom + 2.0f)); 1128 } else { 1129 OffscreenView()->StrokeLine(BPoint(frame.left - 2.0f, frame.top), 1130 BPoint(frame.left - 2.0f, frame.bottom)); 1131 } 1132 } 1133 } 1134 1135 1136 void 1137 BSlider::DrawText() 1138 { 1139 BRect bounds(Bounds()); 1140 BView *view = OffscreenView(); 1141 1142 rgb_color base = LowColor(); 1143 uint32 flags = 0; 1144 if (be_control_look == NULL) { 1145 if (IsEnabled()) { 1146 view->SetHighColor(0, 0, 0); 1147 } else { 1148 view->SetHighColor(tint_color(LowColor(), B_DISABLED_LABEL_TINT)); 1149 } 1150 } else 1151 flags = be_control_look->Flags(this); 1152 1153 font_height fontHeight; 1154 GetFontHeight(&fontHeight); 1155 if (Orientation() == B_HORIZONTAL) { 1156 if (Label()) { 1157 if (be_control_look == NULL) { 1158 view->DrawString(Label(), 1159 BPoint(0.0, ceilf(fontHeight.ascent))); 1160 } else { 1161 be_control_look->DrawLabel(view, Label(), base, flags, 1162 BPoint(0.0, ceilf(fontHeight.ascent))); 1163 } 1164 } 1165 1166 // the update text is updated in SetValue() only 1167 if (fUpdateText != NULL) { 1168 if (be_control_look == NULL) { 1169 view->DrawString(fUpdateText, BPoint(bounds.right 1170 - StringWidth(fUpdateText), ceilf(fontHeight.ascent))); 1171 } else { 1172 be_control_look->DrawLabel(view, fUpdateText, base, flags, 1173 BPoint(bounds.right - StringWidth(fUpdateText), 1174 ceilf(fontHeight.ascent))); 1175 } 1176 } 1177 1178 if (fMinLimitLabel) { 1179 if (be_control_look == NULL) { 1180 view->DrawString(fMinLimitLabel, BPoint(0.0, bounds.bottom 1181 - fontHeight.descent)); 1182 } else { 1183 be_control_look->DrawLabel(view, fMinLimitLabel, base, flags, 1184 BPoint(0.0, bounds.bottom - fontHeight.descent)); 1185 } 1186 } 1187 1188 if (fMaxLimitLabel) { 1189 if (be_control_look == NULL) { 1190 view->DrawString(fMaxLimitLabel, BPoint(bounds.right 1191 - StringWidth(fMaxLimitLabel), bounds.bottom 1192 - fontHeight.descent)); 1193 } else { 1194 be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags, 1195 BPoint(bounds.right - StringWidth(fMaxLimitLabel), 1196 bounds.bottom - fontHeight.descent)); 1197 } 1198 } 1199 } else { 1200 float lineHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent) 1201 + ceilf(fontHeight.leading); 1202 float baseLine = ceilf(fontHeight.ascent); 1203 1204 if (Label()) { 1205 if (be_control_look == NULL) { 1206 view->DrawString(Label(), BPoint((bounds.Width() 1207 - StringWidth(Label())) / 2.0, baseLine)); 1208 } else { 1209 be_control_look->DrawLabel(view, Label(), base, flags, 1210 BPoint((bounds.Width() - StringWidth(Label())) / 2.0, 1211 baseLine)); 1212 } 1213 baseLine += lineHeight; 1214 } 1215 1216 if (fMaxLimitLabel) { 1217 if (be_control_look == NULL) { 1218 view->DrawString(fMaxLimitLabel, BPoint((bounds.Width() 1219 - StringWidth(fMaxLimitLabel)) / 2.0, baseLine)); 1220 } else { 1221 be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags, 1222 BPoint((bounds.Width() 1223 - StringWidth(fMaxLimitLabel)) / 2.0, baseLine)); 1224 } 1225 } 1226 1227 baseLine = bounds.bottom - ceilf(fontHeight.descent); 1228 1229 if (fMinLimitLabel) { 1230 if (be_control_look == NULL) { 1231 view->DrawString(fMinLimitLabel, BPoint((bounds.Width() 1232 - StringWidth(fMinLimitLabel)) / 2.0, baseLine)); 1233 } else { 1234 be_control_look->DrawLabel(view, fMinLimitLabel, base, flags, 1235 BPoint((bounds.Width() 1236 - StringWidth(fMinLimitLabel)) / 2.0, baseLine)); 1237 } 1238 baseLine -= lineHeight; 1239 } 1240 1241 if (fUpdateText != NULL) { 1242 if (be_control_look == NULL) { 1243 view->DrawString(fUpdateText, BPoint((bounds.Width() 1244 - StringWidth(fUpdateText)) / 2.0, baseLine)); 1245 } else { 1246 be_control_look->DrawLabel(view, fUpdateText, base, flags, 1247 BPoint((bounds.Width() 1248 - StringWidth(fUpdateText)) / 2.0, baseLine)); 1249 } 1250 } 1251 } 1252 } 1253 1254 1255 // #pragma mark - 1256 1257 1258 const char* 1259 BSlider::UpdateText() const 1260 { 1261 return NULL; 1262 } 1263 1264 1265 void 1266 BSlider::UpdateTextChanged() 1267 { 1268 // update text label 1269 float oldWidth = 0.0; 1270 if (fUpdateText != NULL) 1271 oldWidth = StringWidth(fUpdateText); 1272 1273 const char* oldUpdateText = fUpdateText; 1274 fUpdateText = UpdateText(); 1275 bool updateTextOnOff = (fUpdateText == NULL && oldUpdateText != NULL) 1276 || (fUpdateText != NULL && oldUpdateText == NULL); 1277 1278 float newWidth = 0.0; 1279 if (fUpdateText != NULL) 1280 newWidth = StringWidth(fUpdateText); 1281 1282 float width = ceilf(max_c(newWidth, oldWidth)) + 2.0f; 1283 if (width != 0) { 1284 font_height fontHeight; 1285 GetFontHeight(&fontHeight); 1286 1287 float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1288 float lineHeight = height + ceilf(fontHeight.leading); 1289 BRect invalid(Bounds()); 1290 if (fOrientation == B_HORIZONTAL) 1291 invalid = BRect(invalid.right - width, 0, invalid.right, height); 1292 else { 1293 if (!updateTextOnOff) { 1294 invalid.left = (invalid.left + invalid.right - width) / 2; 1295 invalid.right = invalid.left + width; 1296 if (fMinLimitLabel) 1297 invalid.bottom -= lineHeight; 1298 invalid.top = invalid.bottom - height; 1299 } 1300 } 1301 Invalidate(invalid); 1302 } 1303 1304 float oldMaxUpdateTextWidth = fMaxUpdateTextWidth; 1305 fMaxUpdateTextWidth = MaxUpdateTextWidth(); 1306 if (oldMaxUpdateTextWidth != fMaxUpdateTextWidth) 1307 InvalidateLayout(); 1308 } 1309 1310 1311 BRect 1312 BSlider::BarFrame() const 1313 { 1314 BRect frame(Bounds()); 1315 1316 font_height fontHeight; 1317 GetFontHeight(&fontHeight); 1318 1319 float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1320 float leading = ceilf(fontHeight.leading); 1321 1322 float thumbInset; 1323 if (fStyle == B_BLOCK_THUMB) 1324 thumbInset = 8.0; 1325 else 1326 thumbInset = 7.0; 1327 1328 if (Orientation() == B_HORIZONTAL) { 1329 frame.left = thumbInset; 1330 frame.top = 6.0 + (Label() || fUpdateText ? textHeight + 4.0 : 0.0); 1331 frame.right -= thumbInset; 1332 frame.bottom = frame.top + fBarThickness; 1333 } else { 1334 frame.left = floorf((frame.Width() - fBarThickness) / 2.0); 1335 frame.top = thumbInset; 1336 if (Label()) 1337 frame.top += textHeight; 1338 if (fMaxLimitLabel) { 1339 frame.top += textHeight; 1340 if (Label()) 1341 frame.top += leading; 1342 } 1343 1344 frame.right = frame.left + fBarThickness; 1345 frame.bottom = frame.bottom - thumbInset; 1346 if (fMinLimitLabel) 1347 frame.bottom -= textHeight; 1348 if (fUpdateText) { 1349 frame.bottom -= textHeight; 1350 if (fMinLimitLabel) 1351 frame.bottom -= leading; 1352 } 1353 } 1354 1355 return frame; 1356 } 1357 1358 1359 BRect 1360 BSlider::HashMarksFrame() const 1361 { 1362 BRect frame(BarFrame()); 1363 1364 if (fOrientation == B_HORIZONTAL) { 1365 frame.top -= 6.0; 1366 frame.bottom += 6.0; 1367 } else { 1368 frame.left -= 6.0; 1369 frame.right += 6.0; 1370 } 1371 1372 return frame; 1373 } 1374 1375 1376 BRect 1377 BSlider::ThumbFrame() const 1378 { 1379 // TODO: The slider looks really ugly and broken when it is too little. 1380 // I would suggest using BarFrame() here to get the top and bottom coords 1381 // and spread them further apart for the thumb 1382 1383 BRect frame = Bounds(); 1384 1385 font_height fontHeight; 1386 GetFontHeight(&fontHeight); 1387 1388 float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1389 1390 if (fStyle == B_BLOCK_THUMB) { 1391 if (Orientation() == B_HORIZONTAL) { 1392 frame.left = floorf(Position() * (_MaxPosition() 1393 - _MinPosition()) + _MinPosition()) - 8; 1394 frame.top = 2 + (Label() || fUpdateText ? textHeight + 4 : 0); 1395 frame.right = frame.left + 17; 1396 frame.bottom = frame.top + fBarThickness + 7; 1397 } else { 1398 frame.left = floor((frame.Width() - fBarThickness) / 2) - 4; 1399 frame.top = floorf(Position() * (_MaxPosition() 1400 - _MinPosition()) + _MinPosition()) - 8; 1401 frame.right = frame.left + fBarThickness + 7; 1402 frame.bottom = frame.top + 17; 1403 } 1404 } else { 1405 if (Orientation() == B_HORIZONTAL) { 1406 frame.left = floorf(Position() * (_MaxPosition() 1407 - _MinPosition()) + _MinPosition()) - 6; 1408 frame.right = frame.left + 12; 1409 frame.top = 3 + fBarThickness + (Label() ? textHeight + 4 : 0); 1410 frame.bottom = frame.top + 8; 1411 } else { 1412 frame.left = floorf((frame.Width() + fBarThickness) / 2) - 3; 1413 frame.top = floorf(Position() * (_MaxPosition() 1414 - _MinPosition())) + _MinPosition() - 6; 1415 frame.right = frame.left + 8; 1416 frame.bottom = frame.top + 12; 1417 } 1418 } 1419 1420 return frame; 1421 } 1422 1423 1424 void 1425 BSlider::SetFlags(uint32 flags) 1426 { 1427 BControl::SetFlags(flags); 1428 } 1429 1430 1431 void 1432 BSlider::SetResizingMode(uint32 mode) 1433 { 1434 BControl::SetResizingMode(mode); 1435 } 1436 1437 1438 void 1439 BSlider::GetPreferredSize(float* _width, float* _height) 1440 { 1441 BSize preferredSize = PreferredSize(); 1442 1443 if (_width) { 1444 // *_width = preferredSize.width; 1445 // NOTE: For compatibility reasons, the BSlider never shrinks 1446 // horizontally. This only affects applications which do not 1447 // use the new layout system. 1448 *_width = max_c(Bounds().Width(), preferredSize.width); 1449 } 1450 1451 if (_height) 1452 *_height = preferredSize.height; 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::_SetLocation(BPoint p) 2034 { 2035 fLocation = p; 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 return fMinSize; 2150 } 2151 2152 2153 // #pragma mark - FBC padding 2154 2155 void BSlider::_ReservedSlider6() {} 2156 void BSlider::_ReservedSlider7() {} 2157 void BSlider::_ReservedSlider8() {} 2158 void BSlider::_ReservedSlider9() {} 2159 void BSlider::_ReservedSlider10() {} 2160 void BSlider::_ReservedSlider11() {} 2161 void BSlider::_ReservedSlider12() {} 2162 2163 2164 BSlider & 2165 BSlider::operator=(const BSlider &) 2166 { 2167 return *this; 2168 } 2169 2170 2171 // #pragma mark - BeOS compatibility 2172 2173 2174 #if __GNUC__ < 3 2175 2176 extern "C" void 2177 GetLimits__7BSliderPlT1(BSlider* slider, int32* minimum, int32* maximum) 2178 { 2179 slider->GetLimits(minimum, maximum); 2180 } 2181 2182 2183 extern "C" void 2184 _ReservedSlider4__7BSlider(BSlider *slider, int32 minimum, int32 maximum) 2185 { 2186 slider->BSlider::SetLimits(minimum, maximum); 2187 } 2188 2189 extern "C" float 2190 _ReservedSlider5__7BSlider(BSlider *slider) 2191 { 2192 return slider->BSlider::MaxUpdateTextWidth(); 2193 } 2194 2195 2196 extern "C" void 2197 _ReservedSlider1__7BSlider(BSlider* slider, orientation _orientation) 2198 { 2199 slider->BSlider::SetOrientation(_orientation); 2200 } 2201 2202 2203 extern "C" void 2204 _ReservedSlider2__7BSlider(BSlider* slider, float thickness) 2205 { 2206 slider->BSlider::SetBarThickness(thickness); 2207 } 2208 2209 2210 extern "C" void 2211 _ReservedSlider3__7BSlider(BSlider* slider, const BFont* font, 2212 uint32 properties) 2213 { 2214 slider->BSlider::SetFont(font, properties); 2215 } 2216 2217 #endif // __GNUC__ < 3 2218