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