tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / Slider.cpp
blob479a81ca6ee8597e7089d7b892625bc9682a1054
1 /*
2 * Copyright 2001-2013 Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 * Axel Dörfler, axeld@pinc-software.de
8 * Marc Flerackers (mflerackers@androme.be)
9 */
12 #include <Slider.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
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>
27 #include <binary_compatibility/Interface.h>
30 #define USE_OFF_SCREEN_VIEW 0
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),
40 fMinLimitLabel(NULL),
41 fMaxLimitLabel(NULL),
43 fMinValue(minValue),
44 fMaxValue(maxValue),
45 fKeyIncrementValue(1),
47 fHashMarkCount(0),
48 fHashMarks(B_HASH_MARKS_NONE),
50 fStyle(thumbType),
52 fOrientation(B_HORIZONTAL),
53 fBarThickness(6.0)
55 _InitBarColor();
57 _InitObject();
58 SetValue(0);
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),
70 fMinLimitLabel(NULL),
71 fMaxLimitLabel(NULL),
73 fMinValue(minValue),
74 fMaxValue(maxValue),
75 fKeyIncrementValue(1),
77 fHashMarkCount(0),
78 fHashMarks(B_HASH_MARKS_NONE),
80 fStyle(thumbType),
82 fOrientation(posture),
83 fBarThickness(6.0)
85 _InitBarColor();
87 _InitObject();
88 SetValue(0);
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),
99 fMinLimitLabel(NULL),
100 fMaxLimitLabel(NULL),
102 fMinValue(minValue),
103 fMaxValue(maxValue),
104 fKeyIncrementValue(1),
106 fHashMarkCount(0),
107 fHashMarks(B_HASH_MARKS_NONE),
109 fStyle(thumbType),
111 fOrientation(posture),
112 fBarThickness(6.0)
114 _InitBarColor();
116 _InitObject();
117 SetValue(0);
121 BSlider::BSlider(BMessage *archive)
122 : BControl(archive)
124 fModificationMessage = NULL;
126 if (archive->HasMessage("_mod_msg")) {
127 BMessage* message = new BMessage;
129 archive->FindMessage("_mod_msg", message);
131 SetModificationMessage(message);
134 if (archive->FindInt32("_sdelay", &fSnoozeAmount) != B_OK)
135 SetSnoozeAmount(20000);
137 rgb_color color;
138 if (archive->FindInt32("_fcolor", (int32 *)&color) == B_OK)
139 UseFillColor(true, &color);
140 else
141 UseFillColor(false);
143 int32 orient;
144 if (archive->FindInt32("_orient", &orient) == B_OK)
145 fOrientation = (orientation)orient;
146 else
147 fOrientation = B_HORIZONTAL;
149 fMinLimitLabel = NULL;
150 fMaxLimitLabel = NULL;
152 const char* minlbl = NULL;
153 const char* maxlbl = NULL;
155 archive->FindString("_minlbl", &minlbl);
156 archive->FindString("_maxlbl", &maxlbl);
158 SetLimitLabels(minlbl, maxlbl);
160 if (archive->FindInt32("_min", &fMinValue) != B_OK)
161 fMinValue = 0;
163 if (archive->FindInt32("_max", &fMaxValue) != B_OK)
164 fMaxValue = 100;
166 if (archive->FindInt32("_incrementvalue", &fKeyIncrementValue) != B_OK)
167 fKeyIncrementValue = 1;
169 if (archive->FindInt32("_hashcount", &fHashMarkCount) != B_OK)
170 fHashMarkCount = 11;
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;
178 int16 sstyle;
179 if (archive->FindInt16("_sstyle", &sstyle) == B_OK)
180 fStyle = (thumb_style)sstyle;
181 else
182 fStyle = B_BLOCK_THUMB;
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);
188 float bthickness;
189 if (archive->FindFloat("_bthickness", &bthickness) == B_OK)
190 fBarThickness = bthickness;
191 else
192 fBarThickness = 6.0f;
194 _InitObject();
198 BSlider::~BSlider()
200 #if USE_OFF_SCREEN_VIEW
201 delete fOffScreenBits;
202 #endif
204 delete fModificationMessage;
205 free(fMinLimitLabel);
206 free(fMaxLimitLabel);
210 void
211 BSlider::_InitBarColor()
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));
221 UseFillColor(false, NULL);
225 void
226 BSlider::_InitObject()
228 fLocation.x = 0;
229 fLocation.y = 0;
230 fInitialLocation.x = 0;
231 fInitialLocation.y = 0;
233 #if USE_OFF_SCREEN_VIEW
234 fOffScreenBits = NULL;
235 fOffScreenView = NULL;
236 #endif
238 fUpdateText = NULL;
239 fMinSize.Set(-1, -1);
240 fMaxUpdateTextWidth = -1.0;
244 BArchivable*
245 BSlider::Instantiate(BMessage *archive)
247 if (validate_instantiation(archive, "BSlider"))
248 return new BSlider(archive);
250 return NULL;
254 status_t
255 BSlider::Archive(BMessage *archive, bool deep) const
257 status_t ret = BControl::Archive(archive, deep);
259 if (ModificationMessage() && ret == B_OK)
260 ret = archive->AddMessage("_mod_msg", ModificationMessage());
262 if (ret == B_OK)
263 ret = archive->AddInt32("_sdelay", fSnoozeAmount);
264 if (ret == B_OK)
265 ret = archive->AddInt32("_bcolor", (const uint32 &)fBarColor);
267 if (FillColor(NULL) && ret == B_OK)
268 ret = archive->AddInt32("_fcolor", (const uint32 &)fFillColor);
270 if (ret == B_OK && fMinLimitLabel)
271 ret = archive->AddString("_minlbl", fMinLimitLabel);
273 if (ret == B_OK && fMaxLimitLabel)
274 ret = archive->AddString("_maxlbl", fMaxLimitLabel);
276 if (ret == B_OK)
277 ret = archive->AddInt32("_min", fMinValue);
278 if (ret == B_OK)
279 ret = archive->AddInt32("_max", fMaxValue);
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);
294 return ret;
298 status_t
299 BSlider::Perform(perform_code code, void* _data)
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:
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;
330 case PERFORM_CODE_SET_LAYOUT:
332 perform_data_set_layout* data = (perform_data_set_layout*)_data;
333 BSlider::SetLayout(data->layout);
334 return B_OK;
336 case PERFORM_CODE_LAYOUT_INVALIDATED:
338 perform_data_layout_invalidated* data
339 = (perform_data_layout_invalidated*)_data;
340 BSlider::LayoutInvalidated(data->descendants);
341 return B_OK;
343 case PERFORM_CODE_DO_LAYOUT:
345 BSlider::DoLayout();
346 return B_OK;
348 case PERFORM_CODE_SET_ICON:
350 perform_data_set_icon* data = (perform_data_set_icon*)_data;
351 return BSlider::SetIcon(data->icon, data->flags);
355 return BControl::Perform(code, _data);
359 void
360 BSlider::WindowActivated(bool state)
362 BControl::WindowActivated(state);
366 void
367 BSlider::AttachedToWindow()
369 ResizeToPreferred();
371 #if USE_OFF_SCREEN_VIEW
372 BRect bounds(Bounds());
374 if (!fOffScreenView) {
375 fOffScreenView = new BView(bounds, "", B_FOLLOW_ALL, B_WILL_DRAW);
377 BFont font;
378 GetFont(&font);
379 fOffScreenView->SetFont(&font);
382 if (!fOffScreenBits) {
383 fOffScreenBits = new BBitmap(bounds, B_RGBA32, true, false);
385 if (fOffScreenBits && fOffScreenView)
386 fOffScreenBits->AddChild(fOffScreenView);
388 } else if (fOffScreenView)
389 fOffScreenBits->AddChild(fOffScreenView);
390 #endif // USE_OFF_SCREEN_VIEW
392 BControl::AttachedToWindow();
394 BView* view = OffscreenView();
395 if (view && view->LockLooper()) {
396 view->SetViewColor(B_TRANSPARENT_COLOR);
397 view->SetLowColor(LowColor());
398 view->UnlockLooper();
401 int32 value = Value();
402 SetValue(value);
403 // makes sure the value is within valid bounds
404 _SetLocationForValue(Value());
405 // makes sure the location is correct
406 UpdateTextChanged();
410 void
411 BSlider::AllAttached()
413 BControl::AllAttached();
417 void
418 BSlider::AllDetached()
420 BControl::AllDetached();
424 void
425 BSlider::DetachedFromWindow()
427 BControl::DetachedFromWindow();
429 #if USE_OFF_SCREEN_VIEW
430 if (fOffScreenBits) {
431 delete fOffScreenBits;
432 fOffScreenBits = NULL;
433 fOffScreenView = NULL;
435 #endif
439 void
440 BSlider::MessageReceived(BMessage *msg)
442 BControl::MessageReceived(msg);
446 void
447 BSlider::FrameMoved(BPoint new_position)
449 BControl::FrameMoved(new_position);
453 void
454 BSlider::FrameResized(float w,float h)
456 BControl::FrameResized(w, h);
458 BRect bounds(Bounds());
460 if (bounds.right <= 0.0f || bounds.bottom <= 0.0f)
461 return;
463 #if USE_OFF_SCREEN_VIEW
464 if (fOffScreenBits) {
465 fOffScreenBits->RemoveChild(fOffScreenView);
466 delete fOffScreenBits;
468 fOffScreenView->ResizeTo(bounds.Width(), bounds.Height());
470 fOffScreenBits = new BBitmap(Bounds(), B_RGBA32, true, false);
471 fOffScreenBits->AddChild(fOffScreenView);
473 #endif
475 Invalidate();
479 void
480 BSlider::KeyDown(const char *bytes, int32 numBytes)
482 if (!IsEnabled() || IsHidden())
483 return;
485 int32 newValue = Value();
487 switch (bytes[0]) {
488 case B_LEFT_ARROW:
489 case B_DOWN_ARROW:
490 newValue -= KeyIncrementValue();
491 break;
493 case B_RIGHT_ARROW:
494 case B_UP_ARROW:
495 newValue += KeyIncrementValue();
496 break;
498 case B_HOME:
499 newValue = fMinValue;
500 break;
501 case B_END:
502 newValue = fMaxValue;
503 break;
505 default:
506 BControl::KeyDown(bytes, numBytes);
507 return;
510 if (newValue < fMinValue)
511 newValue = fMinValue;
512 if (newValue > fMaxValue)
513 newValue = fMaxValue;
515 if (newValue != Value()) {
516 fInitialLocation = _Location();
517 SetValue(newValue);
518 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
522 void
523 BSlider::KeyUp(const char *bytes, int32 numBytes)
525 if (fInitialLocation != _Location()) {
526 // The last KeyDown event triggered the modification message or no
527 // notification at all, we may also have sent the modification message
528 // continually while the user kept pressing the key. In either case,
529 // finish with the final message to make the behavior consistent with
530 // changing the value by mouse.
531 Invoke();
537 Makes sure the \a point is within valid bounds.
538 Returns \c true if the relevant coordinate (depending on the orientation
539 of the slider) differs from \a comparePoint.
541 bool
542 BSlider::_ConstrainPoint(BPoint& point, BPoint comparePoint) const
544 if (fOrientation == B_HORIZONTAL) {
545 if (point.x != comparePoint.x) {
546 if (point.x < _MinPosition())
547 point.x = _MinPosition();
548 else if (point.x > _MaxPosition())
549 point.x = _MaxPosition();
551 return true;
553 } else {
554 if (point.y != comparePoint.y) {
555 if (point.y > _MinPosition())
556 point.y = _MinPosition();
557 else if (point.y < _MaxPosition())
558 point.y = _MaxPosition();
560 return true;
564 return false;
568 void
569 BSlider::MouseDown(BPoint point)
571 if (!IsEnabled())
572 return;
574 if (BarFrame().Contains(point) || ThumbFrame().Contains(point))
575 fInitialLocation = _Location();
577 uint32 buttons;
578 GetMouse(&point, &buttons, true);
580 _ConstrainPoint(point, fInitialLocation);
581 SetValue(ValueForPoint(point));
583 if (_Location() != fInitialLocation)
584 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
586 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
587 SetTracking(true);
588 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
589 } else {
590 // synchronous mouse tracking
591 BPoint prevPoint;
593 while (buttons) {
594 prevPoint = point;
596 snooze(SnoozeAmount());
597 GetMouse(&point, &buttons, true);
599 if (_ConstrainPoint(point, prevPoint)) {
600 int32 value = ValueForPoint(point);
601 if (value != Value()) {
602 SetValue(value);
603 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
607 if (_Location() != fInitialLocation)
608 Invoke();
613 void
614 BSlider::MouseUp(BPoint point)
616 if (IsTracking()) {
617 if (_Location() != fInitialLocation)
618 Invoke();
620 SetTracking(false);
621 } else
622 BControl::MouseUp(point);
626 void
627 BSlider::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
629 if (IsTracking()) {
630 if (_ConstrainPoint(point, _Location())) {
631 int32 value = ValueForPoint(point);
632 if (value != Value()) {
633 SetValue(value);
634 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
637 } else
638 BControl::MouseMoved(point, transit, message);
642 void
643 BSlider::Pulse()
645 BControl::Pulse();
649 void
650 BSlider::SetLabel(const char *label)
652 BControl::SetLabel(label);
656 void
657 BSlider::SetLimitLabels(const char *minLabel, const char *maxLabel)
659 free(fMinLimitLabel);
660 fMinLimitLabel = minLabel ? strdup(minLabel) : NULL;
662 free(fMaxLimitLabel);
663 fMaxLimitLabel = maxLabel ? strdup(maxLabel) : NULL;
665 InvalidateLayout();
667 // TODO: This is for backwards compatibility and should
668 // probably be removed when breaking binary compatiblity.
669 // Applications like our own Mouse rely on this behavior.
670 if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
671 ResizeToPreferred();
673 Invalidate();
677 const char*
678 BSlider::MinLimitLabel() const
680 return fMinLimitLabel;
684 const char*
685 BSlider::MaxLimitLabel() const
687 return fMaxLimitLabel;
691 void
692 BSlider::SetValue(int32 value)
694 if (value < fMinValue)
695 value = fMinValue;
696 if (value > fMaxValue)
697 value = fMaxValue;
699 if (value == Value())
700 return;
702 _SetLocationForValue(value);
704 BRect oldThumbFrame = ThumbFrame();
706 // While it would be enough to do this dependent on fUseFillColor,
707 // that doesn't work out if DrawBar() has been overridden by a sub class
708 if (fOrientation == B_HORIZONTAL)
709 oldThumbFrame.top = BarFrame().top;
710 else
711 oldThumbFrame.left = BarFrame().left;
713 BControl::SetValueNoUpdate(value);
714 BRect invalid = oldThumbFrame | ThumbFrame();
716 if (Style() == B_TRIANGLE_THUMB) {
717 // 1) We need to take care of pixels touched because of anti-aliasing.
718 // 2) We need to update the region with the focus mark as well. (A
719 // method BSlider::FocusMarkFrame() would be nice as well.)
720 if (fOrientation == B_HORIZONTAL) {
721 if (IsFocus())
722 invalid.bottom += 2;
723 invalid.InsetBy(-1, 0);
724 } else {
725 if (IsFocus())
726 invalid.left -= 2;
727 invalid.InsetBy(0, -1);
731 Invalidate(invalid);
733 UpdateTextChanged();
737 int32
738 BSlider::ValueForPoint(BPoint location) const
740 float min;
741 float max;
742 float position;
743 if (fOrientation == B_HORIZONTAL) {
744 min = _MinPosition();
745 max = _MaxPosition();
746 position = location.x;
747 } else {
748 max = _MinPosition();
749 min = _MaxPosition();
750 position = min + (max - location.y);
753 if (position < min)
754 position = min;
755 if (position > max)
756 position = max;
758 return (int32)roundf(((position - min) * (fMaxValue - fMinValue) / (max - min)) + fMinValue);
762 void
763 BSlider::SetPosition(float position)
765 if (position <= 0.0f)
766 SetValue(fMinValue);
767 else if (position >= 1.0f)
768 SetValue(fMaxValue);
769 else
770 SetValue((int32)(position * (fMaxValue - fMinValue) + fMinValue));
774 float
775 BSlider::Position() const
777 float range = (float)(fMaxValue - fMinValue);
778 if (range == 0.0f)
779 range = 1.0f;
781 return (float)(Value() - fMinValue) / range;
785 void
786 BSlider::SetEnabled(bool on)
788 BControl::SetEnabled(on);
792 void
793 BSlider::GetLimits(int32 *minimum, int32 *maximum) const
795 if (minimum != NULL)
796 *minimum = fMinValue;
797 if (maximum != NULL)
798 *maximum = fMaxValue;
802 // #pragma mark - drawing
805 void
806 BSlider::Draw(BRect updateRect)
808 // clear out background
809 BRegion background(updateRect);
810 background.Exclude(BarFrame());
811 bool drawBackground = true;
812 if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0) {
813 // This view is embedded somewhere, most likely the Tracker Desktop
814 // shelf.
815 drawBackground = false;
818 #if USE_OFF_SCREEN_VIEW
819 if (!fOffScreenBits)
820 return;
822 if (fOffScreenBits->Lock()) {
823 fOffScreenView->SetViewColor(ViewColor());
824 fOffScreenView->SetLowColor(LowColor());
825 #endif
827 if (drawBackground && background.Frame().IsValid())
828 OffscreenView()->FillRegion(&background, B_SOLID_LOW);
830 #if USE_OFF_SCREEN_VIEW
831 fOffScreenView->Sync();
832 fOffScreenBits->Unlock();
834 #endif
836 DrawSlider();
840 void
841 BSlider::DrawSlider()
843 if (LockLooper()) {
844 #if USE_OFF_SCREEN_VIEW
845 if (!fOffScreenBits)
846 return;
847 if (fOffScreenBits->Lock()) {
848 #endif
849 DrawBar();
850 DrawHashMarks();
851 DrawThumb();
852 DrawFocusMark();
853 DrawText();
855 #if USE_OFF_SCREEN_VIEW
856 fOffScreenView->Sync();
857 fOffScreenBits->Unlock();
859 DrawBitmap(fOffScreenBits, B_ORIGIN);
861 #endif
862 UnlockLooper();
867 void
868 BSlider::DrawBar()
870 BRect frame = BarFrame();
871 BView *view = OffscreenView();
873 if (be_control_look != NULL) {
874 uint32 flags = be_control_look->Flags(this);
875 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
876 rgb_color rightFillColor = fBarColor;
877 rgb_color leftFillColor = fUseFillColor ? fFillColor : fBarColor;
878 be_control_look->DrawSliderBar(view, frame, frame, base, leftFillColor,
879 rightFillColor, Position(), flags, fOrientation);
880 return;
883 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
884 rgb_color lightenmax;
885 rgb_color darken1;
886 rgb_color darken2;
887 rgb_color darkenmax;
889 rgb_color barColor;
890 rgb_color fillColor;
892 if (IsEnabled()) {
893 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT);
894 darken1 = tint_color(no_tint, B_DARKEN_1_TINT);
895 darken2 = tint_color(no_tint, B_DARKEN_2_TINT);
896 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT);
897 barColor = fBarColor;
898 fillColor = fFillColor;
899 } else {
900 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT);
901 darken1 = no_tint;
902 darken2 = tint_color(no_tint, B_DARKEN_1_TINT);
903 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT);
905 barColor.red = (fBarColor.red + no_tint.red) / 2;
906 barColor.green = (fBarColor.green + no_tint.green) / 2;
907 barColor.blue = (fBarColor.blue + no_tint.blue) / 2;
908 barColor.alpha = 255;
910 fillColor.red = (fFillColor.red + no_tint.red) / 2;
911 fillColor.green = (fFillColor.green + no_tint.green) / 2;
912 fillColor.blue = (fFillColor.blue + no_tint.blue) / 2;
913 fillColor.alpha = 255;
916 // exclude the block thumb from the bar filling
918 BRect lowerFrame = frame.InsetByCopy(1, 1);
919 lowerFrame.top++;
920 lowerFrame.left++;
921 BRect upperFrame = lowerFrame;
922 BRect thumbFrame;
924 if (Style() == B_BLOCK_THUMB) {
925 thumbFrame = ThumbFrame();
927 if (fOrientation == B_HORIZONTAL) {
928 lowerFrame.right = thumbFrame.left;
929 upperFrame.left = thumbFrame.right;
930 } else {
931 lowerFrame.top = thumbFrame.bottom;
932 upperFrame.bottom = thumbFrame.top;
934 } else if (fUseFillColor) {
935 if (fOrientation == B_HORIZONTAL) {
936 lowerFrame.right = floor(lowerFrame.left - 1 + Position()
937 * (lowerFrame.Width() + 1));
938 upperFrame.left = lowerFrame.right;
939 } else {
940 lowerFrame.top = floor(lowerFrame.bottom + 1 - Position()
941 * (lowerFrame.Height() + 1));
942 upperFrame.bottom = lowerFrame.top;
946 view->SetHighColor(barColor);
947 view->FillRect(upperFrame);
949 if (Style() == B_BLOCK_THUMB || fUseFillColor) {
950 if (fUseFillColor)
951 view->SetHighColor(fillColor);
952 view->FillRect(lowerFrame);
955 if (Style() == B_BLOCK_THUMB) {
956 // We don't want to stroke the lines over the thumb
958 PushState();
960 BRegion region;
961 GetClippingRegion(&region);
962 region.Exclude(thumbFrame);
963 ConstrainClippingRegion(&region);
966 view->SetHighColor(darken1);
967 view->StrokeLine(BPoint(frame.left, frame.top),
968 BPoint(frame.left + 1.0f, frame.top));
969 view->StrokeLine(BPoint(frame.left, frame.bottom),
970 BPoint(frame.left + 1.0f, frame.bottom));
971 view->StrokeLine(BPoint(frame.right - 1.0f, frame.top),
972 BPoint(frame.right, frame.top));
974 view->SetHighColor(darken2);
975 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
976 BPoint(frame.right - 1.0f, frame.top));
977 view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f),
978 BPoint(frame.left, frame.top + 1.0f));
980 view->SetHighColor(lightenmax);
981 view->StrokeLine(BPoint(frame.left + 1.0f, frame.bottom),
982 BPoint(frame.right, frame.bottom));
983 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
984 BPoint(frame.right, frame.top + 1.0f));
986 frame.InsetBy(1.0f, 1.0f);
988 view->SetHighColor(darkenmax);
989 view->StrokeLine(BPoint(frame.left, frame.bottom),
990 BPoint(frame.left, frame.top));
991 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
992 BPoint(frame.right, frame.top));
994 if (Style() == B_BLOCK_THUMB)
995 PopState();
999 void
1000 BSlider::DrawHashMarks()
1002 if (fHashMarks == B_HASH_MARKS_NONE)
1003 return;
1005 BRect frame = HashMarksFrame();
1006 BView* view = OffscreenView();
1008 if (be_control_look != NULL) {
1009 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1010 uint32 flags = be_control_look->Flags(this);
1011 be_control_look->DrawSliderHashMarks(view, frame, frame, base,
1012 fHashMarkCount, fHashMarks, flags, fOrientation);
1013 return;
1016 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
1017 rgb_color lightenmax;
1018 rgb_color darken2;
1020 if (IsEnabled()) {
1021 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT);
1022 darken2 = tint_color(no_tint, B_DARKEN_2_TINT);
1023 } else {
1024 lightenmax = tint_color(no_tint, B_LIGHTEN_2_TINT);
1025 darken2 = tint_color(no_tint, B_DARKEN_1_TINT);
1028 float pos = _MinPosition();
1029 int32 hashMarkCount = max_c(fHashMarkCount, 2);
1030 // draw at least two hashmarks at min/max if
1031 // fHashMarks != B_HASH_MARKS_NONE
1032 float factor = (_MaxPosition() - pos) / (hashMarkCount - 1);
1034 if (fHashMarks & B_HASH_MARKS_TOP) {
1036 view->BeginLineArray(hashMarkCount * 2);
1038 if (fOrientation == B_HORIZONTAL) {
1039 for (int32 i = 0; i < hashMarkCount; i++) {
1040 view->AddLine(BPoint(pos, frame.top),
1041 BPoint(pos, frame.top + 5), darken2);
1042 view->AddLine(BPoint(pos + 1, frame.top),
1043 BPoint(pos + 1, frame.top + 5), lightenmax);
1045 pos += factor;
1047 } else {
1048 for (int32 i = 0; i < hashMarkCount; i++) {
1049 view->AddLine(BPoint(frame.left, pos),
1050 BPoint(frame.left + 5, pos), darken2);
1051 view->AddLine(BPoint(frame.left, pos + 1),
1052 BPoint(frame.left + 5, pos + 1), lightenmax);
1054 pos += factor;
1058 view->EndLineArray();
1061 pos = _MinPosition();
1063 if (fHashMarks & B_HASH_MARKS_BOTTOM) {
1065 view->BeginLineArray(hashMarkCount * 2);
1067 if (fOrientation == B_HORIZONTAL) {
1068 for (int32 i = 0; i < hashMarkCount; i++) {
1069 view->AddLine(BPoint(pos, frame.bottom - 5),
1070 BPoint(pos, frame.bottom), darken2);
1071 view->AddLine(BPoint(pos + 1, frame.bottom - 5),
1072 BPoint(pos + 1, frame.bottom), lightenmax);
1074 pos += factor;
1076 } else {
1077 for (int32 i = 0; i < hashMarkCount; i++) {
1078 view->AddLine(BPoint(frame.right - 5, pos),
1079 BPoint(frame.right, pos), darken2);
1080 view->AddLine(BPoint(frame.right - 5, pos + 1),
1081 BPoint(frame.right, pos + 1), lightenmax);
1083 pos += factor;
1087 view->EndLineArray();
1092 void
1093 BSlider::DrawThumb()
1095 if (Style() == B_BLOCK_THUMB)
1096 _DrawBlockThumb();
1097 else
1098 _DrawTriangleThumb();
1102 void
1103 BSlider::DrawFocusMark()
1105 if (!IsFocus())
1106 return;
1108 OffscreenView()->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
1110 BRect frame = ThumbFrame();
1112 if (fStyle == B_BLOCK_THUMB) {
1113 frame.left += 2.0f;
1114 frame.top += 2.0f;
1115 frame.right -= 3.0f;
1116 frame.bottom -= 3.0f;
1117 OffscreenView()->StrokeRect(frame);
1118 } else {
1119 if (fOrientation == B_HORIZONTAL) {
1120 OffscreenView()->StrokeLine(BPoint(frame.left, frame.bottom + 2.0f),
1121 BPoint(frame.right, frame.bottom + 2.0f));
1122 } else {
1123 OffscreenView()->StrokeLine(BPoint(frame.left - 2.0f, frame.top),
1124 BPoint(frame.left - 2.0f, frame.bottom));
1130 void
1131 BSlider::DrawText()
1133 BRect bounds(Bounds());
1134 BView *view = OffscreenView();
1136 rgb_color base = LowColor();
1137 uint32 flags = 0;
1138 if (be_control_look == NULL) {
1139 if (IsEnabled()) {
1140 view->SetHighColor(0, 0, 0);
1141 } else {
1142 view->SetHighColor(tint_color(LowColor(), B_DISABLED_LABEL_TINT));
1144 } else
1145 flags = be_control_look->Flags(this);
1147 font_height fontHeight;
1148 GetFontHeight(&fontHeight);
1149 if (Orientation() == B_HORIZONTAL) {
1150 if (Label()) {
1151 if (be_control_look == NULL) {
1152 view->DrawString(Label(),
1153 BPoint(0.0, ceilf(fontHeight.ascent)));
1154 } else {
1155 be_control_look->DrawLabel(view, Label(), base, flags,
1156 BPoint(0.0, ceilf(fontHeight.ascent)));
1160 // the update text is updated in SetValue() only
1161 if (fUpdateText != NULL) {
1162 if (be_control_look == NULL) {
1163 view->DrawString(fUpdateText, BPoint(bounds.right
1164 - StringWidth(fUpdateText), ceilf(fontHeight.ascent)));
1165 } else {
1166 be_control_look->DrawLabel(view, fUpdateText, base, flags,
1167 BPoint(bounds.right - StringWidth(fUpdateText),
1168 ceilf(fontHeight.ascent)));
1172 if (fMinLimitLabel) {
1173 if (be_control_look == NULL) {
1174 view->DrawString(fMinLimitLabel, BPoint(0.0, bounds.bottom
1175 - fontHeight.descent));
1176 } else {
1177 be_control_look->DrawLabel(view, fMinLimitLabel, base, flags,
1178 BPoint(0.0, bounds.bottom - fontHeight.descent));
1182 if (fMaxLimitLabel) {
1183 if (be_control_look == NULL) {
1184 view->DrawString(fMaxLimitLabel, BPoint(bounds.right
1185 - StringWidth(fMaxLimitLabel), bounds.bottom
1186 - fontHeight.descent));
1187 } else {
1188 be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags,
1189 BPoint(bounds.right - StringWidth(fMaxLimitLabel),
1190 bounds.bottom - fontHeight.descent));
1193 } else {
1194 float lineHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)
1195 + ceilf(fontHeight.leading);
1196 float baseLine = ceilf(fontHeight.ascent);
1198 if (Label()) {
1199 if (be_control_look == NULL) {
1200 view->DrawString(Label(), BPoint((bounds.Width()
1201 - StringWidth(Label())) / 2.0, baseLine));
1202 } else {
1203 be_control_look->DrawLabel(view, Label(), base, flags,
1204 BPoint((bounds.Width() - StringWidth(Label())) / 2.0,
1205 baseLine));
1207 baseLine += lineHeight;
1210 if (fMaxLimitLabel) {
1211 if (be_control_look == NULL) {
1212 view->DrawString(fMaxLimitLabel, BPoint((bounds.Width()
1213 - StringWidth(fMaxLimitLabel)) / 2.0, baseLine));
1214 } else {
1215 be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags,
1216 BPoint((bounds.Width()
1217 - StringWidth(fMaxLimitLabel)) / 2.0, baseLine));
1221 baseLine = bounds.bottom - ceilf(fontHeight.descent);
1223 if (fMinLimitLabel) {
1224 if (be_control_look == NULL) {
1225 view->DrawString(fMinLimitLabel, BPoint((bounds.Width()
1226 - StringWidth(fMinLimitLabel)) / 2.0, baseLine));
1227 } else {
1228 be_control_look->DrawLabel(view, fMinLimitLabel, base, flags,
1229 BPoint((bounds.Width()
1230 - StringWidth(fMinLimitLabel)) / 2.0, baseLine));
1232 baseLine -= lineHeight;
1235 if (fUpdateText != NULL) {
1236 if (be_control_look == NULL) {
1237 view->DrawString(fUpdateText, BPoint((bounds.Width()
1238 - StringWidth(fUpdateText)) / 2.0, baseLine));
1239 } else {
1240 be_control_look->DrawLabel(view, fUpdateText, base, flags,
1241 BPoint((bounds.Width()
1242 - StringWidth(fUpdateText)) / 2.0, baseLine));
1249 // #pragma mark -
1252 const char*
1253 BSlider::UpdateText() const
1255 return NULL;
1259 void
1260 BSlider::UpdateTextChanged()
1262 // update text label
1263 float oldWidth = 0.0;
1264 if (fUpdateText != NULL)
1265 oldWidth = StringWidth(fUpdateText);
1267 const char* oldUpdateText = fUpdateText;
1268 fUpdateText = UpdateText();
1269 bool updateTextOnOff = (fUpdateText == NULL && oldUpdateText != NULL)
1270 || (fUpdateText != NULL && oldUpdateText == NULL);
1272 float newWidth = 0.0;
1273 if (fUpdateText != NULL)
1274 newWidth = StringWidth(fUpdateText);
1276 float width = ceilf(max_c(newWidth, oldWidth)) + 2.0f;
1277 if (width != 0) {
1278 font_height fontHeight;
1279 GetFontHeight(&fontHeight);
1281 float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1282 float lineHeight = height + ceilf(fontHeight.leading);
1283 BRect invalid(Bounds());
1284 if (fOrientation == B_HORIZONTAL)
1285 invalid = BRect(invalid.right - width, 0, invalid.right, height);
1286 else {
1287 if (!updateTextOnOff) {
1288 invalid.left = (invalid.left + invalid.right - width) / 2;
1289 invalid.right = invalid.left + width;
1290 if (fMinLimitLabel)
1291 invalid.bottom -= lineHeight;
1292 invalid.top = invalid.bottom - height;
1295 Invalidate(invalid);
1298 float oldMaxUpdateTextWidth = fMaxUpdateTextWidth;
1299 fMaxUpdateTextWidth = MaxUpdateTextWidth();
1300 if (oldMaxUpdateTextWidth != fMaxUpdateTextWidth)
1301 InvalidateLayout();
1305 BRect
1306 BSlider::BarFrame() const
1308 BRect frame(Bounds());
1310 font_height fontHeight;
1311 GetFontHeight(&fontHeight);
1313 float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1314 float leading = ceilf(fontHeight.leading);
1316 float thumbInset;
1317 if (fStyle == B_BLOCK_THUMB)
1318 thumbInset = 8.0;
1319 else
1320 thumbInset = 7.0;
1322 if (Orientation() == B_HORIZONTAL) {
1323 frame.left = thumbInset;
1324 frame.top = 6.0 + (Label() || fUpdateText ? textHeight + 4.0 : 0.0);
1325 frame.right -= thumbInset;
1326 frame.bottom = frame.top + fBarThickness;
1327 } else {
1328 frame.left = floorf((frame.Width() - fBarThickness) / 2.0);
1329 frame.top = thumbInset;
1330 if (Label())
1331 frame.top += textHeight;
1332 if (fMaxLimitLabel) {
1333 frame.top += textHeight;
1334 if (Label())
1335 frame.top += leading;
1338 frame.right = frame.left + fBarThickness;
1339 frame.bottom = frame.bottom - thumbInset;
1340 if (fMinLimitLabel)
1341 frame.bottom -= textHeight;
1342 if (fUpdateText) {
1343 frame.bottom -= textHeight;
1344 if (fMinLimitLabel)
1345 frame.bottom -= leading;
1349 return frame;
1353 BRect
1354 BSlider::HashMarksFrame() const
1356 BRect frame(BarFrame());
1358 if (fOrientation == B_HORIZONTAL) {
1359 frame.top -= 6.0;
1360 frame.bottom += 6.0;
1361 } else {
1362 frame.left -= 6.0;
1363 frame.right += 6.0;
1366 return frame;
1370 BRect
1371 BSlider::ThumbFrame() const
1373 // TODO: The slider looks really ugly and broken when it is too little.
1374 // I would suggest using BarFrame() here to get the top and bottom coords
1375 // and spread them further apart for the thumb
1377 BRect frame = Bounds();
1379 font_height fontHeight;
1380 GetFontHeight(&fontHeight);
1382 float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1384 if (fStyle == B_BLOCK_THUMB) {
1385 if (Orientation() == B_HORIZONTAL) {
1386 frame.left = floorf(Position() * (_MaxPosition()
1387 - _MinPosition()) + _MinPosition()) - 8;
1388 frame.top = 2 + (Label() || fUpdateText ? textHeight + 4 : 0);
1389 frame.right = frame.left + 17;
1390 frame.bottom = frame.top + fBarThickness + 7;
1391 } else {
1392 frame.left = floor((frame.Width() - fBarThickness) / 2) - 4;
1393 frame.top = floorf(Position() * (_MaxPosition()
1394 - _MinPosition()) + _MinPosition()) - 8;
1395 frame.right = frame.left + fBarThickness + 7;
1396 frame.bottom = frame.top + 17;
1398 } else {
1399 if (Orientation() == B_HORIZONTAL) {
1400 frame.left = floorf(Position() * (_MaxPosition()
1401 - _MinPosition()) + _MinPosition()) - 6;
1402 frame.right = frame.left + 12;
1403 frame.top = 3 + fBarThickness + (Label() ? textHeight + 4 : 0);
1404 frame.bottom = frame.top + 8;
1405 } else {
1406 frame.left = floorf((frame.Width() + fBarThickness) / 2) - 3;
1407 frame.top = floorf(Position() * (_MaxPosition()
1408 - _MinPosition())) + _MinPosition() - 6;
1409 frame.right = frame.left + 8;
1410 frame.bottom = frame.top + 12;
1414 return frame;
1418 void
1419 BSlider::SetFlags(uint32 flags)
1421 BControl::SetFlags(flags);
1425 void
1426 BSlider::SetResizingMode(uint32 mode)
1428 BControl::SetResizingMode(mode);
1432 void
1433 BSlider::GetPreferredSize(float* _width, float* _height)
1435 BSize preferredSize = PreferredSize();
1437 if (Orientation() == B_HORIZONTAL) {
1438 if (_width != NULL) {
1439 // NOTE: For compatibility reasons, a horizontal BSlider
1440 // never shrinks horizontally. This only affects applications
1441 // which do not use the new layout system.
1442 *_width = max_c(Bounds().Width(), preferredSize.width);
1445 if (_height != NULL)
1446 *_height = preferredSize.height;
1447 } else {
1448 if (_width != NULL)
1449 *_width = preferredSize.width;
1451 if (_height != NULL) {
1452 // NOTE: Similarly, a vertical BSlider never shrinks
1453 // vertically. This only affects applications which do not
1454 // use the new layout system.
1455 *_height = max_c(Bounds().Height(), preferredSize.height);
1461 void
1462 BSlider::ResizeToPreferred()
1464 BControl::ResizeToPreferred();
1468 status_t
1469 BSlider::Invoke(BMessage* message)
1471 return BControl::Invoke(message);
1475 BHandler*
1476 BSlider::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
1477 int32 command, const char *property)
1479 return BControl::ResolveSpecifier(message, index, specifier, command,
1480 property);
1484 status_t
1485 BSlider::GetSupportedSuites(BMessage* message)
1487 return BControl::GetSupportedSuites(message);
1491 void
1492 BSlider::SetModificationMessage(BMessage* message)
1494 delete fModificationMessage;
1495 fModificationMessage = message;
1499 BMessage*
1500 BSlider::ModificationMessage() const
1502 return fModificationMessage;
1506 void
1507 BSlider::SetSnoozeAmount(int32 snoozeTime)
1509 if (snoozeTime < 10000)
1510 snoozeTime = 10000;
1511 else if (snoozeTime > 1000000)
1512 snoozeTime = 1000000;
1514 fSnoozeAmount = snoozeTime;
1518 int32
1519 BSlider::SnoozeAmount() const
1521 return fSnoozeAmount;
1525 void
1526 BSlider::SetKeyIncrementValue(int32 incrementValue)
1528 fKeyIncrementValue = incrementValue;
1532 int32
1533 BSlider::KeyIncrementValue() const
1535 return fKeyIncrementValue;
1539 void
1540 BSlider::SetHashMarkCount(int32 hashMarkCount)
1542 fHashMarkCount = hashMarkCount;
1543 Invalidate();
1547 int32
1548 BSlider::HashMarkCount() const
1550 return fHashMarkCount;
1554 void
1555 BSlider::SetHashMarks(hash_mark_location where)
1557 fHashMarks = where;
1558 // TODO: enable if the hashmark look is influencing the control size!
1559 // InvalidateLayout();
1560 Invalidate();
1564 hash_mark_location
1565 BSlider::HashMarks() const
1567 return fHashMarks;
1571 void
1572 BSlider::SetStyle(thumb_style style)
1574 fStyle = style;
1575 InvalidateLayout();
1576 Invalidate();
1580 thumb_style
1581 BSlider::Style() const
1583 return fStyle;
1587 void
1588 BSlider::SetBarColor(rgb_color barColor)
1590 fBarColor = barColor;
1591 Invalidate(BarFrame());
1595 rgb_color
1596 BSlider::BarColor() const
1598 return fBarColor;
1602 void
1603 BSlider::UseFillColor(bool useFill, const rgb_color* barColor)
1605 fUseFillColor = useFill;
1607 if (useFill && barColor)
1608 fFillColor = *barColor;
1610 Invalidate(BarFrame());
1614 bool
1615 BSlider::FillColor(rgb_color* barColor) const
1617 if (barColor && fUseFillColor)
1618 *barColor = fFillColor;
1620 return fUseFillColor;
1624 BView*
1625 BSlider::OffscreenView() const
1627 #if USE_OFF_SCREEN_VIEW
1628 return fOffScreenView;
1629 #else
1630 return (BView*)this;
1631 #endif
1635 orientation
1636 BSlider::Orientation() const
1638 return fOrientation;
1642 void
1643 BSlider::SetOrientation(orientation posture)
1645 if (fOrientation == posture)
1646 return;
1648 fOrientation = posture;
1649 InvalidateLayout();
1650 Invalidate();
1654 float
1655 BSlider::BarThickness() const
1657 return fBarThickness;
1661 void
1662 BSlider::SetBarThickness(float thickness)
1664 if (thickness < 1.0)
1665 thickness = 1.0;
1666 else
1667 thickness = roundf(thickness);
1669 if (thickness != fBarThickness) {
1670 // calculate invalid barframe and extend by hashmark size
1671 float hInset = 0.0;
1672 float vInset = 0.0;
1673 if (fOrientation == B_HORIZONTAL)
1674 vInset = -6.0;
1675 else
1676 hInset = -6.0;
1677 BRect invalid = BarFrame().InsetByCopy(hInset, vInset) | ThumbFrame();
1679 fBarThickness = thickness;
1681 invalid = invalid | BarFrame().InsetByCopy(hInset, vInset)
1682 | ThumbFrame();
1683 Invalidate(invalid);
1684 InvalidateLayout();
1689 void
1690 BSlider::SetFont(const BFont *font, uint32 properties)
1692 BControl::SetFont(font, properties);
1694 #if USE_OFF_SCREEN_VIEW
1695 if (fOffScreenView && fOffScreenBits) {
1696 if (fOffScreenBits->Lock()) {
1697 fOffScreenView->SetFont(font, properties);
1698 fOffScreenBits->Unlock();
1701 #endif
1703 InvalidateLayout();
1707 void
1708 BSlider::SetLimits(int32 minimum, int32 maximum)
1710 if (minimum <= maximum) {
1711 fMinValue = minimum;
1712 fMaxValue = maximum;
1714 int32 value = Value();
1715 value = max_c(minimum, value);
1716 value = min_c(maximum, value);
1718 if (value != Value()) {
1719 SetValue(value);
1725 float
1726 BSlider::MaxUpdateTextWidth()
1728 // very simplistic implementation that assumes the string will be widest
1729 // at the maximum value
1730 int32 value = Value();
1731 SetValueNoUpdate(fMaxValue);
1732 float width = StringWidth(UpdateText());
1733 SetValueNoUpdate(value);
1734 // in case the derived class uses a fixed buffer, the contents
1735 // should be reset for the old value
1736 UpdateText();
1737 return width;
1741 // #pragma mark - layout related
1744 BSize
1745 BSlider::MinSize()
1747 return BLayoutUtils::ComposeSize(ExplicitMinSize(),
1748 _ValidateMinSize());
1752 BSize
1753 BSlider::MaxSize()
1755 BSize maxSize = _ValidateMinSize();
1756 if (fOrientation == B_HORIZONTAL)
1757 maxSize.width = B_SIZE_UNLIMITED;
1758 else
1759 maxSize.height = B_SIZE_UNLIMITED;
1760 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize);
1764 BSize
1765 BSlider::PreferredSize()
1767 BSize preferredSize = _ValidateMinSize();
1768 if (fOrientation == B_HORIZONTAL)
1769 preferredSize.width = max_c(100.0, preferredSize.width);
1770 else
1771 preferredSize.height = max_c(100.0, preferredSize.height);
1772 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), preferredSize);
1776 status_t
1777 BSlider::SetIcon(const BBitmap* icon, uint32 flags)
1779 return BControl::SetIcon(icon, flags);
1783 void
1784 BSlider::LayoutInvalidated(bool descendants)
1786 // invalidate cached preferred size
1787 fMinSize.Set(-1, -1);
1791 // #pragma mark - private
1793 void
1794 BSlider::_DrawBlockThumb()
1796 BRect frame = ThumbFrame();
1797 BView *view = OffscreenView();
1799 if (be_control_look != NULL) {
1800 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1801 uint32 flags = be_control_look->Flags(this);
1802 be_control_look->DrawSliderThumb(view, frame, frame, base, flags,
1803 fOrientation);
1804 return;
1807 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
1808 rgb_color lighten2;
1809 rgb_color lighten1;
1810 rgb_color darken2;
1811 rgb_color darken3;
1812 rgb_color darkenmax;
1814 if (IsEnabled()) {
1815 lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT);
1816 lighten1 = no_tint;
1817 darken2 = tint_color(no_tint, B_DARKEN_2_TINT);
1818 darken3 = tint_color(no_tint, B_DARKEN_3_TINT);
1819 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT);
1820 } else {
1821 lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT);
1822 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT);
1823 darken2 = tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0);
1824 darken3 = tint_color(no_tint, B_DARKEN_1_TINT);
1825 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT);
1828 // blank background for shadow
1829 // ToDo: this also draws over the hash marks (though it's not *that* noticeable)
1830 view->SetHighColor(no_tint);
1831 view->StrokeLine(BPoint(frame.left, frame.top),
1832 BPoint(frame.left, frame.top));
1834 BRect barFrame = BarFrame();
1835 if (barFrame.right >= frame.right) {
1836 // leave out barFrame from shadow background clearing
1837 view->StrokeLine(BPoint(frame.right, frame.top),
1838 BPoint(frame.right, barFrame.top - 1.0f));
1839 view->StrokeLine(BPoint(frame.right, barFrame.bottom + 1.0f),
1840 BPoint(frame.right, frame.bottom));
1841 } else {
1842 view->StrokeLine(BPoint(frame.right, frame.top),
1843 BPoint(frame.right, frame.bottom));
1846 view->StrokeLine(BPoint(frame.left, frame.bottom),
1847 BPoint(frame.right - 1.0f, frame.bottom));
1848 view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f),
1849 BPoint(frame.left, frame.bottom - 1.0f));
1850 view->StrokeLine(BPoint(frame.right - 1.0f, frame.top),
1851 BPoint(frame.right - 1.0f, frame.top));
1853 // Outline (top, left)
1854 view->SetHighColor(darken3);
1855 view->StrokeLine(BPoint(frame.left, frame.bottom - 2.0f),
1856 BPoint(frame.left, frame.top + 1.0f));
1857 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
1858 BPoint(frame.right - 2.0f, frame.top));
1860 // Shadow
1861 view->SetHighColor(0, 0, 0, IsEnabled() ? 100 : 50);
1862 view->SetDrawingMode(B_OP_ALPHA);
1863 view->StrokeLine(BPoint(frame.right, frame.top + 2.0f),
1864 BPoint(frame.right, frame.bottom - 1.0f));
1865 view->StrokeLine(BPoint(frame.left + 2.0f, frame.bottom),
1866 BPoint(frame.right - 1.0f, frame.bottom));
1868 view->SetDrawingMode(B_OP_COPY);
1869 view->SetHighColor(darken3);
1870 view->StrokeLine(BPoint(frame.right - 1.0f, frame.bottom - 1.0f),
1871 BPoint(frame.right - 1.0f, frame.bottom - 1.0f));
1874 // First bevel
1875 frame.InsetBy(1.0f, 1.0f);
1877 view->SetHighColor(darkenmax);
1878 view->StrokeLine(BPoint(frame.left, frame.bottom),
1879 BPoint(frame.right - 1.0f, frame.bottom));
1880 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
1881 BPoint(frame.right, frame.top));
1883 view->SetHighColor(lighten2);
1884 view->StrokeLine(BPoint(frame.left, frame.top),
1885 BPoint(frame.left, frame.bottom - 1.0f));
1886 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
1887 BPoint(frame.right - 1.0f, frame.top));
1889 frame.InsetBy(1.0f, 1.0f);
1891 view->FillRect(BRect(frame.left, frame.top, frame.right - 1.0f, frame.bottom - 1.0f));
1893 // Second bevel and center dots
1894 view->SetHighColor(darken2);
1895 view->StrokeLine(BPoint(frame.left, frame.bottom),
1896 BPoint(frame.right, frame.bottom));
1897 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
1898 BPoint(frame.right, frame.top));
1900 if (Orientation() == B_HORIZONTAL) {
1901 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 2.0f),
1902 BPoint(frame.left + 6.0f, frame.top + 2.0f));
1903 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 4.0f),
1904 BPoint(frame.left + 6.0f, frame.top + 4.0f));
1905 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f),
1906 BPoint(frame.left + 6.0f, frame.top + 6.0f));
1907 } else {
1908 view->StrokeLine(BPoint(frame.left + 2.0f, frame.top + 6.0f),
1909 BPoint(frame.left + 2.0f, frame.top + 6.0f));
1910 view->StrokeLine(BPoint(frame.left + 4.0f, frame.top + 6.0f),
1911 BPoint(frame.left + 4.0f, frame.top + 6.0f));
1912 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f),
1913 BPoint(frame.left + 6.0f, frame.top + 6.0f));
1916 frame.InsetBy(1.0f, 1.0f);
1918 // Third bevel
1919 view->SetHighColor(lighten1);
1920 view->StrokeLine(BPoint(frame.left, frame.bottom),
1921 BPoint(frame.right, frame.bottom));
1922 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
1923 BPoint(frame.right, frame.top));
1927 void
1928 BSlider::_DrawTriangleThumb()
1930 BRect frame = ThumbFrame();
1931 BView *view = OffscreenView();
1933 if (be_control_look != NULL) {
1934 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1935 uint32 flags = be_control_look->Flags(this);
1936 be_control_look->DrawSliderTriangle(view, frame, frame, base, flags,
1937 fOrientation);
1938 return;
1941 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
1942 rgb_color lightenmax;
1943 rgb_color lighten1;
1944 rgb_color darken2;
1945 rgb_color darken3;
1946 rgb_color darkenmax;
1948 if (IsEnabled()) {
1949 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT);
1950 lighten1 = no_tint;
1951 darken2 = tint_color(no_tint, B_DARKEN_2_TINT);
1952 darken3 = tint_color(no_tint, B_DARKEN_3_TINT);
1953 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT);
1954 } else {
1955 lightenmax = tint_color(no_tint, B_LIGHTEN_2_TINT);
1956 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT);
1957 darken2 = tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2);
1958 darken3 = tint_color(no_tint, B_DARKEN_1_TINT);
1959 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT);
1962 if (Orientation() == B_HORIZONTAL) {
1963 view->SetHighColor(lighten1);
1964 view->FillTriangle(
1965 BPoint(frame.left + 1, frame.bottom - 3),
1966 BPoint((frame.left + frame.right) / 2, frame.top + 1),
1967 BPoint(frame.right - 1, frame.bottom - 3));
1969 view->SetHighColor(no_tint);
1970 view->StrokeLine(BPoint(frame.right - 2, frame.bottom - 3),
1971 BPoint(frame.left + 3, frame.bottom - 3));
1973 view->SetHighColor(darkenmax);
1974 view->StrokeLine(BPoint(frame.left, frame.bottom - 1),
1975 BPoint(frame.right, frame.bottom - 1));
1976 view->StrokeLine(BPoint(frame.right, frame.bottom - 2),
1977 BPoint((frame.left + frame.right) / 2, frame.top));
1979 view->SetHighColor(darken2);
1980 view->StrokeLine(BPoint(frame.right - 1, frame.bottom - 2),
1981 BPoint(frame.left + 1, frame.bottom - 2));
1982 view->SetHighColor(darken3);
1983 view->StrokeLine(BPoint(frame.left, frame.bottom - 2),
1984 BPoint((frame.left + frame.right) / 2 - 1, frame.top + 1));
1986 view->SetHighColor(lightenmax);
1987 view->StrokeLine(BPoint(frame.left + 2, frame.bottom - 3),
1988 BPoint((frame.left + frame.right) / 2, frame.top + 1));
1990 // Shadow
1991 view->SetHighColor(0, 0, 0, IsEnabled() ? 80 : 40);
1992 view->SetDrawingMode(B_OP_ALPHA);
1993 view->StrokeLine(BPoint(frame.left + 1, frame.bottom),
1994 BPoint(frame.right, frame.bottom));
1995 } else {
1996 view->SetHighColor(lighten1);
1997 view->FillTriangle(
1998 BPoint(frame.left, (frame.top + frame.bottom) / 2),
1999 BPoint(frame.right - 1, frame.top + 1),
2000 BPoint(frame.right - 1, frame.bottom - 1));
2002 view->SetHighColor(darkenmax);
2003 view->StrokeLine(BPoint(frame.right - 1, frame.top),
2004 BPoint(frame.right - 1, frame.bottom));
2005 view->StrokeLine(BPoint(frame.right - 1, frame.bottom),
2006 BPoint(frame.right - 2, frame.bottom));
2008 view->SetHighColor(darken2);
2009 view->StrokeLine(BPoint(frame.right - 2, frame.top + 2),
2010 BPoint(frame.right - 2, frame.bottom - 1));
2011 view->StrokeLine(
2012 BPoint(frame.left, (frame.top + frame.bottom) / 2),
2013 BPoint(frame.right - 2, frame.top));
2014 view->SetHighColor(darken3);
2015 view->StrokeLine(
2016 BPoint(frame.left + 1, (frame.top + frame.bottom) / 2 + 1),
2017 BPoint(frame.right - 3, frame.bottom - 1));
2019 view->SetHighColor(lightenmax);
2020 view->StrokeLine(
2021 BPoint(frame.left + 1, (frame.top + frame.bottom) / 2),
2022 BPoint(frame.right - 2, frame.top + 1));
2024 // Shadow
2025 view->SetHighColor(0, 0, 0, IsEnabled() ? 80 : 40);
2026 view->SetDrawingMode(B_OP_ALPHA);
2027 view->StrokeLine(BPoint(frame.right, frame.top + 1),
2028 BPoint(frame.right, frame.bottom));
2031 view->SetDrawingMode(B_OP_COPY);
2035 BPoint
2036 BSlider::_Location() const
2038 return fLocation;
2042 void
2043 BSlider::_SetLocationForValue(int32 value)
2045 BPoint loc;
2046 float range = (float)(fMaxValue - fMinValue);
2047 if (range == 0)
2048 range = 1;
2050 float pos = (float)(value - fMinValue) / range *
2051 (_MaxPosition() - _MinPosition());
2053 if (fOrientation == B_HORIZONTAL) {
2054 loc.x = ceil(_MinPosition() + pos);
2055 loc.y = 0;
2056 } else {
2057 loc.x = 0;
2058 loc.y = floor(_MaxPosition() - pos);
2060 fLocation = loc;
2064 float
2065 BSlider::_MinPosition() const
2067 if (fOrientation == B_HORIZONTAL)
2068 return BarFrame().left + 1.0f;
2070 return BarFrame().bottom - 1.0f;
2074 float
2075 BSlider::_MaxPosition() const
2077 if (fOrientation == B_HORIZONTAL)
2078 return BarFrame().right - 1.0f;
2080 return BarFrame().top + 1.0f;
2084 BSize
2085 BSlider::_ValidateMinSize()
2087 if (fMinSize.width >= 0) {
2088 // the preferred size is up to date
2089 return fMinSize;
2092 font_height fontHeight;
2093 GetFontHeight(&fontHeight);
2095 float width = 0.0;
2096 float height = 0.0;
2098 if (fMaxUpdateTextWidth < 0.0)
2099 fMaxUpdateTextWidth = MaxUpdateTextWidth();
2101 if (Orientation() == B_HORIZONTAL) {
2102 height = 12.0 + fBarThickness;
2103 int32 rows = 0;
2105 float labelWidth = 0;
2106 int32 labelRows = 0;
2107 float labelSpacing = StringWidth("M") * 2;
2108 if (Label()) {
2109 labelWidth = StringWidth(Label());
2110 labelRows = 1;
2112 if (fMaxUpdateTextWidth > 0.0) {
2113 if (labelWidth > 0)
2114 labelWidth += labelSpacing;
2115 labelWidth += fMaxUpdateTextWidth;
2116 labelRows = 1;
2118 rows += labelRows;
2120 if (MinLimitLabel())
2121 width = StringWidth(MinLimitLabel());
2122 if (MaxLimitLabel()) {
2123 // some space between the labels
2124 if (MinLimitLabel())
2125 width += labelSpacing;
2127 width += StringWidth(MaxLimitLabel());
2130 if (labelWidth > width)
2131 width = labelWidth;
2132 if (width < 32.0)
2133 width = 32.0;
2135 if (MinLimitLabel() || MaxLimitLabel())
2136 rows++;
2138 height += rows * (ceilf(fontHeight.ascent)
2139 + ceilf(fontHeight.descent) + 4.0);
2140 } else {
2141 // B_VERTICAL
2142 width = 12.0 + fBarThickness;
2143 height = 32.0;
2145 float lineHeightNoLeading = ceilf(fontHeight.ascent)
2146 + ceilf(fontHeight.descent);
2147 float lineHeight = lineHeightNoLeading + ceilf(fontHeight.leading);
2149 // find largest label
2150 float labelWidth = 0;
2151 if (Label()) {
2152 labelWidth = StringWidth(Label());
2153 height += lineHeightNoLeading;
2155 if (MaxLimitLabel()) {
2156 labelWidth = max_c(labelWidth, StringWidth(MaxLimitLabel()));
2157 height += Label() ? lineHeight : lineHeightNoLeading;
2159 if (MinLimitLabel()) {
2160 labelWidth = max_c(labelWidth, StringWidth(MinLimitLabel()));
2161 height += lineHeightNoLeading;
2163 if (fMaxUpdateTextWidth > 0.0) {
2164 labelWidth = max_c(labelWidth, fMaxUpdateTextWidth);
2165 height += MinLimitLabel() ? lineHeight : lineHeightNoLeading;
2168 width = max_c(labelWidth, width);
2171 fMinSize.width = width;
2172 fMinSize.height = height;
2174 ResetLayoutInvalidation();
2176 return fMinSize;
2180 // #pragma mark - FBC padding
2182 void BSlider::_ReservedSlider6() {}
2183 void BSlider::_ReservedSlider7() {}
2184 void BSlider::_ReservedSlider8() {}
2185 void BSlider::_ReservedSlider9() {}
2186 void BSlider::_ReservedSlider10() {}
2187 void BSlider::_ReservedSlider11() {}
2188 void BSlider::_ReservedSlider12() {}
2191 BSlider &
2192 BSlider::operator=(const BSlider &)
2194 return *this;
2198 // #pragma mark - BeOS compatibility
2201 #if __GNUC__ < 3
2203 extern "C" void
2204 GetLimits__7BSliderPlT1(BSlider* slider, int32* minimum, int32* maximum)
2206 slider->GetLimits(minimum, maximum);
2210 extern "C" void
2211 _ReservedSlider4__7BSlider(BSlider *slider, int32 minimum, int32 maximum)
2213 slider->BSlider::SetLimits(minimum, maximum);
2216 extern "C" float
2217 _ReservedSlider5__7BSlider(BSlider *slider)
2219 return slider->BSlider::MaxUpdateTextWidth();
2223 extern "C" void
2224 _ReservedSlider1__7BSlider(BSlider* slider, orientation _orientation)
2226 slider->BSlider::SetOrientation(_orientation);
2230 extern "C" void
2231 _ReservedSlider2__7BSlider(BSlider* slider, float thickness)
2233 slider->BSlider::SetBarThickness(thickness);
2237 extern "C" void
2238 _ReservedSlider3__7BSlider(BSlider* slider, const BFont* font,
2239 uint32 properties)
2241 slider->BSlider::SetFont(font, properties);
2245 #endif // __GNUC__ < 3
2248 extern "C" void
2249 B_IF_GCC_2(InvalidateLayout__7BSliderb, _ZN7BSlider16InvalidateLayoutEb)(
2250 BView* view, bool descendants)
2252 perform_data_layout_invalidated data;
2253 data.descendants = descendants;
2255 view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);